Agent 的基本组成:模型、工具、记忆与循环
前面的文章已经把一次模型调用组织成了有边界的工作流。再往前一步,常会遇到一个词:Agent。它不是“更聪明的聊天机器人”,而是一种由模型根据当前状态选择下一步动作、由程序执行动作并继续循环的应用结构。本篇只聚焦 Agent 的基本组成:模型、工具、记忆与循环。示例不依赖在线服务,用一个可运行的规则模型模拟决策过程,先把程序骨架看清楚,再替换成真实模型。
Agent 和单次调用有什么不同
普通调用通常是“输入提示词,得到文本”:应用预先决定所有步骤,模型只负责生成结果。Agent 则把部分步骤选择权交给模型:模型读取目标和已有状态,决定是直接回答,还是请求某个工具;程序执行工具后,把观察结果放回状态,循环继续。
一个最小 Agent 可以拆成四部分:
- 模型(Model):根据目标、历史和工具说明,提出回答或下一步动作。
- 工具(Tools):模型之外的能力,例如查询数据、计算数值或读取文件。真正执行权在应用程序。
- 记忆(Memory):保存任务目标、历史消息和工具结果,让下一轮决策有上下文。
- 循环(Loop):反复执行“决策—行动—观察”,并用停止条件限制运行范围。
“记忆”不等于一定要接数据库。本地列表就是短期记忆;只有需要跨请求、跨进程保存时,才需要持久化存储。四部分的边界越清楚,测试和排错越容易。
先用 Python 模拟模型决策
为了让示例可以离线运行,下面的 decide 函数暂时扮演模型。它只识别一个简单目标:计算两个数字的和。真实项目中,这个位置可以换成 SDK 调用,但返回给循环的结果最好仍保持类似的结构。
1 | import json |
这里的 TOOLS 是程序维护的白名单,而不是让模型自由调用任意 Python 函数。arguments 仍然是不可信输入,工具内部要做类型检查。真实模型通常会以 JSON 参数或 SDK 对象返回工具请求,应用应先解析和校验,再执行。
实现决策—行动—观察循环
下面的循环把四个组成部分串起来。memory 保存用户目标、模型动作和工具观察结果;decide 读取它并选择下一步;TOOLS 执行动作;得到结果后回到下一轮。max_steps 是硬停止条件,防止模型反复请求工具。
1 | def run_agent(user_text: str, max_steps: int = 3) -> str: |
运行命令是 python agent_basics.py,会输出 计算结果:21。这个结果来自实际的加法函数,不是模型编造的数字。示例为了突出结构,在工具返回后直接结束;接入真实模型时,应把工具结果追加到消息历史,再发起下一次模型请求,由模型生成最终文本。
四个组成部分的职责边界
模型只做决策,不直接执行。 它可以建议调用 add,但不能因为生成了某个函数名就获得文件系统、网络或数据库权限。应用必须检查工具名、参数、用户权限和副作用;涉及删除、付款等动作时,还要加入人工确认。
工具只做一件明确的事。 工具应有窄接口、有限参数和可测试的返回值。不要提供一个可以执行任意代码的“万能工具”,也不要用 eval 把模型文本当程序运行。
记忆要区分短期和长期。 当前任务的消息、动作和观察结果属于短期记忆;用户偏好或业务资料才可能进入长期记忆。保存前要考虑隐私、过期时间和容量,不能无限追加完整历史。
循环必须可停止、可观察。 除步数上限外,还可以设置总耗时、工具调用次数、单次输出大小和总成本上限。每轮记录任务标识、动作名称、耗时和结果状态,但日志中不要写入密钥或未经脱敏的敏感数据。
常见问题
有循环就一定是 Agent 吗? 不一定。固定步骤的流水线也是循环,但如果下一步完全由程序预先写死,就更接近工作流。Agent 的特征是模型参与了下一步动作选择,同时仍受程序边界约束。
为什么示例中的模型这么简单? 这是为了隔离 Agent 的控制结构。先验证状态、白名单和停止条件,再接入具体模型,出现问题时才能判断是模型决策错,还是应用执行错。
工具结果应该直接拼进提示词吗? 可以传递,但应使用明确的结构和角色标记,并限制长度。工具返回的外部文本不能自动成为系统指令;应用还要防范其中夹带的恶意指令。
Agent 越自主越好吗? 不是。自主性增加的同时,权限、成本和不可预测性也增加。只读、低风险、可回滚的任务适合先自动化;高风险动作应保留审批和撤销机制。
小结
Agent 的最小骨架不是一个神秘的新组件,而是四个清晰的部分:模型负责决定下一步,工具提供受控能力,记忆保存状态,循环把决策和行动连接起来。实际开发时,应先用离线假模型验证这条链路,再接入真实模型,并始终保留白名单、参数校验和硬停止条件。下一步可以在这个骨架上实现“单 Agent 最小实现与停止条件”,进一步处理模型连续调用工具时的状态迁移。