什么是Agent,给LLM套个壳吗?这个问题你会怎么回答,把答案背一遍吗?

最近我在思考一个问题:随着大模型能力越来越强,Agent 系统的形态也在发生变化。它不再只是一个“会调用工具的聊天机器人”,而越来越像一个由大模型驱动的运行时系统。

如果做一个简单 Demo,Agent 可能只是:

LLM + Prompt + Tools + while loop

但如果要做一个完整可用、能接入真实业务系统、能稳定运行的 Agent,它的复杂度会迅速上升。此时,它已经不只是一个应用层功能,而更像一个小型操作系统。

换句话说:

Demo Agent 是脚本;
产品级 Agent 是运行时系统;
复杂 Agent 平台则接近一个 Agent OS。

一、LLM 越来越像“自然语言处理器”

可以把大模型理解成一种“自然语言处理器”。

传统 CPU 执行的是机器指令,比如:

LOAD R1, [address]
ADD R1, R2, R3
JMP label

而大模型处理的是自然语言任务,然后生成工具调用、推理步骤和结果整合方案。例如:

{
  "name": "search_documents",
  "arguments": {
    "query": "context compression in agent systems",
    "top_k": 5
  }
}

从这个角度看,LLM 不只是负责回答问题,它更像一个控制核心:理解任务、规划步骤、选择工具、生成参数、解释结果、整合答案。

但 LLM 又不是传统 CPU。CPU 执行指令是确定性的,而 LLM 的输出是概率性的。因此,它可能选错工具、生成错误参数、漏掉步骤,或者错误解释工具结果。

所以更准确地说,LLM 像一个:

自然语言解释器
+ 任务调度器
+ 模糊控制器
+ 工具调用生成器

它具有很强的语义理解和规划能力,但并不天然可靠。因此,工程上不能完全相信它,而要用 Runtime、状态机、权限控制和结果校验来约束它。


二、Tool 像 Agent 的“指令集”

如果把 LLM 看作处理器,那么 Tool 就很像它的指令集。

CPU 通过指令集定义自己能做什么;Agent 通过 Tool Schema 定义模型能调用哪些外部能力。

例如,一个研究型 Agent 可能有这些工具:

search_paper()
parse_pdf()
retrieve_chunks()
update_knowledge_graph()
summarize_experiment()

一个办公 Agent 可能有这些工具:

read_email()
send_email()
create_calendar_event()
query_database()
generate_report()

这些工具就是 Agent 能执行的“外部操作原语”。

更进一步,Tool 可以分成两类:

普通工具 ≈ 函数 / 普通指令
外部工具 ≈ 系统调用
有副作用工具 ≈ 特权指令

比如:

calculate()
format_json()
extract_entities()

这些更像普通函数或普通指令。

而:

send_email()
delete_file()
create_order()
modify_database()
charge_payment()

这些就更像系统调用甚至特权指令,因为它们会影响外部世界,具有副作用,不能随便执行。

这意味着,设计 Tool 不只是“写几个函数给模型用”,而是在设计 Agent 的指令集。

一个好的 Tool 设计应该满足:

语义清晰
参数明确
粒度适中
返回结构化
副作用可控
权限边界清楚
错误信息可处理

如果 Tool 设计得很混乱,比如功能重叠、描述模糊、参数复杂、返回结果太长,模型就很容易选错工具、填错参数,Agent 的整体行为也会变得不稳定。

所以,Agent 工程中一个非常重要的观点是:

Tool design is instruction set design.

工具设计,本质上就是为大模型设计一套可执行的外部指令集。


三、Agent Runtime 像操作系统

LLM 本身并不能真正访问数据库、文件、浏览器、邮件、日历或业务系统。它只是生成“我要调用什么工具,用什么参数”。

真正负责执行的是 Agent Runtime。

因此,Agent Runtime 很像操作系统。

操作系统负责管理:进程、内存、文件、权限、设备、调度、异常、资源、安全

Agent Runtime 也要管理类似的东西:

操作系统 Agent 系统
进程调度 Agent workflow / node 调度
指令执行 Tool calling
内存管理 Context / Memory 管理
文件系统 文档库 / 向量库 / 知识库
设备驱动 API / MCP Server / 外部工具
权限控制 Tool permission / Human confirmation
中断处理 用户打断 / 工具异常 / 超时
日志系统 Trace / Audit log
资源限制 Token budget / Cost budget / Rate limit
沙箱机制 Tool sandbox / 安全边界
任务队列 Async jobs / Background execution
错误恢复 Retry / Fallback / Rollback

从这个角度看,Agent Runtime 的职责远远不只是“帮模型执行函数”。

它需要负责:工具注册、工具调度、参数校验、权限控制、并发执行、超时处理、错误恢复、状态保存、成本限制、日志追踪、结果压缩、安全隔离

