提示词工程-02【Function Calling】
function calling的定义:是一种让模型调用外部函数的能力。模型不仅仅根据自身数据库知识进行问答,还可以额外挂载一个函数库,然后根据用户提问,模型返回调用指令,应用执行调用指令,模型再基于调用函数的结果进行回答。参考⽹址:https://json-schema.org/learn/getting-started-step-by-step。一次function calling中,大模型第
大模型为什么要和外部世界连接?
- 训练数据不涵盖所有信息,比如垂直、非公开的数据
- 模型数据无法实时更新,训练一次消耗大
- 大模型表现得逻辑、推理其实是训练文本的统计规律,结果拥有不确定性
因此需要对接真逻辑系统,来控制输出的不确定性和幻觉,达到预期效果。
OpenAI Function Calling:https://platform.openai.com/docs/guides/function-calling
function calling的定义:是一种让模型调用外部函数的能力。模型不仅仅根据自身数据库知识进行问答,还可以额外挂载一个函数库,然后根据用户提问,模型返回调用指令,应用执行调用指令,模型再基于调用函数的结果进行回答。
function calling的完整调用流程:

JSON Schema:用于描述JSON数据格式和结构的元数据标注。
参考⽹址:https://json-schema.org/learn/getting-started-step-by-step
function 调用示例(获取高德地图开放接口)
from openai import OpenAI
import os
from dotenv import load_dotenv
load_dotenv()
import requests
import json
from typing import Optional, Dict, Any, List #导入注释包
api_key = os.getenv("SPARKAI_API_KEY") # 替换自己的api key和url
api_base = os.getenv("SPARKAI_API_BASE")
client = OpenAI(api_key=api_key, base_url=api_base)
# 假设 amap_key 在模块级变量或配置中提供
amap_key = os.getenv("AMAP_KEY")#高德地图申请免费key
def get_location_coordinate(location: str, city: str = "北京", timeout: int = 5) -> Optional[Dict[str, Any]]:
"""
使用 高德的 Place Text 检索(text)接口,根据关键词和城市返回第一个 POI(若有)。
返回 POI dict(原始 API 返回的 pois[0]),或 None(无结果或出错)。
"""
url = "https://restapi.amap.com/v5/place/text"
params = {
"key": amap_key,
"keywords": location,
# 高德 v5 文档中可使用 region 或 city,通常 region 更通用(也可用 city)
"region": city,
"limit": 1, # 只要第一个结果
"output": "JSON"
}
try:
resp = requests.get(url, params=params, timeout=timeout)
resp.raise_for_status()
result = resp.json()
except Exception as e:
# 记录日志(此处简化为打印)
print(f"get_location_coordinate 请求出错: {e}")
return None
# 高德接口会返回一个结构,通常包含 count、pois 等
pois = result.get("pois")
if pois:
return pois[0]
return None
def format_pois_text(pois):
"""将 POI 列表拼接为文本"""
lines = []
for p in pois:
name = p.get("name", "未知名称")
address = p.get("address", "未知地址")
distance = p.get("distance", "")
if distance:
distance_str = f"距离:{distance}米"
else:
distance_str = ""
lines.append(f"{name}\n{address}\n{distance_str}\n")
return "\n".join(lines)
def search_nearby_pois(longitude: float, latitude: float, keyword: str,
radius: int = 2000, limit: int = 3, timeout: int = 5) -> str:
"""
周边检索并直接返回格式化好的文本(使用 format_pois_text)
"""
url = "https://restapi.amap.com/v5/place/around"
params = {
"key": amap_key,
"keywords": keyword,
"location": f"{longitude},{latitude}",
"radius": radius,
"limit": limit,
"sortrule": "distance"
}
try:
resp = requests.get(url, params=params, timeout=timeout)
resp.raise_for_status()
result = resp.json()
except Exception as e:
print("search_nearby_pois error:", e)
return "查询失败,请稍后再试。"
pois = result.get("pois", [])[:limit]
if not pois:
return "附近没有找到相关地点。"
return format_pois_text(pois)
#大模型调用方法
def generate_from_llm(messages,model="spark-x"):
try:
response = client.chat.completions.create(
model=model, # 模型可替换
messages=messages,
temperature=0,
tools=[{
"type": "function",
"function": {
"name": "get_location_coordinate",
"description": "根据地点名称和城市,获取该地点的经纬度坐标。",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "POI名称必须为中文"
},
"city": {
"type": "string",
"description": "POI所在城市的名称,必须为中文"
}
},
"required": ["location", "city"],
}
}
},
{
"type": "function",
"function": {
"name": "search_nearby_pois",
"description": "根据经纬度坐标和关键词,搜索附近的POI信息。",
"parameters": {
"type": "object",
"properties": {
"longitude": {
"type": "number",
"description": "经度"
},
"latitude": {
"type": "number",
"description": "纬度"
},
"keyword": {
"type": "string",
"description": "目标POI的搜索关键词"
}
},
"required": ["longitude", "latitude", "keyword"],
}
}
}
]
)
return response.choices[0].message
except Exception as e:
return f"-- 生成 SQL 时出错:{e}"
prompt = "上海陆家嘴附近的咖啡"
messages = [
{"role": "system", "content": "你是⼀个地图通,你可以找到任何地址。"},
{"role": "user", "content": prompt}
]
response = generate_from_llm(messages)
if (response.content is None): # 解决 OpenAI 的⼀个 400 bug
response.content = ""
messages.append(response) # 把⼤模型的回复加⼊到对话中,包括传入的参数和调用哪个函数
print("=====AI回复=====")
print(response)
# 如果返回的是函数调⽤结果,则打印出来
while (response.tool_calls is not None):
# 1106 版新模型⽀持⼀次返回多个函数调⽤请求
for tool_call in response.tool_calls:
args = json.loads(tool_call.function.arguments)
print(args)
if (tool_call.function.name == "get_location_coordinate"):
print("Call: get_location_coordinate")
result = get_location_coordinate(**args)
elif (tool_call.function.name == "search_nearby_pois"):
print("Call: search_nearby_pois")
result = search_nearby_pois(**args)
print("=====函数返回=====")
print(result)
messages.append({
"tool_call_id": tool_call.id, # ⽤于标识函数调⽤的 ID
"role": "tool",
"name": tool_call.function.name,
"content": str(result) # 数值result 必须转成字符串
})
response = generate_from_llm (messages)
if (response.content is None): # 解决 OpenAI 的⼀个 400 bug
response.content = ""
messages.append(response) # 把⼤模型的回复加⼊到对话中
print("=====最终回复=====")
print(response.content)
重点详解:
tools参数: ⽤ JSON 格式描述函数。可以定义多个,由⼤模型决定调⽤谁。
tools=[{
"type": "function",
"function": {
"name": "get_location_coordinate",
"description": "根据地点名称和城市,获取该地点的经纬度坐标。",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "POI名称必须为中文"
},
"city": {
"type": "string",
"description": "POI所在城市的名称,必须为中文"
}
},
"required": ["location", "city"],
}
}
},
{
"type": "function",
"function": {
"name": "search_nearby_pois",
"description": "根据经纬度坐标和关键词,搜索附近的POI信息。",
"parameters": {
"type": "object",
"properties": {
"longitude": {
"type": "number",
"description": "经度"
},
"latitude": {
"type": "number",
"description": "纬度"
},
"keyword": {
"type": "string",
"description": "目标POI的搜索关键词"
}
},
"required": ["longitude", "latitude", "keyword"],
}
}
}
]
JSON Schema自动生成工具:
def auto_functions(func_list: List[Callable]) -> List[dict]:
tools_description = []
for func in func_list:
# 获取函数的签名信息
sig = inspect.signature(func)
func_params = sig.parameters
# 函数的参数描述
parameters = {
'type': 'object',
'properties': {},
'required': []
}
for param_name, param in func_params.items():
# 添加参数描述和类型
parameters['properties'][param_name] = {
'description': param.annotation.__doc__ if param.annotation is not inspect._empty else "",
'type': str(param.annotation) if param.annotation != param.empty else 'Any'
}
# 如果参数有默认值,那么它不是必须的
if param.default != param.empty:
parameters['required'].append(param_name)
# 函数描述字典
func_dict = {
"type": "function",
"function": {
"name": func.__name__,
"description": func.__doc__.strip(),
"parameters": parameters
}
}
tools_description.append(func_dict)
return tools_description
一次function calling中,大模型第一次返回的结果如下,后续每次调用时一定要先把之前历史消息response和id加到messages中。
ChatCompletionMessage(
content='', #content内容为空,因为模型选择调用工具(function),就不再返回自然语言文本
refusal=None,
role='assistant', #模型生成的回复,不是用户消息
annotations=None,
audio=None,
function_call=None,
tool_calls=[ #模型请求你调用一个函数
ChatCompletionMessageFunctionToolCall(
id='Call_000dcbfb@dx19aeed4d0cdb81c3321',
function=Function(
arguments='{"city":"上海","location":"陆家嘴"}',
name='get_location_coordinate'
),
type='function'
)
],#模型调用函数的原因
reasoning_content='用户想找上海陆家嘴附近的咖啡,首先需要获取陆家嘴的经纬度坐标,因为search_nearby_pois工具需要经纬度和关键词。可用get_location_coordinate工具,城市是上海,地点是陆家嘴,这样就能得到陆家嘴的经纬度,之后才能用这个经纬度搜索附近的咖啡。所以先调用get_location_coordinate。'
)
聚合平台:juhe.cn
爬虫:spidertools.cn
魔搭社区:https://modelscope.cn/models
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)