上篇博客咱们把 Agent 的“核心闭环”给扒光了,现在你已经知道它本质上是个 while(想 -> 做 -> 看) 的死循环。

但这里面藏着一个极其关键的问题:大模型怎么“做”?

你直接问普通的大模型:“帮我查一下今天公司的销售额”,它大概率会一本正经地胡说八道,或者委屈巴巴地告诉你:“我只是个语言模型,无法访问实时数据。” 这就是典型的大模型困境——缸中之脑。它有超越绝大多数人的知识储备和推理能力,但被死死锁在一个没有网线、没有键盘的黑盒子里。

今天咱们要聊的 工具调用(Tool Use / Function Calling),就是给这个“缸中之脑”接上网线,装上机械臂。它是 Agent 打破知识与行动壁垒的唯一通道。

一、 核心误区:大模型其实“什么都没执行”

一提到“工具调用”,很多刚接触 Agent 开发的同学会产生一个致命的幻觉:以为是大模型顺着网线爬过去,悄悄执行了你的数据库查询。

大错特错。大模型永远只输出文本,它不执行任何物理动作。

你可以把大模型想象成一个高智商、但全身瘫痪的**“老板”,而你的业务代码(宿主程序)是四肢健全的“实习生”**。

工具调用的真实交互逻辑是这样的:

  1. 实习生(你的代码): 老板,我这有三个工具:发邮件查天气搜数据库。这是它们的使用说明书(Tool Schema)。

  2. 老板(LLM): 看了看用户的需求,又看了看说明书。然后递给实习生一张**“工单(JSON格式)”**,上面写着:“去调 查天气 工具,参数填 杭州”。

  3. 实习生(你的代码): 拿到工单,转头去调用真正的天气 API,拿到“晴,25度”的结果。

  4. 实习生(你的代码): 回头把结果报告给老板。老板再根据这个结果,组织一段好听的话回复给用户。

决策与执行的绝对分离,是理解工具调用最核心的前提。模型负责“决定做什么(What)”,你的代码负责“怎么做(How)”。

二、 灵魂伪代码:签一份“API 契约”

为了让老板和实习生能无缝对接,我们需要定义一份标准的契约。在代码里,这就是 tools 数组。

咱们用一段极简的伪代码,来看看这个“装上手脚”的过程到底是怎么跑通的:

Python

# 1. 给老板准备的“工具说明书” (JSON Schema 格式)
# 这一步极其关键,模型完全靠这份说明书来理解工具
tools = [
    {
        "name": "query_order_status",
        "description": "根据订单号查询电商订单的实时物流状态",
        "parameters": {
            "type": "object",
            "properties": {
                "order_id": {"type": "string", "description": "12位的纯数字订单号"}
            },
            "required": ["order_id"]
        }
    }
]

# 2. 用户发起请求,带上工具集
user_input = "帮我看看单号 123456789012 到哪了?"
response = llm.chat(prompt=user_input, available_tools=tools)

# 3. 核心分发逻辑(你的宿主代码接管!)
if response.has_tool_call():
    # 老板下发了工单!
    tool_name = response.tool_call.name          # 拿到工具名: "query_order_status"
    tool_args = response.tool_call.arguments     # 拿到参数: {"order_id": "123456789012"}
    
    print(f"准备调用本地方法: {tool_name},参数: {tool_args}")
    
    # 4. 本地代码真正执行查询(可能是调内部 RPC,查 MySQL 等)
    # 这一步大模型根本不参与!
    real_data = my_backend_service.query_order(tool_args["order_id"]) 
    
    # 5. 把拿到的真实数据塞回去,让老板做最终总结
    final_answer = llm.chat(prompt="这是工具返回的结果,请回答用户", history=real_data)
    print(final_answer)

看到没有?所谓的 Function Calling,本质上就是强制要求 LLM 按照你预设的 JSON 格式输出决策

三、 带着泥土气息的工程体感:写 Tool 的三个巨坑

官方文档里的 Demo 总是跑得很完美,但在真实业务里,把 Tool 玩好是一门玄学。以下三个坑,几乎每个搞 Agent 架构的人都踩过:

坑一:工具描述(Description)就是你的命

很多人写 description 特别敷衍,比如 "description": "搜索数据"。 这就好比你给老板的说明书上写“能找东西”。当用户问“今天天气怎么样”时,模型可能也会跑去调你的数据库搜索工具。 工程解法:description 当成高精度的 Prompt 来写。必须包含:能做什么、不能做什么、在什么业务场景下使用。 比如:"用于查询 2024 年以后的内部员工财务报销记录,不支持查询业务销售数据。" 描述越精准,模型抓取工具的准确率越高。

坑二:参数幻觉(幻读)

你定义了一个参数 date,要求是 YYYY-MM-DD 格式。结果大模型一上头,给你传了个 "明天" 或者 "next Friday"。你的代码一执行,直接报 Type Mismatch 崩溃。 更可怕的是“参数瞎编”。用户根本没提供订单号,模型为了“完成任务”,强行捏造了一个 order_id="000000" 传给你。 工程解法: 1. 在 parameters 的 description 里写死格式规约,例如:"必须严格符合 YYYY-MM-DD 格式,不要返回相对时间"。 2. 宿主代码里必须做强校验。不要信任模型传过来的任何参数,把它当成外部不可信用户的输入一样,加上 try-catch 和正则校验。

坑三:上下文撑爆(The "Too Much Info" Bomb)

你写了个 fetch_webpage 的工具,让大模型去抓取一篇新闻。结果你的代码实诚地把整个 HTML 源码(几万个 Token,包含无数的 CSS 和 JS 标签)当成 observation 扔回给了大模型。 只听“砰”的一声,上下文窗口撑爆了,或者模型因为长文本注意力涣散,彻底忘了最初要干嘛。 工程解法: 给工具套一层“滤网”。你的宿主代码在拿到结果后,先用 BeautifulSoup 剔除 HTML 标签,或者干脆起一个小模型做个 500 字的 Summary,然后再把“脱水”后的精简信息喂给大模型。

四、 进阶视界:万物皆可 Tool

理解了上面这些,你的思路就可以彻底打开了。

只要你能用代码封装成函数的逻辑,全都可以变成 Agent 的手脚:

  • 查数据库? 封装一个 execute_sql()

  • 物理世界交互? 封装一个 turn_on_smart_light() 去调物联网 API。

  • 执行代码? 封装一个 python_interpreter(),让它自己写代码解决数学题(这也是数据分析 Agent 的底层逻辑)。

当“大模型的常识推理”遇上“无限扩展的外部 API”,我们实际上在构建一种全新的软件交互范式(Software 2.0)。你不再需要为每一个功能画 UI 按钮,大模型就是最强大的中央路由,它会自主思考,帮你调动整个数字世界的资源。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