Agent和ChatGPT有什么区别?→ ReAct循环原理(不到50行代码)+FC vs MCP协议对比,附代码走读
Agent 的本质:ReAct 循环与 MCP 协议
问题场景:LLM 能回答问题,但不会主动查天气、调 API、执行多步任务——你需要 Agent。很多人以为 Agent 就是"更聪明的 ChatGPT",但 Agent 的本质是一个不到 50 行的 while 循环:思考→行动→观察→再思考。这篇文章把循环拆开给你看,再对比 Function Calling 和 MCP 两种工具调用协议的优劣。
30秒速览:ReAct 循环 = Thought(分析当前状态,决定下一步做啥)→ Action(调用工具执行)→ Observation(看工具返回的结果,决定下一步)→ 循环直到任务完成。核心代码不到 50 行,却能支撑从单工具查天气到多 Agent 协作的所有场景。Function Calling vs MCP——FC 是"告诉模型我能做什么"(OpenAI 定义的 API 协议,传 tools 数组→模型返回 tool_calls→你执行),MCP 是"告诉模型去哪找能做什么的东西"(Anthropic 定义的标准化协议,Server 独立进程,JSON-RPC 通信,支持工具发现→调用→结果返回完整链路)。选型口诀:单工具+单模型→FC 够用;多工具+多模型切换→MCP 是标准答案。
跟其他 Agent 文章不同:这篇不讲哲学不讲概念——直接从 ReAct 循环的 50 行 while 代码讲起,跑通后对比 FC 和 MCP 的架构差异,最后给出从单 Agent 到多 Agent 的演进路径。每一步都有可运行的代码。
系列第 3 篇(共 14 篇)。
⚠️ 时效性提示:本文基于 2026 年 7 月的技术状态撰写。大模型版本迭代迅速(通常 3-6 个月一次大版本更新),文中涉及的模型名称、API 端点及性能基准数据请以各厂商最新公告为准。建议重点关注文章中的架构原理与设计决策——这些内容具有更长的时效性。
一、Agent 的本质
三种 AI 能力的递进:
| 纯 LLM | RAG | Agent | |
|---|---|---|---|
| 能力 | 仅靠训练数据回答 | LLM + 检索外部知识 | LLM + 调用工具 + 多步推理 |
| 能做什么 | 回答问题 | 基于文档回答 | 执行任务(查数据库、调 API、操作文件) |
| 例子 | “什么是 MVCC” | “根据知识库文档回答锁升级过程” | “查表找出交易量最大的客户,生成 Excel 报表” |
Agent 的三个核心能力:
- 规划:把复杂任务拆成可执行的步骤
- 工具调用:自主决定何时调用什么工具
- 反馈循环:观察工具结果 → 调整下一步计划 → 继续执行
二、ReAct 模式深入
ReAct(Reasoning + Acting)是当前最主流的 Agent 推理范式:
循环结构(Thought → Action → Observation → 回到 Thought):
用户:"出 5 道 Java 多线程的单选题,难度中等"
→ Thought: 需要先从知识库检索多线程相关内容
→ Action: call rag_search(query="Java 多线程 知识点")
→ Observation: 返回 12 个文本片段(synchronized/ReentrantLock/线程池/volatile/AQS...)
→ Thought: 有足够的知识点,可以出题了。结合 LLM 知识出 5 道中等难度单选题。
→ Action: call exam_generation(
topics=["synchronized","ReentrantLock","线程池","volatile","AQS"],
questionType="单选", count=5, difficulty="中等"
)
→ Observation: 生成了 5 道题目
→ Thought: 题目生成完成,检查一下是否覆盖了要求的 5 个主题
→ Final Answer: (输出 5 道题目,标注每题来源)
和传统编程的本质区别:
- 传统:人思考 → 人写代码 → 人检查
- Agent:AI 思考 → AI 调工具 → AI 看结果 → AI 调整 → AI 输出
ReAct 的进化版:
- Plan-and-Execute:先一次性规划出完整步骤(Plan),再逐步执行(Execute),执行中发现问题就调整 Plan。适合复杂多步骤任务
- Reflexion:执行失败后不只重试,而是先反思"为什么失败"→ 把反思记入上下文,下次自动避免同类错误
三、Function Calling vs MCP
两者的关系不是对立,而是演进:
| 维度 | Function Calling | MCP |
|---|---|---|
| 定义者 | 各厂商各自定义(OpenAI/Anthropic 格式不同) | Anthropic 提出的开放标准,社区共建 |
| 工具描述 | 写在每次 API 请求的 tools 参数里 | Server 启动时通过 tools/list 动态提供 |
| 工具发现 | 应用代码硬编码工具列表 | 连接到 Server 时自动发现可用工具 |
| 新增工具 | 改代码 + 重新部署 | 启动新 MCP Server 即可(热插拔) |
| 生态 | 厂商锁定 | 社区共建,已有 10000+ Server |
PrismAI 为什么 V1 用 Function Calling:
- 工具集固定(RAG 检索/AI 搜索/题库查询/代码沙箱),不会频繁变动
- Spring AI 的 ToolCallback 机制开发效率高,
AgentTool接口注册到AgentToolRegistry后自动适配 - 个人项目单机部署,MCP 每个 Server 独立进程增加运维负担
V2 改造方向:
当前(FC):Agent → @Tool 注解的 Java 方法 → 直接调用
V2(MCP):Agent → MCP Client → MCP Server(RAG检索) → 向量库
→ MCP Server(题库) → MySQL
→ MCP Server(沙箱) → Docker
改造后的价值:
- 工具热插拔:新增"简历评估服务"作为独立 MCP Server,不停机上线
- 工具复用:RAG 检索 MCP Server 可被其他应用直接复用
- 协议演进意义:“V1 用 FC 快速落地,V2 改造 MCP 开放生态”
Function Calling vs MCP = 各厂商的私有充电口 vs USB-C。前者够用但换手机得换线,后者一次适配到处能用。关键是知道什么时候用什么——初期 FC 快速验证,规模上来切 MCP。
四、多 Agent 协作模式
单 Agent 能解决大部分问题,但当任务规模超过单一上下文的承载能力时,需要多 Agent 协作。
四种经典模式:
| 模式 | 结构 | 适合场景 | 例子 |
|---|---|---|---|
| 顺序流水线 | A→B→C 串行 | 步骤有明确依赖关系 | 需求→设计→编码→测试→审查 |
| 辩论模式 | 多 Agent 独立分析→互相质疑→收敛 | 高风险决策 | 技术方案评审、安全审查 |
| 投票/集成 | 多 Agent 独立输出→汇总比较 | 有多个可行解的开放问题 | Bug 根因分析、SQL 优化方案 |
| 分层委派 | 主 Agent 拆任务→N 个子 Agent→汇总 | 大型跨模块任务 | 项目框架升级 |
顺序流水线——Code Review 是最典型的例子:Agent A 做 lint 检查和代码风格校验,输出修改建议;Agent B 对修改后的代码做安全扫描(SQL 注入、XSS);Agent C 生成可读性报告,最终交由人类审查。每一步的输出是下一步的输入,串行推进。主要风险:误差传播——上游 Agent 的一处误判会逐级放大,到流水线末端可能已偏离原始意图。对策是在每个环节保留原始输入,让下游 Agent 能回溯对比,而非只看上一环的输出。
辩论模式——适合架构决策这类没有标准答案的场景。例如:Agent A 提出"用 Redis Cluster 做分布式缓存",Agent B 扮演反方质疑"数据量大时集群扩容有脑裂风险,是否考虑 Redis + 本地 Caffeine 两级缓存?“双方交替发言,直到观点收敛或达到预设轮次上限。主要风险:无限辩论——两个 Agent 各执一词、反复纠缠细节却不收敛。对策是设置最大辩论轮次(通常 3-5 轮),并定义明确的收敛判据(如"剩余分歧不影响可执行性”),超时由人拍板。
投票/集成——Bug 根因分析场景:同一个生产异常,3 个 Agent 分别从日志堆栈、数据库慢查询、近期代码变更三个维度独立分析,各自输出一份根因假设和证据链,最后由汇总 Agent(或人)对比三份结论,取交集最高的原因为主方向。主要风险:群体盲从——如果三个 Agent 基于相似训练数据得出结论,投票只是把同一个偏见复读了三次,并非真正的多角度验证。对策是给各 Agent 分配不同的分析视角和工具集,确保输入多样化。
分层委派——大型重构场景:主 Agent(协调者)收到"将单体应用拆分为微服务"的需求,拆出用户模块、订单模块、支付模块三个子任务,分别委派给三个子 Agent 独立完成。每个子 Agent 在自己的限界上下文内分析代码、生成重构方案,主 Agent 汇总后检查模块间接口兼容性。主要风险:子 Agent 幻觉不被察觉——子 Agent 可能虚构了不存在的 API 或错误理解了业务逻辑,主 Agent 若不做校验直接汇总,错误就会进入最终方案。对策是主 Agent 对子 Agent 的输出做交叉验证(如检查引用的类和方法是否真实存在于代码库中),关键模块由人复审。
关键原则:人永远是总指挥——任务的拆解边界、验收标准、最终决策权在人手上。多 Agent 协作的价值是放大人的决策带宽,而非替代人的判断。AI 是执行者,不是决策者。
五、语义缓存与降级策略
两个容易被忽视但实际价值极大的工程手段。
语义缓存:
- 不是缓存 key-value 字符串,是缓存"语义相似的问题"
- “synchronized 原理” = “Java 内置锁的工作机制” → 语义相同 → 缓存命中
- 相似度阈值 ≥ 0.95 直接返回,不走 LLM
- BYOK 模式下,每省一次 LLM 调用 = 帮用户省钱
降级策略(PrismAI 的三级降级):
RAG 检索
├── 返回结果充足 → 基于知识库生成,标注来源
├── 返回结果不足 → 降级到 LLM 训练数据 + 提示"以下答案基于通用知识,非您的资料"
└── RAG 不可用(异常)
├── LLM 训练数据 → 降级兜底
├── 两者均不可用 → 提示用户"服务暂不可用,请稍后重试"
└── 绝不让用户看到 500 / 空白页
六、实战:PrismAI Agent + ReAct 代码走读
PrismAI 的 Agent 实现完整展示了如何用 Spring AI 构建 ReAct 循环。
架构设计:
mindforge-agent 模块(独立 Maven 模块,不依赖 mindforge-service)
AgentTool (接口)
├── getName() → "rag_search"
├── getDescription() → "从用户知识库中检索..."(LLM 决策依据)
├── getParametersSchema() → JSON Schema(LLM 理解参数格式)
└── execute(Map params) → ToolResult(实际执行逻辑)
AgentToolRegistry (注册中心)
├── Spring 自动注入所有 AgentTool Bean
├── buildToolsDescription() → 生成工具列表文本,注入 System Prompt
└── get(String name) → 按名查找工具
ReActLoop (循环调度器)
├── buildInitialMessages() → System Prompt + 对话历史 + 用户查询
├── buildToolCallbacks() → AgentTool → Spring AI ToolCallback 适配
└── execute() → ChatClient.prompt().tools(...).call().chatResponse()
关键设计:AgentTool → ToolCallback 适配(ReActLoop.java:142-193):
// 将 AgentTool 适配为 Spring AI 2.0 ToolCallback
// 核心是三个动作:
// 1. 提供 ToolDefinition(名/描述/参数Schema)—— LLM 用来决定是否调用
// 2. 实现 call(String input) —— Spring AI 自动解析 LLM 的工具调用请求
// 3. 合并系统注入参数(_provider, _apiKey, _directionId)—— 这些是 LLM 不知道的
ToolCallback callback = new ToolCallback() {
@Override
public ToolDefinition getToolDefinition() {
return DefaultToolDefinition.builder()
.name(tool.getName())
.description(tool.getDescription())
.inputSchema(tool.getParametersSchema())
.build();
}
@Override
public String call(String input) {
Map<String, Object> params = new HashMap<>(JSON.parseObject(input, Map.class));
// 合并系统注入的额外参数
if (context.getExtraToolParams() != null) {
params.putAll(context.getExtraToolParams());
}
// 发送 SSE thinking 事件 → 前端展示"正在检索知识库..."
emitThinking(context, 0, tool.getName(), "started", null);
ToolResult result = tool.execute(params);
emitThinking(context, 0, tool.getName(),
result.isSuccess() ? "completed" : "failed", buildSummary(result));
return result.isSuccess() ? result.getData() : "ERROR: " + result.getErrorMessage();
}
};
ReActContext(ReActContext.java):
// 封装一次 Agent 调用所需的全部参数
ChatModel chatModel; // 由调用方通过 LlmChatRouter 获取
String systemPrompt; // 含工具列表 + 角色定义 + 行为规则
String userQuery; // 用户输入的原始问题
List<Message> history; // 对话历史(多轮对话)
Map<String, Object> extraToolParams; // 系统注入参数(_provider, _apiKey 等)
Consumer<ThinkingEvent> thinkingCallback; // SSE 事件回调
int maxIterations = 10; // 兜底:单次最多 10 步
int toolTimeoutSeconds = 30; // 单次工具调用 30 秒超时
工具列表(已实现的 3 个 + 设计中的 3 个):
| 工具 | 类 | 功能 |
|---|---|---|
rag_search |
RagSearchTool | 知识库语义检索 |
ai_search |
AiSearchTool | 外部网络搜索(DuckDuckGo 免费兜底,按 Provider 路由) |
question_bank_query |
QuestionBankQueryTool | 历史题库查询 |
exam_generation |
(设计中) | 根据检索结果生成考题 |
answer_evaluation |
(设计中) | 评估用户答案,输出评分+建议 |
follow_up_decision |
(设计中) | 从四维度判断是否继续追问 |
上下文窗口管理(ContextWindowManager.java):
- 会话超过 20 轮时,自动摘要前 10 轮
- 摘要注入为 SystemMessage,后续轮次基于摘要继续
- 摘要 LLM 调用失败时降级为简单截断(保留最近 10 轮)
核心要点回顾
- Agent 的本质 = 规划 + 工具调用 + 反馈循环。它是从"聊天机器人"到"能干活的系统"的关键跃升
- ReAct(Thought→Action→Observation) 是当前最主流的 Agent 推理范式,PrismAI 完整实现了这个循环
- FC vs MCP = 私有充电口 vs USB-C——初期 FC 快速验证,规模上来切 MCP
- 语义缓存是省钱最重要的手段——考试场景下高频问题重复率很高,缓存命中直接省掉 LLM 调用
- AgentTool → ToolCallback 的适配展示了如何将自定义工具接口接入 Spring AI 框架——中间通过系统注入参数解耦 Agent 层和 Service 层
上一篇:《RAG从入门到工程落地:文档切分+Embedding选型+CRAG纠错》 | 下一篇:《AI工程化实践:安全/成本/可观测性/部署全景复盘》
系列专栏:AI专栏
💬 聊聊你的经历:你做过的 Agent 项目,工具调用用的是 Function Calling 还是 MCP?从单工具升级到多工具时遇到了什么问题?我先说:一开始用 FC 挺好的,工具多了以后 tools 数组膨胀、每个模型格式还不一样——换 MCP 之后标准化了,但 MCP Server 的进程管理又是个新坑。你呢?
如果这篇文章帮你看懂了 ReAct 循环的本质和 FC vs MCP 的选型逻辑,欢迎收藏+点赞 🙏
更多推荐




所有评论(0)