把 GPT-4 关在一个没有输入输出的房间里,它能写诗、能解方程、能编代码------但它什么也做不了。没有眼睛看屏幕,没有手去点鼠标,没有记忆记住昨天用户说了什么,没有工具可以调用。这就是当前大语言模型的处境:拥有一颗超强的大脑,却没有一个身体。 Agent 要解决的,正是这个问题。本文是 Agent 基础系列的开篇,先把"Agent 到底是什么、为什么需要它"这件事讲清楚,建立起整体认知框架。
一、LLM 的困局:能想,但不能动
打开任何一个大模型的对话界面,输入一段 prompt,几秒后拿到回复------这就是绝大多数人与 LLM 交互的全部。看起来已经很厉害了,但仔细想一下这个过程的本质:
输入一段文本 → 输出一段文本。仅此而已。
模型不会主动去查数据库,不会帮你打开浏览器下单,不会记住你上周说过的偏好,更不会在任务出错时自己调整方案。它是一个极其强大的"推理引擎",但推理的起点和终点都是文本------它被锁在一个纯符号的世界里。
这种"有脑无身"的状态,在工程上带来三个根本性问题:
1. 无法感知环境:模型不知道当前几点了、不知道用户的文件在哪里、不知道上游系统返回了什么状态码。它只能处理你塞进 prompt 的信息,其余一概不知。
2. 无法采取行动:模型可以告诉你"建议执行 SQL 查询",但它自己不会执行。它给出的永远是一个"建议",而不是一个"结果"。从建议到结果之间,还需要人去做最后一步。
3. 没有持续记忆:每次 API 调用都是独立的,模型不记得上一轮对话。所谓"对话上下文",是应用层把历史消息重新拼好再传一遍------这不是记忆,这是每次考试都把草稿纸重抄一遍。
这三个问题的本质是一样的:LLM 缺少与外部世界的闭环交互能力。 Agent 就是为了补上这块拼图。
二、Agent 的本质:LLM + 感知 + 行动 + 记忆
"Agent"这个概念不是 LLM 时代才出现的。早在 1997 年,Franklin 和 Graesser 就给出了自主 Agent 的经典定义 1:一个身处环境中、能感知环境、能作用于环境、随时间推进自身目标的系统。
到了 LLM 时代,OpenAI 研究员 Lilian Weng 在 2023 年发表了一篇被广泛引用的博文 2,给出了一个更具体的架构分解:
Agent = LLM(大脑) + Planning(规划) + Memory(记忆) + Tool Use(工具使用)
LLM 负责推理和决策,其余三个模块分别解决"怎么拆解任务"、"怎么记住经验"和"怎么扩展能力"的问题。
如果用人体来类比,整个架构就很直观了:
| Agent 组件 | 人体对应 | 功能 |
|---|---|---|
| LLM | 大脑(前额叶) | 推理、规划、决策------理解目标、拆解步骤、做出判断 |
| 感知(Perception) | 眼睛、耳朵 | 读取屏幕、解析文件、接收用户指令、获取系统状态 |
| 行动(Action / Tools) | 手、嘴 | 调用 API、操作浏览器、执行代码、写入数据库------对世界产生副作用 |
| 记忆(Memory) | 工作记忆 + 长期记忆 | 工作记忆 = 当前对话上下文;长期记忆 = 用户偏好、历史经验 |
| 环境(Environment) | 外部世界 | 操作系统、文件系统、Web 应用、第三方服务------Agent 行动的对象 |
没有 LLM,系统只是一个自动化脚本;没有感知、行动、记忆,LLM 只是一个放在培养皿里的大脑。Agent 的本质,就是给大脑装上身体,让它从"能想"变成"能干"。
三、Agent 运行时架构:一张图看懂核心组件
在深入代码之前,先用一张架构图建立全局视角。一个典型的 Agent 运行时由以下组件组成:
bash
┌─────────────────────────────────────────────────────────────────┐
│ Agent Runtime │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Execution Loop(执行循环) │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ │
│ │ │ Thought │───►│ Action │───►│ Observation │ │ │
│ │ │ (LLM推理) │ │(工具调用) │ │ (结果解析) │ │ │
│ │ └──────────┘ └──────────┘ └──────────────┘ │ │
│ │ ▲ │ │ │
│ │ │ Loop until done │ │ │
│ │ └─────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────┐ ┌──────────────┐ ┌────────────────┐ │
│ │ LLM │ │ Tool Registry│ │ Memory │ │
│ │ (推理引擎) │ │ (工具注册表) │ │ (记忆系统) │ │
│ │ │ │ │ │ │ │
│ │ - Chat API │ │ - 工具描述 │ │ - 短期:上下文 │ │
│ │ - Tool Call │ │ - 参数Schema │ │ - 长期:向量库 │ │
│ │ - Streaming │ │ - 调用适配器 │ │ - 摘要:压缩缓存│ │
│ └─────────────┘ └──────────────┘ └────────────────┘ │
│ │ │ │ │
└─────────┼──────────────────┼──────────────────┼─────────────────┘
│ │ │
▼ ▼ ▼
┌─────────┐ ┌────────────┐ ┌──────────┐
│模型服务 │ │ 外部工具 │ │ 存储后端 │
│OpenAI │ │ - REST API │ │ - Redis │
│Claude │ │ - DB Query │ │ - PgVec │
│通义千问 │ │ - Browser │ │ - 文件系统│
└─────────┘ └────────────┘ └──────────┘
四个核心组件的职责:
- Execution Loop:Agent 的"心跳",驱动 Thought → Action → Observation 循环,直到任务完成或触发终止条件
- LLM:推理引擎,接收当前上下文(用户输入 + 工具结果 + 历史),输出下一步决策(继续调用工具 or 返回最终答案)
- Tool Registry:工具注册表,管理所有可用工具的名称、描述、参数 Schema 和执行逻辑。LLM 通过 Function Calling 机制与它交互
- Memory:记忆系统,短期记忆维护当前对话上下文(直接塞进 prompt),长期记忆通过向量检索增强(本系列的 RAG 和 Memory 专题会深入展开)
关键设计决策:Execution Loop 不在 LLM 内部,而是在应用层。LLM 只负责单次推理,"要不要继续循环、什么时候停"是应用层的逻辑。这个解耦是理解 Agent 架构的关键------LLM 是引擎,Loop 是方向盘。
四、Chatbot vs Agent:不是程度差异,是范式差异
很多人会把 Agent 理解为"更高级的聊天机器人"。但从架构层面看,两者是完全不同的范式:
| 维度 | Chatbot | AI Agent |
|---|---|---|
| 核心功能 | 回应消息 | 完成目标 |
| 交互模式 | 反应式:输入 → 输出 | 自主式:感知 → 规划 → 行动 → 观察 |
| 工具使用 | 极少或无 | 核心能力 |
| 多步执行 | 否,单次推理 | 是,循环调用工具直到完成 |
| 错误恢复 | 需要人重新提示 | 任务内自我修正 |
| 副作用 | 默认无 | 可产生真实世界行动(发邮件、改数据库) |
一句话总结:Chatbot 回应问题,Agent 完成目标。 一个是你问它答,一个是你说目标它干活。
但 Agent 也不是非黑即白的概念,它是一个光谱------最左边是纯规则聊天机器人(关键词匹配 → 预设回复),中间是带插件的 Chatbot(能调用一两个工具,但不自主循环),最右边是完全自主的 Agent(可以连续运行数小时、执行数十次工具调用、无需人在环)。当前绝大多数生产级产品处于中间偏右的位置。
五、ReAct:让推理"接地"的核心循环
Agent 和 Chatbot 最大的架构差异在于:Agent 不是"想完再说",而是"边想边做边看"。这个循环有一个正式的名字------ReAct(Reasoning + Acting) 3。
ReAct 的核心循环只有三步:
Thought(思考)→ Action(行动)→ Observation(观察)→ 重复
看起来很简单,但它的核心洞察是:推理帮助行动,行动也帮助推理。 推理让模型制定计划、处理异常;行动让模型与外部世界交互,获取新信息来支撑下一步推理。两者互为补充,形成闭环。
为什么这个循环如此重要?因为纯推理(Chain-of-Thought)有一个致命缺陷:它是静态的。模型完全依赖内部知识,不与外部世界交互,一旦内部知识有误,错误就会一路传播下去。
ReAct 论文(Yao et al., ICLR 2023)的实验数据很说明问题:
| 方法 | 事实核查准确率 | 幻觉率 |
|---|---|---|
| 纯推理(CoT) | 56.3% | 14% |
| ReAct(推理+行动) | 60.9% | 6% |
| ReAct + 多路采样 | 64.6% | --- |
数据来源:ReAct 论文 Table 3 3
ReAct 的幻觉率仅为纯推理的不到一半。原因很直观:每一步推理之后,Agent 可以通过行动(比如搜索、查数据库)来验证自己的判断,而不是"蒙着头往下推"。
回到人类的做事方式,其实也是这样------你不会把一道菜的全部步骤在脑子里过一遍才动手,而是切一刀看看厚度、炒两步尝尝咸淡、随时调整。这种"边想边做边看"的模式,就是 ReAct 在 Agent 中的实现。
5.1 ReAct 循环的伪代码
在进入真实框架代码之前,先用伪代码看清 ReAct 循环的本质。无论用什么框架、什么语言,Agent 的核心循环都可以归结为:
bash
function agentRun(userQuery):
messages = [systemPrompt, userQuery]
loop:
response = llm.chat(messages, tools=registeredTools)
if response.hasToolCalls():
for each toolCall in response.toolCalls:
// 执行工具
result = toolRegistry.execute(
toolCall.name,
toolCall.arguments
)
// 把工具结果追加到消息列表
messages.append(toolResult(toolCall.id, result))
continue // 继续循环,让 LLM 看到结果后决定下一步
else:
// LLM 直接返回文本,没有工具调用 → 任务完成
return response.text
就这么简单。整个 Agent 的核心就是一个 while 循环:LLM 决定调用工具就执行工具、把结果喂回去继续跑;LLM 决定直接回答就结束循环。复杂的是工具和记忆,不是循环本身。
5.2 用 Java + Spring AI 实现最小 ReAct Agent
Spring AI 是 Spring 生态的 AI 集成框架。以下代码展示的是手动实现 ReAct 循环 的方式,目的是让你看清循环内部的每一步。Spring AI 2.0 已经通过 ToolCallingAdvisor 内置了这个循环------实际项目中你不需要手写,但理解内部机制对调试和定制至关重要。
bash
/**
* 最小 ReAct Agent 实现(Spring AI)
* 演示 Thought → Action → Observation 循环在 Java 中怎么跑
*/
@Service
public class ReActAgent {
private final ChatClient chatClient;
private final ToolRegistry toolRegistry;
private final int maxIterations = 10; // 防止死循环
public ReActAgent(ChatModel chatModel, ToolRegistry toolRegistry) {
this.toolRegistry = toolRegistry;
// 构建 ChatClient,注册所有可用工具
this.chatClient = ChatClient.builder(chatModel)
.defaultSystem("""
你是一个智能助手。请按照以下步骤处理用户请求:
1. 先分析问题,思考需要什么信息
2. 如果需要外部数据,调用合适的工具
3. 根据工具返回结果,继续分析或直接回答
4. 重复直到收集够信息,给出最终答案
""")
.defaultTools(toolRegistry.getAllToolCallbacks())
.build();
}
/**
* Agent 执行入口
* 核心:让 LLM 自主决定调用工具还是直接回答
*/
public String run(String userQuery) {
// 消息历史 = 短期记忆
List<Message> messages = new ArrayList<>();
messages.add(new UserMessage(userQuery));
for (int i = 0; i < maxIterations; i++) {
// 调用 LLM(带工具定义)
ChatResponse response = chatClient.prompt()
.messages(messages)
.call()
.chatResponse();
AssistantMessage assistantMsg = response.getResult().getOutput();
// 判断:LLM 是要调用工具,还是直接回答?
if (hasToolCalls(assistantMsg)) {
// --- Action 阶段:执行工具调用 ---
messages.add(assistantMsg); // 记录 LLM 的决策
for (ToolCall toolCall : assistantMsg.getToolCalls()) {
// Observation 阶段:获取工具执行结果
String result = executeTool(toolCall);
// 将工具结果追加到消息列表,供下一轮推理使用
messages.add(new ToolResponseMessage(
toolCall.id(),
toolCall.name(),
result
));
log.info("[ReAct 循环 #{}] 工具: {} → {}",
i, toolCall.name(), truncate(result, 200));
}
// continue → 回到循环顶部,让 LLM 看到工具结果
} else {
// --- 最终回答:LLM 认为任务完成 ---
log.info("[ReAct 循环 #{}] 任务完成,共 {} 轮迭代",
i, i + 1);
return assistantMsg.getText();
}
}
throw new AgentTimeoutException(
"Agent 在 " + maxIterations + " 轮内未能完成任务");
}
private String executeTool(ToolCall toolCall) {
try {
return toolRegistry.execute(
toolCall.name(),
toolCall.arguments()
);
} catch (Exception e) {
// 工具执行失败时,把错误信息返回给 LLM
// 让 LLM 决定是重试、换个工具、还是放弃
return "工具执行失败: " + e.getMessage()
+ "。请考虑其他方案。";
}
}
}
代码关键点解读:
- 循环是核心 :整个 Agent 就是一个
for循环 + 条件判断。LLM 返回 tool_calls → 执行工具 → 结果塞回 messages → 继续循环。LLM 返回纯文本 → 结束。 - LLM 是决策者:每次循环,LLM 看到完整的消息历史(包括之前的工具结果),自主决定下一步是调用工具还是给出最终答案。Agent 代码不做任何硬编码的流程控制。
- maxIterations 是安全阀:生产环境中必须设置最大迭代次数,防止 LLM 陷入死循环(反复调用同一个工具、或者在两个工具之间来回切换)。
- 工具错误不回退,交给 LLM 处理:工具执行失败时,把错误信息作为 Observation 返回给 LLM,让 LLM 决定下一步。这比硬编码的重试逻辑灵活得多。
5.3 用 Python + LangChain 实现同一件事
对比一下 Python 生态的实现,感受不同框架的风格差异:
bash
from langchain.agents import create_react_agent, AgentExecutor
from langchain_openai import ChatOpenAI
from langchain.tools import tool
# 定义工具(用装饰器,非常简洁)
@tool
def search_github_trending(language: str, period: str) -> str:
"""搜索 GitHub Trending 项目,返回项目名称、Star 数和描述"""
# 实际实现:调用 GitHub API
return f"找到 25 个 {language} 项目,本周最热: spring-ai (Star +1200)..."
@tool
def filter_ai_projects(project_list: str) -> str:
"""从项目列表中筛选 AI 相关项目"""
return "筛选出 8 个 AI 相关项目: spring-ai, langchain4j, ..."
# 定义 Agent(一行代码)
llm = ChatOpenAI(model="gpt-4o", temperature=0)
agent = create_react_agent(
model=llm,
tools=[search_github_trending, filter_ai_projects],
prompt=react_prompt_template # ReAct 格式的 prompt
)
# 执行(AgentExecutor 负责循环)
executor = AgentExecutor(agent=agent, tools=tools, max_iterations=10)
result = executor.invoke({"input": "查最近一周 GitHub 上 Star 增长最快的 Java AI 项目"})
对比两个版本,核心逻辑完全一样(LLM → 工具调用 → 结果喂回 → 循环),但风格差异很大:
- Spring AI:显式控制循环,类型安全,适合需要精细控制的场景
- LangChain:高度封装,几行代码搞定,但调试时需要理解框架内部的抽象层
六、主流 Agent 框架横评:LangChain vs LangGraph vs Spring AI vs AgentScope
不同框架对 Agent 的抽象方式差异很大。以下从开发者最关心的几个维度做横向对比:
6.1 Agent 定义方式
| 维度 | LangChain | LangGraph | Spring AI | AgentScope |
|---|---|---|---|---|
| 语言 | Python | Python | Java | Python / Java / TypeScript |
| Agent 定义 | create_agent() 函数式 |
图(Graph)+ 节点(Node) | ChatClient + @Tool 注解 |
ReActAgent / HarnessAgent(Java)/ DialogAgent(Python) |
| 控制流 | 框架内置循环 | 开发者自定义图结构 | 框架内置工具循环(ToolCallingAdvisor) |
框架内置循环 |
| 工具定义 | @tool 装饰器 |
@tool 装饰器 |
@Tool 注解 / ToolCallback |
Toolkit 注册(Python)/ @Tool 注解(Java) |
| 多 Agent | 需要手动编排 | 原生支持(多节点 = 多 Agent) | 需要自行实现 | 内置 Pipeline / MsgHub / A2A |
6.2 工具调用编排
LangChain :工具调用对开发者几乎透明。AgentExecutor 自动处理"LLM 要调用工具 → 执行 → 结果返回 LLM"的循环。好处是简单,坏处是调试困难------出问题时不知道循环内部发生了什么。
bash
# LangChain: 工具调用对开发者透明,一行 invoke 搞定
result = executor.invoke({"input": "今天北京天气怎么样?"})
# 中间发生了什么?你不知道(除非开 verbose=True)
LangGraph:把循环拆成显式的图节点,每个节点是一个处理步骤,边定义了控制流。开发者完全控制 Agent 的执行路径。
bash
from langgraph.graph import StateGraph, END
# LangGraph: 显式定义执行图
graph = StateGraph(AgentState)
graph.add_node("agent", call_model) # 节点1:LLM 推理
graph.add_node("tools", call_tools) # 节点2:执行工具
graph.add_edge("tools", "agent") # 工具结果 → 回到 LLM
graph.add_conditional_edges("agent", # LLM 决策分支
should_continue,
{"continue": "tools", "end": END}
)
app = graph.compile()
result = app.invoke({"messages": [HumanMessage(content="...")]})
Spring AI :工具定义用 Java 注解,强类型,IDE 友好。Spring AI 2.0 引入了 ToolCallingAdvisor(递归 Advisor),框架自动驱动工具调用循环------开发者只需定义工具并注册到 ChatClient,循环逻辑不再需要手写。
bash
// Spring AI: 用 @Bean 注册工具,类型安全
@Bean
@Description("搜索 GitHub Trending 项目") // 工具描述,传给 LLM
public Function<GithubRequest, String> searchGithub() {
return request -> githubService.getTrending(
request.language(), request.period());
}
AgentScope :面向多 Agent 场景设计,通过 MsgHub 实现多 Agent 之间的消息广播和协调。
bash
from agentscope.agents import DialogAgent
from agentscope.service import ServiceToolkit
# AgentScope: 面向多 Agent 的对话编排
toolkit = ServiceToolkit()
toolkit.add(search_github)
agent1 = DialogAgent("planner", model_config_name="gpt-4",
service_toolkit=toolkit)
agent2 = DialogAgent("executor", model_config_name="gpt-4",
service_toolkit=toolkit)
# Pipeline 串行执行,MsgHub 广播通信
pipeline = SequentialPipeline([agent1, agent2])
6.3 记忆管理对比
| 框架 | 短期记忆 | 长期记忆 | 记忆管理方式 |
|---|---|---|---|
| LangChain | ConversationBufferMemory(全量保留) |
需自行集成向量库 | 提供 Memory 抽象类,但实现需要开发者接入 |
| LangGraph | State 对象中的 messages 列表 |
通过 Checkpoint 持久化 State | 显式状态管理,每个节点读写 State |
| Spring AI | MessageWindowChatMemory(滑动窗口) |
VectorStore 接口(支持 PgVector / Milvus) |
接口抽象好,但自动摘要等高级功能需自行实现 |
| AgentScope | Agent 内部 memory 队列 |
需自行集成 | 提供 Memory 基类,支持自定义序列化 |
选型建议:如果你的项目是 Java 技术栈、需要强类型和 IDE 支持 → Spring AI。如果是 Python 技术栈、需要快速原型 → LangChain。如果需要精细控制执行流(比如复杂的多 Agent 编排、条件分支、人机交互节点) → LangGraph。如果核心需求是多 Agent 协作 → AgentScope 值得尝试。
七、从"能推理"到"能行动":Agent 能力的三次跃迁
回顾 Agent 的发展脉络,能力的跃迁可以分成三个阶段:
7.1 第一阶段:函数调用(2023)
2023 年,OpenAI 在 GPT-3.5/4 中引入 Function Calling,让模型能够输出结构化的函数调用请求。这是 Agent 从"纯文本生成"走向"工具调用"的第一步。LangChain 等框架迅速跟进,Agent 开发者可以通过预定义的 API 来扩展模型能力。这个阶段的核心模式是"为模型造工具"------每个能力都需要专门定义接口、写好描述、注册到模型中。
7.2 第二阶段:操作电脑(2024-2025)
2024 年 10 月,Anthropic 发布 Computer Use 5,让 Claude 直接看屏幕截图、计算鼠标坐标、点击按钮和输入文字。这意味着 Agent 不再需要为每个软件写专门 API------它可以直接像人一样操作任何有图形界面的软件。OpenAI 在 2025 年初跟进发布了 Operator 8(搭载 CUA 模型),进一步验证了这条路线。
这个阶段的范式转变是:从"为模型造工具"到"让模型学会用人的工具"。 Agent 可以操作人类已经用了几十年的软件,而不需要等每个软件开放 API。
7.3 第三阶段:套件化场景落地(2026)
2026 年,Agent 赛道进入"场景深耕"阶段。国内以百度搭子为代表,一个月内连推自媒体、设计、金融三个垂直套件 7,把通用 Agent 能力封装为"一个套件解决一类问题"的产品形态。国外 OpenAI 把 Operator 整合进 ChatGPT Agent,Anthropic 通过 API 让开发者广泛接入 Computer Use。
IDC 在 WAIC 2026 上首发的《DAA研究报告》9 预测,全球活跃 Agent 数量将从 2025 年的 2860 万增长到 2030 年的 22.16 亿。Agent 正在从极客玩具变成生产力基础设施。
八、一个真实例子:ReAct 循环怎么跑起来的
抽象概念说完了,用一个具体例子来看 Agent 是怎么工作的。
假设用户说:"帮我查一下最近一周 GitHub 上 Star 增长最快的 Java AI 项目,整理成表格。"
Chatbot 的做法:输出一段文字建议------"你可以去 GitHub Trending 页面查看,或者用 GitHub API 自己写个脚本......"然后结束。用户还得自己去做。
Agent 的做法则完全不同:
- Thought 1:用户要查 GitHub 上 Java AI 项目近一周的 Star 增长。我需要先获取 GitHub Trending 数据。
- Action 1:调用浏览器工具,打开 GitHub Trending 页面,筛选 Java 语言、时间范围选 This Week。
- Observation 1:页面加载完成,获取到前 25 个项目的名称、描述、Star 数。
- Thought 2:拿到了项目列表,但用户要的是"增长最快",需要筛选出 AI 相关的项目。我来逐个分析描述。
- Action 2:对每个项目的描述做关键词匹配 + 语义判断,筛选出 AI/LLM/RAG/Agent 相关项目。
- Observation 2:筛选出 8 个 AI 相关项目。
- Thought 3:数据收集完成,现在生成 Markdown 表格。
- Action 3:生成表格并返回给用户。
整个过程经历了 3 轮 Thought → Action → Observation 循环,调用了浏览器、语义分析、文件生成等多个工具。这就是 ReAct 循环在实际 Agent 中的运作方式------不是"一次性想好再输出",而是"想一步做一步看一步"。
8.1 从开发者视角看同一件事
作为开发者,你在日志中看到的 ReAct 循环执行过程是这样的:
bash
[Agent] 收到用户请求: "查最近一周 GitHub 上 Star 增长最快的 Java AI 项目"
[ReAct #1] LLM 决策: 调用 search_github_trending(language="java", period="weekly")
[ReAct #1] 工具返回: "找到 25 个项目: spring-ai(+1200⭐), langchain4j(+890⭐)..."
[ReAct #1] Token 消耗: prompt=1,200 + completion=350 = 1,550
[ReAct #2] LLM 决策: 调用 filter_ai_projects(projects="spring-ai,langchain4j,...")
[ReAct #2] 工具返回: "AI 相关 8 个: spring-ai, langchain4j, deeplearning4j..."
[ReAct #2] Token 消耗: prompt=2,100 + completion=280 = 2,380
[ReAct #3] LLM 决策: 直接回答(不调用工具)
[ReAct #3] 生成最终 Markdown 表格
[Agent] 任务完成,共 3 轮迭代,总 Token 消耗: 5,480
每一轮循环的 Token 消耗是累加的------因为每一轮都需要把之前的所有消息历史重新传给 LLM。这就是为什么 Agent 的 Token 消耗远大于单次 Chatbot 调用,也是为什么成本控制是生产级 Agent 必须面对的工程问题。
九、生产级 Agent 的工程挑战
把 Agent 从 Demo 带到生产,会碰到一系列 Demo 中不会遇到的问题。以下是最关键的四个。
9.1 错误恢复:工具调用失败怎么办
在 Demo 中,工具调用总是成功的。在生产中,工具会超时、会返回错误、会返回不符合预期的数据。
核心原则:把错误信息返回给 LLM,让它自己决定怎么处理。
回到第五节的 Java 代码,executeTool 方法中的错误处理已经体现了这个思路:
bash
private String executeTool(ToolCall toolCall) {
try {
return toolRegistry.execute(toolCall.name(), toolCall.arguments());
} catch (TimeoutException e) {
return "工具 " + toolCall.name() + " 执行超时(30s),"
+ "建议:1) 缩小查询范围重试 2) 换用其他数据源";
} catch (Exception e) {
return "工具执行失败: " + e.getMessage()
+ "。请考虑其他方案。";
}
}
关键设计:
- 不让工具异常中断 Agent 循环。异常被捕获,转化为"Observation"返回给 LLM
- 给 LLM 提供恢复建议。不是简单地说"失败了",而是告诉它"可以尝试什么"
- LLM 通常能做出合理的恢复决策------换一个工具、调整参数重试、或者告诉用户"我做不到"
但也要注意:LLM 可能在恢复时陷入死循环(反复用相同参数重试同一个失败的工具)。所以 maxIterations 限制和"重复工具调用检测"是必须的。
bash
// 检测重复调用:如果连续 2 次调用同一工具且参数相同,强制终止
private Set<String> recentCalls = new HashSet<>();
private String executeToolWithDedup(ToolCall toolCall) {
String callSignature = toolCall.name() + ":" + toolCall.arguments();
if (recentCalls.contains(callSignature)) {
return "检测到重复调用。该工具已用相同参数调用过一次且"
+ "未成功。请更换策略或告知用户当前限制。";
}
recentCalls.add(callSignature);
// ... 执行逻辑
}
9.2 成本控制:Token 消耗的优化
Agent 的 Token 消耗是"乘法效应"的:每一轮循环都要把完整的历史消息传给 LLM。3 轮循环的 Token 消耗不是 3 倍,而是 1+2+3=6 倍(因为每轮的消息数在增长)。
实际优化手段:
| 策略 | 原理 | 效果 |
|---|---|---|
| 滑动窗口 | 只保留最近 N 轮消息,丢弃更早的 | 减少 30-50% Token |
| 工具结果压缩 | 工具返回的原始数据太大时,先做摘要再塞入上下文 | 减少 50-80% Token |
| 选择更便宜的模型 | 简单步骤用小模型(如 GPT-4o-mini),关键决策用大模型 | 减少 60-90% 费用 |
| 缓存重复查询 | 相同工具+相同参数 → 返回缓存结果 | 减少重复调用的 Token |
Spring AI 中的滑动窗口记忆实现:
bash
// 只保留最近 20 条消息,防止上下文无限膨胀
ChatMemory memory = MessageWindowChatMemory.builder()
.chatMemoryRepository(new InMemoryChatMemoryRepository())
.maxMessages(20)
.build();
工具结果压缩的示例------不要把整个网页塞进去:
bash
@Tool(description = "搜索网页内容")
public String searchWeb(String query) {
String rawHtml = httpClient.get(url);
// 不要直接返回 rawHtml(可能有 50KB)
// 提取关键信息,压缩到 2KB 以内
return extractAndSummarize(rawHtml, query);
}
9.3 超时处理:Agent 不能无限跑下去
生产环境中,一个 Agent 任务可能需要 10 秒到 5 分钟不等。必须设置多层超时:
bash
public class AgentConfig {
// 第一层:单次 LLM 调用超时
private Duration llmCallTimeout = Duration.ofSeconds(30);
// 第二层:单次工具调用超时
private Duration toolCallTimeout = Duration.ofSeconds(60);
// 第三层:整个 Agent 任务超时
private Duration agentTimeout = Duration.ofMinutes(5);
// 第四层:最大迭代次数
private int maxIterations = 10;
}
每一层超时触发后的处理方式:
- LLM 调用超时:重试一次,仍然超时则终止任务,返回已有的中间结果
- 工具调用超时:把超时信息返回给 LLM,让它决定是否换方案
- Agent 总超时:强制终止,返回当前最佳结果 + 说明哪些步骤未完成
9.4 幂等性设计:重复执行不能出事故
Agent 会调用工具产生"副作用"(发邮件、写数据库、创建订单)。如果因为重试或异常恢复导致工具被重复调用,必须保证幂等性。
bash
@Tool(description = "发送邮件给指定收件人")
public String sendEmail(String to, String subject, String body) {
// 幂等性设计:用请求 ID 去重
String requestId = generateRequestId(to, subject, body);
if (processedRequests.contains(requestId)) {
return "邮件已发送(检测到重复调用,跳过)";
}
emailService.send(to, subject, body);
processedRequests.add(requestId);
return "邮件发送成功";
}
生产环境中的幂等性方案:
- 写操作:用唯一请求 ID 去重,或者用数据库唯一约束
- 查询操作:天然幂等,但要注意缓存一致性
- 不可逆操作(如删除):增加确认步骤,或在 Agent 循环中设置"人工审批"节点
十、这个框架在系列中的位置
本文建立了 Agent 的整体认知框架。这个框架不是孤立的------此前已发布的每个专题系列,都在这个框架上深入了一个具体模块:
| 系列 | 对应 Agent 组件 | 解决什么问题 |
|---|---|---|
| RAG 系列 | Memory(长期记忆) | Agent 怎么从海量文档中精准找到需要的知识?怎么做检索增强? |
| MCP 系列 | Tool Use(工具使用) | Agent 怎么用统一协议连接外部工具和数据源?MCP 解决了什么问题? |
| Memory 系列 | Memory(多层记忆) | Agent 怎么跨会话记住用户偏好?怎么从经验中学习?记忆的工程分层怎么做? |
而 Agent 基础篇本身,接下来还会继续拆解两个关键话题:
| 本篇 | 内容 |
|---|---|
| AGENT-02 | 四大核心机制全景图------Planning、Tool Use、Memory、Reflection 各自怎么工作,怎么协作 |
| AGENT-03 | 从单 Agent 到多 Agent------为什么需要多 Agent?怎么编排?和单 Agent 的本质区别是什么? |
理解了今天的整体框架,再去看每个系列的具体实现,就会清楚它们各自在 Agent 架构中扮演什么角色。
结语
LLM 给了 AI 一颗强大的大脑,但大脑不能独立存在------它需要感知来理解世界,需要行动来改变世界,需要记忆来积累经验,需要一个完整的闭环来持续运转。这就是 Agent 的本质:不是一个新的模型,而是一种新的架构范式------让 LLM 从"能说"进化为"能做"。
从代码层面看,Agent 的核心就是一个 while 循环------但这个循环让 LLM 从"被动回答问题"变成了"主动完成目标"。真正的工程挑战不在循环本身,而在循环周围的一切:工具的可靠性、记忆的管理、成本的控制、错误的恢复、副作用的安全。
下一篇,我们进入 Agent 基础篇第二篇,拆解 Planning、Tool Use、Memory、Reflection 四大核心机制------看看这个"身体"的每个器官具体怎么工作。
有问题评论区见,欢迎交流~
参考资料:
1 Franklin, S. & Graesser, A. (1997). Is it an Agent, or just a Program?: A Taxonomy for Autonomous Agents. Proceedings of the Third International Workshop on Agent Theories, Architectures, and Languages (ATAL '96), Springer LNAI Vol.1388. cs.memphis.edu/~franklin/a...
2 Lilian Weng, "LLM Powered Autonomous Agents". LLM Powered Autonomous Agents | Lil'Log
3 Yao et al. (2023). ReAct: Synergizing Reasoning and Acting in Language Models. ICLR 2023. arxiv.org/pdf/2210.03...
4 Lei Wang et al. (2024). A survey on large language model based autonomous agents. Frontiers of Computer Science 2024. www.researchgate.net/publication...
5 Anthropic, "Developing a computer use model". Developing a computer use model \ Anthropic
6 Catio, "Agentic AI Reference Architecture". Agentic AI Reference Architecture: Components & Layers | Catio
7 央广网,百度搭子 WAIC 2026 报道. 2030年全球DAA将超22亿个 百度AI三大发布亮相WAIC_央广网
8 Presenc AI, "OpenAI Operator Update Tracker". OpenAI Operator Update Tracker: From Operator to ChatGPT Agent (2026) | Presenc AI
9 IDC,《DAA研究报告》, WAIC 2026 首发. 36kr.com/newsflashes...
