你的AI Agent为什么总在“来回改“?一次真实实验给出的答案 ——融合控制工程PID的Harness实践

控制论与AI Agent融合封面

你是不是遇到过这些情况?

  • 让Agent迭代优化代码,改了4轮反而中间比第2轮更差?
  • 无反馈重试时,模型"随机发挥"引入新Bug,分数断崖下跌?
  • 想加个"连续N轮没提升就停"的规则,但不知道阈值怎么设?

如果你点头了,这篇文章就是写给你的。

这不是假设,是我们刚刚跑出来的真实数据:

LRU Cache任务(10个测试用例)——简单重试的分数轨迹:
Round 1: 7/10
Round 2: 8/10  ← 有改善
Round 3: 4/10  ← 断崖退步!popitem方向改反了
Round 4: 10/10 ← 终于蒙对了

第3轮从8/10退步到4/10。模型没有收到任何反馈,独立重新生成了代码,结果引入了更多Bug。这种"振荡退步"不是概率事件——它是无反馈重试系统的结构性缺陷

而同样的任务,使用PID增强模式(精确反馈+控制信号),轨迹是这样的:

Round 1: 7/10
Round 2: 10/10 ← 一步到位

精确反馈告诉模型"T3/T7/T10失败是因为get()未更新访问顺序,同时验证put()的recency更新"——模型一次性修复了所有关联Bug。

本文基于我们2026年5月的真实对比实验,分享一套经过初步验证的方法论:用精确反馈+PID控制让迭代收敛更快,同时诚实说明它的适用边界和局限。


从真实实验说起

我们设计了3个编程任务的对比实验,测试三种迭代策略:

策略 方法
简单重试 每轮独立重新生成,不提供任何上轮信息
Self-Refine 给上轮代码+“请改进”,不给具体失败信息
PID增强模式 精确失败用例+root cause分析+控制信号调节反馈强度

实验环境:Generator和Evaluator均为Claude,测试用例为确定性Python单元测试(pass/fail),PID参数 Kp=0.6, Ki=0.2, Kd=0.15。

结果一览

任务 难度 测试数 简单重试 Self-Refine PID增强模式
T1: 合并有序链表 简单 5 1轮通过 1轮通过 1轮通过
T2: 螺旋矩阵 中等 10 2轮通过 2轮通过 2轮通过
T3: LRU缓存 困难 10 4轮(振荡) 3轮 2轮

关键发现:简单任务(T1、T2)三种策略完全无差异。只有在复杂多Bug任务(T3)上,PID增强模式的优势才显现出来。


1. 你的Agent迭代系统,其实就是一个反馈控制系统

控制系统对比图

把你的Generator-Evaluator架构画成框图,和经典控制系统一一对应:

控制系统概念 Agent系统对应
参考输入 r(t) 任务目标/评估标准(“通过全部10个测试用例”)
控制器 C(s) Evaluator的反馈策略(如何组织critique)
被控对象 G(s) Generator(LLM本身)
输出 y(t) 生成的代码
误差 e(t) (目标通过率 - 当前通过率) / 目标通过率
反馈通道 H(s) 测试用例执行结果

关键洞察:你的系统是一个离散时间闭环反馈系统。每一轮迭代就是一个采样周期。控制论的工具——增益调节、稳定性分析——可以直接应用。

传统做法(简单重试)相当于没有反馈通道——每轮独立生成,系统是开环的。Self-Refine有反馈但不精确——相当于传感器精度不够。PID增强模式提供精确反馈+增益控制——这才是完整的闭环控制系统。


2. 一个实证发现 + 两个理论预测

实验发现与理论预测

实证发现:无反馈重试会振荡退步(有数据支撑)

在T3(LRU缓存)实验中,简单重试策略的轨迹为 7→8→4→10。第3轮从8/10暴跌到4/10,原因是:

Round 2: 使用OrderedDict, 修好了get的move_to_end, 但put更新时遗漏 → 8/10
Round 3: 重新独立生成, 用了dict+时间戳方案, popitem(last=True)方向错误 → 4/10

没有反馈意味着每轮都是"从零开始赌运气"。模型可能换一个实现方案,而新方案引入的Bug比旧方案更多。这不是模型不够强的问题,是系统设计的问题。

对比之下:

  • Self-Refine保留了上轮代码作为参考,轨迹 7→8→10,单调递增无振荡
  • PID增强模式给出精确失败信息+预防性提示,轨迹 7→10,一步到位

