智能体面试准备(十八):Function Calling 全链路——Agent 真正动手的最后一公里

智能体面试准备(十八):Function Calling 全链路------Agent 真正"动手"的最后一公里

前面讲了单 Agent(ReAct)、多 Agent(B15)、安全(B16)、规划(B17)。但所有这些要"落地",都绕不开一个最基础也最关键的能力:Function Calling(函数调用)------让 LLM 决定"调哪个工具、传什么参数"。这一篇把全链路从定义到生产跑通:工具 schema 设计 → 模型决策 → 参数解析 → 执行 → 结果回填 → 并行调用与错误处理。每节给:原理 → 代码 → 面试速答 + 高频追问。配合 B14(MCP)看,刚好从"单进程工具"走到"跨进程协议"。


一、Function Calling 在 Agent 链路里的位置

它是 LLM 与外部世界之间的"接口层":

复制代码
   用户意图
      │
      ▼
   ┌─────────┐   ① 选工具+填参    ③ 回填结果
   │   LLM   │◀────────────────┐
   └────┬────┘                 │
        │ ② tool_call(JSON)    │
        ▼                     │
   ┌─────────┐                │
   │ 执行器   │──▶ 真实工具/API │
   └─────────┘                │
        │ ④ observation       │
        └─────────────────────┘

没它,LLM 只能"说";有它,LLM 才能"做"。


二、工具 Schema:怎么告诉模型有哪些工具

用 JSON Schema 描述每个工具的元信息,喂给模型:

复制代码
{
  "name": "get_weather",
  "description": "查询指定城市当前天气",
  "parameters": {
    "type": "object",
    "properties": {
      "city": {"type": "string", "description": "城市名,如 '北京'"},
      "unit": {"type": "string", "enum": ["celsius","fahrenheit"], "default": "celsius"}
    },
    "required": ["city"]
  }
}

关键原则:description 写清楚"什么时候用、参数怎么填",模型靠它选工具。描述含糊会导致选错工具或漏填参数。


三、模型决策与参数解析

主流模型(GPT-4o、Qwen、GLM 等)原生支持 function calling,返回结构化的 tool_calls:

复制代码
import openai, json
tools = [get_weather_schema, search_schema]   # 上面的 JSON Schema 列表

resp = openai.chat.completions.create(
    model="gpt-4o",
    messages=[{"role":"user","content":"北京今天天气咋样?"}],
    tools=tools,
    tool_choice="auto",     # auto=模型自选;也可强制指定
)
msg = resp.choices[0].message
if msg.tool_calls:
    for call in msg.tool_calls:
        name = call.function.name
        args = json.loads(call.function.arguments)   # 模型填的参数
        print(name, args)        # get_weather {'city':'北京'}

面试速答:tool_choice="auto" 和强制指定有什么区别?auto 让模型自己判断是否调用、调哪个;强制(如 {"type":"function","function":{"name":"x"}})则模型必须调用指定工具,常用于固定流程。


四、执行器:调用真实工具并回填

把模型给的调用转成真实执行,再把结果作为新消息回传给模型:

复制代码
def dispatch(name, args):
    if name == "get_weather":   return real_weather_api(args["city"])
    if name == "search":        return real_search(args["query"])
    raise ValueError(f"unknown tool {name}")

# 把 assistant 的 tool_call 和 tool 结果都加入上下文
messages = [{"role":"user","content":"北京今天天气咋样?"}]
resp = openai.chat.completions.create(model="gpt-4o", messages=messages, tools=tools)
msg = resp.choices[0].message
messages.append(msg)                      # 必须保留带 tool_calls 的 assistant 消息
for call in msg.tool_calls:
    result = dispatch(call.function.name, json.loads(call.function.arguments))
    messages.append({"role":"tool", "tool_call_id": call.id, "content": str(result)})
final = openai.chat.completions.create(model="gpt-4o", messages=messages, tools=tools)
print(final.choices[0].message.content)  # 自然语言回答

注意:tool 消息必须带 tool_call_id 且对应 assistant 的 call,否则 API 报错。


五、并行调用与错误处理(生产必备)

5.1 并行调用

模型可以在一次回复里返回多个 tool_call(如"查北京和上海天气"):

