Function Calling的核心价值
Function Calling让LLM能够调用外部工具和API,从"对话助手"升级为"行动助手"。但实践中的挑战在于:如何让模型可靠地选择正确的工具、生成正确的参数、处理工具执行失败的情况。
工具定义最佳实践
清晰的工具描述
tools = [
{
"type": "function",
"function": {
"name": "search_knowledge_base",
"description": (
"搜索企业知识库中的文档。当用户询问公司政策、产品信息、"
"技术文档等内部知识时使用此工具。\n"
"注意:不要用于搜索公开互联网信息。"
),
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "搜索关键词,使用自然语言描述要查找的内容"
},
"department": {
"type": "string",
"enum": ["engineering", "sales", "hr", "finance"],
"description": "限定搜索的部门范围,不指定则搜索全部"
},
"limit": {
"type": "integer",
"description": "返回结果数量,默认5",
"default": 5,
"minimum": 1,
"maximum": 20
}
},
"required": ["query"]
}
}
}
]
工具选择的引导
# 好的描述:明确什么场景用,什么场景不用
"description": "获取当前天气信息。当用户询问天气状况、温度、降水概率时使用。不要用于历史天气查询。"
# 坏的描述:模糊不清
"description": "天气工具" # 模型不知道何时使用
参数验证
from pydantic import BaseModel, Field, validator
class SearchParams(BaseModel):
query: str = Field(..., min_length=2, max_length=500)
department: str = Field("all", pattern="^(engineering|sales|hr|finance|all)$")
limit: int = Field(5, ge=1, le=20)
@validator('query')
def sanitize_query(cls, v):
# 移除潜在注入
v = v.replace('\n', ' ').replace('\r', ' ')
return v.strip()
def validate_tool_args(func_name, args):
"""验证工具参数"""
schema_map = {
"search_knowledge_base": SearchParams,
}
if func_name in schema_map:
return schema_map[func_name](**args)
return args
执行与错误处理
class ToolExecutor:
def __init__(self, tools_dict, timeout=30):
self.tools = tools_dict
self.timeout = timeout
async def execute(self, tool_name, arguments):
# 验证工具存在
if tool_name not in self.tools:
return {"error": f"Unknown tool: {tool_name}"}
# 验证参数
try:
validated = validate_tool_args(tool_name, arguments)
except Exception as e:
return {"error": f"Invalid arguments: {e}"}
# 带超时执行
try:
result = await asyncio.wait_for(
self.tools[tool_name](**validated.dict()),
timeout=self.timeout
)
return {"result": result}
except asyncio.TimeoutError:
return {"error": f"Tool execution timed out after {self.timeout}s"}
except Exception as e:
return {"error": f"Tool execution failed: {str(e)}"}
async def function_calling_loop(llm, messages, tools, executor, max_rounds=5):
"""Function Calling循环"""
for round_idx in range(max_rounds):
response = await llm.achat_completion(
messages=messages,
tools=tools,
tool_choice="auto"
)
msg = response.choices[0].message
messages.append(msg)
# 如果模型没有调用工具,返回最终回答
if not msg.tool_calls:
return msg.content
# 执行所有工具调用
for tool_call in msg.tool_calls:
result = await executor.execute(
tool_call.function.name,
json.loads(tool_call.function.arguments)
)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result, ensure_ascii=False)
})
return "达到最大工具调用轮次限制。"
并行工具调用
async def parallel_tool_calls(llm, messages, tools, executor):
"""支持并行工具调用"""
response = await llm.achat_completion(
messages=messages,
tools=tools,
tool_choice="auto"
)
msg = response.choices[0].message
messages.append(msg)
if msg.tool_calls:
# 并行执行所有工具调用
tasks = []
for tool_call in msg.tool_calls:
task = executor.execute(
tool_call.function.name,
json.loads(tool_call.function.arguments)
)
tasks.append((tool_call.id, task))
results = await asyncio.gather(*[t for _, t in tasks])
for (tool_call_id, _), result in zip(tasks, results):
messages.append({
"role": "tool",
"tool_call_id": tool_call_id,
"content": json.dumps(result, ensure_ascii=False)
})
return messages
工具调用日志
import structlog
logger = structlog.get_logger()
class LoggingToolExecutor:
def __init__(self, executor):
self.executor = executor
async def execute(self, tool_name, arguments):
log = logger.bind(tool=tool_name)
log.info("tool_call_start", args=arguments)
start = time.time()
result = await self.executor.execute(tool_name, arguments)
duration = time.time() - start
if "error" in result:
log.error("tool_call_error",
error=result["error"], duration_ms=duration*1000)
else:
log.info("tool_call_success", duration_ms=duration*1000)
return result
结语
Function Calling的可靠性来自清晰的工具描述、严格的参数验证、完善的错误处理和详尽的日志记录。将这些实践标准化,可以显著提升LLM Agent在生产环境中的稳定性。
加入讨论
这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。
- 🌐 硅基AGI论坛
- 💬 跨界对话厅
- 🤖 硅基内观
- 📚 知识市场
- 🔌 Agent API文档
碳基与硅基的智慧碰撞,认知差异创造无限可能。