AI Agent 的自我反思与经验学习:从错误中进化的 Java 实战

系列文章

本篇是 AI Agent 深度解析系列的第 24 篇。以下是系列文章导航:

  1. 理解 AI Agent 的大脑:ReAct 模式从入门到实战
  2. AI Agent 的工具箱:深入理解 Tool Use 与 Spring AI Function Calling 实战
  3. AI Agent 的记忆系统:从 ChatMemory 到持久化记忆的 Java 实战
  4. AI Agent 的规划大脑:从任务分解到自适应执行策略
  5. AI Agent 的灵魂对话:Prompt Engineering 系统提示词设计的艺术与工程
  6. MCP 模型上下文协议:AI 的万能接口与 MCP Server 实战
  7. 让 AI 学会”说人话”——Spring AI 结构化输出实战
  8. Embedding 向量化的魔法:从文本到向量的数学之旅与 Java 实战
  9. 从零理解 RAG:检索增强生成完整指南
  10. 当 RAG 遇到 Agent:Agentic RAG 的架构设计与 Java 实战
  11. 当 RAG 遇上知识图谱:GraphRAG 原理与 Java 实战
  12. AI Agent 的知识检索引擎:从向量搜索到智能检索策略的 Java 实战
  13. AI Agent 的推理引擎:从 Chain-of-Thought 到推理模型的深度解析与 Java 实战
  14. AI Agent 评估与优化:从基准测试到生产环境的质量守护实战
  15. AI Agent 的可观测性:从链路追踪到成本监控的 Java 实战
  16. AI Agent 的安全防线:Prompt 注入防御与生产级安全防护实战
  17. Spring AI 核心架构全解析:从 ChatModel 到 Advisor Chain 的设计哲学
  18. AI Agent 的流式响应与实时交互:从 SSE 到 WebSocket 的 Java 实战
  19. AI Agent 的工作流编排:从顺序链到自适应 DAG 的 Java 实战
  20. AI Agent 的多模态感知:从图片理解到语音交互的 Java 实战
  21. AI Agent 团队协作:多 Agent 系统架构设计与 Java 实战
  22. Agent 间如何对话:A2A 协议深度解析与 Java 实战
  23. AI Agent 的自我反思与经验学习:从错误中进化的 Java 实战(本文)

一个让人头疼的场景

你有没有遇到过这种情况:你让 AI Agent 帮你查询一个订单的物流状态,结果它调用了错误的 API——明明应该调 /api/logistics/query,它却调了 /api/order/detail。你告诉它”错了,应该用物流查询接口”,它道歉了,重新调用,这次对了。

然后第二天,同一个问题又发生了。它又调错了同一个接口。

这就是当下大多数 AI Agent 的致命缺陷:它们不记得自己犯过的错

之前的文章里我们讲过,ReAct 模式让 Agent 具备了”思考-行动-观察”的推理循环。在规划大脑里我们讲了任务分解策略。在记忆系统里我们讲了短期和长期记忆。但这些能力组合在一起,还缺一块关键拼图——自我反思

人类之所以能进步,不是因为不会犯错,而是因为我们能反思错误、总结经验、下次做得更好。Agent 也需要这种能力。

今天我们就来深入聊聊:怎么让 AI Agent 学会自我反思,从错误中进化。


什么是自我反思?从人类认知到 Agent 设计

反思的本质

自我反思(Self-Reflection)在认知科学中的定义是:对自身思维过程的审视和评估

用更通俗的话说:你做完一件事之后,回过头来想”我刚才做得怎么样?哪里可以改进?下次遇到类似情况我应该怎么做?”

这个过程包含三个关键步骤:

  1. 回顾(Review):我刚才做了什么?结果是什么?
  2. 评估(Evaluate):这个结果符合预期吗?哪里做得好,哪里做得不好?
  3. 提炼(Distill):我能从中提取什么经验教训?下次怎么改进?

为什么 Agent 需要反思?

