Function Calling与工具编排#
一句话答案#
Function Calling 是 LLM 调用外部工具的核心机制,通过输出结构化 JSON 指定工具名和参数,配合多步依赖管理和参数校验实现复杂任务编排。
核心要点
1. Function Calling 工作流程#
用户提问 → LLM 推理
├── 直接回答 → 返回用户
└── 需要工具 → 输出 tool_call JSON(工具名+参数)
→ Runtime 校验参数 → 执行函数 → 结果回传 LLM
→ LLM 继续推理(可能再次调用工具)plaintext2. Tool Schema 设计原则#
| 原则 | 说明 | 反例 |
|---|---|---|
| 描述精确 | 工具用途一句话说清 | ”处理数据”(太模糊) |
| 参数约束 | enum/required/类型严格限定 | 无约束的 string |
| 最小权限 | 只暴露必要参数 | 暴露 SQL 直接执行 |
| 幂等设计 | 相同参数多次调用结果一致 | 每次调用都创建新记录 |
3. 多步工具依赖管理#
- 顺序依赖:查用户→查订单→查物流(前者输出是后者输入)
- 并行调用:独立工具并行执行(查天气 + 查日历)
- 拓扑排序:复杂依赖图用 DAG 拓扑排序确定执行顺序
4. 工具幻觉与防御#
LLM 可能调用不存在的工具或编造参数。
防御手段:
- Schema 白名单校验:工具名必须在已注册列表中
- 参数类型检查:严格按 JSON Schema 校验
- 枚举值约束:限定参数取值范围
5. 错误处理链路#
工具调用
├── 参数校验失败 → 提示 LLM 修正参数重试
├── 执行超时(>5s)→ 重试1次(指数退避)
├── 执行异常 → 降级到备选工具
└── 无可用工具 → LLM 直接回答或提示用户plaintext面试回答(2分钟版)
Function Calling是LLM调用外部工具的核心机制。工作流程是这样的:用户提问后LLM推理判断是否需要工具,如果需要就输出一段结构化的JSON,包含工具名和参数,由Runtime执行后把结果回传给LLM继续推理。这个过程可能循环多次直到生成最终答案。工具Schema设计是关键,要遵循几个原则:描述要精确不能模糊,参数要有类型和枚举约束防止LLM编造值,遵循最小权限原则只暴露必要参数,关键操作要设计为幂等的。多步工具编排分两种:有依赖关系的串行执行比如先查用户再查订单,独立的工具可以并行调用提高效率。工具幻觉是常见问题,LLM可能调用不存在的工具或编造参数,需要通过Schema白名单和参数校验来防御。错误处理要有降级链路:参数错误提示LLM修正重试,执行超时用指数退避重试一次,执行失败降级到备选工具,实在不行让LLM直接回答。高风险工具比如删除数据或发邮件要加人工审批环节。
追问与易错
追问方向:
- Schema 设计太粗太细怎么权衡?工具描述多长合适? → 描述 1-2 句说清用途和约束,参数用 enum/required 强约束。过粗导致工具幻觉(LLM 编造参数),过细浪费 token。经验值:每个工具描述 50-100 token
- 多步工具依赖怎么避免死循环?最大深度怎么设? → 检测连续相同工具+相同参数的调用立即终止,设 max_steps(5-15)。用 DAG 拓扑排序管理依赖,有环则报错
- 高风险工具(删数据、发邮件)怎么加人工审批环节? → 工具分三级权限:read-only 自动执行、write 需用户确认、admin 需管理员审批。LangGraph 的 Human-in-the-loop 断点天然支持
- 并行工具调用的结果怎么合并回 Context? → 每个工具结果带工具名和调用 ID 标记,按调用顺序拼接回 Context。独立工具并行执行后统一追加,有依赖的串行等待
易错点:
- ❌ “让 LLM 自己决定所有参数” → 必须做强类型校验,LLM 会编造不存在的枚举值
- ❌ “工具描述越详细越好” → 过长的描述占用 token,增加成本且可能误导模型
- ❌ “工具调用失败就报错” → 应该有降级链路:重试→备选工具→纯 LLM 回答
项目实践(DocMind): 5 个工具采用双路径架构:同一个 Tool Bean 同时服务内部 Agent(Spring AI Function Calling)和外部客户端(MCP Server HTTP)。工具结果通过 AgentToolContext(ThreadLocal 侧信道)绕过 LLM 直接回传 Agent,解决了 Spring AI 回调签名固定无法额外传参的问题。工具选择不走 LLM Function Calling(~300ms),改用 RetrievalPlanner 规则引擎(<1ms),4 个布尔信号映射到工具子集,100% 可测且 PathDecision.reason 全程可追溯。