从一次模型调用到一个 Agent

LLM、Prompt、Context、RAG、Tool、Workflow、Loop、ReAct、Memory 和 Agent 经常出现在同一段介绍里,却不处于同一层:模型负责生成,上下文与 RAG 补充材料,Tool 连接外部能力,Loop 和 ReAct 组织决策,Agent 把它们变成一套运行系统。

接入一个模型 API,应用可以对话;再接一个搜索接口,介绍里开始出现 RAG;让模型调用几个工具,Agent 也来了;外面再套一层 while,有时连 ReAct 都一起写上。

名词涨得比代码快。

麻烦在于,这些词并不处于同一个层面。LLM 是模型,Prompt 和 Context 是输入,RAG 和 Tool 给模型补充外部能力,Workflow、Loop 和 ReAct 负责组织执行过程,Agent 才是把它们装起来运行的系统。MCP 和 Pi 这类协议或框架,又在更外面一层。

判断一个大模型应用,比较有用的办法不是先看它叫什么,而是看四件事:模型每轮能看到什么,下一步由谁决定,外部操作由谁执行,任务最后怎样停下。

从这四个问题出发,概念之间的关系会清楚许多。

LLM、上下文、外部能力、编排、状态与 Agent 边界组成的分层概念图

English version: From One Model Call to an Agent

LLM 只是模型,不是整个应用

大语言模型(Large Language Model,LLM)是大模型应用里的生成与决策部件。

从训练原理看,它根据已有 token 预测后续 token;从应用开发的角度看,它接收一组消息,再生成文本或结构化输出。应用调用模型完成生成的过程,通常叫推理(Inference)。

最小的调用可以只有一句要求:

请用三句话总结下面这段内容。

模型收到要求和待总结的文字,返回结果,调用结束。这就是一次普通的 LLM 调用。模型不需要工具,不需要循环,也不需要 Agent。

模型本身和完整应用不是一回事。页面、用户身份、数据库、权限、历史消息、重试和计费都由外围系统负责。即使聊天界面看起来记得前文,也是应用或模型服务保存了对话状态,并在后续推理中提供相关内容。

从模型本身看,当前输出取决于当前可用的上下文。模型参数不会因为昨天回答过某个用户,今天就自动多出一段针对这个用户的记忆。

Prompt 决定任务,Context 决定模型看见什么

Prompt 和 Context 经常被混在一起,但两者范围不同。

Prompt 主要描述任务:模型扮演什么角色,要完成什么目标,遵守哪些规则,按什么格式回答。System Prompt、用户问题和输出格式约束都属于这一层。

不同 API 对 Prompt 一词的用法并不统一,有时它也泛指整包模型输入。为了把职责说清楚,这里采用较窄的定义:Prompt 表达任务,Context 包含模型本轮能看到的一切。

Context 的范围更大。只要某段内容进入本轮模型输入,它就是上下文的一部分,例如:

  • 系统指令和用户问题
  • 需要处理的原始材料
  • 对话历史
  • 检索回来的文档
  • 可用工具的说明
  • 前几轮工具返回的结果

上下文窗口(Context Window)规定一次调用最多能容纳多少 token。Token 是模型处理文本的基本单位,可能是一个汉字、半个英文单词或一个标点,不能直接和字符数画等号。

Prompt 工程更关心指令怎样表达。上下文工程(Context Engineering)还要决定材料从哪里来、什么时候加入、保留多久,以及超过预算时先删什么。

可以把 LLM 想成坐在桌前处理材料的人。Prompt 是任务单,Context 是桌上全部资料,Context Window 是桌面大小。桌子更大当然能放下更多东西,但把资料室里的纸全搬上来,不会自动得到更好的判断。

RAG 给生成过程补充外部资料

模型训练完成以后,不会自动知道企业私有文档、刚发生的新闻或数据库里的最新记录。检索增强生成(Retrieval-Augmented Generation,RAG)用外部检索补上这部分信息。

一个常见问题是:

公司的差旅报销上限是多少?

模型可以凭训练数据猜一个答案,也可以先从公司制度里查到相关条款,再依据条款回答。后者的基本流程是:

问题 -> 检索相关资料 -> 加入上下文 -> 调用模型 -> 生成答案

RAG 改变的是模型本轮收到的资料,不是模型参数。制度文件更新以后,只需更新资料库,不必重新训练模型。

这也是 RAG 和 Fine-tuning 的主要区别。RAG 在推理时补充事实,适合持续变化的知识;Fine-tuning 通过训练调整模型参数,更适合改变稳定的行为、风格或特定任务能力。两者可以一起用,但解决的不是同一个问题。

RAG 也不等于向量数据库。Embedding 会把文本转换成便于比较语义距离的向量,向量数据库负责保存和检索这些向量。它们是语义检索的常用实现,却不是 RAG 的定义。

全文搜索、关键词匹配、SQL 查询,甚至按文件路径读取文档,也能为生成过程补充外部资料。只要系统先取回相关信息,再把信息用于生成,就具备 RAG 的基本结构。

