← 返回 AI应用开发图解 目录
🤖 Agent开发模式图解
从"能聊天"到"能干活" · ReAct/RAG/FunctionCalling/Multi-Agent · 主流框架对比与实战代码
📍 真实场景 · 2025年某互联网公司 · AI产品团队
🔴 "AI客服只会说漂亮话,但解决不了实际问题"
用户问:"我上周买的订单什么时候发货?"
普通AI回答:"一般来说,订单会在1-3个工作日内发货,具体时间取决于您的所在地区..."(❌ 套话!用户想查的是他的具体订单)
产品经理拍桌子:"我要的不是会聊天的机器人!我要它能:查订单系统、调物流API、操作数据库——真正帮用户解决问题!"
💥 核心矛盾:普通LLM = 只能"说话"(基于训练数据生成文字)
Agent = 能"干活"(调用工具、执行代码、操作真实系统)
→ 这就是为什么你需要理解 Agent 的核心原理和开发模式
🗺️ S1: 本页学习路线
推荐阅读顺序:按 S2→S3→...→S7 依次阅读,每节都有代码+图+类比
🤖 S2: Agent是什么 — 从"问答"到"干活"
Agent = 实习生
💬 普通AI对话 =
热线客服(只能回答问题,不能帮你做事)
🤖 Agent =
实习生(能理解任务、规划步骤、调用工具、自主执行)
| 🆚 对比维度 | 普通AI对话 | Agent |
| 能力边界 | 只能"说话"(生成文字) | 能"说话"+"做事"(调工具/执行) |
| 数据来源 | 只有训练数据(可能过时) | 可查实时数据(API/数据库/文件) |
| 执行方式 | 一问一答,被动响应 | 主动规划多步骤,自主循环直到完成 |
| 典型例子 | ChatGPT闲聊 | Cursor写代码 / Perplexity搜索 / Devin修Bug |
Agent三大要素闭环:不是一次性回答,而是不断感知→规划→执行→观察的循环
记住一句话:普通LLM是"嘴上功夫"(只输出文字),Agent是"手脑并用"(思考+调用工具)。
你在 Cursor 里让 AI 改代码——它不只是告诉你怎么改,而是直接改了文件、跑了测试、给你看结果——这就是 Agent。
面试口诀:Agent = LLM + Tool Calling + Memory + Planning Loop(四个组件缺一不可)
🔄
接下来:Agent的核心运行引擎到底是什么?—— S3 ReAct模式,
它是几乎所有Agent框架底层的统一运行范式(Thought→Action→Observation循环)
🔄 S3: ReAct模式 — Agent的核心运行引擎
ReAct = 思考→行动→观察 循环(侦探破案)
🤔 Thought(思考):"根据线索,凶手应该住在3楼"
🏃 Action(行动):"我去3楼搜查"(调工具)
👁️ Observation(观察):"发现血迹,指向304房间"
🤔 Thought(再思考):"继续搜304,找凶器"
🏃 Action(再行动):"搜查304房间"
👁️ Observation(再观察):"找到凶器!证据充分,结案!" → 输出最终答案
关键点:ReAct 不是走一遍就结束——它是循环的!每次Observation都会影响下一步Thought。
ReAct = Reasoning + Acting,每次循环都包含思考和行动,直到得出最终结论
ReActLoop.java — ReAct循环的伪代码实现
// =============================================
// ReAct 循环核心逻辑(所有Agent框架的底层都是这个)
// =============================================
public class ReActAgent {
public String run(String userQuery) {
String thought = ""; #1 当前思考内容
Object observation = null; #2 上一步的执行结果
List<String> history = new ArrayList<>(); #3 完整历史记录
for (int round = 1; round <= @MaxRounds(10); round++) {
// ===== Thought: 让LLM思考下一步该做什么 =====
thought = llm.think(
userQuery, # 用户原始问题
history, # 之前的所有Thought+Action+Observation
toolRegistry # 可用的工具清单
);
history.add("Thought" + round + ": " + thought);
// ===== 判断:是否已经可以给出最终答案? =====
if (thought.contains("FINAL_ANSWER") || thought.contains("任务完成")) {
return extractFinalAnswer(thought); #4 结束循环,返回结果
}
// ===== Action: 从Thought中解析出要调用的工具和参数 =====
ToolCall action = parseToolCall(thought);
history.add("Action" + round + ": " + action);
// ===== Observation: 真正执行工具,拿到结果 =====
observation = toolRegistry.execute(action);
history.add("Observation" + round + ": " + observation);
// 🔄 循环:把observation放回history,下一轮LLM会基于新信息重新思考
}
return "达到最大轮次限制,未完成任务"; #5 防止死循环!必须设上限
}
}
ReAct的3个关键设计要点:
1️⃣ History累积:每一轮的Thought/Action/Observation都追加到历史中,LLM能看到完整上下文
2️⃣ 最大轮次限制(@MaxRounds):防止Agent陷入无限循环(比如反复调同一个工具)
3️⃣ 终止条件判断:LLM在Thought中输出特殊标记(如FINAL_ANSWER)表示"我可以给答案了"
❌ 常见错误:不设最大轮次
如果去掉 `round <= @MaxRounds(10)` 这个限制,Agent可能会陷入死循环:
Thought:"查一下" → Action:query() → Observation:没结果 → Thought:"再查一次" → ... 无限重复
✅ 正确做法:始终设置 maxIterations / maxRounds / maxSteps,一般 5-15 轮足够
🔧
接下来:ReAct里的Action到底是怎么"调用工具"的?—— S4 Function Calling,
它是LLM厂商原生支持的工具调用协议(OpenAI/Claude/Gemini都实现了)
🔧 S4: Function Calling — 工具调用的底层协议
Function Calling = 给AI一张"工具说明书",它看完后告诉你该调哪个工具、传什么参数
就像你给新员工一本操作手册:
📘 手册写着:"如果要查天气 → 调 get_weather(city),参数city是城市名"
👤 你问:"北京今天天气如何?"
🤖 AI翻手册 → 发现应该调 get_weather → 回复你:{"name":"get_weather","args":{"city":"北京"}}
⚙️ 你的代码拿到这个JSON → 真正去调天气API → 把结果告诉AI → AI组织语言回复你
注意:AI并没有真的调API!它只是告诉你"该调什么"。真正的调用是你的代码做的。
Function Calling 完整流程(3步握手)
① 定义工具
→
② AI选择工具+参数
→
③ 你的代码执行
→
④ 结果回传AI
→
⑤ AI生成回答
FunctionCallingDemo.java — OpenAI FC完整流程(Spring AI版)
// ========== 第1步:定义工具(告诉AI有什么工具可以用)==========
@Tool("查询数据库中的用户数量")
public int queryUserCount() {
// 这里是你自己写的Java方法
return userRepository.count(); #1 Spring Data JPA查库
}
@Tool("发送告警通知")
public void sendAlert(String message) {
alertService.send(message); #2 调告警服务
}
// ========== 第2步:用户提问 → AI决定调哪个工具 ==========
String userQuestion = "查一下有多少用户,超过10万就告警";
// Spring AI 自动处理FC协议:
// 1. 把@Tool方法的信息发给OpenAI(工具名+描述+参数)
// 2. OpenAI返回:{"name":"queryUserCount","args":{}}
// 3. Spring AI自动帮你调用这个方法!
ChatResponse response = chatClient.prompt()
.user(userQuestion)
.tools("queryUserCount", "sendAlert") #3 注册工具
.call(); #4 自动完成多轮FC循环
// 最终拿到的就是AI的文本回答
String answer = response.getResult().getOutput().getText();
// answer ≈ "当前用户数为150,000人,已超过10万阈值,告警已发送 ✅"
FC协议:你的代码 ↔ LLM API ↔ 外部工具,三方协作完成"智能+执行"
Function Calling vs MCP vs Skills 的区别:
| | Function Calling | MCP (Model Context Protocol) | Skills |
|---|---|---|---|
| 谁定义的 | OpenAI/各LLM厂商 | Anthropic提出,跨厂商标准 | WorkBuddy自定义
| 作用 | LLM选工具+拼参数 | AI连接外部工具/数据的USB-C接口 | 预封装的能力包
| 粒度 | 单个函数调用 | 整个工具服务器 | 一整套工作流
| 关系 | FC是底层的"调用机制" | MCP是更上层的"连接标准" | Skill是最上层的"能力封装"
一句话记忆:FC = 怎么调(机制),MCP = 连哪去(协议),Skill = 能做什么(能力)
⚡ Function Calling 常见坑:❌ 错误 vs ✅ 正确
❌ 坑1:工具描述写得太模糊 → AI不知道什么时候该调
@Tool("查东西") #1 太模糊!AI不知道这个工具干嘛的
public Object query(String param) { ... }
✅ 正确:描述详细 + 参数说明清晰
@Tool(
description = "查询数据库中的用户总数。当用户想知道系统中有多少注册用户时调用此工具。",
name = "query_user_count"
)#1 描述清楚:何时用、做什么、返回什么
public int queryUserCount() { ... }
❌ 坑2:工具内部抛异常没处理 → Agent循环卡死
@Tool
public String queryOrder(String orderId) {
Order order = orderRepo.findById(orderId)
.orElseThrow(() -> new RuntimeException("找不到")); #2 异常直接抛出!Agent收到错误信息不知道怎么办
}
✅ 正确:返回结构化错误信息,让Agent能理解并重试
@Tool
public Map<String, Object> queryOrder(String orderId) {
return orderRepo.findById(orderId)
.map(order -> Map.of(
"status", "success",
"data", order
))
.orElseGet(() -> Map.of(
"status", "not_found", #2 结构化错误!Agent看到not_found就知道该换一个orderId
"message", "订单ID不存在,请检查输入格式",
"hint", "orderId格式应该是ORD-数字"
));
}
📚
接下来:Agent能"做事"了,但如果它的知识不够新/不够专业怎么办?
—— S5 RAG模式,让Agent变成"开卷考试的学霸"
📚 S5: RAG模式 — 让Agent变"开卷考试"的学霸
RAG = 开卷考试
❌ 没有RAG = 闭卷考试 —— AI只能凭"记忆"(训练数据截止日期)回答,知识过时或不对
✅ 有RAG = 开卷考试 —— AI先翻"教材"(你的文档/数据库),再基于找到的内容回答
RAG ≠ Agent!RAG只是给Agent加了个"图书馆",Agent还是那个Agent(ReAct循环照跑)
最佳组合:Agent(会思考+会调工具)+ RAG(有专业知识) = 最强大的AI助手
RAG = Retrieval(检索) + Augmentation(增强) + Generation(生成)。离线建库一次,在线查询每次
RAGDemo.java — Spring AI RAG 最简实现
// ========== RAG 三步走 ==========
// Step 1: 离线 — 文档向量化入库(启动时做一次)
@Bean
public VectorStore vectorStore(EmbeddingModel embeddingModel) {
return new PgVectorStore(pgTemplate, embeddingModel); #1 PostgreSQL向量扩展
}
// 导入PDF文档 → 自动分块 → 自动Embedding → 存入PGVector
DocumentReader reader = new PdfDocumentReader("docs/refund-policy.pdf");
vectorStore.add(reader.get()); #2 一次调用搞定全部流程
// Step 2: 在线 — 用户提问时检索相关知识
@Autowired private VectorStore vectorStore;
public List<Document> retrieve(String question) {
#3 把用户问题也转成向量,然后在向量库中找最相似的文档
return vectorStore.similaritySearch(
SearchRequest.builder()
.query(question)
.topK(5) #4 取最相关的5个文档块
.similarityThreshold(0.7) #5 相似度低于0.7的不要
.build()
); #6 返回List<Document>
}
// Step 3: 组装Prompt → LLM生成回答
public String ask(String question) {
List<Document> docs = retrieve(question);
String prompt = """
根据以下资料回答问题。如果资料中没有相关信息,请如实告知。
参考资料:
%s
用户问题:%s
""".formatted(
docs.stream().map(Document::getText).collect(Collectors.joining("\n")),
question
);
return chatClient.prompt().user(prompt).call();
}
⚠️ RAG 的 3 个经典坑:
1️⃣ 检索不准:用户问"A产品的退款政策",但检索到了"B产品"的文档 → 解决:优化文档分块策略 + 用 reranking 二次排序
2️⃣ 幻觉依然存在:即使给了正确资料,LLM也可能"编造"不在资料里的内容 → 解决:Prompt里强调"只基于参考资料回答,不要编造"
3️⃣ 上下文窗口溢出:检索到的文档太长,超出模型的token限制 → 解决:控制topK数量 + 截断长文档 + 用长上下文模型
👥
最后一块拼图:当一个Agent干不完复杂任务时怎么办?
—— S6 多Agent协作,像公司团队一样分工合作
👥 S6: 多Agent协作 — 团队作战模式
多Agent协作 = 公司项目团队
👔 Orchestrator(编排者) = 项目经理(分配任务、汇总结果、把控进度)
🔍 Researcher(研究员) = 调研员(搜资料、分析数据、写报告)
💻 Coder(编码员) = 程序员(写代码、跑测试、修Bug)
✅ Reviewer(审查员) = QA(检查质量、提改进意见)
三种协作拓扑:
1️⃣ 串行流水线:A做完交给B → B做完交给C(适合有严格依赖关系的任务)
2️⃣ 并行独立:A/B/C同时做不同部分 → 最后合并(适合可并行拆分的任务)
3️⃣ 层级管理(最常见):Orchestrator分配→各Agent执行→汇总→Orchestrator决定下一步
层级协作:项目经理分配→各专家执行→汇总评审→不通过则重做
MultiAgentTeam.java — 层级协作伪代码(CrewAI风格)
// ========== 定义团队角色 ==========
Agent researcher = AgentBuilder
.role("研究员")
.goal("搜索和分析技术方案相关的最新信息")
.tools(webSearch, ragRetriever) #1 研究员能用搜索和RAG
.build();
Agent coder = AgentBuilder
.role("程序员")
.goal("根据需求编写高质量代码并通过测试")
.tools(fileWriter, bashRunner, gitTool) #2 程序员能写文件、跑命令、操作Git
.build();
Agent reviewer = AgentBuilder
.role("审查员")
.goal("审查代码质量和安全性,给出改进建议")
.tools(fileReader, linter) #3 审查员能读文件和跑Linter
.build();
// ========== 编排任务流程 ==========
Task researchTask = TaskBuilder
.description("调研Redis缓存方案的优缺点")
.agent(researcher)
.expectedOutput("包含对比表格的技术报告")
.build();
Task codeTask = TaskBuilder
.description("实现Redis缓存层代码")
.agent(coder)
.context(researchTask) #4 程序员的上下文来自研究员的输出!
.build();
Task reviewTask = TaskBuilder
.description("审查代码并提供修改建议")
.agent(reviewer)
.context(codeTask) #5 审查员的上下文来自程序员的输出!
.build();
// ========== 执行(Orchestrator自动调度) ==========
Crew crew = CrewBuilder
.agents(researcher, coder, reviewer)
.tasks(researchTask, codeTask, reviewTask)
.process(Process.HIERARCHICAL) #6 层级模式:自动编排执行顺序
.maxRounds(5) #7 每个Agent最多5轮(防死循环)
.build();
CrewOutput result = crew.kickoff(); #8 一行启动整个团队!
多Agent协作的 3 个关键设计原则:
1️⃣ 上下文传递:下游Agent必须能拿到上游Agent的输出(如代码Task依赖研究报告)
2️⃣ 每个Agent专注一件事:Researcher不写代码,Reviewer不改代码——单一职责
3️⃣ 必须有终止条件:maxRounds + Review把关,否则Agent之间可能无限来回修改
🛠️
最后一站:概念都懂了,实际开发该用什么框架?
—— S7 框架选型,按场景选对工具事半功倍
🛠️ S7: Agent框架选型 — 用什么开发Agent
主流Agent框架对比(2025年)
| 框架 | 语言 | 核心能力 | 最适合场景 | 上手难度 | 类比 |
| LangChain |
Python / JS |
Chain编排 + Agent + RAG + Tools |
通用AI应用原型/快速验证 |
⭐⭐ |
瑞士军刀(啥都有) |
| LlamaIndex |
Python / TS |
RAG专用(文档索引+检索) |
知识库问答/企业文档检索 |
⭐⭐ |
图书馆管理员(擅长找书) |
| CrewAI |
Python |
多Agent角色扮演+任务编排 |
复杂任务团队协作/自动化工作流 |
⭐⭐⭐ |
项目经理带团队 |
| Spring AI |
Java |
企业级AI集成(FC/RAG/VectorStore) |
Java后端接入AI/企业级安全合规 |
⭐⭐⭐ |
企业级安全帽(稳) |
| Dify |
平台(低代码) |
可视化拖拽搭建AI应用 |
非程序员/快速搭建MVP/运营自建 |
⭐ |
乐高积木(拖拽搭) |
| AutoGen |
Python |
多Agent对话/辩论/共识 |
Agent间讨论式决策/研究辅助 |
⭐⭐⭐ |
会议室讨论(互相PK) |
按需选择:越往下越强大但也越复杂。大多数情况从左边开始够用了
❓ Agent开发面试高频FAQ
Q1:Agent和普通AI对话到底有什么区别?(必考)
核心区别:Agent能"做事",普通AI只能"说话"
| 维度 | 普通AI对话(Chatbot) | Agent |
| 能力边界 | 只能生成文字(基于训练数据) | 能调工具/执行代码/操作系统 |
| 数据时效性 | 受训练截止日期限制 | 可通过API获取实时数据 |
| 交互方式 | 一问一答,被动 | 主动规划多步,循环直到完成 |
| 四大组件 | 只需要LLM | LLM + Tools + Memory + Planning |
面试加分回答:"我在项目中用Spring AI的ChatClient+@Tool实现了Agent,能自动查数据库并发告警。底层用的是OpenAI的Function Calling协议。"
Q2:ReAct模式的完整流程是什么?和普通的Chain有什么区别?
ReAct = Reasoning(推理)+ Acting(行动),核心是一个循环:
Thought(思考)
→
Action(调工具)
→
Observation(看结果)
→
回到Thought(循环)
vs Chain的区别:
- Chain:固定的线性步骤(A→B→C),每步做什么是预先写死的
- ReAct:每一步做什么由LLM动态决定,可以根据上一步结果调整计划
类比:Chain = 跟着导航走固定路线 / ReAct = 出租车司机根据路况随时改道
Q3:Function Calling是什么?AI真的在执行函数吗?
Function Calling = LLM厂商提供的一种标准化协议,流程分3步:
- 定义:你告诉LLM有哪些工具可用(名字+描述+参数格式)
- 选择:LLM根据用户问题,输出 {"name":"xxx","args":{...}} JSON
- 执行(关键!):LLM 并不执行函数!而是你的代码拿到JSON后自己去调用真正的函数
面试常考点:很多人误以为LLM在执行函数。实际上是LLM只负责"决策调用哪个",真正的执行在你的代码里。这也是为什么叫"Function Calling"而不是"Function Execution"。
Q4:RAG和微调(Fine-tuning)怎么选?
| RAG(检索增强) | Fine-tuning(微调) |
| 适用场景 | 知识经常变化/需要准确来源 | 特定领域语言风格/格式 |
| 更新成本 | 低(更新文档即可) | 高(需要重新训练) |
| 准确性 | 高(基于真实文档) | 中(可能产生幻觉) |
| 成本 | 低(不需要GPU训练) | 高(GPU算力贵) |
| 典型例子 | 企业知识库/客服问答 | 医疗诊断报告/法律文书 |
最佳实践:先用RAG!大部分场景RAG就够了。只有在RAG效果不好(比如需要特殊的输出格式/领域术语体系)时才考虑微调。而且两者可以组合:微调让模型学会领域语言 + RAG保证事实准确。
Q5:开发Agent用Python还是Java?Spring AI成熟吗?
取决于团队技术栈:
🐍 Python生态:LangChain/CrewAI/LlamaIndex 更丰富,AI原生社区活跃。AI研究和原型阶段首选。
☕ Java/Spring AI:2024年后发展很快,已经支持主流LLM提供商 + VectorStore + Function Calling + RAG。如果你的后端是Java,Spring AI是最佳选择——不用维护一套Python基础设施。
混合架构(推荐):AI核心逻辑用Python(训练/RAG/Agent编排)+ 企业集成层用Java(Spring Boot做胶水层)+ REST/gRPC互联
Q6:多Agent协作中,Agent之间怎么通信?消息格式是什么?
主流通信方式有两种:
- 共享黑板(Blackboard):所有Agent读写同一个共享状态(CrewAI默认模式)。简单但容易冲突。
- 消息传递(Message Passing):Agent之间通过结构化消息通信(AutoGen模式)。更像真实的团队邮件沟通。
消息格式通常包含:sender / receiver / content / context / timestamp。框架会帮你序列化和路由。
Q7:Agent的安全风险有哪些?怎么防范?
- Prompt注入攻击:恶意用户在输入中藏指令让Agent执行危险操作
→ 防御:输入过滤 + 输出审核 + 权限最小化(Agent只能调白名单工具)
- 工具滥用:Agent被诱导反复调用付费API或删除数据
→ 防御:Rate Limiting + 操作审计日志 + 危险操作需人工确认
- 数据泄露:Agent把敏感信息塞进prompt发给LLM服务商
→ 防御:本地部署LLM / 数据脱敏 / 使用企业版API(不用于训练)
- 无限循环:Agent陷入死循环消耗资源
→ 防御:强制maxRounds/maxSteps + 超时控制 + 成本预算限制
Q8:MCP、Skills、Function Calling 三者的关系?
三者是不同层次的概念:
| Function Calling | MCP | Skills |
| 定位 | 底层调用机制 | 连接协议标准 | 上层能力封装 |
| 谁定义 | OpenAI/LLM厂商 | Anthropic(跨厂商) | 平台/开发者 |
| 作用 | LLM选工具+拼参数 | AI连接外部世界的USB-C | 预打包的一套工作流 |
| 比喻 | 电话拨号功能 | 电话网络协议 | 一键拨号快捷键 |
| 例子 | @Tool注解的方法 | MCP Server(DB/Git/File) | 微信支付Skill |
一句话:FC是"怎么调",MCP是"连哪里",Skills是"能做什么"。三层叠加构成完整的Agent能力栈。
— 全栈资料库 · Agent开发模式图解 · 7大章节 · 6个SVG · 8个IDE代码窗口 · 8道FAQ —
核心三原则速记
AI应用三原则:① 能RAG就不裸调(减少幻觉)→ ② Prompt是工作说明书,角色/约束/格式都要写清 → ③ 始终预留人工审核通道