真正难的不是让模型调用工具,而是让模型在复杂环境中稳定、安全、低成本地调用工具。


四、Context 和 Memory 像 Agent 的内存系统

在传统计算机系统里,CPU 需要 RAM、Cache、Disk 等多级存储系统。

Agent 也一样。

Agent 的“内存”可以分成几层:

当前上下文窗口:短期工作内存
对话历史:会话级记忆
向量数据库:语义记忆
结构化数据库:事实记忆
知识图谱:关系记忆
文件系统:长期资料存储

不过,LLM 的内存有一个特殊问题:它不像 RAM 那样可以随机访问。模型每次调用时,只能看到上下文窗口里的内容。

这就导致一个重要问题:

模型 API 通常是无状态的。
如果每次调用都需要 50KB 上下文,那么客户端通常每次都要重新发送这 50KB 内容。

虽然部分模型服务可能提供 prompt caching 或 context caching,但工程上不能默认服务端会自动帮你保存上下文。默认应该按照无状态 API 来设计。

因此,长上下文 Agent 最容易出现的问题是:

每次模型调用都重复发送大量上下文
工具调用轨迹越来越长
RAG 结果越塞越多
多轮推理成本持续膨胀
最终答案不长,但后台 token 消耗巨大

所以,产品级 Agent 必须有上下文管理模块。

它要决定每次模型调用时到底带什么内容:

哪些历史必须保留?
哪些工具结果可以压缩?
哪些中间步骤可以丢弃?
哪些事实应该写入长期记忆?
哪些 RAG 结果真正相关?

这很像操作系统里的内存管理、缓存淘汰、分页和换入换出。

一个好的 Agent 不能无脑把所有历史塞进 prompt,而应该做到:

只传当前步骤真正需要的信息。

五、长上下文下,为什么要减少高频 LLM 调用?

Agent 的调用成本不能只看输出 token,而要看每次调用的完整输入和输出。

一次 LLM 调用的 token 成本大致包括:

系统提示词
开发者提示词
工具定义
对话历史
agent工作记忆
检索上下文
用户当前
工具调用结果
模型输出

如果上下文已经达到几十 KB,那么每多一次 LLM 调用,就可能意味着大量上下文被重复发送。

可以用一个简单公式估算:

总成本 ≈ 调用次数 × 固定上下文成本
      + 动态输入总量
      + 工具返回总量
      + 模型输出总量

也就是:

Total ≈ N × C_fixed + ΣC_dynamic + ΣC_output

其中:

N = LLM 调用次数
C_fixed = 每轮重复发送的上下文
C_dynamic = 每轮新增输入、工具结果、检索内容
C_output = 模型输出

如果固定上下文很大,那么高频小调用会非常昂贵。

比如每次调用都要带 20k tokens 输入:

调用 1 次:约 20k input tokens
调用 5 次:约 100k input tokens
调用 20 次:约 400k input tokens

用户可能只看到几百 tokens 的最终答案,但后台已经消耗了大量 token。

因此,长上下文 Agent 的核心优化方向是:

减少 LLM 调用次数
减少重复上下文
压缩工具结果
只传必要状态
把确定性逻辑交给代码
把低价值判断交给小模型或规则

六、工具并行:像指令级并行

既然 LLM 调用很贵,而很多工具调用相对便宜,那么实际工程中就可以让模型一次性规划多个工具调用,由后端并行执行。

这类似于处理器中的指令级并行。

低效的 Agent 流程可能是:

LLM 判断是否需要检索
↓
调用检索工具
↓
LLM 看结果后决定是否查知识图谱
↓
调用知识图谱工具
↓
LLM 看结果后决定是否查数据库
↓
调用数据库工具
↓
LLM 生成答案

这种模式会导致多次 LLM 调用,每次都重复发送上下文。

更好的流程是:

LLM 一次性生成工具计划
↓
后端并行调用向量库、BM25、知识图谱、数据库
↓
工具层去重、过滤、rerank、压缩
↓
LLM 基于压缩结果生成答案

这样可以减少:

LLM 调用次数
重复上下文输入
网络往返延迟
Agent action-observation 轮数

不过要注意,工具并行不等于把所有工具结果都塞回模型。

并行工具调用后,必须做结果处理:

去重
过滤
排序
截断
摘要
结构化提取
只保留有价值证据

否则你虽然减少了 LLM 调用次数,却可能让工具结果撑爆上下文窗口。


七、投机式工具调用:Agent 里的“分支预测”

还可以进一步借鉴 CPU 的分支预测思想。

在 CPU 中,分支预测是指:

我还不知道 if 分支会走哪边,
但为了不让流水线停下来,
先预测一个分支并提前执行。