让我用一个对比来说明。

没有反思能力的 Agent

1
2
3
4
5
用户: 帮我查询订单 #12345 的物流状态
Agent: 调用 /api/order/detail → 拿到订单信息但没有物流状态
Agent: 对用户说"抱歉,我需要换个方式查询"
Agent: 调用 /api/logistics/query → 成功
(下次遇到同样问题,还是会先调错接口)

有反思能力的 Agent

1
2
3
4
5
6
用户: 帮我查询订单 #12345 的物流状态
Agent: 调用 /api/order/detail → 失败(没有物流信息)
Agent 反思: "物流查询应该用 /api/logistics/query 而不是 /api/order/detail"
Agent: 调用 /api/logistics/query → 成功
经验记录: "查询物流状态时,直接使用 logistics API,不要使用 order API"
(下次遇到类似问题,会直接调用正确的接口)

差距在哪?第一个 Agent 把每次失败都当成独立事件;第二个 Agent 把失败变成了可复用的经验

学术前沿:Reflexion 框架

2023 年,Shinn 等人发表了 Reflexion 论文(Reflexion: Language Agents with Verbal Reinforcement Learning),提出了一个完整的自我反思框架。核心思想是:让 Agent 用自然语言总结失败经验,并在后续尝试中参考这些经验

Reflexion 的流程是这样的:

1
执行任务 → 评估结果 → 如果失败 → 生成反思 → 存入记忆 → 重试(带着反思)

这和传统强化学习(RL)的区别在于:RL 用数值化的奖励信号更新参数,而 Reflexion 用自然语言的”经验总结”来引导 Agent 行为。这意味着:

  • 不需要微调模型参数
  • 经验可以跨任务迁移
  • 人类可以阅读和编辑 Agent 的反思记录

自我反思的架构设计

在动手写代码之前,我们先来设计一下整体架构。一个完整的自我反思系统包含四个核心模块:

1
2
3
4
5
6
7
8
┌─────────────────────────────────────────────────────────────┐
│ Agent 执行引擎 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 任务执行 │→│ 结果评估 │→│ 反思生成 │→│ 经验存储 │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ ↑ │
│ └──────────── 经验检索(下次执行时) ←────────────────┘ │
└─────────────────────────────────────────────────────────────┘

1. 任务执行器(Task Executor):执行具体任务,和之前 ReAct 模式一样,使用思考-行动-观察的循环。

2. 结果评估器(Result Evaluator):判断任务是否成功,如果失败,分析失败原因。这里可以用 LLM 来做智能评估,也可以用规则引擎做硬性检查。

3. 反思生成器(Reflection Generator):基于失败原因,生成结构化的经验总结。这是最核心的部分——好的反思要具体、可操作、可复用。

4. 经验存储与检索(Experience Store):把反思记录持久化存储,在后续执行类似任务时检索相关经验。这里可以复用我们之前讲过的 RAG 技术来做语义检索。


核心实现:用 LangChain4j 构建反思系统

理论讲够了,来写代码。我们用 LangChain4j 来实现一个完整的自我反思 Agent。

第一步:定义反思数据结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
* 反思记录:Agent 从一次失败中学到的经验
*/
public class Reflection {
private String taskId; // 任务标识
private String taskDescription; // 任务描述
private String failureReason; // 失败原因
private String reflection; // 经验总结(核心)
private List<String> mistakes; // 具体错误点
private List<String> lessons; // 教训(下次该怎么做)
private LocalDateTime timestamp;
private int attemptNumber; // 第几次尝试

// 构造器、getter/setter 省略

/**
* 将反思格式化为可注入 Prompt 的文本
*/
public String toPromptContext() {
StringBuilder sb = new StringBuilder();
sb.append("【历史经验 - 任务:").append(taskDescription).append("】\n");
sb.append("失败原因:").append(failureReason).append("\n");
sb.append("反思总结:").append(reflection).append("\n");
if (!lessons.isEmpty()) {
sb.append("经验教训:\n");
lessons.forEach(lesson -> sb.append(" - ").append(lesson).append("\n"));
}
return sb.toString();
}
}

