Function Calling 实战指南:Tool Use 从原理到多工具编排,2026 完全手册

发布时间:2026/5/19 22:56:00

Function Calling 实战指南:Tool Use 从原理到多工具编排,2026 完全手册 工具调用是 Agent 的手——手不好用脑子再聪明也白搭2026 年所有主流模型的工具调用能力已经趋同——但怎么设计工具、怎么编排工具、怎么处理工具错误仍然是拉开 Agent 质量差距的核心因素。这篇文章不讲理论讲实战中真正影响 Agent 表现的五个关键决策。第一原则Schema 设计决定 80% 的工具调用准确率Agent 选工具的准确率本质上是 Schema 信息量的函数。模糊的 Schema 随机的调用。反面教材# 这个工具定义太模糊——Agent 不知道什么时候该用它 { name: search, description: 搜索信息, parameters: { type: object, properties: { query: {type: string, description: 搜索关键词} } } }正面教材def search_knowledge_base( query: str, category: Literal[技术文档, 产品手册, 运维指南, HR政策], max_results: int 5, min_relevance: float 0.7 ) - list[dict]: 搜索公司内部知识库。适用场景用户询问公司产品功能、技术架构、 运维流程、HR政策等需要查阅内部文档的问题。不适用实时股价、天气 等外部信息。 Args: query: 搜索关键词使用用户原话中的核心名词。例如用户说 年假怎么请query 应为 年假 申请 流程 category: 限定搜索范围。从用户问题推断问流程→运维指南 问福利→HR政策问功能→产品手册问架构→技术文档 max_results: 返回文档数量简单问题3-5条复杂问题5-10条 min_relevance: 最低相关性阈值。已知答案明确→0.6探索性问题→0.4 ...Schema 设计清单要素要求示例name动词名词语义明确search_knowledge_base而非searchdescription写清适用场景和不适用场景适用于内部文档查询不适用于实时数据参数名全称别缩写max_results而非n参数description包含格式、取值范围、使用建议格式 ORD-YYYY-XXXXX从订单确认邮件中提取enum所有可选值都列出来[技术文档,产品手册,运维指南,HR政策]第二原则并行调用——让 Agent 同时干多件事当一个用户问题需要多个工具时串行调用浪费 50% 以上的时间。import asyncio from openai import AsyncOpenAI client AsyncOpenAI() tools [ { type: function, function: { name: get_stock_price, description: 获取美股实时股价, parameters: { type: object, properties: { symbols: { type: array, items: {type: string}, description: 股票代码列表如 [AAPL, TSLA, MSFT] } }, required: [symbols] } } }, { type: function, function: { name: get_news, description: 获取公司最新新闻, parameters: { type: object, properties: { company: {type: string}, days: {type: integer, default: 3} }, required: [company] } } } ] async def agent_with_parallel_tools(user_query: str): response await client.chat.completions.create( modelgpt-5.1, messages[{role: user, content: user_query}], toolstools, parallel_tool_callsTrue # 启用并行调用 ) # 模型可能一次返回多个 tool_calls tool_calls response.choices[0].message.tool_calls # 并行执行所有工具 async def execute_tool(call): args json.loads(call.function.arguments) if call.function.name get_stock_price: return await fetch_prices(args[symbols]) elif call.function.name get_news: return await fetch_news(args[company], args.get(days, 3)) results await asyncio.gather(*[execute_tool(c) for c in tool_calls]) return results # 用户问分析一下苹果和特斯拉→ Agent 并行调用 # get_stock_price([AAPL, TSLA]) get_news(Apple) get_news(Tesla) # 3个调用同时发出总耗时 max(单个耗时) 而非 sum(所有耗时)三家模型的并行支持差异特性OpenAIClaudeDeepSeek原生并行✅parallel_tool_calls: true✅ 自动检测独立调用⚠️ 需手动实现并行上限无明确限制受上下文窗口约束建议 ≤5推荐做法启用即可工具定义里写清独立性自己写并发逻辑第三原则智能路由——不是所有问题都需要调工具一个最常见的错误无论用户问什么都先调search_knowledge_base。结果是今天天气怎么样也去搜内部文档。def smart_router(user_query: str) - Literal[direct_answer, use_tools]: 判断是否需要工具调用 # 快速分类闲聊 / 简单事实 / 需要工具 classification_prompt f判断以下问题类型只回复一个字母 C: 闲聊/打招呼你好、谢谢、再见 F: 简单事实/常识什么是Python、11等于几 T: 需要工具/数据查询订单、搜索文档、获取实时信息 问题: {user_query} 类型: resp client.chat.completions.create( modelgpt-5.1-mini, # 用小模型做路由省成本 messages[{role: user, content: classification_prompt}], max_tokens1 ) result resp.choices[0].message.content.strip() return use_tools if result T else direct_answer路由层次全栈架构 -L1 路由器闲聊/常识 → 直接回答零成本 -L2 路由器工具需求 → 选最相关的 5-10 个工具用 embedding 相似度 -L3 执行器并行/串行调度 错误重试 结果融合第四原则工具返回要结构化不是扔一段文本Agent 拿到工具返回后还要理解它。如果返回一段自然语言描述Agent 二次理解的错误率会增加。# 不好的返回自然语言描述 def bad_tool_response(): return 用户张三有3个待处理订单订单号ORD-2026-001金额500元状态待支付ORD-2026-002... # 好的返回结构化 JSON def good_tool_response(): return { status: success, summary: {total_orders: 3, pending: 2, shipped: 1}, orders: [ {id: ORD-2026-001, amount: 500.0, status: pending, date: 2026-05-18}, {id: ORD-2026-002, amount: 1200.0, status: shipped, date: 2026-05-17}, {id: ORD-2026-003, amount: 350.0, status: pending, date: 2026-05-19} ], error: None }结构化返回三要素 -statussuccess/partial/errorAgent 据此判断是否重试 -summary高层聚合信息Agent 用于快速判断是否需要深入查询 -data详细结构化数据Agent 用于精确回答用户问题第五原则错误处理——工具失败 ≠ Agent 失败工具调用可能在三个环节失败Schema 不匹配、工具超时、返回异常数据。Agent 需要对每种情况都有应对策略。import asyncio from typing import Optional async def resilient_tool_call( tool_name: str, arguments: dict, max_retries: int 2, timeout: float 10.0 ) - dict: 带重试、超时、降级的工具调用包装器 for attempt in range(max_retries 1): try: result await asyncio.wait_for( execute_tool(tool_name, arguments), timeouttimeout ) return {status: success, data: result} except asyncio.TimeoutError: if attempt max_retries: continue return { status: error, error_type: timeout, message: f工具{tool_name}超时({timeout}s)请缩小查询范围重试, suggestion: 尝试减少参数或拆分查询 } except ValueError as e: return { status: error, error_type: invalid_args, message: f参数格式错误: {e}, suggestion: 请检查参数格式后重试 } except Exception as e: return { status: error, error_type: unknown, message: f工具执行异常: {e}, fallback_data: get_cached_or_default(tool_name, arguments) }Agent 拿到 error 返回后的正确行为 -timeout→ Agent 说查询范围太大我缩小一下再试 -invalid_args→ Agent 检查参数格式修正后重试 -unknown→ Agent 用 fallback_data 继续说部分数据获取失败以下是已有信息生产环境工具数量管理工具从 5 个涨到 50 个时所有模型的选择准确率都下降。工具选择需要分层# 不要一次性把所有工具塞给模型 ALL_TOOLS {...} # 340个工具 # 用 embedding 相似度预筛选 from sklearn.metrics.pairwise import cosine_similarity def select_relevant_tools(query: str, tools: dict, top_k: int 8) - list: 基于 query embedding 筛选最相关的 top_k 个工具 query_emb embed(query) scores [] for name, tool in tools.items(): tool_text f{name}: {tool[description]} tool_emb embed(tool_text) score cosine_similarity([query_emb], [tool_emb])[0][0] scores.append((name, score)) scores.sort(keylambda x: x[1], reverseTrue) return [name for name, _ in scores[:top_k]] # 用户问帮我查订单状态→ 预筛出订单、物流、支付 3 个相关工具 # 而不是把所有 340 个工具都塞给 Agent小结Function Calling 的工程实践远比 API 调用复杂。五个原则总结Schema 是工具调用的说明书——写具体、写边界、写反例能并行的绝不串行——并行调用是免费的性能提升小模型路由 大模型执行——省 token 还更快工具返回结构化——让 Agent 的二次理解变成直接使用错误要有建议——告诉 Agent 怎么重试而不是只返回失败下一篇预告多工具编排进阶——工具依赖图、流水线优化、动态工具组合。

相关新闻