固定 RAG 的检索步骤通常由代码安排。服务端收到问题,生成查询,取回前几条结果,再调用一次模型。整个过程可以只有一个 Workflow,不需要 Agent,也不需要 ReAct。

Tool 让模型请求外部能力

RAG 主要解决“回答前缺少资料”,Tool 的范围更大。

Tool 可以读取信息,也可以执行动作。查询订单、搜索日志、读取文件属于读操作;发送邮件、创建工单、修改日程和执行部署属于写操作。模型本身通常不直接拥有这些权限,它只能表达调用意图。

常见的 Function Calling 会把工具名称、用途和参数结构告诉模型。用户问“订单什么时候发货”,模型可以返回:

{
  "tool": "get_order",
  "args": { "orderId": "1234567890123" }
}

这段 JSON 不会自己查询数据库。应用需要验证订单号和当前用户权限,执行 get_order,再把结果交给模型组织回答。

模型负责提出“想调用什么”,运行时负责决定“能不能调用”和“怎样执行”。这个分工很重要。否则只要模型生成了一段看起来像命令的文本,系统就得照着做,迟早会出事。

Tool 和 RAG 会有交集。把“搜索知识库”做成 Tool,模型调用它以后,结果进入生成过程,这同时是 Tool Use 和 RAG。发送邮件也是 Tool Use,却通常不会被叫作 RAG,因为重点是改变外部系统,不是补充回答资料。

调用一次 Tool 也不必然构成 Agent。固定地查询一次订单、生成一次回复,仍然可以是一段普通 Workflow。

Workflow、Loop 和 ReAct 回答三个问题

Workflow、Loop 和 ReAct 经常被放在一起比较,其实它们回答的是三个不同问题。

Workflow 预设路径、Loop 按条件重复、ReAct 根据观察选择下一步

Workflow 关心步骤由谁安排。比如一套故障报告流程由代码规定:先查服务状态,再读取最近错误日志,然后汇总部署记录,最后交给模型生成报告。路径已经写好,模型只处理其中的内容。

Loop 关心过程是否重复。重试三次是 Loop,轮询任务状态是 Loop,模型连续修改代码直到测试通过也是 Loop。循环只是一种控制结构,本身没有“智能”。

ReAct关心模型在循环里怎样选择下一步。这个名字来自 Reasoning and Acting:模型根据当前信息作出判断,选择一个 Action,看到 Observation,再继续判断。

同样是排查故障,ReAct-style Loop 可能走出这样的路径:

观察告警
-> 决定查询错误日志
-> 发现数据库连接超时
-> 决定读取连接池配置
-> 发现配置刚被修改
-> 决定查询部署记录
-> 给出诊断结论

外层代码负责继续或终止循环,模型根据每轮 Observation 选择下一个 Action。查询顺序不是服务端提前写死的,而是由已经取得的证据决定。

Loop 不等于 ReAct。固定 Workflow 可以放在 Loop 里反复执行,重试循环也不需要模型参与。ReAct 只是模型驱动循环的一种决策方式,也不是构建 Agent 的唯一办法。

实现 ReAct 也不要求保存完整的长思维过程。运行时记录简短的行动理由、工具参数和观察结果,通常已经足够还原任务路径。真正需要审计的是模型做了什么、系统返回了什么,以及哪条规则允许它继续。

Agent 把模型、工具和运行时装在一起

Agent 没有一条所有人都接受的分界线。有些产品只要调用过 Tool,就愿意在名称里加上 Agent。工程实现仍然需要一个能落到代码上的定义。

可以把 Agent 理解为一套围绕目标持续决策和行动的运行系统。一个可用的 Agent 通常能找到这些部件:

  • Model:理解当前状态并决定下一步
  • Instructions 与 Context:提供目标、规则和本轮材料
  • Tools:读取信息或影响外部系统
  • State:保存任务进度和已经发生的事件
  • Control Loop:组织模型、Action 和 Observation 之间的往返
  • Stop Conditions:定义完成、失败、超时和人工接管
  • Boundaries:限制权限、预算、步数和危险操作

写成一个不太严谨但容易记住的式子,就是:

Agent = Model + Context + Tools + State + Loop + Boundaries

Agent 的模型、工具、状态、运行时、停止条件和权限边界

以线上故障诊断为例,Agent 的目标是找出告警原因。它可以读取监控、日志、配置和部署记录,在多轮查询中保留线索,最后提交诊断方案。代码还要限制最大步数、总时间和可用工具,写操作则停下来等待人工确认。

“越自主越好”不是 Agent 的目标。生产系统往往需要更多边界:只读工具、调用预算、超时、审批、校验、幂等和回滚。这些工作不新鲜,却决定 Agent 出错时会留下一个失败任务,还是留下一场事故。

Workflow 和 Agent 也不是非黑即白。代码决定得越多,系统越接近固定 Workflow;模型能根据状态选择的路径越多,系统越 agentic。可靠的实现通常会把两者组合起来:模型负责探索,固定流程负责验证、审批和执行。

