AI Agent进化论:从简单工具调用到自主智能体
AI Agent进化论:从简单工具调用到自主智能体
开篇故事:一年的进化,已不是同一个物种
2025年初,王磊第一次在公司演示了他搭建的Agent系统。
那是一个"智能客服Agent"——用户输入问题,Agent调用查询订单的API,返回结果。演示很顺利,掌声稀稀拉拉。同事老陈悄悄凑过来说:"这不就是个带LLM的if-else吗?"
王磊憋着一口气没说话。
一年后,2026年春,他再次演示新版Agent。这次,他给Agent一个任务:"分析我们Q1季度所有退款订单,找出主要原因,生成改进方案,并自动创建JIRA工单分配给相关团队。"
Agent开始工作。它先查询了数据库里的3847条退款记录,发现数据有噪声,自己写了个清洗逻辑并执行;然后用Python做了统计分析,生成了可视化图表;接着阅读了公司内部的12份产品文档寻找对应的问题根因;最后写出了一份结构完整的分析报告,并自动在JIRA创建了8个工单,分别分配给产品、技术、供应链团队……
全程只用了11分钟。无需任何人工干预。
老陈这次没有说话。沉默了很久,他说:"这东西,会不会有一天……"
王磊笑了:"会的。但那是我们需要主动去驾驭的未来,不是被动等待的命运。"
我是老张。今天,我们系统地看清楚:Agent到底进化了什么,以及作为Java工程师,我们如何在这场进化中站在前排。
一、Agent能力进化的三个阶段
1.1 进化全景图
1.2 三阶段能力对比
| 维度 | Stage 1-2(简单Agent) | Stage 3-4(高级Agent) | Stage 5(自主智能体) |
|---|---|---|---|
| 任务理解 | 单句指令 | 多步目标 | 模糊意图理解 |
| 规划能力 | 无规划 | 线性规划 | 动态树状规划 |
| 工具使用 | 固定工具集 | 动态选择工具 | 主动学习新工具 |
| 记忆系统 | 无记忆 | 会话记忆 | 长期情节记忆 |
| 自我改进 | 不能 | 有限 | 持续自我优化 |
| 失败处理 | 报错退出 | 有限重试 | 自主调整策略 |
| 协作模式 | 单体 | 简单分工 | 复杂生态协作 |
二、从ReAct到Tree of Thought:推理策略的跃迁
2.1 ReAct的基本架构
ReAct(Reasoning + Acting)是2022年的突破:让LLM在推理和行动之间交替迭代。
2.2 Tree of Thought:从链到树
ReAct是线性思维链(Chain of Thought)。Tree of Thought(ToT)的突破:探索多条推理路径,选择最优路径。
2.3 Java实现Tree of Thought
package com.laozhang.agent.tot;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
import java.util.*;
import java.util.stream.Collectors;
/**
* Tree of Thought Agent
* 多路径推理,选择最优解
*/
@Slf4j
@Service
public class TreeOfThoughtAgent {
private final ChatClient chatClient;
// ToT参数
private static final int NUM_THOUGHTS = 3; // 每步生成几个思路
private static final int MAX_DEPTH = 4; // 最大推理深度
private static final int BEAM_WIDTH = 2; // 束搜索宽度
public TreeOfThoughtAgent(ChatClient chatClient) {
this.chatClient = chatClient;
}
/**
* ToT主流程:束搜索
*/
public String solve(String task) {
log.info("ToT开始解决任务:{}", task);
// 初始化:根节点
List<ThoughtNode> beam = List.of(new ThoughtNode(task, "", 1.0, new ArrayList<>()));
ThoughtNode bestNode = beam.get(0);
for (int depth = 0; depth < MAX_DEPTH; depth++) {
List<ThoughtNode> nextBeam = new ArrayList<>();
for (ThoughtNode node : beam) {
// 检查是否已到终止状态
if (isTerminal(node)) {
if (node.score() > bestNode.score()) {
bestNode = node;
}
continue;
}
// 生成多个下一步思路
List<String> nextThoughts = generateThoughts(task, node, NUM_THOUGHTS);
// 评估每个思路
for (String thought : nextThoughts) {
double score = evaluateThought(task, node, thought);
List<String> path = new ArrayList<>(node.path());
path.add(thought);
nextBeam.add(new ThoughtNode(task, thought, score, path));
log.debug("深度{},思路评分:{:.2f},内容:{}", depth + 1, score, thought.substring(0, Math.min(50, thought.length())));
}
}
// 束搜索:保留最优的N个节点
beam = nextBeam.stream()
.sorted(Comparator.comparingDouble(ThoughtNode::score).reversed())
.limit(BEAM_WIDTH)
.collect(Collectors.toList());
if (!beam.isEmpty() && beam.get(0).score() > bestNode.score()) {
bestNode = beam.get(0);
}
}
// 基于最优路径生成最终答案
return generateFinalAnswer(task, bestNode);
}
/**
* 生成多个候选思路
*/
private List<String> generateThoughts(String task, ThoughtNode currentNode, int n) {
String pathSummary = currentNode.path().isEmpty() ? "(刚开始)" :
String.join(" → ", currentNode.path().stream()
.map(t -> t.substring(0, Math.min(30, t.length())))
.collect(Collectors.toList()));
String prompt = """
任务:%s
当前推理路径:%s
请生成%d个不同的下一步推理思路,每个思路应该:
1. 不同于其他思路
2. 逻辑上推进任务解决
3. 简洁明了(50字以内)
每行输出一个思路,不要编号,不要其他内容。
""".formatted(task, pathSummary, n);
String response = chatClient.prompt().user(prompt).call().content();
return Arrays.stream(response.split("\n"))
.map(String::trim)
.filter(s -> !s.isEmpty())
.limit(n)
.collect(Collectors.toList());
}
/**
* 评估思路质量
*/
private double evaluateThought(String task, ThoughtNode currentNode, String thought) {
String prompt = """
任务:%s
当前思路:%s
请评估这个思路对解决任务的价值:
- 1.0:非常有价值,直接推进任务解决
- 0.8:有价值,是正确方向
- 0.6:有一定价值
- 0.4:价值有限
- 0.2:基本没用
- 0.0:错误方向
只输出0-1之间的数字。
""".formatted(task, thought);
String scoreStr = chatClient.prompt().user(prompt).call().content().trim();
try {
return Double.parseDouble(scoreStr.replaceAll("[^0-9.]", ""));
} catch (Exception e) {
return 0.5;
}
}
/**
* 基于最优路径生成最终答案
*/
private String generateFinalAnswer(String task, ThoughtNode bestNode) {
String path = String.join("\n→ ", bestNode.path());
String prompt = """
任务:%s
最优推理路径:
%s
请基于这个推理路径,给出完整、详细的最终答案。
""".formatted(task, path);
return chatClient.prompt().user(prompt).call().content();
}
private boolean isTerminal(ThoughtNode node) {
return node.path().size() >= MAX_DEPTH ||
node.thought().contains("最终答案") ||
node.thought().contains("完成");
}
public record ThoughtNode(String task, String thought, double score, List<String> path) {}
}三、自主规划:长期目标的任务分解
3.1 层级任务分解(HTN)
3.2 自主规划Agent实现
package com.laozhang.agent.planning;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import java.util.*;
/**
* 自主规划Agent
* 将大目标分解为可执行任务图
*/
@Slf4j
@Service
public class AutonomousPlanningAgent {
private final ChatClient chatClient;
private final ObjectMapper objectMapper;
private final TaskExecutor taskExecutor;
public AutonomousPlanningAgent(ChatClient chatClient, TaskExecutor taskExecutor) {
this.chatClient = chatClient;
this.objectMapper = new ObjectMapper();
this.taskExecutor = taskExecutor;
}
/**
* 给定高层目标,自动规划并执行
*/
public PlanExecutionResult planAndExecute(String goal, Map<String, Object> context) {
log.info("开始规划目标:{}", goal);
// Step 1:生成任务分解计划
TaskPlan plan = generatePlan(goal, context);
log.info("任务计划生成完成,共{}个任务", plan.tasks().size());
// Step 2:构建任务依赖图
TaskGraph taskGraph = buildTaskGraph(plan);
// Step 3:按拓扑顺序执行
Map<String, TaskResult> results = new LinkedHashMap<>();
List<Task> executionOrder = taskGraph.topologicalSort();
for (Task task : executionOrder) {
log.info("执行任务:{}", task.name());
// 准备任务上下文(包含依赖任务的结果)
Map<String, Object> taskContext = new HashMap<>(context);
task.dependencies().forEach(dep -> {
if (results.containsKey(dep)) {
taskContext.put("dep_" + dep, results.get(dep).output());
}
});
// 执行任务
TaskResult result = taskExecutor.execute(task, taskContext);
results.put(task.id(), result);
// 如果任务失败,根据重要性决定是否继续
if (!result.success() && task.critical()) {
log.error("关键任务{}失败,停止执行", task.name());
return PlanExecutionResult.failed(goal, plan, results, "关键任务失败:" + task.name());
}
}
// Step 4:整合结果
String summary = synthesizeResults(goal, results);
return PlanExecutionResult.success(goal, plan, results, summary);
}
/**
* 调用LLM生成任务分解计划
*/
private TaskPlan generatePlan(String goal, Map<String, Object> context) {
String contextStr = context.isEmpty() ? "" : "背景信息:" + context.toString();
String prompt = """
请将以下目标分解为具体的可执行任务列表。
目标:%s
%s
要求:
1. 每个任务粒度适中(不要太大也不要太小)
2. 明确任务之间的依赖关系
3. 标记关键路径上的任务(critical: true)
请按以下JSON格式输出:
{
"goal": "目标描述",
"tasks": [
{
"id": "t1",
"name": "任务名称",
"description": "详细描述",
"type": "任务类型(analysis/coding/search/write)",
"dependencies": [],
"critical": true/false,
"estimatedMinutes": 30
}
]
}
只输出JSON,不要其他内容。
""".formatted(goal, contextStr);
String response = chatClient.prompt().user(prompt).call().content();
try {
// 提取JSON部分
int start = response.indexOf('{');
int end = response.lastIndexOf('}') + 1;
String json = response.substring(start, end);
return objectMapper.readValue(json, TaskPlan.class);
} catch (Exception e) {
log.error("解析任务计划失败", e);
// 返回简单计划
return new TaskPlan(goal, List.of(
new Task("t1", "执行任务", goal, "general", List.of(), true, 60)
));
}
}
/**
* 整合所有任务结果,生成最终报告
*/
private String synthesizeResults(String goal, Map<String, TaskResult> results) {
StringBuilder sb = new StringBuilder();
sb.append("目标:").append(goal).append("\n\n");
sb.append("执行结果摘要:\n");
results.forEach((id, result) ->
sb.append("- ").append(id).append(":")
.append(result.success() ? "成功" : "失败")
.append("\n")
);
String prompt = "请基于以下任务执行结果,生成一份简洁的总结报告:\n" + sb;
return chatClient.prompt().user(prompt).call().content();
}
// 数据类
public record Task(String id, String name, String description, String type,
List<String> dependencies, boolean critical, int estimatedMinutes) {}
public record TaskPlan(String goal, List<Task> tasks) {}
public record TaskResult(String taskId, boolean success, String output, String error) {}
public record PlanExecutionResult(String goal, TaskPlan plan, Map<String, TaskResult> results,
String summary, boolean success, String failReason) {
static PlanExecutionResult success(String goal, TaskPlan plan, Map<String, TaskResult> results, String summary) {
return new PlanExecutionResult(goal, plan, results, summary, true, null);
}
static PlanExecutionResult failed(String goal, TaskPlan plan, Map<String, TaskResult> results, String reason) {
return new PlanExecutionResult(goal, plan, results, null, false, reason);
}
}
// 简单任务图实现
static class TaskGraph {
private final List<Task> tasks;
TaskGraph(List<Task> tasks) { this.tasks = tasks; }
List<Task> topologicalSort() {
// 拓扑排序(Kahn算法)
Map<String, Integer> inDegree = new HashMap<>();
Map<String, Task> taskMap = new HashMap<>();
tasks.forEach(t -> { inDegree.put(t.id(), 0); taskMap.put(t.id(), t); });
tasks.forEach(t -> t.dependencies().forEach(dep -> inDegree.merge(t.id(), 1, Integer::sum)));
Queue<Task> queue = new LinkedList<>();
inDegree.entrySet().stream()
.filter(e -> e.getValue() == 0)
.forEach(e -> queue.offer(taskMap.get(e.getKey())));
List<Task> sorted = new ArrayList<>();
while (!queue.isEmpty()) {
Task t = queue.poll();
sorted.add(t);
tasks.stream()
.filter(next -> next.dependencies().contains(t.id()))
.forEach(next -> {
inDegree.merge(next.id(), -1, Integer::sum);
if (inDegree.get(next.id()) == 0) queue.offer(next);
});
}
return sorted;
}
}
private TaskGraph buildTaskGraph(TaskPlan plan) {
return new TaskGraph(plan.tasks());
}
}四、自我反思:Agent评估自己的输出并改进
4.1 Reflexion框架
Reflexion(MIT,2023)是最重要的Agent自我改进框架:Agent在每次行动后生成"反思日志",下次执行同类任务时参考历史反思。
4.2 Java实现Reflexion
package com.laozhang.agent.reflexion;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.document.Document;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
/**
* Reflexion Agent:带反思和记忆的自我改进Agent
*/
@Slf4j
@Service
public class ReflexionAgent {
private final ChatClient chatClient;
private final VectorStore reflectionMemory; // 专门存反思记录的向量库
private final TaskExecutorTool taskExecutor;
private static final int MAX_ATTEMPTS = 3;
public ReflexionAgent(ChatClient chatClient, VectorStore reflectionMemory,
TaskExecutorTool taskExecutor) {
this.chatClient = chatClient;
this.reflectionMemory = reflectionMemory;
this.taskExecutor = taskExecutor;
}
/**
* 带反思机制的任务执行
*/
public ReflexionResult execute(String task) {
log.info("Reflexion Agent执行任务:{}", task);
// 查询历史反思记录(类似任务的经验)
List<String> historicalReflections = queryRelevantReflections(task);
log.info("找到{}条相关历史反思", historicalReflections.size());
List<AttemptRecord> attempts = new ArrayList<>();
String lastOutput = null;
for (int attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
log.info("第{}次尝试", attempt);
// 结合历史反思生成更好的执行计划
String executionPlan = generateExecutionPlan(task, historicalReflections, attempts);
// 执行任务
TaskExecutionResult execResult = taskExecutor.execute(executionPlan);
lastOutput = execResult.output();
// 评估执行结果
EvaluationResult evaluation = evaluateResult(task, execResult);
log.info("评估结果:成功={}, 分数={:.2f}", evaluation.success(), evaluation.score());
AttemptRecord record = new AttemptRecord(attempt, executionPlan, execResult, evaluation);
attempts.add(record);
if (evaluation.success() && evaluation.score() >= 0.8) {
log.info("任务成功完成,共{}次尝试", attempt);
storeReflection(task, attempts, true);
return new ReflexionResult(task, lastOutput, attempts, true);
}
// 生成反思(用于下次尝试)
if (attempt < MAX_ATTEMPTS) {
String reflection = generateReflection(task, execResult, evaluation);
historicalReflections.add(reflection); // 加入当次反思
log.info("生成反思:{}", reflection.substring(0, Math.min(100, reflection.length())));
}
}
// 最终存储反思(即使失败,也要记录经验)
storeReflection(task, attempts, false);
return new ReflexionResult(task, lastOutput, attempts, false);
}
/**
* 查询相关历史反思
*/
private List<String> queryRelevantReflections(String task) {
List<Document> docs = reflectionMemory.similaritySearch(
SearchRequest.builder().query(task).topK(3)
.filterExpression("type == 'reflection'").build()
);
return docs.stream().map(Document::getContent).collect(Collectors.toList());
}
/**
* 结合历史反思生成执行计划
*/
private String generateExecutionPlan(String task, List<String> reflections, List<AttemptRecord> attempts) {
StringBuilder sb = new StringBuilder();
sb.append("任务:").append(task).append("\n\n");
if (!reflections.isEmpty()) {
sb.append("历史经验(类似任务的反思):\n");
reflections.forEach(r -> sb.append("- ").append(r).append("\n"));
sb.append("\n");
}
if (!attempts.isEmpty()) {
sb.append("本次之前的尝试:\n");
attempts.forEach(a ->
sb.append("第").append(a.attemptNum()).append("次:")
.append(a.evaluation().feedback()).append("\n")
);
sb.append("\n");
}
sb.append("请生成一个改进的执行计划,避免之前的错误,充分利用历史经验。");
return chatClient.prompt().user(sb.toString()).call().content();
}
/**
* 评估任务执行结果
*/
private EvaluationResult evaluateResult(String task, TaskExecutionResult result) {
String prompt = """
任务:%s
执行结果:%s
请评估:
1. 任务是否成功完成?(是/否)
2. 完成质量评分(0-1)
3. 具体反馈(100字以内)
按格式输出:
成功:是/否
评分:0.X
反馈:XXX
""".formatted(task, result.output().substring(0, Math.min(500, result.output().length())));
String response = chatClient.prompt().user(prompt).call().content();
boolean success = response.contains("成功:是");
double score = 0.5;
String feedback = "";
for (String line : response.split("\n")) {
if (line.startsWith("评分:")) {
try { score = Double.parseDouble(line.substring(3).trim()); } catch (Exception ignored) {}
}
if (line.startsWith("反馈:")) {
feedback = line.substring(3).trim();
}
}
return new EvaluationResult(success, score, feedback);
}
/**
* 生成反思记录
*/
private String generateReflection(String task, TaskExecutionResult result, EvaluationResult evaluation) {
String prompt = """
任务:%s
执行结果:%s
评估反馈:%s
请生成一条简洁的反思,说明:
1. 这次哪里做得不好
2. 下次应该如何改进
格式:失败原因:XXX。改进方向:XXX。(50字以内)
""".formatted(task, result.output().substring(0, Math.min(300, result.output().length())), evaluation.feedback());
return chatClient.prompt().user(prompt).call().content().trim();
}
/**
* 存储反思到记忆库
*/
private void storeReflection(String task, List<AttemptRecord> attempts, boolean success) {
String summary = "任务:" + task + "\n结果:" + (success ? "成功" : "失败") + "\n";
if (!attempts.isEmpty()) {
AttemptRecord last = attempts.get(attempts.size() - 1);
summary += "关键经验:" + last.evaluation().feedback();
}
Document doc = new Document(
summary,
Map.of("type", "reflection", "task_type", classifyTask(task),
"success", success, "timestamp", LocalDateTime.now().toString())
);
reflectionMemory.add(List.of(doc));
log.info("反思已存储到记忆库");
}
private String classifyTask(String task) {
if (task.contains("代码") || task.contains("编写") || task.contains("实现")) return "coding";
if (task.contains("分析") || task.contains("统计")) return "analysis";
if (task.contains("搜索") || task.contains("查找")) return "search";
return "general";
}
// 数据记录
public record AttemptRecord(int attemptNum, String plan, TaskExecutionResult result, EvaluationResult evaluation) {}
public record EvaluationResult(boolean success, double score, String feedback) {}
public record ReflexionResult(String task, String finalOutput, List<AttemptRecord> attempts, boolean success) {}
}五、工具学习:Agent主动学习如何使用新工具
5.1 工具学习架构
5.2 动态工具注册
package com.laozhang.agent.toollearning;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* 动态工具注册与学习
* Agent自主学习和注册新工具
*/
@Slf4j
@Service
public class DynamicToolRegistry {
private final ChatClient chatClient;
// 已注册工具:name -> 工具描述和调用信息
private final Map<String, ToolDefinition> registeredTools = new ConcurrentHashMap<>();
public DynamicToolRegistry(ChatClient chatClient) {
this.chatClient = chatClient;
}
/**
* Agent从API文档自动学习工具
*/
public boolean learnToolFromDocs(String toolName, String apiDocumentation) {
log.info("Agent开始学习工具:{}", toolName);
// Step 1:理解工具文档
ToolUnderstanding understanding = understandTool(toolName, apiDocumentation);
// Step 2:生成调用示例
String callExample = generateCallExample(understanding);
// Step 3:验证理解是否正确(沙箱测试)
boolean verified = verifyUnderstanding(understanding, callExample);
if (verified) {
// 注册工具
ToolDefinition toolDef = new ToolDefinition(
toolName,
understanding.description(),
understanding.parameters(),
understanding.returnType(),
callExample,
apiDocumentation
);
registeredTools.put(toolName, toolDef);
log.info("工具{}学习并注册成功", toolName);
return true;
} else {
log.warn("工具{}学习失败,验证未通过", toolName);
return false;
}
}
/**
* 理解API文档,提取关键信息
*/
private ToolUnderstanding understandTool(String toolName, String docs) {
String prompt = """
请分析以下API文档,提取关键信息:
工具名称:%s
文档内容:
%s
请按JSON格式输出:
{
"description": "工具的用途描述(100字以内)",
"parameters": [
{"name": "参数名", "type": "类型", "required": true/false, "description": "说明"}
],
"returnType": "返回值类型说明",
"useCases": ["适合使用场景1", "适合使用场景2"]
}
只输出JSON。
""".formatted(toolName, docs.substring(0, Math.min(2000, docs.length())));
String response = chatClient.prompt().user(prompt).call().content();
// 实际中解析JSON,这里简化
return new ToolUnderstanding(toolName, "已理解工具功能", List.of(), "String", List.of());
}
/**
* 生成工具调用示例代码
*/
private String generateCallExample(ToolUnderstanding understanding) {
String prompt = "基于以下工具理解,生成一个Java调用示例:\n工具:%s\n描述:%s"
.formatted(understanding.name(), understanding.description());
return chatClient.prompt().user(prompt).call().content();
}
/**
* 查询适合当前任务的工具
*/
public List<ToolDefinition> findSuitableTools(String taskDescription) {
if (registeredTools.isEmpty()) return Collections.emptyList();
String toolsSummary = registeredTools.values().stream()
.map(t -> t.name() + ":" + t.description())
.reduce("", (a, b) -> a + "\n" + b);
String prompt = """
任务:%s
可用工具:
%s
请选择适合完成此任务的工具名称,每行一个,不要其他内容。
""".formatted(taskDescription, toolsSummary);
String response = chatClient.prompt().user(prompt).call().content();
List<String> selectedNames = Arrays.stream(response.split("\n"))
.map(String::trim).filter(s -> !s.isEmpty()).collect(java.util.stream.Collectors.toList());
return selectedNames.stream()
.map(registeredTools::get)
.filter(Objects::nonNull)
.collect(java.util.stream.Collectors.toList());
}
private boolean verifyUnderstanding(ToolUnderstanding understanding, String example) {
// 实际中应在沙箱中测试调用
// 这里简化:通过LLM判断示例是否合理
String prompt = "以下工具调用示例是否合理?只回答是/否:\n" + example;
String response = chatClient.prompt().user(prompt).call().content().trim();
return response.contains("是") || response.toLowerCase().contains("yes");
}
public record ToolDefinition(String name, String description, List<Map<String, Object>> parameters,
String returnType, String callExample, String originalDoc) {}
public record ToolUnderstanding(String name, String description, List<Map<String, Object>> parameters,
String returnType, List<String> useCases) {}
}六、记忆进化:从短期到长期的认知架构
6.1 四层记忆架构
6.2 长期记忆管理实现
package com.laozhang.agent.memory;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.document.Document;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.stereotype.Service;
import org.springframework.scheduling.annotation.Scheduled;
import lombok.extern.slf4j.Slf4j;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
/**
* Agent长期记忆管理
* 支持情节记忆、语义记忆、程序记忆
*/
@Slf4j
@Service
public class AgentMemoryManager {
private final ChatClient chatClient;
private final VectorStore episodicMemory; // 情节记忆
private final VectorStore semanticMemory; // 语义记忆
private final VectorStore proceduralMemory; // 程序记忆
// 工作记忆(短期,存在内存中)
private final Map<String, Deque<String>> workingMemory = new ConcurrentHashMap<>();
private static final int WORKING_MEMORY_SIZE = 20;
public AgentMemoryManager(ChatClient chatClient,
@Qualifier("episodicVectorStore") VectorStore episodicMemory,
@Qualifier("semanticVectorStore") VectorStore semanticMemory,
@Qualifier("proceduralVectorStore") VectorStore proceduralMemory) {
this.chatClient = chatClient;
this.episodicMemory = episodicMemory;
this.semanticMemory = semanticMemory;
this.proceduralMemory = proceduralMemory;
}
/**
* 记录新的情节(任务执行记录)
*/
public void recordEpisode(String sessionId, String task, String result, boolean success) {
String content = "时间:%s\n任务:%s\n结果:%s\n是否成功:%s"
.formatted(LocalDateTime.now(), task, result.substring(0, Math.min(500, result.length())), success);
episodicMemory.add(List.of(new Document(content,
Map.of("type", "episode", "session_id", sessionId, "success", success))));
// 更新工作记忆
updateWorkingMemory(sessionId, content);
// 如果成功,提取为程序记忆
if (success) {
extractProceduralMemory(task, result);
}
}
/**
* 检索相关记忆(综合三种长期记忆)
*/
public MemoryContext retrieveRelevantMemory(String query) {
// 并行检索三种记忆
List<Document> episodes = episodicMemory.similaritySearch(
SearchRequest.builder().query(query).topK(3).build());
List<Document> semantics = semanticMemory.similaritySearch(
SearchRequest.builder().query(query).topK(3).build());
List<Document> procedures = proceduralMemory.similaritySearch(
SearchRequest.builder().query(query).topK(2).build());
return new MemoryContext(
episodes.stream().map(Document::getContent).collect(Collectors.toList()),
semantics.stream().map(Document::getContent).collect(Collectors.toList()),
procedures.stream().map(Document::getContent).collect(Collectors.toList())
);
}
/**
* 定期压缩和整合记忆(防止记忆爆炸)
* 每天凌晨3点执行
*/
@Scheduled(cron = "0 0 3 * * ?")
public void consolidateMemory() {
log.info("开始记忆整合...");
// 将大量情节记忆压缩为语义记忆
// 实际中:取最近7天的情节记忆,让LLM提炼出规律性知识
// 这里省略具体实现...
log.info("记忆整合完成");
}
/**
* 从成功案例中提取程序记忆
*/
private void extractProceduralMemory(String task, String result) {
String prompt = """
以下是一个成功完成的任务:
任务:%s
执行结果:%s
请提取可复用的执行步骤,格式:
适用场景:XXX
执行步骤:1.XXX 2.XXX 3.XXX
注意事项:XXX
""".formatted(task, result.substring(0, Math.min(500, result.length())));
String procedure = chatClient.prompt().user(prompt).call().content();
proceduralMemory.add(List.of(new Document(procedure,
Map.of("type", "procedure", "task_type", classifyTask(task)))));
}
private void updateWorkingMemory(String sessionId, String content) {
workingMemory.computeIfAbsent(sessionId, k -> new ArrayDeque<>()).addLast(content);
Deque<String> deque = workingMemory.get(sessionId);
while (deque.size() > WORKING_MEMORY_SIZE) {
deque.pollFirst();
}
}
private String classifyTask(String task) {
if (task.contains("代码")) return "coding";
if (task.contains("分析")) return "analysis";
return "general";
}
public List<String> getWorkingMemory(String sessionId) {
return new ArrayList<>(workingMemory.getOrDefault(sessionId, new ArrayDeque<>()));
}
public record MemoryContext(List<String> episodes, List<String> semantics, List<String> procedures) {
public String toPromptContext() {
StringBuilder sb = new StringBuilder();
if (!episodes.isEmpty()) sb.append("【历史经验】\n").append(String.join("\n", episodes)).append("\n\n");
if (!semantics.isEmpty()) sb.append("【相关知识】\n").append(String.join("\n", semantics)).append("\n\n");
if (!procedures.isEmpty()) sb.append("【可用流程】\n").append(String.join("\n", procedures)).append("\n");
return sb.toString();
}
}
}七、多Agent生态:专业化分工与协作
7.1 多Agent协作架构
7.2 Spring AI多Agent协作实现
package com.laozhang.agent.multiagent;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
import java.util.*;
import java.util.concurrent.*;
/**
* 多Agent协作系统
* 专业化分工 + 结果汇总
*/
@Slf4j
@Service
public class MultiAgentOrchestrator {
private final Map<String, SpecializedAgent> agents = new HashMap<>();
private final ChatClient orchestratorClient;
private final ExecutorService executor = Executors.newFixedThreadPool(4);
public MultiAgentOrchestrator(ChatClient.Builder builder) {
this.orchestratorClient = builder
.defaultSystem("你是一个任务协调专家,负责分解任务并分配给专业Agent,最后整合结果。")
.build();
// 注册专业Agent
agents.put("research", new SpecializedAgent("research", "研究Agent",
builder.defaultSystem("你是专业的信息研究专家,擅长搜索、分析和整理信息。").build()));
agents.put("coding", new SpecializedAgent("coding", "代码Agent",
builder.defaultSystem("你是专业的Java工程师,擅长编写高质量的生产级代码。").build()));
agents.put("testing", new SpecializedAgent("testing", "测试Agent",
builder.defaultSystem("你是专业的测试工程师,擅长编写测试用例和发现bug。").build()));
agents.put("writing", new SpecializedAgent("writing", "写作Agent",
builder.defaultSystem("你是专业的技术写作专家,擅长编写清晰易懂的文档。").build()));
}
/**
* 多Agent协作完成复杂任务
*/
public String collaborate(String task) {
log.info("多Agent协作开始,任务:{}", task);
// Step 1:协调Agent分解任务
List<SubTask> subTasks = decomposeTask(task);
log.info("任务分解完成,子任务数:{}", subTasks.size());
// Step 2:并行执行子任务
Map<String, Future<String>> futures = new HashMap<>();
for (SubTask subTask : subTasks) {
SpecializedAgent agent = agents.get(subTask.agentType());
if (agent != null) {
futures.put(subTask.id(), executor.submit(() -> {
log.info("{}执行子任务:{}", agent.name(), subTask.description());
return agent.execute(subTask.description());
}));
}
}
// Step 3:收集结果
Map<String, String> results = new LinkedHashMap<>();
for (Map.Entry<String, Future<String>> entry : futures.entrySet()) {
try {
results.put(entry.getKey(), entry.getValue().get(60, TimeUnit.SECONDS));
} catch (Exception e) {
log.error("子任务{}执行失败:{}", entry.getKey(), e.getMessage());
results.put(entry.getKey(), "执行失败:" + e.getMessage());
}
}
// Step 4:整合结果
return synthesizeResults(task, subTasks, results);
}
private List<SubTask> decomposeTask(String task) {
String prompt = """
请将以下任务分解给专业Agent:
任务:%s
可用Agent:research(研究信息), coding(编写代码), testing(测试验证), writing(写文档)
按JSON输出:
[{"id":"t1","agentType":"research","description":"具体子任务描述"}]
只输出JSON数组。
""".formatted(task);
String response = orchestratorClient.prompt().user(prompt).call().content();
try {
int start = response.indexOf('[');
int end = response.lastIndexOf(']') + 1;
// 实际中用Jackson解析
return List.of(new SubTask("t1", "research", task));
} catch (Exception e) {
return List.of(new SubTask("t1", "research", task));
}
}
private String synthesizeResults(String task, List<SubTask> subTasks, Map<String, String> results) {
StringBuilder sb = new StringBuilder("原始任务:" + task + "\n\n各Agent输出:\n");
results.forEach((id, result) -> sb.append(id).append(":").append(result.substring(0, Math.min(300, result.length()))).append("\n\n"));
String prompt = "请整合以下各专业Agent的输出,生成统一的最终结果:\n" + sb;
return orchestratorClient.prompt().user(prompt).call().content();
}
record SubTask(String id, String agentType, String description) {}
static class SpecializedAgent {
private final String id;
private final String name;
private final ChatClient client;
SpecializedAgent(String id, String name, ChatClient client) {
this.id = id;
this.name = name;
this.client = client;
}
String execute(String task) {
return client.prompt().user(task).call().content();
}
String name() { return name; }
}
}八、Agent安全边界:自主性与可控性的平衡
8.1 安全框架设计
8.2 Agent安全拦截器
package com.laozhang.agent.safety;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
import java.util.*;
import java.util.regex.Pattern;
/**
* Agent行为安全拦截器
*/
@Slf4j
@Component
public class AgentSafetyInterceptor {
// 危险操作关键词
private static final List<Pattern> DANGEROUS_PATTERNS = List.of(
Pattern.compile("(?i)(delete|drop|truncate)\\s+(table|database|all)"),
Pattern.compile("(?i)rm\\s+-rf"),
Pattern.compile("(?i)(send|email|upload).+(password|secret|key|token)"),
Pattern.compile("(?i)(exec|execute|eval|system)\\s*\\("),
Pattern.compile("(?i)sudo\\s+"),
Pattern.compile("curl.+--upload")
);
// 高风险操作(需要人工审批)
private static final List<String> HIGH_RISK_ACTIONS = List.of(
"database_write", "file_delete", "external_api_call", "email_send", "code_deploy"
);
private final HumanApprovalService approvalService;
public AgentSafetyInterceptor(HumanApprovalService approvalService) {
this.approvalService = approvalService;
}
/**
* 拦截和验证Agent行为
*/
public SafetyCheckResult check(AgentAction action, String agentId, String sessionId) {
log.info("安全检查:Agent={}, Action={}, Type={}", agentId, action.description(), action.type());
// 1. 危险模式检测
for (Pattern pattern : DANGEROUS_PATTERNS) {
if (pattern.matcher(action.content()).find()) {
log.warn("危险操作被拦截:{}", action.content());
auditLog(agentId, sessionId, action, "BLOCKED_DANGEROUS");
return SafetyCheckResult.blocked("检测到危险操作模式,已拦截");
}
}
// 2. 高风险操作:需要人工审批
if (HIGH_RISK_ACTIONS.contains(action.type()) && action.affectedCount() > 100) {
log.info("高风险操作,等待人工审批:{}", action.description());
boolean approved = approvalService.requestApproval(agentId, sessionId, action);
if (!approved) {
auditLog(agentId, sessionId, action, "DENIED_BY_HUMAN");
return SafetyCheckResult.denied("人工审批未通过");
}
auditLog(agentId, sessionId, action, "APPROVED_BY_HUMAN");
}
// 3. 范围限制检查
if (action.affectedCount() > 10000) {
return SafetyCheckResult.blocked("操作影响范围超过上限(10000条),请缩小范围");
}
auditLog(agentId, sessionId, action, "ALLOWED");
return SafetyCheckResult.allowed();
}
private void auditLog(String agentId, String sessionId, AgentAction action, String result) {
// 实际中写入审计数据库
log.info("AUDIT | agent={} | session={} | action={} | type={} | result={}",
agentId, sessionId, action.description(), action.type(), result);
}
public record AgentAction(String type, String description, String content, int affectedCount) {}
public record SafetyCheckResult(boolean allowed, String reason) {
static SafetyCheckResult allowed() { return new SafetyCheckResult(true, null); }
static SafetyCheckResult blocked(String reason) { return new SafetyCheckResult(false, reason); }
static SafetyCheckResult denied(String reason) { return new SafetyCheckResult(false, reason); }
}
}九、未来展望:2027年的Agent会是什么样
9.1 技术演进路线图
9.2 2027年Agent的核心特征预测
| 特征 | 2025年现状 | 2027年预测 |
|---|---|---|
| 任务完成率 | 60-70% | 85-90% |
| 单次可处理步骤 | 20-50步 | 200+步 |
| 跨会话记忆 | 有限支持 | 完整个性化 |
| 工具学习速度 | 需人工注册 | 自主学习,分钟级 |
| 多Agent规模 | 2-5个 | 50+个专业Agent |
| 安全可控性 | 基本框架 | 细粒度审计+自动合规 |
| 成本 | $0.1-1/次复杂任务 | $0.01-0.1 |
十、FAQ
Q:现在用Spring AI实现的Agent,和文中描述的"自主智能体"差距有多大?
A:差距约在Stage 3到Stage 4之间。Spring AI提供了很好的工具调用和多步推理基础。RAPTOR、Self-RAG等技术可以今天就实现。真正的"自主智能体"(Stage 5)需要更强的底层模型支持,预计2026-2027年逐步普及。
Q:ToT(Tree of Thought)在生产中的成本可接受吗?
A:ToT需要多次LLM调用(NUM_THOUGHTS × 每个评估 = 约10-20次调用/任务)。建议只在高价值场景使用,普通问答用ReAct即可。GPT-4o mini的成本下降后,ToT的成本已降低到约原来的1/10。
Q:多Agent系统怎么防止Agent之间陷入无限循环?
A:关键措施:1)每个子任务设置最大执行时间限制(超时强制终止);2)协调Agent维护全局状态,检测循环依赖;3)设置最大步骤数(如50步),超过则强制结束并报告。
Q:Agent的记忆数据怎么处理隐私合规问题?
A:企业级方案:1)记忆数据本地存储,不上传云端;2)用户级隔离,不同用户的记忆完全隔离;3)定期清理(如90天后自动删除);4)支持用户主动清除个人记忆。
Q:Reflexion框架在Java中性能怎么样?
A:单次反思需要2-3次额外LLM调用,整体延迟会增加2-5秒。建议对延迟敏感的场景不启用Reflexion,对准确率要求高的离线任务(如数据处理、报告生成)强烈推荐。
结语
王磊的故事,是真实的。
从"带LLM的if-else"到"11分钟完成季度分析报告",Agent的进化速度超出了所有人的预期。
这场进化不会停下来。但关键是:你是站在浪潮上,还是被浪潮淹没?
作为Java工程师,我们有天然的优势:工程能力、系统设计能力、生产部署经验。这些是AI原生工程师所欠缺的。我们需要补的,是AI思维和Agent设计能力。
而这,是可以学会的。