Agent 中也可以有类似策略。

假设有四个工具:

ToolA
ToolB
ToolC
ToolD

正常流程是:

先调用 ToolA
↓
根据 ToolA 的结果,再决定是否调用 ToolB / ToolC / ToolD

ToolB、ToolC、ToolD都有可能被调用。但如果 ToolB、ToolC、ToolD 都是只读、低成本、无副作用的工具,而 LLM 调用很贵,那么可以采用投机式工具调用:

一次性调用 ToolA + ToolB + ToolC + ToolD
↓
等待结果返回
↓
根据 ToolA 的结构化结果,决定采用 B / C / D 中哪些结果
↓
丢弃无效分支结果

本质上,这是用便宜的工具冗余调用,换取昂贵的 LLM 调用次数减少。

这可以称为:

Speculative Tool Execution
投机式工具执行

它适合的场景是:

工具是只读的
工具调用成本低
工具无副作用
工具结果量可控
多个分支有较高概率被用到
分支选择规则可以结构化判断

例如订单查询场景:

ToolA:查订单状态
ToolB:查物流信息
ToolC:查退款记录
ToolD:查客服工单

用户问:“我的订单怎么回事?”

可以同时查:

订单状态 + 物流 + 退款 + 客服工单

然后根据订单状态决定主要采用哪部分结果:

已发货 → 使用物流信息
已退款 → 使用退款记录
有争议 → 使用客服工单

但投机式工具调用有一个重要边界:不能用于有副作用的工具。

比如:

发送邮件
删除文件
创建订单
修改数据库
发起付款
预约会议

这些工具不能因为“可能会用到”就提前执行。否则即使最后不用,副作用也已经发生了。

因此,投机工具调用只适合:

查询型工具
只读工具
幂等工具
可取消工具
低风险工具

八、Agent Workflow 像程序,LangGraph 像运行时框架

简单 Agent 循环通常是:

while not done:
    LLM decides next action
    execute tool
    append observation

这种方式适合 Demo,但不适合复杂系统。原因是:

流程不可控
状态不清晰
容易死循环
难以调试
难以插入规则节点
难以做权限控制
难以恢复失败

对于复杂 Agent,更好的方式是用图结构或状态机来控制流程。

例如 LangGraph 这类框架提供了:

节点:执行阶段
边:状态转移
State:显式状态
Conditional Edge:条件分支
Checkpoint:状态保存
Human-in-the-loop:人工确认

这就更像一个 Agent Runtime。

一个复杂研究型 Agent 可以设计成:

Planner Node
    ↓
Retriever Node
    ↓
Reranker Node
    ↓
Evidence Compressor Node
    ↓
Reasoning Node
    ↓
Critic Node
    ↓
Answer Generator Node

或者对于投机工具调用,可以设计成:

Planner Node
    ↓
Speculative Tool Executor Node
    ↓
Branch Selector Node
    ↓
Result Compressor Node
    ↓
Answer Generator Node

这里的关键思想是:

模型负责语义理解和不确定性判断;
代码负责确定性流程控制;
Graph 负责状态流转;
Runtime 负责工具执行、安全边界和错误恢复。

也就是说,产品级 Agent 不能完全依赖模型自由发挥,而应该把模型放在受控的运行时系统中。


九、产品级 Agent 至少需要哪些模块?

如果把 Agent 当作一个小型操作系统,那么一个产品级 Agent 至少需要以下模块。

1. 任务调度模块

负责决定 Agent 当前处于什么阶段:

理解任务
制定计划
调用工具
观察结果
修正计划
生成答案
结束任务

2. 上下文管理模块

负责决定每次 LLM 调用时带什么内容:

系统提示词
当前任务
历史对话
工具调用轨迹
RAG 结果
长期记忆
中间状态

目标是:

只带当前必要的信息,不把所有内容都塞进上下文。

3. 工具管理模块

负责:

工具注册
工具描述
参数校验
权限控制
超时控制
重试机制
结果格式化
副作用隔离

4. 状态管理模块

Agent 不能只靠 prompt 记住自己做了什么,必须有显式状态:

{
    "task_id": "xxx",
    "goal": "分析几篇论文",
    "current_step": "retrieval",
    "retrieved_docs": [...],
    "used_tools": [...],
    "pending_questions": [...],
    "token_budget": 12000,
    "errors": []
}

显式状态可以让流程更稳定,也方便调试和恢复。

5. 错误处理模块

真实工具一定会失败:

API 超时
数据库连接失败
检索无结果
权限不足
参数格式错误
返回内容为空
模型输出 JSON 解析失败

因此需要:

retry
fallback
skip
ask user
degrade gracefully
rollback

6. 成本控制模块

Agent 很容易失控:

