Reflection Agent 学习笔记
一、核心概念
Reflection(反思)模式是一种通过自我评审和迭代优化来提升代码质量的 Agent 设计模式。
核心流程:
初始生成 → 反思评审 → 优化改进 → 再次反思 → ... → 最终输出
二、架构设计
2.1 Memory 类 - 记忆管理
python
class Memory:
def __init__(self):
self.records = [] # 存储所有执行和反思记录
def add_record(self, record_type: str, content: str):
"""添加记录,类型为 'execution' 或 'reflection'"""
def get_last_execution(self) -> str:
"""获取最近一次执行的代码"""
def get_trajectory(self) -> str:
"""格式化所有记录为提示词文本"""
作用:维护完整的迭代历史,供后续反思使用。
2.2 ReflectionAgent 类 - 核心逻辑
python
class ReflectionAgent:
def __init__(self, llm_client, max_iterations=2):
self.llm_client = llm_client
self.memory = Memory()
self.max_iterations = max_iterations
关键方法:
run(task): 执行完整的反思循环_get_llm_response(prompt): 调用 LLM 获取响应
三、三阶段提示词模板
阶段 1:初始生成(INITIAL_PROMPT_TEMPLATE)
python
INITIAL_PROMPT_TEMPLATE = """
你是一位资深的Python开发工程师。请根据以下任务编写代码:
任务: {task}
请直接输出Python代码,使用markdown代码块格式。
"""
真实输出示例:
python
def find_primes(n):
if n < 2:
return []
primes = []
for num in range(2, n + 1):
is_prime = True
for i in range(2, int(num ** 0.5) + 1):
if num % i == 0:
is_prime = False
break
if is_prime:
primes.append(num)
return primes
特点:使用试除法,时间复杂度 O(n√n)
阶段 2:反思评审(REFLECT_PROMPT_TEMPLATE)
REFLECT_PROMPT_TEMPLATE
"""
你是一位极其严格的代码评审专家和资深算法工程师。
原始任务:
{task}
待审查的代码:
```python
{code}
请分析该代码的时间复杂度,并思考是否存在算法上更优的解决方案。
如果存在,请清晰地指出当前算法的不足,并提出具体的改进建议。
如果代码在算法层面已经达到最优,才能回答"无需改进"。
"""
真实输出示例:
当前算法采用试除法,时间复杂度为 O(n√n),在 n 较大时效率低下。
主要瓶颈:对每个数独立进行素性判断,重复计算多,无法利用已知素数信息。
算法上更优的解决方案:埃拉托斯特尼筛法 (Sieve of Eratosthenes)。
改进建议:
- 使用布尔数组 is_prime[0..n] 初始化为 True
- 从 2 开始,若 is_prime[i] 为 True,则将 i 的所有倍数(从 i² 开始)标记为 False
- 最终收集所有 is_prime[i] == True 的 i
该方法时间复杂度为 O(n log log n),显著优于 O(n√n)。
结论:必须改进,采用埃拉托斯特尼筛法。
关键点:
- 明确指出算法瓶颈
- 提供具体可行的改进方案
- 给出复杂度对比
阶段 3:优化改进(REFINE_PROMPT_TEMPLATE)
python
REFINE_PROMPT_TEMPLATE = """
你是一位资深的Python开发工程师。
# 原始任务:
{task}
# 上一轮代码:
```python
{last_code_attempt}
``
# 评审员反馈:
{feedback}
请根据反馈优化代码,直接输出改进后的完整代码。
"""
真实输出示例:
python
def find_primes(n):
"""
使用埃拉托斯特尼筛法,时间复杂度为 O(n log log n)
"""
if n < 2:
return []
# 创建布尔数组
is_prime = [True] * (n + 1)
is_prime[0] = is_prime[1] = False
for i in range(2, int(n**0.5) + 1):
if is_prime[i]:
# 从 i² 开始标记 i 的倍数为合数
for j in range(i * i, n + 1, i):
is_prime[j] = False
# 收集所有素数
return [num for num in range(2, n + 1) if is_prime[num]]
改进效果:
- 算法从 O(n√n) 优化到 O(n log log n)
- 测试成功率:100%(10/10 通过)
四、完整执行流程
┌─────────────────────────────────────────────────────┐
│ 1. 初始生成 │
│ 输入:任务描述 │
│ 输出:试除法实现(O(n√n)) │
│ 记忆:[execution] 初始代码 │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ 2. 第一轮反思 │
│ 输入:任务 + 初始代码 │
│ 输出:指出算法瓶颈,建议使用筛法 │
│ 记忆:[reflection] 评审反馈 │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ 3. 第一轮优化 │
│ 输入:任务 + 初始代码 + 反馈 │
│ 输出:埃拉托斯特尼筛法实现(O(n log log n)) │
│ 记忆:[execution] 优化后代码 │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ 4. 第二轮反思 │
│ 输入:任务 + 优化后代码 │
│ 输出:"无需改进" │
│ 终止条件:检测到停止信号 │
└─────────────────────────────────────────────────────┘
五、记忆轨迹可视化
执行结束后,系统会展示完整的记忆轨迹:
============================================================
记忆轨迹可视化
============================================================
[步骤 1] 类型: execution
------------------------------------------------------------
```python
def find_primes(n):
"""
找出1到n之间所有的素数。
Args:
n (int): 上限数值,函数将找出1到n之间的所有素数
Returns:
list: 包含1到n之间所有素数的列表,按升序排列
Raises:
ValueError: 当n小于1时抛出异常
...
[步骤 2] 类型: reflection
------------------------------------------------------------
当前算法采用试除法,对每个数 `num` 检查其是否能被 `2` 到 `√num` 之间的数整除,时间复杂度为 **O(n√n)**,在 `n` 较大时效率低下。
主要瓶颈:对每个数独立进行素性判断,重复计算多,无法利用已知素数信息。
算法上更优的解决方案:**埃拉托斯特尼筛法 (Sieve of Eratosthenes)**。
改进建议:使用埃拉托斯特尼筛法,预先标记合数,仅保留素数。该...
[步骤 3] 类型: execution
------------------------------------------------------------
```python
def find_primes(n):
"""
找出1到n之间所有的素数。
使用埃拉托斯特尼筛法(Sieve of Eratosthenes)实现,时间复杂度为 O(n log log n),
相比试除法具有显著的性能优势,尤其适用于较大的 n。
Args:
n (int): 上限数值,函数将找出1到n之间的所有素数
...
[步骤 4] 类型: reflection
------------------------------------------------------------
无需改进
六、Reflection 机制的成本收益分析
尽管 Reflection 机制在提升任务解决质量上表现出色,但这种能力的获得并非没有代价。在实际应用中,我们需要权衡其带来的收益与相应的成本。
(1)主要成本
模型调用开销增加
这是最直接的成本。每进行一轮迭代,至少需要额外调用两次大语言模型(一次用于反思,一次用于优化)。如果迭代多轮,API 调用成本和计算资源消耗将成倍增加。
任务延迟显著提高
Reflection 是一个串行过程,每一轮的优化都必须等待上一轮的反思完成。这使得任务的总耗时显著延长,不适合对实时性要求高的场景。
提示工程复杂度上升
如我们的案例所示,Reflection 的成功在很大程度上依赖于高质量、有针对性的提示词。为"执行"、"反思"、"优化"等不同阶段设计和调试有效的提示词,需要投入更多的开发精力。
(2)核心收益
解决方案质量的跃迁
最大的收益在于,它能将一个"合格"的初始方案,迭代优化成一个"优秀"的最终方案。这种从功能正确到性能高效、从逻辑粗糙到逻辑严谨的提升,在很多关键任务中是至关重要的。
鲁棒性与可靠性增强
通过内部的自我纠错循环,智能体能够发现并修复初始方案中可能存在的逻辑漏洞、事实性错误或边界情况处理不当等问题,从而大大提高了最终结果的可靠性。
(3)适用场景判断
✅ 适合使用 Reflection 的场景:
- 生成关键的业务代码或技术报告
- 在科学研究中进行复杂的逻辑推演
- 需要深度分析和规划的决策支持系统
- 对最终结果的质量、准确性和可靠性有极高要求
- 对任务完成的实时性要求相对宽松
❌ 不适合使用 Reflection 的场景:
- 需要快速响应的实时系统
- "大致正确"的答案就已经足够的场景
- API 调用成本敏感的应用
- 简单任务,初始方案已经足够好
结论:Reflection 机制是一种典型的"以成本换质量"的策略。选择时需根据具体场景权衡质量要求与成本约束。
七、实际应用场景
- 算法优化:从暴力解到高效算法
- 代码重构:改善代码结构和可读性
- 性能调优:识别并优化性能瓶颈
- 安全加固:发现并修复安全漏洞
八、扩展思路
- 多轮深度反思:增加迭代次数,逐步逼近最优解
- 多角度评审:从性能、安全、可读性等多维度反思
- 对比测试:自动对比优化前后的性能差异
- 人工介入:在关键决策点引入人工审核