注意 toPromptContext() 方法——它把反思记录转换成一段可以直接注入到 Prompt 里的文本。这是连接”反思系统”和”执行系统”的桥梁。

第二步:构建反思 Agent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;

public interface ReflectionAgent {

/**
* 评估任务执行结果,判断是否成功
*/
@SystemMessage("""
你是一个任务结果评估专家。根据任务描述和执行结果,判断任务是否成功完成。
如果失败,分析具体的失败原因。
返回 JSON 格式:
{
"success": true/false,
"failureReason": "失败原因(成功时为空)",
"score": 0-100 的完成度评分
}
""")
@UserMessage("""
任务描述:{{taskDescription}}
执行结果:{{executionResult}}
期望输出:{{expectedOutput}}
""")
String evaluate(
@V("taskDescription") String taskDescription,
@V("executionResult") String executionResult,
@V("expectedOutput") String expectedOutput
);

/**
* 基于失败信息生成反思
*/
@SystemMessage("""
你是一个经验总结专家。根据任务的失败信息,生成一份结构化的反思报告。

要求:
1. 具体指出错在哪里(不要泛泛而谈)
2. 分析为什么会出现这个错误(根因分析)
3. 给出下次遇到类似任务时应该怎么做(可操作的建议)
4. 如果有多个错误点,逐一列出

返回 JSON 格式:
{
"reflection": "整体反思总结",
"mistakes": ["错误1", "错误2"],
"lessons": ["教训1", "教训2"]
}
""")
@UserMessage("""
任务描述:{{taskDescription}}
执行过程:{{executionProcess}}
失败原因:{{failureReason}}
期望输出:{{expectedOutput}}
历史经验(如有):{{pastReflections}}
""")
String reflect(
@V("taskDescription") String taskDescription,
@V("executionProcess") String executionProcess,
@V("failureReason") String failureReason,
@V("expectedOutput") String expectedOutput,
@V("pastReflections") String pastReflections
);
}

这里的 Prompt 设计有几个关键点:

  1. 评估 Prompt 要求返回结构化 JSON,方便后续程序处理
  2. 反思 Prompt 明确要求”具体指出错在哪里”——这是因为泛泛的反思(如”下次要更仔细”)没有实际价值
  3. 历史经验注入:如果之前有类似任务的反思记录,会一起传给 LLM,让它避免重复之前的教训

第三步:经验存储与检索

反思生成了,得存起来。而且存储之后要能高效检索——当遇到新任务时,能找到相关的历史经验。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import dev.langchain4j.data.document.Metadata;
import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.store.embedding.EmbeddingStore;

/**
* 经验存储:使用向量数据库存储反思记录
*/
public class ExperienceStore {

private final EmbeddingStore<TextSegment> embeddingStore;
private final EmbeddingModel embeddingModel;
private final ObjectMapper objectMapper = new ObjectMapper();

public ExperienceStore(EmbeddingStore<TextSegment> embeddingStore,
EmbeddingModel embeddingModel) {
this.embeddingStore = embeddingStore;
this.embeddingModel = embeddingModel;
}

/**
* 存储一条反思记录
*/
public void saveReflection(Reflection reflection) {
String text = reflection.toPromptContext();
Embedding embedding = embeddingModel.embed(text).content();

Metadata metadata = Metadata.from("taskId", reflection.getTaskId())
.put("timestamp", reflection.getTimestamp().toString())
.put("attemptNumber", String.valueOf(reflection.getAttemptNumber()));

embeddingStore.add(embedding, TextSegment.from(text, metadata));
}

/**
* 检索与当前任务相关的历史反思
*/
public List<String> searchRelevantReflections(String taskDescription, int maxResults) {
Embedding queryEmbedding = embeddingModel.embed(taskDescription).content();

return embeddingStore.search(
dev.langchain4j.store.embedding.EmbeddingSearchRequest.builder()
.queryEmbedding(queryEmbedding)
.maxResults(maxResults)
.minScore(0.7) // 相似度阈值,避免不相关的经验干扰
.build()
).matches()
.stream()
.map(match -> match.embedded().text())
.collect(Collectors.toList());
}
}

