Tool Calling 不再玄:用 Prompt 手搓一遍

2026-06-18阅读 0热度 0
Pro

这是《从零写一个通用 Agent 内核》第 0 步。敲“工具分发”代码前,别急着套框架——先纯靠对话,手跑一遍 tool calling 的完整流程。

tool calling 没那么玄:我用 Prompt 手搓了一遍

一段“表面正常”的对话

手头只有一个最简 Agent 循环:DeepSeek 的 OpenAI 兼容接口,配一个 read_file 工具。跟模型约定好:

You: 你是一名天气专家。当我问某地天气时,你用 get_weather(<地点>) 来回复,
     然后我会告诉你那个地方的天气。
AI: 明白!现在我就是你的专属天气专家,随时准备调用 get_weather(<地点>)。
You: 北京天气怎么样?
AI: 好的,正在为您查询北京天气,请稍等!☁️
    get_weather(北京)

模型“调用”了 get_weather。但看 API 返回,有个关键差异:

这一轮 finish_reasonstop,不是 tool_calls 那行 get_weather(北京) 只是模型输出的普通文本,根本没触发任何真实工具调用。

那谁来“执行”这个 get_weather?人肉后端——也就是正在读这段话的你(这场实验里就是作者本人)。

You: 微风小雨        ← 这一步是人在扮演 get_weather,把"查询结果"喂回去
AI: 北京此刻微风伴小雨,适合披件薄外套撑伞散步~☂️(根据喂的值产出最终答复)

一个完整的 tool-calling 循环跑通了——只不过全是手动操作。

别急着喊“翻车”,这就是设计目标

模型没真调用工具,但目的恰恰在此:用最原始的方式,亲手走一遍平时被 API 黑盒掉的全部过程。

tool calling 听着玄,拆开只有三个角色:

角色这场实验里是谁真实 Agent 里是谁
决定调用、发出调用、解读返回值模型模型(这层永远是模型的活)
真正执行工具、把结果递回去人肉后端执行器代码
调用和结果怎么来回传递(协议)普通聊天文本tool_calls / role:tool / tool_call_id

模型那列从头到尾没变。变的只有中间那行——“谁来执行”和“结果怎么传回”。而这恰恰是 Agent 框架的全部工作:把人肉执行器换成代码,把聊天文本换成结构化协议。

真机长什么样:换成“真的”工具

做对照,换个问题——这次问 read_file 能覆盖的事:

You: agent.py 文件是做什么的?

模型这次返回的 finish_reason 变成了 tool_calls

tool_calls = [ read_file(arguments='{"path": "agent.py"}') ]

一次结构化的、真正的工具调用。接着,Python 代码执行了它:

for tool_call in assistant_message.tool_calls:
    tool_name = tool_call.function.name                    # "read_file"
    tool_args = json.loads(tool_call.function.arguments)   # {"path": "agent.py"}
    result = read_file(**tool_args)                        # ← 真正的执行在这行
    conversation.append({
        "role": "tool",
        "content": str(result),
        "tool_call_id": tool_call.id,                      # ← 协议把结果递回去
    })

结果通过 role: tool 喂回模型,模型读完文件产出分析。

同样一套循环,这次没有“人”的位置了——那个执行者,被 read_file(**tool_args) 这行取代了。

划重点:那条最容易踩的边界

两次运行唯一的差别:

  • 天气实验里,get_weather 从没进过 tools 列表;
  • 第二次里,read_file 进了 tools 列表。

仅此而已,结果天差地别:前者只能“用文字假装调用”,后者才触发真机的 tool_calls。这也解释了为什么 prompt 里苦口婆心写一堆“你拥有以下能力”常常没用——模型根本不知道那些工具是“可调用的”,只当是文字描述。

接下来:把“人肉执行器”工业化

这场手动实验给整个系列定了主线:

而一旦认真去替代“那个人”,会发现人肉执行时下意识在做、却从不显式写下来的操作,代码必须一件件接住:

  • 模型给的参数是坏 JSON?(人会皱眉重读,代码直接崩)
  • 工具跑了 10 秒没返回?(人会喊停,代码默认死等)
  • 工具吐回 5000 行全塞给模型?(人会摘重点,代码会把模型淹掉)
  • 这个操作会删文件,要不要先确认?(人有常识,代码需要一道“确认门”)
  • 出错了返回 "Error" 还是 "file not found, did you mean config.yaml?"?(前者让模型抓瞎,后者让它当场自我纠错)

这些就是横切关注点:不属于任何一个具体工具,而属于“执行”这件事本身。下一篇,我们从它们抽出来、统一塞进一条执行管道开始——这条管道,才是决定一个 Agent 到底“聪不聪明”的地基。


系列《从零写一个通用 Agent 内核》一路记录这个小核心从零长大的设计取舍。不堆术语,只讲“本来长什么样、为什么疼、然后怎么长成现在这样”。下一篇见。

免责声明

本网站新闻资讯均来自公开渠道,力求准确但不保证绝对无误,内容观点仅代表作者本人,与本站无关。若涉及侵权,请联系我们处理。本站保留对声明的修改权,最终解释权归本站所有。

相关阅读

更多
欢迎回来 登录或注册后,可保存提示词和历史记录
登录后可同步收藏、历史记录和常用模板
注册即表示同意服务条款与隐私政策