已验证发现2:激进反馈/强模式更容易退步 ✅ 实验B1数据

实验B1在LRU缓存任务上对比了两种模式:

普通模式(精确修改指令):40→80→100→100→100  单调收敛,0次退步
激进模式(要求彻底重写):40→50→90→100→90   出现退步,R5从100跌到90

激进模式要求"彻底重写所有相关代码",结果模型放弃了已经正确的实现,引入新Bug。这就像方向盘太灵敏——轻轻一动就打满舵,车就蛇形走位。

实践建议:用更强模型或更激进的prompt时,必须降低反馈增益(用更温和的修改指令)。

已验证发现3:间隔反馈省50%评估成本 ✅ 实验B2数据

实验B2在TTLCache任务上对比了每轮反馈(N=1)和隔轮反馈(N=2):

N=1(每轮反馈):8次评估,第3轮收敛到100%
N=2(隔轮反馈):4次评估,第3轮收敛到100%

同样的收敛速度,但N=2只用了一半的评估调用。Generator在没有新反馈的轮次继续消化上一轮的修改建议,效果不打折。

实践建议:当评估成本高(人工评审或昂贵API调用)时,间隔反馈是有效的成本优化策略。


3. PID控制:精确反馈是核心,PID提供系统化框架

PID控制示意图

我们的实验表明,PID增强模式的真正优势来自两个层面:

  1. 精确反馈:告诉模型具体哪些测试失败、为什么失败、关联Bug是什么
  2. 系统化框架:PID控制信号决定反馈强度级别,避免"每次都全力重写"

真实实验中的PID计算

T3第1轮结束后的PID计算:

# 实际实验参数
target = 1.0  # 目标:100%通过率
score = 0.7   # 当前:70%通过率 (7/10)

e = target - score  # e = 0.30
integral_e = 0.30   # 首轮,积分等于当前误差
delta_e = 0.30      # 首轮,变化量等于当前误差

# PID计算
Kp, Ki, Kd = 0.6, 0.2, 0.15
u = Kp * e + Ki * integral_e + Kd * delta_e
u = 0.6 * 0.30 + 0.2 * 0.30 + 0.15 * 0.30
u = 0.18 + 0.06 + 0.045
u = 0.285  # light intensity(轻度修改)

u=0.285 意味着什么? 系统判断只需要"轻度修改"而非重写。这个信号决定了反馈的语气和要求强度。

PID控制信号如何映射到Prompt

def phi_map_to_prompt(u, failed_tests, diagnosis, original_output):
    """
    将控制信号u映射为不同强度的反馈prompt
    u < 0.3: 轻度修改——保留主体,修复具体Bug
    u 0.3~0.6: 中度修改——重构问题部分
    u > 0.6: 重度修改——考虑换方案
    """
    if u < 0.3:  # light — 实验中T3的情况
        return f"""上一版本整体框架正确,只需修复具体Bug:
失败用例:{failed_tests}
根因分析:{diagnosis}
修复建议:针对性修改,同时检查是否有关联的相似Bug。
保留原有架构,仅修改有问题的方法。"""

    elif u < 0.6:
        return f"""上一版本有结构性问题需要修改:
失败用例:{failed_tests}
根因分析:{diagnosis}
请重构问题部分,但保留正确的逻辑。"""

    else:
        return f"""上一版本问题较多,建议换实现方案:
失败用例:{failed_tests}
根因分析:{diagnosis}
考虑使用不同的数据结构或算法。"""

T3中PID增强模式的实际反馈内容

这是实验中第1轮后发给Generator的精确反馈:

u = 0.285 (light intensity)
Failed: T3 (got 2, expected -1), T7 (got 3, expected -1), T10 (got 1, expected -1)
Diagnosis: get() does not update access order.
  - T3: After get(1), key 1 should be most recent, key 2 should be evicted
  - T7: After get(1),get(2), key 3 is LRU and should be evicted
  - T10: After get(2), key 1 is LRU and should be evicted
Action: Add move_to_end in get(). Also verify put() for existing keys updates recency.

注意最后一句"Also verify put() for existing keys updates recency"——这是PID增强模式的预防性提示。它基于root cause分析推断出:如果get()遗漏了move_to_end,put()更新现有key时很可能也遗漏了。

正是这个预防性提示让模型一次性修复了两个关联Bug——而Self-Refine(没有这个提示)花了两轮才分别修复它们。


4. Lyapunov监控:什么时候该停下来

迭代系统还有一个经典问题:什么时候该停?