复制代码
calls = msg.tool_calls or []
results = [dispatch(c.function.name, json.loads(c.function.arguments)) for c in calls]
# 用线程池并发执行真实工具,缩短延迟
from concurrent.futures import ThreadPoolExecutor
with ThreadPoolExecutor() as ex:
    results = list(ex.map(lambda c: dispatch(c.function.name,
                    json.loads(c.function.arguments)), calls))

5.2 错误处理与兜底

复制代码
def safe_dispatch(call):
    try:
        return dispatch(call.function.name, json.loads(call.function.arguments))
    except Exception as e:
        # 把错误作为 observation 回传,让模型自我修正(如换参数/换工具)
        return f"ERROR: {type(e).__name__}: {e}。请修正参数或换工具重试。"

把错误回传而非中断,模型往往能纠错(例如把城市名拼写改对)。


六、常见坑与最佳实践

现象 对策
参数 JSON 不合法 arguments 不是合法 JSON 用 json.loads 容错 + 让模型重生成
工具描述含糊 选错工具/漏必填 写清 description + required
上下文丢 tool_call_id API 报不一致 严格保留 assistant+tool 消息对
工具执行慢/超时 整轮卡住 超时熔断 + 并行 + 降级
工具返回过大 撑爆上下文 截断/摘要后再回填

面试速答:为什么要把工具结果"回填"给模型而不是只执行?因为 LLM 是无状态的,它需要根据工具真实返回继续推理(例如"天气是雨,建议带伞"),不回填模型就不知道结果、无法生成最终回答。


七、面试速答 + 高频追问清单(汇总)

速答 TOP 8:

  1. Function Calling 是 LLM 调外部工具的接口层,含 schema→决策→解析→执行→回填。

  2. 工具用 JSON Schema 描述,description 决定模型会不会选对。

  3. 模型返回 tool_calls(name + arguments JSON)。

  4. 执行后必须以 role=tool 且带 tool_call_id 回填上下文。

  5. 并行调用:一次返回多个 call,用线程池并发。

  6. 错误应回传 observation 让模型自我修正,而非直接中断。

  7. tool_choice=auto 让模型自选,也可强制指定。

  8. 坑:JSON 不合法、描述含糊、丢 id、返回过大。

追问清单:

  • 模型有时不按 schema 填参、编造字段,怎么约束?

  • 几十上百个工具时,全塞进 context 会爆,怎么裁剪(tool retrieval)?

  • Function Calling 与 MCP(B14)的关系?MCP 解决了什么?

  • 流式(streaming)场景下 tool_call 怎么逐步拼装?

  • 如何防止模型陷入"反复调用同一工具"的死循环?


八、下一篇预告

Function Calling 讲完"动手的最后一公里",Agent 基础能力(感知-规划-行动-工具-安全)基本闭环。后续可进入 B19 RAG 评估B20 可观测性( tracing/logging/评测),把 Agent 从"能跑"推向"可运维"。评论区告诉我优先级。

相关推荐
ZGi.ai17 小时前
审批一直没人处理,Workflow 怎么做超时升级?
工作流·智能体·审批流程·任务自动化·zgi·超时升级
东方-教育技术博主18 小时前
自进化编码智能体04——记忆治理是如何体现的,我如何人工介入进行记忆治理
智能体
墨心@20 小时前
《AI Agent 入门》
agent·智能体·datawhale共学
美林数据Tempodata1 天前
智能体技术应用专业(510219)怎么建:先定岗位能力链,再定课程与实训
大数据·人工智能·智能体·产教融合·职业教育
Xuantong_901 天前
WAIC2026 深度观察:玄同科技 × 润建股份,共建中国东盟 AI 跨境 Token 产业生态,开启数字出海新范式
人工智能·ai·智能体
SpiderCodeJ2 天前
【UE5】- UE MCP :在UE5.8编辑器中内置链接Codex
ue5·codex·智能体·mcp
ZGi.ai3 天前
不同请求走不同流程,ZGI Workflow 怎么配?
工作流·智能体·变量管理·zgi·条件分支
澳鹏Appen3 天前
AppenTalk | 当AI推理不再只是模型的事:Dan Roth谈智能体的真正边界
人工智能·大语言模型·智能体·大模型推理
像风一样自由20203 天前
29.Redis在大模型应用中有哪些用途缓存会话与限流
数据库·人工智能·redis·缓存·大模型·milvus·智能体
星核0penstarry4 天前
ToolGrad:把数据生成倒过来,工具调用样本通过率提到 99.8%
人工智能·测试工具·llm·函数调用·数据合成·文本调用