这两周继续围绕 multi-agent credit assignment 读了两篇 counterfactual 方向的工作——C3 和 CCPO。
补了一轮 Q&A,把全对/全错 trajectory 的价值问题从 GRPO 梯度信号和 NPO 辅助轨迹的角度理了一遍。
另外手动复现了一个 minimal-agent-harness,为后面的 multi-agent RL 实验搭 rollout 环境。
本周清单
- 阅读 C3 / CCPO 两篇 counterfactual credit assignment 论文
- Q&A:全对 / 全错 trajectory 是否一定没有价值(DAPO / NPO)
- 复现 minimal-agent-harness,搭建单 agent rollout 环境
- to-do:CAD-GRPO 实验框架规划
一、阅读论文
multi-agent 训练里不能直接把 team reward 粗暴广播给所有 agent,否则 free-riding / lazy agent / credit contamination 这些问题都会冒出来。但代价也很明显,都要构造某种反事实轨迹或反事实 replay。
1.1 C3: Contextual Counterfactual Credit Assignment for Multi-Agent Reinforcement Learning in LLM Collaboration (arXiv:2603.06859)
C3 这篇主要解决的是 trajectory-level reward 太粗的问题。multi-agent LLM collaboration 里一条轨迹通常是多个 agent 轮流发 message,最后才有一个 sparse terminal reward。standard GRPO / MAGRPO 这类方法会把最终 reward 反传到整条轨迹上,但它并不知道到底是哪一句 message 让系统从失败变成成功,或者从成功变成失败。
核心 idea: C3 把每个 message 当成一个 macro-action,在某个 decision point 上固定已有 transcript-derived context,然后只替换当前 message,后面的交互用固定 continuation replay 继续跑。
可以粗略写成:
其中 是某个 message-level decision point, 是在这个固定 context 下采样到的一个候选 message, 是把这个 message 接到后续 fixed continuation 之后得到的平均回报。后半项就是 leave-one-out baseline,用同一个 context 下的其他候选 action 做对照。
控制住了 prompt 难度、上下文状态、后续 agent 的随机性等因素。比直接用 trajectory reward 干净很多。
启发:
把 credit assignment 的粒度从 trajectory-level 往下拉到了 message-level。不过有一点还没完全想清楚:C3 的 fixed continuation replay 要求框架能非常稳定地复现某个中间上下文和后续执行,这在真实 coding agent / tool-use agent 里不一定容易。tool 调用有副作用,环境状态也可能不是纯文本 transcript 能完全表示的,这块后面如果要接到 harness 上,需要单独 verify。
1.2 CCPO: Counterfactual Credit Policy Optimization for Multi-Agent Collaboration (arXiv:2603.21563)
CCPO 关注的问题是多个 agent 协作解题时,team reward 会掩盖每个 agent 的真实贡献,导致有的 agent 明明没帮忙也跟着吃正 reward,有的 agent 明明做对了但被队友拖累。
核心 idea: 对每个 agent 构造一个"去掉该 agent 贡献"的反事实 baseline,然后用真实 team reward 和反事实 reward 的差作为这个 agent 的 marginal contribution:
其中 表示把 agent 的贡献删除或屏蔽之后得到的 counterfactual trajectory, 是这个反事实轨迹的 team reward。如果 ,说明 agent 对最终结果有正贡献;如果 ,说明它的输出可能是冗余的,甚至是有害的。
论文里在不同 collaboration topology 下定义了不同的反事实构造方式。比如 Think-Reason dyad 里,可以把 thinker 的 reasoning trace 拿掉,让 solver 直接回答;voting 场景里,可以直接从投票集合里删掉某个 agent 的 vote,再重新聚合剩下的 vote。后者几乎不需要额外 decoding,所以 counterfactual 的成本会低很多。
然后 CCPO 还加了 global-history-aware normalization,用历史 rollout 的统计量去校准不同阶段、不同任务分布下的 scale。
启发:
CCPO 可以看成 CAD-GRPO 的一个强 counterfactual baseline。可以在小规模环境里先用反事实方法估一个更可信的 marginal credit,再看 CAD-GRPO 的 和它的相关性。
1.3 C3 和 CCPO 的对比
| 方法 | 粒度 | 反事实对象 | 主要优势 | 对 CAD-GRPO 的意义 |
|---|---|---|---|---|
| C3 | message / decision-level | 固定 context 下替换某个 message | 信号非常细,可以定位具体哪一步有用 | 给后续 step-level extension 提供思路 |
| CCPO | agent-level | 删除某个 agent 的贡献 | 更贴近 multi-agent credit decomposition | 可以作为 CAD-GRPO 的 counterfactual baseline |
| CAD-GRPO | agent-level | 不显式生成反事实,用 batch 内自然变异做回归 | 理论上零额外 rollout 开销 | 需要验证 的充分性和 的稳定性 |
实验里应该同时有"强反事实 baseline"(比如 CCPO / SHARP / C3 的某种简化版本)和"零开销 baseline"(比如 Dr.MAS),然后证明 CAD-GRPO 处在一个比较好的 cost-performance tradeoff 上。
二、Q&A
关于全对 / 全错 trajectory 是否一定没有价值
Q: 这个全对和全错的 traj 一定没有价值吗?这个感觉可以思考,可以看看 NPO (arXiv:2604.20733v1),即往里面加一个好的 traj,或者修正 traj,其实是有利于探索的。全错里面不一定就是没有有用的信息,可能只是一个步骤的问题导致 failure,因此它里面是可以进行探索的。
A: 我觉得对 standard GRPO objective 有没有梯度价值,和这条 trajectory 本身有没有信息价值不是一回事。
在 GRPO 里,如果同一个 prompt 下采样的 group 全对或者全错,那么所有 reward 都一样:
组内归一化之后:
这里要么 ,要么实现里会用某种 clipping / filtering 直接跳过。也就是说,从这一角度看,全对 / 全错 group 确实没有可用的相对优势信号。DAPO 里的 dynamic sampling 会把这类 batch 丢掉。
但 trajectory 有信息价值。
(1)全对 trajectory 的价值
全对说明当前 prompt 对模型来说可能太容易了,组内没有差异,GRPO 没法区分哪条更好。NPO 这篇的思路里,有效学习信号看 traj 和当前 policy 的距离。论文把这个 tradeoff 写成:
其中 代表 auxiliary trajectory 的质量, 代表因为 off-policy / distribution mismatch 带来的 variance cost。外部 teacher 的 trajectory 可能 高但 也高;过去 checkpoint 的 trajectory 可能 低但 不够。NPO 的做法是用同一次训练里的 near-future checkpoint 提供辅助轨迹,因为它比当前 policy 强一点,同时又没有离当前分布太远。
如果当前 group 全对,本身没有 pairwise contrast,但可以从另一个角度构造 contrast。比如加入一个 near-future / repaired / higher-quality trajectory。这样全对不再只是 binary reward 下的一坨 1,而是可以变成一个更细粒度的学习信号来源。
(2)全错 trajectory 的价值
我倾向于把全错 trajectory 看成需要被重新表征的信息。可行的处理方式可能有几种:
- process / step-level proxy:给中间步骤引入可验证指标,比如代码场景里的 compile success、unit test partial pass、lint/format 是否通过。
- counterfactual repair:像 C3 那样固定 context,只替换某个 message 或 step,看是否能把失败轨迹修成成功轨迹。
- guided contrast:像 NPO 那样加入一个更强但分布相近的 guide trajectory,让原本全错的 group 里出现可比较对象。
- agent-level decomposition:用 CAD-GRPO 的 尝试识别"虽然 team reward 为 0,但 agent 的局部质量是否高于队友/高于均值"。
综上,全对/全错的 trajectory 是有价值的,如果只用 binary final reward,很多 batch 会没有足够 variance,回归也会不稳。
三、复现 minimal-agent-harness
先在 blog 上写了 Claude Code 的架构分析,然后手动复现了一个 400loc 的单 agent harness。重点关注状态、权限、上下文和错误恢复怎么组织。
该项目 minimal-agent-harness,已经推送 GitHub。这个版本没有追求功能完整,主要目标是把最小闭环跑通:
- 消息循环:维护 system / user / assistant / tool result 这几类消息,让模型每次都能在完整历史上继续决策。
- tool registry:把可用工具注册成结构化 schema,模型只负责提出 tool call,执行逻辑由 harness 统一接管。
- tool dispatch:根据 tool name 分发到对应 handler,并把 stdout / stderr / error message 重新包装成 observation。
- 终止条件:区分"模型给出最终回答"和"模型继续请求工具",避免 loop 无限制跑下去。
- 错误处理:tool 调用失败时不直接崩掉,而是把失败结果作为上下文返回给模型,让它有机会 self-correct。
本质上就是一个受控的 rollout environment。LLM policy 每次输出 action,tool/environment 返回 observation,context manager 决定历史怎么保留,stop condition 决定 episode 什么时候结束。
这和后面要做的 multi-agent RL 实验是直接相关的。因为如果要训练 coding agent 或 multi-agent solver,首先就要把每次 rollout 记录成结构化数据:
在 LLM agent 里, 是一整段 message history; 可能是自然语言,也可能是 tool call; 可能是命令输出、文件 diff、测试结果或者 verifier feedback。 自然是奖励信号。
从 CAD-GRPO 的角度看,harness 至少要额外暴露几类信息:
- 每个 agent / role 在每个 step 的输出;
- 每个 tool call 的执行结果和失败类型;
- 每个 agent 对最终 artifact 的可验证 proxy,比如 compile success、test pass、格式约束、局部 verifier score;
- team-level final reward;
- prompt id / group id / rollout id,方便后面做 group-relative normalization 和 batch-level regression。
四、to-do
4.1 开始搭建 CAD-GRPO 实验框架
尝试先把数据流打通:
- rollout schema:定义
prompt_id / group_id / rollout_id / agent_id / step_id / message / tool_call / observation / q_i / reward这些字段,保证后面能直接从日志里还原 per-agent trajectory。 - quality proxy:先在 coding 或 toy math 环境里定义便宜的 ,比如格式合规、局部答案可验证、compile/test partial pass、是否产生有效中间结果等。
- batch-level ridge regression:实现最小版 CAD-GRPO credit estimator:
然后输出每个 agent 的 和去混淆 advantage。
- baseline:先实现 naive team-reward GRPO、Dr.MAS-style per-agent normalization、以及一个简化的 counterfactual oracle(小环境里可以手工删 agent 或替换 agent 输出)。
4.2 把 harness 的实验日志接起来
让它能产出训练需要的日志。计划先加三个东西:
- 每次 tool call 和 message 的结构化 trace export;
- 一个简单 verifier,把 final reward 和局部 proxy 同时写入日志;
- 一个离线 analysis script,读日志之后能直接生成 regression matrix 和 reward vector 。
如果这条链路能跑通,后面就可以先在小 batch 上验证 CAD-GRPO 的 能不能奏效。
劳动节快乐!