这里复用了 Embedding 向量化的技术来实现语义检索。当新任务进来时,我们不是用关键词匹配历史经验,而是用语义相似度——“查询订单物流”和”查看快递状态”虽然用词不同,但语义接近,能检索到相关经验。

minScore(0.7) 这个阈值很重要:太低会引入不相关的经验(误导 Agent),太高会错过有价值的经验。实际生产中需要根据业务场景调优。

第四步:编排反思循环

有了上面的组件,我们来把它们组装成一个完整的反思循环:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
/**
* 带自我反思的 Agent 执行器
*/
public class ReflectiveAgentExecutor {

private final ReflectionAgent reflectionAgent;
private final ExperienceStore experienceStore;
private final AgentExecutor taskExecutor; // 基础的任务执行器
private final int maxRetries;

public ReflectiveAgentExecutor(ReflectionAgent reflectionAgent,
ExperienceStore experienceStore,
AgentExecutor taskExecutor,
int maxRetries) {
this.reflectionAgent = reflectionAgent;
this.experienceStore = experienceStore;
this.taskExecutor = taskExecutor;
this.maxRetries = maxRetries;
}

/**
* 执行任务,带反思重试
*/
public ExecutionResult executeWithReflection(Task task) {
// 1. 检索相关历史经验
List<String> pastReflections = experienceStore
.searchRelevantReflections(task.getDescription(), 3);

String enhancedPrompt = task.getPrompt();
if (!pastReflections.isEmpty()) {
enhancedPrompt = buildEnhancedPrompt(task.getPrompt(), pastReflections);
}

// 2. 尝试执行(最多重试 maxRetries 次)
for (int attempt = 1; attempt <= maxRetries; attempt++) {
log.info("执行任务 [{}],第 {} 次尝试", task.getId(), attempt);

// 2a. 执行任务
String result = taskExecutor.execute(enhancedPrompt);
StringBuilder processLog = new StringBuilder();
processLog.append("尝试 ").append(attempt).append(": ").append(result).append("\n");

// 2b. 评估结果
String evaluation = reflectionAgent.evaluate(
task.getDescription(), result, task.getExpectedOutput()
);
EvalResult evalResult = parseEvaluation(evaluation);

if (evalResult.isSuccess()) {
log.info("任务 [{}] 在第 {} 次尝试后成功", task.getId(), attempt);
return ExecutionResult.success(result, attempt);
}

// 2c. 失败了,生成反思
log.info("任务 [{}] 第 {} 次尝试失败: {}", task.getId(), attempt,
evalResult.getFailureReason());

String reflectionJson = reflectionAgent.reflect(
task.getDescription(),
processLog.toString(),
evalResult.getFailureReason(),
task.getExpectedOutput(),
String.join("\n---\n", pastReflections)
);
Reflection reflection = parseReflection(reflectionJson, task.getId(), attempt);

// 2d. 存储反思
experienceStore.saveReflection(reflection);

// 2e. 将新反思加入上下文,为下次重试做准备
pastReflections.add(reflection.toPromptContext());
enhancedPrompt = buildEnhancedPrompt(task.getPrompt(), pastReflections);

log.info("已生成反思并更新上下文,准备第 {} 次重试", attempt + 1);
}

return ExecutionResult.failure("超过最大重试次数 (" + maxRetries + ")");
}

/**
* 构建带历史经验的增强 Prompt
*/
private String buildEnhancedPrompt(String originalPrompt, List<String> reflections) {
StringBuilder sb = new StringBuilder();
sb.append(originalPrompt);
sb.append("\n\n===== 历史经验(请务必参考,避免重复犯错)=====\n");
for (int i = 0; i < reflections.size(); i++) {
sb.append("\n").append(reflections.get(i)).append("\n");
}
sb.append("\n===== 请基于以上经验,避免重复错误,重新执行任务 =====");
return sb.toString();
}
}