Memory 不等于更大的上下文窗口

Agent 跨越多轮甚至多个任务工作时,需要把一部分信息保存在模型调用之外。

Context 是模型本轮能看到的内容,Memory 则是应用长期保存、以后可能再次取用的信息。Memory 可以放在数据库、文件或专门的存储系统里,但保存完成不代表模型已经知道。应用仍要在合适的时候读取它,再放回当前 Context。

应用里的“记忆”至少有三类:

  • Conversation state:当前对话说过什么
  • Domain memory:用户偏好、业务事实、文档摘要等长期信息
  • Runtime state:任务走到哪一步、调用过哪些工具、失败过几次

把所有历史消息一直带着,不等于设计好了 Memory。那只是让 Context 越来越长。真正的 Memory 还要决定写入什么、什么时候读取、信息冲突时信谁,以及过期内容怎么处理。

MCP、Pi 和 Agent framework 属于基础设施

模型、输入、工具和编排方式分清以后,协议与框架的位置也容易判断。

模型上下文协议(Model Context Protocol,MCP)提供一种标准方式,让宿主连接外部的 Tool、Resource 和 Prompt。它解决的是能力怎样暴露和连接,不会替 Agent 决定任务目标、循环策略和权限边界。

Pi、LangChain、Mastra 一类项目则会处理一部分通用工程工作,例如统一模型接口、组织消息、声明工具、运行循环、记录 trace,或者接入不同 provider。每个项目覆盖的层次不同,不能只看“Agent Framework”这个名字。

以我在项目中评估过的 Pi 为例,@earendil-works/pi-ai 更接近模型接入层,负责 provider、消息、流式输出、tool call 和 usage 的统一。采用相应的 Agent runtime,还能少写一部分循环与工具执行代码。领域规则仍然留在应用里:哪些数据可以读,哪些动作必须确认,什么结果才算完成。

框架像运行底盘,MCP 像连接标准。它们能减少通用代码,但不会替产品完成领域判断。

把这些概念放在同一张图里

按大模型应用的运行层次,可以这样理解这些概念:

应用目标与边界
└── Agent
    ├── 编排:Workflow / Loop / ReAct
    ├── 外部能力:RAG / Tool
    ├── 状态:Memory / Runtime State
    ├── 本轮输入:Prompt / Context
    └── 决策与生成:LLM

基础设施:Model SDK / Agent framework / MCP

它们各自负责的事情也可以压缩成一张表:

概念 在应用里负责什么
LLM 理解输入、作出判断、生成输出
Prompt 描述本轮任务、规则和输出格式
Context 提供模型本轮能看到的全部材料
RAG 从外部资料中取回相关内容,加入生成过程
Embedding / Vector DB 按语义表示和检索资料,是 RAG 的一种实现路径
Fine-tuning 通过训练调整模型参数和稳定行为
Tool / Function Calling 让模型表达调用外部能力的意图
Workflow 按代码预先定义的路径组织步骤
Loop 让执行过程按条件重复
ReAct 让模型根据观察结果选择下一次行动
Memory / State 在模型调用之外保存信息和任务进度
Agent 把模型、工具、状态、循环和边界组织成运行系统
MCP 用标准协议连接宿主与外部能力
Agent framework 提供模型接入、循环、工具执行等通用基础设施

判断一个 Agent 时,可以先问什么

面对一个自称 Agent 的功能,不必急着争论名字。先看实现里有没有几个明确答案:

  1. 模型一共会被调用几次,每轮能看到哪些 Context?
  2. 检索内容由代码固定,还是模型根据结果继续选择?
  3. Tool 由谁执行,参数和权限在哪里校验?
  4. 中间状态存在哪里,失败后能不能恢复?
  5. Loop 在什么条件下结束,有没有步数、时间和费用上限?
  6. 哪些动作可以自动完成,哪些动作必须让人确认?

如果这些问题没有答案,系统即使能演示,也很难长期运行。Agent 的难处很少在第一次调用模型,而在第二轮以后:上下文开始膨胀,工具可能失败,状态需要保存,权限也开始真正产生后果。

一个真实的落地例子

概念最终还是要回到代码。

我在小说应用 Novevia 里做过一个全书整理功能。最早的版本把作品设定、章节、事实和记忆一次性发给模型,两次调用跑了八分多钟。后来它改成先给 manifest,再由模型按需查询章节、路线和事实库。

这个实现同时包含 RAG、Tool Use 和一个有限的 ReAct-style Loop。任务系统还负责状态、停止条件、只读权限、方案校验、人工确认、恢复和回滚,因此整个功能也可以称为一个受约束的 Agent。

《AI 整理全书为什么不能只靠一个大 Prompt》记录了那次改造的完整过程。它只是一种业务实现,不是这些概念的定义;反过来,这些概念也只是解释代码的地图,不是给功能贴金的称号。

下次再看到一个功能自称 Agent,可以先找三样东西:下一步由谁决定,状态存在哪里,谁负责让它停手。三个问题,比产品页上的名字有用。

正在加载讨论...