用T3简单重试的数据来说明问题——轨迹 7→8→4→10:

def lyapunov_monitor(scores, target=10):
    """
    V(i) = 0.5 * (target - score)^2 —— "不满意程度"
    ΔV = V(i) - V(i-1)
    ΔV < 0: 系统在改善
    ΔV > 0: 系统在恶化
    """
    V = [0.5 * (target - s)**2 for s in scores]
    for i in range(1, len(V)):
        delta_V = V[i] - V[i-1]
        status = "改善" if delta_V < 0 else "恶化⚠️"
        print(f"Round {i+1}: {scores[i]}/10, ΔV={delta_V:.1f} ({status})")

# 简单重试轨迹
lyapunov_monitor([7, 8, 4, 10])
# Round 2: 8/10,  ΔV=-2.5 (改善)
# Round 3: 4/10,  ΔV=16.0 (恶化⚠️)  ← 严重退步!
# Round 4: 10/10, ΔV=-18.0 (改善)

# Self-Refine轨迹
lyapunov_monitor([7, 8, 10])
# Round 2: 8/10, ΔV=-2.5 (改善)
# Round 3: 10/10, ΔV=-2.0 (改善)  ← 单调收敛

如果在Round 3看到ΔV=+16.0这样的剧烈恶化,Lyapunov监控会触发WARNING。配合PID控制,此时应该:

  1. 降低控制增益(避免过度修改)
  2. 或切换为带反馈的模式(别再盲目重试了)

5. 实战Quick-Start:什么时候该用PID增强模式

Quick-Start流程图

基于真实实验的选择指南

场景 推荐方案 理由(实验支撑)
简单任务(首轮>70%通过) 直接生成或Self-Refine T1/T2数据:三策略无差异,PID的overhead不值得
复杂多Bug任务 PID增强模式 T3数据:2轮 vs 3轮 vs 4轮(含振荡)
需要避免退步 PID增强模式或Self-Refine T3数据:带反馈的策略不会振荡退步
多Agent系统 待验证 未测试,已从skill功能中移除
不知道何时停 Lyapunov监控 比"3轮没提升就停"更精确,能检测退步

如果你希望把这套方法直接接到 Codex 的工作流里,我把实验里的判断逻辑整理成了一个可复用的 Skill:harness-design。它做的不是"每个任务都强行上PID",而是先用 Generator-Evaluator 跑出首轮结果:简单任务走普通迭代;复杂多Bug、出现退步或需要控制反馈强度时,再自动升级到PID增强模式。这也正是本文反复强调的工程边界:先评估,再决定反馈力度。

判断你的任务是否需要PID增强模式

def should_use_harness_pid_mode(first_round_pass_rate, num_distinct_bugs, has_correlated_bugs):
    """
    基于实验观察的决策函数
    """
    if first_round_pass_rate >= 0.7:
        return "不需要 — 简单重试或Self-Refine就够"

    if num_distinct_bugs <= 1:
        return "Self-Refine够用 — 单Bug类型不需要root cause分析"

    if has_correlated_bugs:
        return "推荐PID增强模式 — 精确反馈+预防性提示能一次修复关联Bug"

    return "Self-Refine或PID增强模式均可"

完整PID增强模式实现

class PIDHarness:
    def __init__(self, generator, evaluator, target=1.0,
                 Kp=0.6, Ki=0.2, Kd=0.15, max_rounds=8):
        self.generator = generator
        self.evaluator = evaluator
        self.target = target
        self.Kp, self.Ki, self.Kd = Kp, Ki, Kd
        self.max_rounds = max_rounds
        self.scores = []
        self.integral_e = 0.0

    def run(self, task):
        output = self.generator(task)  # 首轮无反馈

        for round_i in range(self.max_rounds):
            score, failed_tests, diagnosis = self.evaluator(output)
            self.scores.append(score)

            if score >= self.target:
                print(f"[Round {round_i+1}] 达标: {score:.0%}")
                break

            # PID计算
            e = self.target - score
            self.integral_e += e
            delta_e = e if len(self.scores) < 2 else (
                (self.target - self.scores[-1]) - (self.target - self.scores[-2])
            )

            u = max(0.0, min(1.0,
                self.Kp * e + self.Ki * self.integral_e + self.Kd * delta_e
            ))

            # Lyapunov监控
            if len(self.scores) >= 3:
                V = [0.5 * (self.target - s)**2 for s in self.scores]
                if V[-1] > V[-2] and V[-2] > V[-3]:
                    print(f"[Round {round_i+1}] Lyapunov STOP: 连续恶化")
                    break

            # Phi映射 + 生成
            prompt = phi_map_to_prompt(u, failed_tests, diagnosis, output)
            output = self.generator(task, feedback=prompt)

        return output, self.scores

