几年前,我还在用 ChatGPT 查技术资料、翻译文档、写文案前先理个大纲。那时候它是“聊天窗口”:我打字,它回文字,聊完就关掉。
后来出现的 Agent 工具,真的能帮我干活了——写代码、执行命令行、调用 API 查数据。
从“聊”到“干”,这几年发生了什么?
要回答这个问题,得先看清一件被很多人忽略的事:大模型从头到尾,只会做一件事。
模型只会补全文字
不管外面把它包装成聊天助手还是 Agent,模型干的事情始终只有一件:给你一段上文,它预测下一个最可能出现的词,然后把这个词接上去,再预测下一个,一直循环,直到吐出一段完整的文字。
这个过程有个名字,叫“自回归生成”。它的输入是文字,输出还是文字。
所以模型天生有几个边界,跟它聪不聪明没关系,是它的结构决定的:
- 它没有手,不会真的去点一个按钮、执行一行代码;
- 它没有网络,不会自己发一个 HTTP 请求去查天气;
- 它没有文件系统,不会自己创建一个文件、改一行代码。
它唯一能做的,是根据上文,猜下一段文字该怎么写。
理解了这一点,再回头看那个问题就清楚了一半:一个只会补全文字的模型,是怎么“干起活”来的?
让它“说”出要干什么
答案藏在一个很朴素的技巧里。
模型只会写字,那我们就让它把“想干什么”用固定格式写出来。
比如,我们在提示词里跟模型约定:
如果你想查天气,就严格输出下面这个格式,别的什么都别写:
{"action": "get_weather", "city": "北京"}
然后用户问“北京今天天气怎么样”。模型还是只会补全文字,只不过这一次,它补全出来的不是一段聊天,而是一行长得很像代码的 JSON:
{"action": "get_weather", "city": "北京"}
到这里,模型仍然什么都没做。它只是“说出”了:我想调 get_weather,参数是北京。
真正动手的是模型外面的程序。它拿到这行 JSON,解析出 action 是 get_weather、city 是北京,然后自己去调用真正的查天气函数:
import json, re
# 模型输出的那行文字
model_output = '{"action": "get_weather", "city": "北京"}'
# 程序解析它
data = json.loads(model_output)
if data["action"] == "get_weather":
result = get_weather(data["city"]) # 真正查天气的是这行代码
拿到天气结果后,程序再把这个结果塞回给模型,模型补全成一句人话:“北京今天多云,25 度”。
看明白了吗?整条链路里,模型从头到尾没执行过任何东西。它只是把“想干什么”用固定格式说了出来,动手的是外面那段解析加执行的代码。
看起来模型“长出了手脚”,其实是外面套了一双手。
这套做法,其实是 Function Calling 的前身
上面那套“提示词逼出格式、代码解析执行”的做法,不是谁凭空想出来的玩具。在原生 Function Calling 出现之前,它是开发者让模型调用工具的标准办法——先用提示词约定好输出格式,再用正则表达式或者 JSON 解析器去抠出工具名和参数。
它好用,但也很脆。模型会输出残缺的 JSON、在 JSON 外面多写一句“好的,我帮你查”、或者干脆忘掉约定格式。这些问题,逼着人们后来把这件事从“提示词技巧”升级成了“协议”——也就是第二话要讲的 Function Calling。
但不管怎么升级,底层逻辑从没变过:
模型只负责用固定的格式,把“要干什么”说清楚;真正的执行,永远发生在模型外面。
想通这一点,整个 Agent 世界就有一把钥匙了。你之后会看到的所有花活——多步循环、记忆、MCP、Skill——都在做同一件事的不同部分:让模型把意图说清楚,让外面那套代码把事做成,再把结果喂回去。
而外面这套代码,我们给它起了个名字,叫 Runtime。它才是 Agent 真正“长”出来的那只手。
下一篇,我们就从这套做法的标准化形态开始:模型怎么用一套协议,规规矩矩地提出一次工具调用——Function Calling。
参考资料:
- OpenAI, Function Calling
- Yao et al., ReAct: Synergizing Reasoning and Acting in Language Models

留言
欢迎分享你的想法。评论提交后会在审核通过后显示。