智能体面试准备(十八):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 从"能跑"推向"可运维"。评论区告诉我优先级。

相关推荐
cxr8283 小时前
第四章 查询与推理能力
人工智能·架构·知识图谱·智能体
key_3_feng6 小时前
企业快速开发自己的 Agent:工具注册、规划、执行、记忆
python·agent·智能体
熊猫钓鱼>_>1 天前
用Seed Evolving+高德地图做城市旅行规划Agent
大模型·agent·规划·智能体·豆包·火山·seed evolving
thesky1234561 天前
智能体面试准备(十六):智能体安全实战——提示注入、越狱、工具滥用与防御
防御·智能体·ai安全·面试准备·越狱·提示注入
安逸sgr2 天前
Zero-shot、Few-shot 和 One-shot Prompt 有什么区别?
人工智能·ai·大模型·agent·智能体
weixin_471383033 天前
03 Function Calling
llm·functioncalling
梦想画家3 天前
让 Agent 自己跑到终点线:Goal 模式智能体实战
智能体·langgraph·goal模式
ZGi.ai3 天前
ZGI Agent:企业怎样治理工具调用?
工具调用·aiagent·权限治理·zgi·agentruntime·humanintheloop
科研小刘带你玩学术4 天前
AI Agent正在改变人工智能应用模式:从工具助手走向自主智能系统
人工智能·大语言模型·未来趋势·智能体·智能系统