Agent 行动模块:从“决策“到“动手“的翻译层
Agent 行动模块:从"决策"到"动手"的翻译层
副标题:API 调用、代码执行与自然语言响应背后的工程原理
参考范围:本文仅梳理国外前沿文献与工业实践(ReAct、Toolformer、CodeAct、MCP 等)
阅读时间:约 25 分钟
难度:中等(需要基础 LLM 概念,我们会用你熟悉的 RNN、全连接层做类比)
0. 先画一张大图:Agent 的完整循环里,行动模块坐在哪?
别急,我们先不碰代码。想象一下你面前有一个正在工作的 Agent——比如一个帮你订机票的智能助手。它并不是"唰"地一下就把票订好了,而是经历了一个不断重复的循环。
如果把这个循环画成一张流程图,它会长什么样?
看到那个标红的方框了吗?那就是我们今天要聊的行动模块(Action Module)。它位于"决策"和"真实世界"之间,就像一个翻译官:左边说的是高层意图(“我想查天气”),右边需要的是可执行的具体指令(GET /weather?city=Tokyo)。
听起来抽象对吧?没关系,我们一步一步来。
第一节:为什么需要"翻译层"?LLM 不是已经会输出了吗?
好,先解决第一个认知疑点:LLM 本身不就在生成文本吗?为什么还要一个专门的"行动模块"来翻译?
这是一个特别好的问题。我们先从一个你熟悉的概念锚定:回想一下 RNN 或者早期的 seq2seq 模型。它们接收输入,经过隐藏状态变换,最后输出一个概率分布——在词表上做一个 softmax,挑出一个词。这个过程是端到端的,输入是文本,输出也是文本。
但 Agent 场景下,输出不再只是给人看的自然语言了。Agent 的输出需要被机器解析和执行。这就引入了一个根本性的断裂:
LLM 的 native 输出空间 = 自然语言 token 的序列
环境的可接受输入空间 = JSON 格式的 API 参数 / Python 代码 / 数据库 SQL / 机器人控制信号
这两个空间并不重合。如果直接把 LLM 生成的自由文本丢给 API,大概率会报 400 Bad Request。
所以,行动模块本质上是一个"格式对齐层"(Format Alignment Layer)。它的任务是把 LLM 的"想法"翻译成环境能听懂的"方言"。
我们用一张对比图来看清这个翻译过程:
如果画成图,左边是一串漂浮的 token(像 RNN 的隐藏状态流),中间是一个红色的翻译工厂,右边是一个规整的 JSON 方块。没有中间这层,LLM 就像一位只会说母语的诗人,而环境是一位只接受 JSON 的德国工程师——他们需要一个翻译。
第二节:行动模块的三种"方言"——API、代码、自然语言
现在我们已经了解了"翻译层"的必要性。接下来看看,这个翻译层到底能输出哪几种"方言"?
国外文献中,目前主流的行动输出格式有三种,它们构成了一个光谱:从高度结构化到完全自由。
2.1 第一种方言:JSON 格式的 API 调用(Function Calling)
这是目前工业界最成熟的形态。OpenAI 在 2023 年 6 月首次将 Function Calling 作为一等 API 能力发布citeweb_search:3#5,随后 Anthropic、Google 等厂商迅速跟进。
它的核心思想是:把工具定义成 JSON Schema,让 LLM 输出符合该 Schema 的结构化 JSON。这就像是给 LLM 发了一本**“填表说明书”**——你只需要按空格填参数,不要自由发挥。
我们来看一个 toy example。假设我们的词表超级小,只有两个工具:
get_weather(city: str)calculate(expression: str)
如果用户问:“东京现在多少度?” LLM 在推理层已经决定调用 get_weather。那么行动模块要做什么?
第一步(向量级视角):LLM 的输出头(output head)实际上是在做一个多标签分类 + 序列生成的混合任务。它先决定"调用哪个工具"(可以类比全连接层的分类头),再决定"每个参数填什么值"(类比 seq2seq 的生成头)。
第二步(矩阵实现视角):在真实系统中,这通常通过约束解码(Constrained Decoding)或后处理解析完成。模型要么在 logits 层面被强制输出合法 JSON(如 Outlines、Guidance 库),要么先自由生成再由行动模块解析并校验。
2.2 第二种方言:可执行代码(Code as Action)
听起来抽象对吧?代码怎么能是一种"行动输出"?
这里我们要引入一篇关键论文:Wang et al. (2024) 在 ICML 上发表的 CodeActciteweb_search:4#9。他们的核心发现是:让 LLM 直接生成可执行的 Python 代码,比让它生成 JSON 工具调用成功率提高多达 20%,而且需要的交互轮数减少 30%。
为什么?因为代码天然具备两种能力:
- 控制流(Control Flow):
if、for、while——一个 action 就能处理分支和循环 - 数据流(Data Flow):变量赋值、中间结果复用——前一个工具的输出可以直接作为后一个工具的输入
如果画成图,JSON 调用像是一排独立的积木,每块之间没有连接;而代码像是一张管道网,数据可以在里面自由流动:
cities ----------------------^
上图左边是 JSON 模式:三个孤立的 API 调用,每次都要重新把城市名写进参数。右边是 CodeAct 模式:一个 for 循环搞定,而且结果存在 results 字典里,后续代码可以直接用。
这就是 Wang et al. 说的:**“Code inherently supports control and data flow.”**citeweb_search:4#9
2.3 第三种方言:自然语言作为行动(ReAct 模式)
最后一种看起来最"弱",但恰恰是最通用的——自然语言本身也可以是一种行动。
在 Yao et al. (2023) 的经典论文 ReAct(Reasoning + Acting)中,Action 不一定是 API 调用,它可以是:
Search[query]—— 搜索某个关键词Lookup[keyword]—— 在文档中查找Finish[answer]—— 结束任务并给出答案
这些 action 的语法是自然语言,但语义是预定义的操作。它处于"完全自由文本"和"严格结构化 JSON"之间的 sweet spot。
如果画成一张三层架构图,我们可以这样理解三种方言的位置:
现在我们已经了解了三种输出形态,接下来看看它们在循环中是如何运转的。
第三节:ReAct 循环——行动模块的心跳节律
在继续之前,我们先停一下。你可能在想:这些行动输出是怎么被触发的?是一次性生成,还是反复迭代?
答案是:循环。Agent 的核心不是单次前向传播,而是一个带有反馈的闭环。这正是 ReAct(Reasoning + Acting)范式揭示的真理citeweb_search:3#2。
如果把这个循环画成一张时序图,它就像心电图一样有规律的节律:
看到那个循环了吗?每一轮都遵循 Thought → Action → Observation 的三拍子。行动模块位于 Action 拍,它接收 LLM 的原始输出,翻译成可执行格式,驱动环境,再把环境的反馈(Observation)重新编码成 LLM 能理解的上下文。
现在,让我们用结构化伪代码把这个循环精确地描述出来。注意,这里的风格是类 Pascal/Algol 的学术标准伪代码:
Algorithm: ReAct_Agent_Loop
Input: user_query ∈ String
tool_set ∈ {Tool<sub>1</sub>, Tool<sub>2</sub>, ..., Tool<sub>n</sub>}
max_iter ∈ ℕ (默认 10)
Output: final_answer ∈ String 或 failure_signal
1. context ← Initialize_Context(user_query, system_prompt, tool_set)
// context 是一个消息队列,类似 RNN 的隐藏状态历史 h<sub>1:t</sub>
2. for iter ← 1 to max_iter do
3. response ← LLM.Generate(context)
// LLM 生成 Thought + Action 的联合输出
4. thought ← Parse_Thought(response)
// 提取推理链,类似从隐藏状态解码语义
5. action ← Parse_Action(response)
// 提取行动指令,这是行动模块的入口
6. if action.type = "Finish" then
7. return action.content
// 终止条件:LLM 决定直接回答用户
8. if action.type = "Tool_Call" then
9. tool_name ← action.tool_name
10. args ← action.arguments
11. // ═══════════════════════════════════════
// 行动模块核心:决策 → 执行的翻译
// ═══════════════════════════════════════
12. validated_args ← Validate_Parameters(tool_name, args, tool_set)
// 参数校验:类型检查、范围约束、必填项验证
13. if validated_args = ∅ then
14. observation ← "ERROR: Parameter validation failed"
15. else
16. observation ← Execute_Tool(tool_name, validated_args)
// 沙箱执行,带超时和资源限制
17. // 将观察结果编码为 LLM 可理解的格式
18. context ← context ⊕ Format_Observation(observation, tool_call_id)
// ⊕ 表示追加操作,类似 RNN 的时间步更新 h<sub>t+1</sub> = f(h<sub>t</sub>, x<sub>t</sub>)
19. end for
20. return "Agent exceeded maximum iterations"
// 循环终止但未完成,触发降级策略
这段伪代码有几个关键细节值得注意:
- 第 12 行:
Validate_Parameters是行动模块的安全闸口。它确保 LLM 产生的参数符合 JSON Schema,就像全连接层的输出要经过激活函数约束一样。 - 第 16 行:
Execute_Tool运行在沙箱中。这是工业界的铁律——你永远不要信任 LLM 生成的代码或 API 调用可以直接在主机上运行。Anthropic 的 Computer Use 和 OpenAI 的 Code Interpreter 都遵循这一原则citeweb_search:3#0。 - 第 18 行:
Format_Observation是反向翻译。环境返回的原始数据(可能是二进制、JSON、错误堆栈)需要被重新包装成文本,才能被注入 LLM 的上下文窗口。
第四节:从 Toy Example 到真实规模——参数填充的微观机制
别急,概念已经够多了。我们现在钻进行动模块的最微观层面:当 LLM 决定调用一个工具时,那些参数是怎么"填"进去的?
4.1 Toy Example:只有两个工具的微型世界
假设我们的 Agent 生活在一个极简世界里,只有两个工具:
Tool<sub>1</sub>: get_weather(city: string, units: "celsius" | "fahrenheit")
Tool<sub>2</sub>: calculate(expr: string)
用户的 query 是:“东京比伦敦热多少度?”
LLM 在推理层已经分解了任务:
- 查东京天气 →
get_weather - 查伦敦天气 →
get_weather - 计算温差 →
calculate
现在到了行动模块。它需要为第一次 get_weather 调用填充参数。在向量级视角下,这类似于一个序列标注 + 生成的混合问题:
// 微观视角:单步参数生成
Input: context = [用户 query, Thought, Action 意图]
Output: parameter_json = {city: "Tokyo", units: "celsius"}
过程:
1. 工具选择 ← argmax<sub>i</sub> P(Tool<sub>i</sub> | context)
// 类似分类头的 softmax,在工具集上做离散选择
2. 对于每个必填参数 p<sub>j</sub> ∈ Tool<sub>i</sub>.required_params:
value<sub>j</sub> ← Decode(context, p<sub>j</sub>.name, p<sub>j</sub>.type)
// 类似 seq2seq 解码,但受限于 JSON Schema 的语法约束
3. 组装 ← {p<sub>1</sub>: value<sub>1</sub>, p<sub>2</sub>: value<sub>2</sub>, ...}
如果画成一张信息流动图,参数填充就像水流过管道:
4.2 真实规模:从 2 个工具到 16,000 个 API
现在我们已经了解了 toy example,接下来看看真实世界有多疯狂。
Qin et al. (2024) 的 ToolLLM 项目构建了一个包含 16,000+ 真实 RESTful API 的基准测试集 ToolBenchciteweb_search:4#1。在这个规模下,工具选择不再是简单的 softmax 分类——因为类别空间太大了。
这时候,行动模块需要引入检索增强机制:
这张图展示了一个两阶段漏斗:先用向量检索(类似 RAG)从 16K 个 API 中粗筛出 50 个候选,再用 LLM 的上下文学习能力精排并选择。ToolLLM 的实验表明,这种分层策略比 brute-force 枚举所有 API 的准确率高出一大截citeweb_search:4#13。
第五节:CodeAct 的深层优势——为什么代码是更好的"行动语言"
在继续之前,让我们回顾一下第二节提到的 CodeAct。Wang et al. (2024) 的论文不只是提出了一个新格式,它揭示了一个更深层的原理:代码是 LLM 的"母语"之一citeweb_search:4#9。
为什么这么说?因为现代 LLM(如 GPT-4、Claude、Llama、Mistral)的预训练数据中,代码占比极高(通常 10%-30%)。这意味着:
- 格式熟悉度:LLM 写 Python 函数调用的准确率,往往比写自定义 JSON Schema 更高
- 自调试能力:当代码执行报错时,LLM 能读懂 Traceback 并自我修正——这是 JSON 模式不具备的
- 组合能力:代码可以把多个工具调用、控制流、数据处理打包成一个 action
我们用一张能力雷达图(用文字描述其形状)来对比三种格式:
从这张图可以看出,JSON 在"结构化"和"可解析性"上满分,但组合性和自调试能力很弱。自然语言正好相反。而 CodeAct 位于 sweet spot——它在所有维度上都保持较高水平,没有明显短板。
这也是 HuggingFace 的 Smolagents 框架选择"代码即动作"作为默认模式的原因citeweb_search:3#0。
第六节:MCP——当工具数量爆炸时,我们需要一个"通用插座"
现在我们已经了解了行动模块的内部机制。但还有一个中观层面的问题没解决:如果每个 API 都有自己的接入方式,Agent 怎么管理成百上千的工具?
想象你家里的电器——手机、电脑、台灯、吹风机——它们不需要各自发明一种插头,而是统一使用国标插座。Anthropic 在 2024 年提出的 Model Context Protocol (MCP) 就是 Agent 世界的"国标插座"citeweb_search:4#3。
MCP 的核心思想是解耦。它把 Agent 和工具之间的 N×M 集成问题,降维成 N+M 问题:
传统模式:3 个 LLM 提供商 × 5 个工具 = 15 种定制集成
MCP 模式:3 个提供商各实现 1 次 MCP + 5 个工具各实现 1 次 MCP = 8 种实现
如果画成架构图,MCP 就像一个三层网络:
在这个架构中,行动模块不再直接对接杂乱的 API,而是对接统一的 MCP Client。Client 负责:
- 发现(Discovery):运行时枚举 Server 提供的工具列表
- 协商(Negotiation):确认参数 Schema 和能力边界
- 路由(Routing):把行动模块的输出翻译成 Server 能理解的 JSON-RPC 2.0 请求
- 回传(Callback):把执行结果标准化后返回给 Agentciteweb_search:4#5
这就像是行动模块和外部世界之间加了一个适配器层(Adapter Layer)。无论后端是 PostgreSQL、Slack API 还是本地文件系统,行动模块看到的都是统一的接口。
第七节:安全层——行动模块的"刹车系统"
在结束技术细节之前,我们必须聊一个不能回避的话题:安全。
行动模块是 Agent 唯一能与外部世界产生副作用的通道。如果 LLM 被 prompt injection 攻击,恶意指令可能让 Agent 执行危险操作——比如删除数据库、转账、泄露敏感文件citeweb_search:4#4。
所以,一个生产级的行动模块必须内置多层"刹车系统":
这五层防御就像漏斗一样,越往下通过率越低,但安全性越高:
- 语法校验:确保输出是合法的 JSON/Python,参数类型匹配 Schema
- 语义校验:检查参数值是否在合理范围(比如
transfer_amount不能是负数或超大值) - 权限校验:基于 RBAC(角色权限控制)判断当前 Agent 是否有权调用该工具
- 人工确认:对于删除、转账、权限提升等高风险操作,强制暂停并等待人类审批
- 沙箱执行:即使前面都通过了,实际执行也在隔离环境中进行,带超时和资源配额citeweb_search:3#2
Anthropic 的 MCP 规范甚至把**访问控制(Access Control)**作为协议原语,在连接建立时就进行能力协商citeweb_search:4#3。
第八节:结构化伪代码——行动模块的完整算法
现在我们已经从宏观架构聊到了微观安全机制。让我们用一段完整的结构化伪代码,把行动模块的核心逻辑封装起来。这段代码融合了前面所有的概念:ReAct 循环、工具选择、参数填充、MCP 路由、安全检查。
Algorithm: Action_Module_Translation
Input: raw_llm_output ∈ String
available_tools ∈ {Tool<sub>1</sub>, ..., Tool<sub>n</sub>} // 通过 MCP 发现
session_context ∈ Context
safety_policy ∈ Policy
Output: execution_result ∈ Result 或 rejection_reason ∈ String
// ═══════════════════════════════════════════════════════
// Phase 1: 意图解析(Parsing)
// ═══════════════════════════════════════════════════════
1. intent ← Parse_LLM_Output(raw_llm_output)
// 识别输出类型:Direct_Response / Tool_Call / Code_Block / Finish
2. if intent.type = Direct_Response then
3. return Result{type: "NL", content: intent.text}
// 自然语言响应,无需翻译,直接返回给用户
// ═══════════════════════════════════════════════════════
// Phase 2: 工具解析与选择(Tool Resolution)
// ═══════════════════════════════════════════════════════
4. if intent.type = Tool_Call then
5. tool_name ← intent.extracted_tool_name
6. candidate ← MCP_Discover(tool_name, available_tools)
// 通过 MCP Client 在已连接 Server 中查找匹配工具
7. if candidate = ∅ then
8. return Rejection{reason: "Tool not found in registry"}
9. schema ← candidate.input_schema // JSON Schema
// ═══════════════════════════════════════════════════════
// Phase 3: 参数填充与校验(Parameter Binding)
// ═══════════════════════════════════════════════════════
10. raw_params ← intent.extracted_arguments
11. // 渐进式校验(类似编译器的多趟扫描)
12. validated ← ∅
13. for each (key, value) ∈ raw_params do
14. if key ∉ schema.required ∧ key ∉ schema.optional then
15. return Rejection{reason: "Unknown parameter: " + key}
16. if Type_Match(value, schema.properties[key].type) = False then
17. return Rejection{reason: "Type mismatch for: " + key}
18. if Range_Check(value, schema.properties[key].constraints) = False then
19. return Rejection{reason: "Value out of range: " + key}
20. validated ← validated ∪ {(key, value)}
21. end for
22. if schema.required ⊄ Keys(validated) then
23. return Rejection{reason: "Missing required parameters"}
// ═══════════════════════════════════════════════════════
// Phase 4: 安全策略检查(Safety Gate)
// ═══════════════════════════════════════════════════════
24. risk_score ← Evaluate_Risk(candidate, validated, session_context)
// 基于工具敏感度、参数值、用户权限计算风险分
25. if risk_score > safety_policy.auto_approve_threshold then
26. if risk_score > safety_policy.hard_block_threshold then
27. return Rejection{reason: "High-risk action blocked by policy"}
28. else
29. status ← Request_Human_Approval(candidate, validated, risk_score)
30. if status = Denied then
31. return Rejection{reason: "Rejected by human operator"}
32. end if
33. end if
// ═══════════════════════════════════════════════════════
// Phase 5: 执行与观察(Execution & Observation)
// ═══════════════════════════════════════════════════════
34. if candidate.execution_mode = "remote_api" then
35. observation ← MCP_Invoke(candidate.endpoint, validated)
36. else if candidate.execution_mode = "local_code" then
37. observation ← Sandbox_Execute(candidate.code_template, validated)
// 在隔离容器中运行,限制 CPU / 内存 / 网络
38. else if candidate.execution_mode = "natural_language" then
39. observation ← Route_To_NL_Engine(candidate.handler, validated)
40. end if
41. // 后处理:将原始观察转化为 LLM 友好的文本
42. formatted_obs ← Truncate(observation, max_tokens=2000)
43. formatted_obs ← Strip_Sensitive_Patterns(formatted_obs, safety_policy.pii_filter)
44. return Result{type: "Observation", content: formatted_obs, tool_call_id: UUID()}
45. end if // Tool_Call branch
// ═══════════════════════════════════════════════════════
// Phase 6: 代码块执行(CodeAct Branch)
// ═══════════════════════════════════════════════════════
46. if intent.type = Code_Block then
47. // CodeAct 模式:直接执行 Python 代码
48. static_analysis ← Lint_Check(intent.code)
49. if static_analysis.severity = "error" then
50. return Rejection{reason: "Static analysis failed"}
51. end if
52.
53. observation ← Sandbox_Execute_Python(intent.code, timeout=30s)
54. return Result{type: "Code_Observation", stdout: observation.stdout,
stderr: observation.stderr, exit_code: observation.code}
55. end if
这段伪代码虽然长,但它展示了行动模块的完整生命周期:从一团 LLM 生成的原始文本,经过解析、校验、安全审查,最终变成环境可执行的指令,再把环境的反馈翻译回 LLM 能消化的文本。
这就像一条双向翻译流水线:
- 正向:LLM 语言 → 环境语言
- 反向:环境语言 → LLM 语言
闭环结尾:这在训练和实际使用中意味着什么?
好了,我们已经从宏观的 Agent 循环,一路剥到了微观的参数校验和安全策略。现在让我们把盖子盖回去,回答一个实际问题:了解这些原理后,在训练和部署 Agent 时,我们应该怎么做?
训练阶段的启示
-
数据格式选择:如果你正在微调一个开源模型来做 Agent,优先选择 CodeAct 格式(Python 代码作为 action)而非自定义 JSON。因为 LLM 对代码的"熟悉度"天然更高,预训练数据中已经包含了大量代码-执行-反馈的隐含模式citeweb_search:4#9。Wang et al. 的 CodeActInstruct 数据集(7K 高质量多轮轨迹)就是一个很好的起点。
-
课程学习(Curriculum Learning):在训练数据中,先从单工具、单参数的 toy example 开始,逐步增加到多工具组合、嵌套参数、多轮对话的复杂案例。这与我们本文的讲解顺序一致——渐进披露不仅适合教学,也适合模型学习。
-
负样本工程:行动模块的校验层需要见过足够多的"错误样例"才能学会拒绝。在训练集中加入故意写错的参数、越界的数值、格式错误的 JSON,让模型学会"说不"。
部署阶段的启示
-
永远不要信任 LLM 的输出:即使 GPT-4 或 Claude 3.5 的准确率已经很高,行动模块的五层安全闸(语法→语义→权限→人工→沙箱)一个都不能少。Invant Labs 在 2025 年 4 月披露的 MCP Tool Poisoning Attack 证明,攻击者可以在工具描述中嵌入恶意指令,诱导 Agent 执行未授权操作citeweb_search:4#4。
-
采用 MCP 标准化工具接入:如果你的 Agent 需要对接超过 5 个外部工具,强烈建议实现 MCP Server/Client 架构。这不仅能降低集成成本,还能获得动态发现、统一鉴权、版本协商等能力citeweb_search:4#3。
-
监控行动模块的"翻译失败率":在生产环境中,跟踪以下指标:
- Schema Violation Rate:LLM 输出不符合 JSON Schema 的比例
- Execution Error Rate:参数合法但执行失败的比例(如 API 超时、权限不足)
- Safety Trigger Rate:安全层拦截 action 的比例
这些指标比单纯的"任务成功率"更能反映行动模块的健康状况。
-
为代码执行配置强沙箱:如果启用 CodeAct,确保 Python 解释器运行在隔离容器(如 Docker、gVisor)中,禁用网络访问(除非必要),限制文件系统只能访问特定目录,并设置严格的 CPU/内存/时间配额。SWE-Agent、OpenHands 等代码 Agent 框架都遵循这一原则citeweb_search:4#12。
延伸阅读推荐
如果你想深入某个方向,以下是国外文献中各子领域的代表作:
| 方向 | 推荐文献 | 核心贡献 |
|---|---|---|
| ReAct 推理-行动循环 | Yao et al., 2023, ReAct: Synergizing Reasoning and Acting in Language Models | 提出 Thought-Action-Observation 三拍子循环 |
| 工具学习奠基 | Schick et al., 2023, Toolformer: Language Models Can Teach Themselves to Use Tools | 通过自监督学习让模型学会插入 API 调用 |
| 大规模 API 调用 | Qin et al., 2024, ToolLLM: Facilitating Large Language Models to Master 16000+ Real-world APIs | ToolBench 基准,16K API 的泛化测试 |
| 代码作为行动 | Wang et al., 2024, CodeAct: Executable Code Actions Elicit Better LLM Agents (ICML) | CodeAct 框架,代码比 JSON 更适合做 action |
| API 专用模型 | Patil et al., 2023, Gorilla: Large Language Model Connected with Massive APIs | 检索增强微调,让模型学会调用海量 API |
| 工具协议标准 | Anthropic, 2024, Model Context Protocol (MCP) Specification | 统一 Agent-工具通信协议,N×M → N+M |
| Agent 安全 | Hou et al., 2025, Analyzing MCP Lifecycle Vulnerabilities | MCP 安全分析,Tool Poisoning 攻击面 |
| 数字环境 Agent | Zhou et al., 2024, WebArena: A Realistic Web Environment for Building Autonomous Agents | 浏览器环境下的长程任务基准 |
| 操作系统 Agent | Xie et al., 2024, OSWorld: Benchmarking Multimodal Agents for Open-Ended Tasks in Real Computer Environments | 真实 OS 环境下的多模态 Agent 测试 |
结语
我们从一张 Agent 的宏观循环图出发,逐步剥开了行动模块这个"翻译层"的内部结构:
- 为什么需要翻译层——因为 LLM 的 native 输出空间(自然语言 token)与环境的输入空间(JSON/API/代码)不重合
- 三种输出方言——JSON 工具调用(确定性最高)、Python 代码(灵活性最强)、自然语言(通用性最广)
- ReAct 循环——Thought → Action → Observation 的三拍子心跳,行动模块位于 Action 拍
- 参数填充的微观机制——从 toy example 的向量级分类+生成,到真实规模的检索增强+精排
- MCP 协议——当工具数量爆炸时,用统一协议把 N×M 集成降维成 N+M
- 安全五层盾——语法、语义、权限、人工、沙箱,缺一不可
行动模块可能是 Agent 架构中最容易被低估的组件。它不像 LLM 那样"聪明",也不像感知层那样"敏锐",但它是唯一能让 Agent 从"动嘴"变成"动手"的桥梁。没有它,再强大的推理能力也只是纸上谈兵。
希望这篇文章帮你建立了从宏观到微观的完整认知地图。下次当你调用 OpenAI 的 function_call、配置 Anthropic 的 MCP Server、或者调试 HuggingFace Smolagents 的 CodeAgent 时,你会知道在那几行简洁的 API 调用背后,藏着怎样一套精密的翻译、校验、执行与反馈机制。
我们下篇见。
本文所有架构图使用 Mermaid 绘制,伪代码遵循结构化 Pascal/Algol 风格。参考文献均为国外前沿论文与工业标准,未引用中文二手资料。
更多推荐



所有评论(0)