模型调用过多
上下文越来越长
工具结果越来越多
循环无法停止
RAG 返回太多 chunk
多 Agent 互相对话膨胀

所以要设置:

最大调用次数
最大 token budget
最大工具调用次数
最大执行时间
最大递归深度
明确终止条件

7. 可观测性模块

产品级 Agent 必须能 debug。

需要记录:

每次 LLM 输入输出
每次工具调用参数
工具返回结果
节点执行路径
token 消耗
耗时
失败原因
最终答案来源

否则 Agent 出错时,很难判断问题到底出在哪里:

是检索错了?
工具选错了?
参数错了?
模型理解错了?
上下文污染了?
还是状态流转错了?

可观测性不是锦上添花,而是 Agent 工程化的基础设施。


十、Agent 工程的核心不是 Prompt,而是 Runtime

很多人刚开始做 Agent,会把重点放在 Prompt 上:

怎么写提示词?
怎么让模型听话?
怎么让模型规划?

但做深以后会发现,真正难的是:

状态怎么管理?
上下文怎么裁剪?
工具怎么注册?
权限怎么控制?
错误怎么恢复?
调用链怎么追踪?
成本怎么限制?
结果怎么验证?
任务什么时候终止?

这已经不是单纯的 NLP 问题,而是系统设计问题。

因此,Agent 可以用一个公式表示:

Agent = LLM + Runtime + Tools + Memory + Workflow + Guardrails + Observability

LLM 是核心,但不是全部。

真正决定 Agent 是否可用的是:

Runtime
状态管理
工具治理
上下文管理
权限控制
错误恢复
成本控制
可观测性

十一、Agent 与操作系统的完整类比

可以用下面这张表总结:

计算机系统 Agent 系统
CPU LLM
指令集 ISA Tool Schema / Function Calling
程序 Prompt / Workflow / User Task
操作系统 Agent Runtime
进程调度 Agent Node 调度
内存 Context / Short-term Memory
硬盘 Vector DB / Document Store / Knowledge Base
Cache Prompt Cache / Retrieved Context
系统调用 External Tool / API Call
特权指令 有副作用工具
设备驱动 MCP Server / API Adapter
中断 用户打断 / 工具异常
分支预测 Speculative Tool Execution
流水线 Multi-stage Agent Workflow
多核 Multi-Agent / Parallel Tool Execution
权限环 Tool Permission / Human Confirmation
日志系统 Trace / Audit / Observability
资源限制 Token Budget / Rate Limit / Cost Control

这个类比并不是为了炫技,而是能帮助我们更清楚地设计 Agent。

当我们把 LLM 看成处理器,把 Tool 看成指令集,把 Runtime 看成操作系统,很多工程问题就变得清晰了:

工具设计 => 指令集设计
上下文管理 => 内存管理
工具调用 => 系统调用
权限控制 => 特权管理
调用链追踪 => 系统日志
成本控制 => 资源调度
投机工具调用 => 分支预测
多工具并行 => 指令级并行
多 Agent 协作 => 多核系统

这说明,Agent 工程不是简单地“调 Prompt”,而是在构建一个由大模型驱动的运行时系统。


十二、结论:未来的 Agent 更像一个操作系统

我越来越觉得,未来的复杂 Agent 不会只是一个聊天框,也不会只是几个 API 工具的拼接。

它更像一个新的计算系统:

LLM 是自然语言处理器;
Tool 是它的指令集;
Agent Runtime 是操作系统;
Context 是内存;
Memory / RAG 是外部存储;
Workflow 是程序;
Guardrails 是安全机制;
Observability 是调试系统。

但是和传统操作系统不同,Agent OS 的核心是不确定的。

传统 CPU 执行指令是确定性的,而 LLM 的指令生成是概率性的。因此,Agent Runtime 必须比传统运行时承担更多责任:

约束模型行为
校验工具参数
控制副作用
验证工具结果
压缩上下文
限制资源消耗
处理异常路径
保留审计日志

也正因为如此,一个完整可用的 Agent,难度并不低。它不是“LLM 加几个工具”就能完成的,而是一个系统工程问题。

一句话总结:

Demo Agent 是脚本,产品级 Agent 是运行时系统,复杂 Agent 平台则接近一个小型操作系统。

如果未来大模型继续发展,Agent 的能力可能会逐渐内化进模型本身。但在真实工程中,只要 Agent 需要访问外部世界、调用工具、管理状态、保证安全和控制成本,Runtime 就依然不可或缺。

因此,真正有价值的 Agent 开发能力,不只是会写 Prompt,也不是只会接几个工具,而是能够从系统角度设计:

工具体系
状态流转
上下文管理
权限边界
错误恢复
成本控制
可观测性

这才是从 Demo Agent 走向产品级 Agent 的关键。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