Featured image of post 第一话|大模型只会补全文字,Agent 的“手脚”是怎么来的?

第一话|大模型只会补全文字,Agent 的“手脚”是怎么来的?

大模型本质上只会做文本补全。人们用提示词技巧让它输出格式化内容,外部程序再解析执行,看起来就像模型长出了手脚。

系列 GYA(Get Your Agent) 第 1 / 12 篇
主题 LLMAgentFunction-Calling

几年前,我还在用 ChatGPT 查技术资料、翻译文档、写文案前先理个大纲。那时候它是“聊天窗口”:我打字,它回文字,聊完就关掉。

后来出现的 Agent 工具,真的能帮我干活了——写代码、执行命令行、调用 API 查数据。

从“聊”到“干”,这几年发生了什么?

要回答这个问题,得先看清一件被很多人忽略的事:大模型从头到尾,只会做一件事。

模型只会补全文字

不管外面把它包装成聊天助手还是 Agent,模型干的事情始终只有一件:给你一段上文,它预测下一个最可能出现的词,然后把这个词接上去,再预测下一个,一直循环,直到吐出一段完整的文字。

这个过程有个名字,叫“自回归生成”。它的输入是文字,输出还是文字。

所以模型天生有几个边界,跟它聪不聪明没关系,是它的结构决定的:

  • 没有手,不会真的去点一个按钮、执行一行代码;
  • 没有网络,不会自己发一个 HTTP 请求去查天气;
  • 没有文件系统,不会自己创建一个文件、改一行代码。

它唯一能做的,是根据上文,猜下一段文字该怎么写。

理解了这一点,再回头看那个问题就清楚了一半:一个只会补全文字的模型,是怎么“干起活”来的?

让它“说”出要干什么

答案藏在一个很朴素的技巧里。

模型只会写字,那我们就让它把“想干什么”用固定格式写出来

比如,我们在提示词里跟模型约定:

如果你想查天气,就严格输出下面这个格式,别的什么都别写:

{"action": "get_weather", "city": "北京"}

然后用户问“北京今天天气怎么样”。模型还是只会补全文字,只不过这一次,它补全出来的不是一段聊天,而是一行长得很像代码的 JSON:

{"action": "get_weather", "city": "北京"}

到这里,模型仍然什么都没做。它只是“说出”了:我想调 get_weather,参数是北京。

真正动手的是模型外面的程序。它拿到这行 JSON,解析出 actionget_weathercity 是北京,然后自己去调用真正的查天气函数

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。


参考资料:

留言

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