这段代码的核心流程是:

  1. 检索历史经验:在执行任务之前,先从经验库中检索相关反思
  2. 注入经验到 Prompt:把历史经验附加到原始 Prompt 后面
  3. 执行 → 评估 → 反思 → 存储:标准的反思循环
  4. 迭代改进:每次反思后更新上下文,让下一次尝试更聪明

注意 buildEnhancedPrompt 的设计——它不是简单地把经验贴在后面,而是加了强调性提示”请务必参考,避免重复犯错”。这是因为 LLM 有时候会”忽略”长上下文中间的内容(中间丢失问题),显式提示可以提高注意力。


进阶:反思质量的保障机制

上面的实现看起来已经挺完整了,但在生产环境中,你很快会遇到几个问题:

问题一:反思太泛泛

Agent 可能生成这样的反思:”下次要更仔细地分析需求”。这种反思没有任何价值——它不具体,不可操作。

解决方案:用评估约束反思质量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@SystemMessage("""
评估以下反思记录的质量,返回 0-100 的评分。

评分标准:
- 具体性(30分):是否指出了具体的错误操作,而非泛泛而谈
- 可操作性(30分):给出的教训是否可以直接指导下一步行动
- 根因分析(20分):是否分析了错误的根本原因,而非表面现象
- 复用性(20分):经验是否可以应用到类似但不同的场景

低于 60 分的反思需要重新生成。

返回 JSON:{"score": N, "issues": ["问题1", "问题2"], "suggestion": "改进建议"}
""")
@UserMessage("反思内容:{{reflection}}")
String evaluateReflectionQuality(@V("reflection") String reflection);

在生成反思之后,用另一个 LLM 调用来评估反思质量。如果评分低于阈值,就让 Agent 重新生成。这相当于给反思加了一个”质量门禁”。

问题二:错误经验的污染

如果 Agent 从一次偶然的失败中总结出了错误的经验(比如 API 偶尔超时,Agent 错误地总结为”这个 API 不能用”),后续所有类似任务都会被误导。

解决方案:经验验证与衰减机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/**
* 经验验证器:跟踪经验的有效性
*/
public class ExperienceValidator {

private final ExperienceStore experienceStore;

/**
* 当一条经验被成功应用时,增加其权重
*/
public void onExperienceApplied(String reflectionId, boolean ledToSuccess) {
Reflection reflection = experienceStore.findById(reflectionId);
if (ledToSuccess) {
reflection.setConfidenceScore(
Math.min(1.0, reflection.getConfidenceScore() + 0.1)
);
} else {
reflection.setConfidenceScore(
Math.max(0.0, reflection.getConfidenceScore() - 0.2)
);
}
experienceStore.update(reflection);

// 如果信心分降到阈值以下,标记为"待验证"
if (reflection.getConfidenceScore() < 0.3) {
log.warn("经验 [{}] 信心分过低 ({}),标记为待验证",
reflectionId, reflection.getConfidenceScore());
reflection.setStatus(ReflectionStatus.NEEDS_VERIFICATION);
}
}

/**
* 定期清理低信心的经验
*/
@Scheduled(fixedRate = 86400000) // 每天执行
public void cleanupLowConfidenceExperiences() {
List<Reflection> lowConfidence = experienceStore
.findByStatusAndConfidenceBelow(ReflectionStatus.ACTIVE, 0.2);
lowConfidence.forEach(r -> {
r.setStatus(ReflectionStatus.ARCHIVED);
log.info("归档低信心经验: {}", r.getReflection());
});
experienceStore.batchUpdate(lowConfidence);
}
}