6. 实验局限性(诚实声明)

在你决定在生产环境中使用PID增强模式之前,你需要知道这些局限:

局限 说明
样本量极小 仅3个编程任务,每个只跑1次。无统计显著性。
仅1个任务显示差异 T1/T2完全无差异,只有T3(复杂多Bug)看到了优势
Generator和Evaluator是同一模型 Claude既生成又评估(虽然评估是确定性测试用例)
仅测试了编程任务 写作、设计等任务未测试
多Agent场景未验证 未测试,已从harness-design skill功能中移除
PID参数未调优 使用"推荐"默认参数,未验证是否最优
k_G估计流程未实测 Skill中描述的灵敏度估计方法未在实验中使用

我们能确信的结论:

  • 精确反馈(具体失败用例+root cause)比模糊反馈(“请改进”)有效
  • 无反馈重试在复杂任务上会振荡退步
  • PID提供了一个系统化的框架来组织反馈,但它的数学公式本身不是魔法

我们不确信的:

  • PID是否在所有任务类型上都优于精心设计的Self-Refine
  • 最优PID参数是多少(可能因任务而异)
  • 强模型是否真的比弱模型更不稳定

常见坑

坑1: 无脑重试——“再跑一次说不定就好了”

症状:代码任务通过了8/10测试,不给任何反馈就再跑一轮,结果变成4/10。

真实数据:T3简单重试策略,Round 3从8/10退步到4/10。原因是模型换了实现方案(从OrderedDict换成dict+时间戳),新方案的popitem方向搞反了。

解法:至少给Self-Refine级别的反馈(保留上轮代码+告知通过率)。如果任务有多个关联Bug,升级到PID增强模式的精确反馈。

坑2: 反馈不精确——“请改进代码”

症状:每轮只修复一个Bug,需要3+轮才能全部修完。

真实数据:T3 Self-Refine策略,Round 2修好了get()的move_to_end但遗漏了put()的相同问题。因为反馈只说"8/10,请改进",没有指出"put更新现有key时也需要move_to_end"。

解法:在反馈中附带root cause分析和预防性提示。PID增强模式的"Also verify put()"就是这种预防性提示的例子。

坑3: 过度工程——简单任务上硬套PID

症状:花了很多时间配置PID参数、设计反馈模板,最后发现直接跑就能一次通过。

真实数据:T1(合并有序链表)和T2(螺旋矩阵),三种策略表现完全相同。LLM首轮就能做对或一轮修复的任务,不需要控制论加持。

解法:先跑一轮看首轮通过率。>70%通过就别折腾了。


总结:精确反馈是核心,PID提供系统化框架

基于真实实验,我们能说三件事:

  1. 精确反馈显著优于无反馈或模糊反馈——在复杂多Bug任务上,精确的失败信息+root cause分析+预防性提示让模型一次修复关联Bug(2轮 vs 4轮)。

  2. PID控制信号提供了结构化的"反馈强度"决策框架——u=0.285告诉你"轻度修改就够",避免了每次都"请大幅重写"的冲动。但诚实说,目前实验中PID数学公式的贡献可能不如"精确反馈内容"本身大。

  3. 简单任务不需要这些——先评估再决定。首轮通过率>70%的任务,Self-Refine甚至直接重试就够了。

下次你的Agent系统出现振荡时,不要急着换模型、加重试。先问自己:你给了模型精确的反馈吗?它知道具体哪里错了、为什么错、还有哪些关联的坑吗?

这是工程思维,也是实验结论。


参考

  • Tsien, H.S. (1954). Engineering Cybernetics. McGraw-Hill.
  • Madaan, A. et al. (2023). Self-Refine: Iterative Refinement with Self-Feedback. NeurIPS.
  • Shinn, N. et al. (2023). Reflexion: Language Agents with Verbal Reinforcement Learning. NeurIPS.
  • An, S. et al. (2018). PID Controller Approach for Stochastic Optimization of Deep Networks. CVPR.
  • Zheng, L. et al. (2023). Judging LLM-as-a-Judge with MT-Bench and Chatbot Arena. NeurIPS.
  • AICE: AI Control Engineering — 将经典控制理论应用于LLM迭代优化的系统框架.

更多推荐