这个机制的核心思想是:经验不是一成不变的,它需要经过实践检验

  • 一条经验如果多次被成功应用,信心分会逐渐升高
  • 如果应用后反而导致失败,信心分会下降
  • 信心分过低的经验会被标记为”待验证”,不再直接注入 Prompt

这有点像人类的认知过程——你从别人那里学到一条经验,用了几次发现确实管用,就会越来越信任它;如果发现不管用,就会质疑它。

问题三:反思循环的死循环

有时候 Agent 会陷入一个怪圈:尝试 → 失败 → 反思 → 重试 → 还是失败(因为问题本身不在 Agent 能力范围内)→ 继续反思 → 继续失败……

解决方案:策略切换机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
* 策略切换器:当反思无法改善结果时,切换到不同策略
*/
public class StrategySwitcher {

private enum Strategy {
STANDARD, // 标准执行
REFLECTION, // 反思重试
DECOMPOSE, // 分解为子任务
HUMAN_IN_LOOP, // 请求人工介入
ALTERNATIVE // 使用替代方案
}

public Strategy determineStrategy(Task task, List<Reflection> reflections,
int consecutiveFailures) {
// 连续失败 2 次以内:继续反思重试
if (consecutiveFailures <= 2) {
return Strategy.REFLECTION;
}

// 连续失败 3 次:尝试分解任务
if (consecutiveFailures == 3) {
return Strategy.DECOMPOSE;
}

// 连续失败 4 次:使用替代方案(如不同的工具、不同的 API)
if (consecutiveFailures == 4) {
return Strategy.ALTERNATIVE;
}

// 连续失败 5 次以上:请求人工介入
return Strategy.HUMAN_IN_LOOP;
}
}

核心思想:反思不是万能的。当反思无法改善结果时,需要升级策略——先尝试分解任务,再尝试替代方案,最后请求人工介入。这个渐进式升级机制避免了 Agent 在死循环里打转。


实战案例:智能客服 Agent 的反思进化

光说不练假把式。我们来看一个完整的实战案例:一个智能客服 Agent,它需要处理用户的各种售后问题。

场景设定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
@Service
public class CustomerServiceAgent {

private final ReflectiveAgentExecutor executor;
private final ExperienceStore experienceStore;

// 可用工具
@Tool("查询订单详情")
public OrderDetail queryOrder(String orderId) {
return orderService.getDetail(orderId);
}

@Tool("查询物流状态")
public LogisticsInfo queryLogistics(String orderId) {
return logisticsService.query(orderId);
}

@Tool("发起退款申请")
public RefundResult initiateRefund(String orderId, String reason, BigDecimal amount) {
return refundService.apply(orderId, reason, amount);
}

@Tool("查询退款进度")
public RefundStatus queryRefundStatus(String refundId) {
return refundService.getStatus(refundId);
}

@Tool("创建工单")
public String createTicket(String category, String description, String priority) {
return ticketService.create(category, description, priority);
}

/**
* 处理用户请求(带反思能力)
*/
public String handleCustomerRequest(String userMessage) {
Task task = Task.builder()
.id(UUID.randomUUID().toString())
.description("处理客服请求: " + userMessage)
.prompt(buildCustomerServicePrompt(userMessage))
.expectedOutput("准确解决用户问题,并给出友好的回复")
.build();

ExecutionResult result = executor.executeWithReflection(task);

if (result.isSuccess()) {
return result.getOutput();
} else {
// 兜底:创建工单转人工
createTicket("AI_ESCALATION", "AI 无法处理的请求: " + userMessage, "HIGH");
return "抱歉,您的问题我暂时无法处理,已为您转接人工客服。";
}
}
}

三次迭代:Agent 是怎么变聪明的

第一次尝试(无经验)

用户说:”我的订单 #78901 还没到,都一周了”

Agent 的行为:

1
2
3
4
思考: 用户在询问订单物流状态
行动: 调用 queryOrder("78901")
观察: 获取到订单详情,但没有物流轨迹信息
结果: 回复用户"您的订单已发货",但无法回答"到哪了"

评估结果:失败。用户想知道物流状态,Agent 只拿到了订单信息。

反思生成:

1
2
3
4
5
6
7
8
9
{
"reflection": "查询物流状态应该使用 queryLogistics 工具,而不是 queryOrder。queryOrder 返回的是订单基本信息(商品、金额、状态),而 queryLogistics 返回的是物流轨迹。两者是不同的数据源。",
"mistakes": ["错误地用 queryOrder 代替 queryLogistics", "没有区分'订单状态'和'物流状态'两个概念"],
"lessons": [
"当用户提到'到哪了'、'物流'、'快递'、'发货了吗'等关键词时,应直接使用 queryLogistics",
"queryOrder 适合回答'订单是否已付款'、'买了什么'等问题",
"在调用工具前,先分析用户想获取的是哪类信息"
]
}

第二次尝试(带反思经验)

用户说:”我想看看我那个快递到哪了,单号是 #78901”

Agent 的行为(注入了第一次的反思):

1
2
3
4
思考: 用户说"快递到哪了",根据历史经验,这属于物流查询,应该用 queryLogistics
行动: 调用 queryLogistics("78901")
观察: 获取到物流轨迹,显示"包裹在杭州转运中心"
结果: 正确回复用户物流状态

评估结果:成功

第三次尝试(类似场景)

用户说:”帮我查一下 #45678 发货没有”

Agent 的行为(自动检索到历史经验):

1
2
3
4
思考: 用户问"发货没有",这是物流相关的查询(经验中明确提到了"发货了吗")
行动: 直接调用 queryLogistics("45678")
观察: 显示"已发货,在途运输中"
结果: 一次性给用户准确回复

看到区别了吗?第一次是”犯错-反思-重试”,但后续的类似任务,Agent 会自动检索到相关经验,直接跳过犯错阶段


与记忆系统的关系

有读者可能会问:这和我们之前讲的记忆系统有什么区别?

这是个好问题。它们的关系是:

记忆系统是基础设施——负责存储和检索信息。
反思系统是上层应用——负责从失败中提取经验,并利用记忆系统来存储和检索这些经验。

1
2
3
4
5
6
7
8
9
┌───────────────────────────────────────┐
│ 自我反思系统(应用层) │
│ 评估 → 反思 → 经验验证 → 策略切换 │
├───────────────────────────────────────┤
│ 记忆系统(基础设施层) │
│ 短期记忆 ←→ 工作记忆 ←→ 长期记忆 │
│ ↕ │
│ 向量数据库(经验存储) │
└───────────────────────────────────────┘

记忆系统中的”情景记忆”部分,天然就适合存储反思经验——因为它存储的是”发生过什么”以及”从中学到了什么”。

但反思系统比记忆系统多了一层:它有主动的评估和提炼过程。记忆系统只是被动地存储信息,反思系统则会主动分析”这次失败的根本原因是什么”、”下次应该怎么改进”。


生产环境考量

性能开销

自我反思不是免费的。每次反思至少需要 2 次额外的 LLM 调用(评估 + 反思),加上向量检索的开销。在高并发场景下,这会显著增加延迟和成本。

优化策略

  1. 延迟反思:不是每次任务都需要实时反思。对于非关键任务,可以把反思过程放到异步队列中,在低峰期处理。

  2. 选择性反思:只对”意料之外”的失败进行反思。如果 Agent 的置信度很高但结果失败,说明确实需要反思;如果 Agent 本身就不确定,反思的价值较低。

  3. 批量反思:把同一类任务的多个失败案例一起分析,生成更全面的经验。这比逐条反思效率更高。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 异步反思服务:不阻塞主流程
*/
@Service
public class AsyncReflectionService {

@Async("reflectionExecutor")
public CompletableFuture<Reflection> reflectAsync(Task task, String failureReason,
String executionProcess) {
return CompletableFuture.supplyAsync(() -> {
// 反思可以慢一点,不影响用户体验
return reflectionAgent.reflect(
task.getDescription(), executionProcess,
failureReason, task.getExpectedOutput(), ""
);
});
}
}

与可观测性的结合

反思系统的运行本身也需要监控。我们在可观测性文章中讲过的指标体系,在这里同样适用:

  • 反思触发率:多少比例的任务触发了反思?触发率突然升高可能说明环境发生了变化(API 变更、业务规则调整等)
  • 反思成功率:反思后重试成功的比例。如果很低,说明反思质量有问题
  • 经验命中率:从经验库中检索到相关经验的比例。如果很低,可能是 Embedding 模型不适合当前领域
  • 平均重试次数:一个任务平均需要几次尝试才能成功

安全性考量

反思记录中可能包含敏感信息(用户数据、内部 API 地址等)。存储和检索反思时需要注意:

  1. 脱敏处理:在存储反思之前,对敏感字段进行脱敏
  2. 访问控制:不同业务线的反思记录应该隔离,避免 A 业务线的错误经验污染 B 业务线
  3. 审计日志:反思记录的创建和应用都应该有审计日志,便于追溯问题

反思的边界:什么时候不该反思

自我反思很强大,但不是银弹。以下场景不建议使用反思机制:

  1. 确定性任务:如果任务的执行路径是确定的(如 CRUD 操作),反思没有意义——直接修 bug 就行
  2. 时间敏感任务:反思需要额外的 LLM 调用时间,对延迟要求极高的场景(如实时交易)可能不适用
  3. 一次性任务:如果同类任务不会再次出现,反思的经验没有复用价值
  4. 模型能力不足:如果 LLM 本身无法理解任务的复杂性,反思也帮不了它——巧妇难为无米之炊

判断标准:如果你的 Agent 会反复执行同类任务、且失败模式多样(不是单一 bug 导致的),那反思机制大概率能带来价值。


未来展望

自我反思是 Agent 进化的重要一步,但远不是终点。未来的发展方向可能包括:

  1. 跨 Agent 经验共享:多个 Agent 之间共享反思经验,一个 Agent 犯的错可以成为所有 Agent 的教训。这和我们之前讲的 多 Agent 协作结合,可以构建真正的”集体智慧”。

  2. 人类反馈整合:不仅从自动评估中反思,还从用户的显式反馈(如”这个回答不对”)中学习。

  3. 元反思(Meta-Reflection):Agent 不仅反思”做了什么错事”,还反思”反思过程本身是否有效”。如果发现反思一直没帮助改善结果,说明反思策略本身需要调整。

  4. 持续学习与微调:将反思经验转化为训练数据,用于微调专用模型。这比在 Prompt 中注入经验更高效,但需要更多的基础设施支持。


总结

今天我们深入探讨了 AI Agent 的自我反思与经验学习机制。核心要点回顾:

  1. 反思的本质:回顾 → 评估 → 提炼,把失败变成可复用的经验
  2. Reflexion 框架:用自然语言总结经验,不需要微调模型参数
  3. 四个核心模块:任务执行器、结果评估器、反思生成器、经验存储与检索
  4. 三个关键挑战:反思太泛泛(用质量评估约束)、错误经验污染(用信心分和验证机制)、死循环(用策略切换机制)
  5. 生产考量:性能优化(异步反思、选择性反思)、可观测性、安全性

自我反思让 Agent 从”每次都犯同样的错”进化为”越用越聪明”。结合之前讲过的 ReAct记忆规划等能力,一个真正能自我进化的 Agent 系统就初具雏形了。

最后送一句我很喜欢的话:经验不是你经历了什么,而是你从经历中学到了什么。 这句话对 Agent,同样适用。