先说清楚要解决的是什么问题,因为这决定了后面所有设计。
我同时推着几个项目,时间被会议切碎。拿到一个新任务,我能提供的原料通常只有会议记录和聊天记录 —— 而我没有时间把它们读完、想清楚、再设计。
自然的想法是让 agent 去做。但这里有个矛盾:
我必须是那个做决策的人 ← 做错了是我的责任
但我付不起「理解全部上下文」的时间成本
传统上这两件事绑死:你要决策,就得先理解。
这套循环要做的,就是把它们解耦:
agent 承担理解成本 —— 读 68KB 的会议记录
↓
压缩成决策形态 —— N 个选择题,每个带推荐、代价、能不能拖
↓
我在【不读原文】的情况下,做出和【读了原文】一样的决策
所以它的价值不是「省时间」,是**「在不理解全部的前提下,依然能做对决策」**。
一、整体架构
三个关键分离:
① 对话 session 不干活。 它只负责入口和出口 —— 建任务、看结果、拍板。真正干活的是定时任务启动的另一个独立进程。
这个分离是必须的。如果对话 session 自己干活,那就是普通的 agent 用法 —— 我得一直在场。
② 定时任务和对话 session 之间只能通过文件说话。 它们是两个进程,不共享内存。这不是设计选择,是硬约束 —— 而它恰好逼出了后面文件分层的设计。
③ 只有一个 agent 能写文件。 圆桌全部只读,结论交回主 agent 落盘。这条抄自 Cognition:额外的 agent 贡献判断,不贡献动作。
二、队列机制:目录即状态
任务是 markdown 文件,状态用目录位置表示:
tasks/
├── queued/ 排队 序号小的先取
├── running/ 在跑 WIP 上限
├── blocked/ 挂起 卡在外部,等答案
├── done/ 完成
└── dropped/ 废弃 显式标原因,不删文件
取任务 mv queued/010__项目__标题.md running/
挂起 mv running/xxx.md blocked/
完成 mv running/xxx.md done/
mv 是原子操作,没有中间态。整个生命周期就是文件在五个目录之间搬家:
注意 running --> queued 那条:agent 死了没人知道,只能靠文件 mtime 超时判定。
而撞预算走的是 blocked 而不是 queued —— 否则会被超时机制自动重跑,无限烧钱。
为什么不把状态写在文件里
一开始我在 frontmatter 里写 status: running,同时文件也在 running/ 目录。跑了几次就出问题了 —— agent 挂掉之后,目录说它在跑,而另一张记录表说没有任务在跑,谁都不知道该信哪个。
同一件事有两个记法,就一定会不一致。目录位置是唯一真相源,frontmatter 只放位置装不下的东西(比如「在等哪条决策」)。
命名:序号前缀,不是时间戳
010__项目名__任务标题.md
015__项目名__临时插进来的.md ← 留空隙,插队只是改个文件名
020__项目名__另一个任务.md
时间戳只能表达「什么时候加的」,表达不了优先级。而队列的顺序就是优先级 —— 那是我扔进去的顺序,agent 不许重排。
分隔符用双下划线是踩出来的:第一版用单横杠 序号-项目-标题,结果接第二个项目时,项目名里带横杠,解析直接崩。文件名当结构化数据用,分隔符必须和内容不冲突。
双层队列:入队的是原料,不是任务
这一条我没在别处见过,但它是整套设计里最贴合真实场景的一处。
传统任务队列 你想清楚 → 写成任务 → 入队
⚠️ 它假设你已经知道任务是什么
我的场景 原料入队 → agent 消化 → 任务从原料里长出来
⭐ 因为我的痛点恰恰是「没时间想清楚任务是什么」
所以是两层,中间有一道人闸:
原料队列 我随便扔,零成本
↓ 消化
任务候选 agent 提出「我觉得这些是要做的事」
↓ 我确认 ← 这道闸不能省,否则它会自己给自己派活
任务队列 有序,agent 从上往下取
WIP 上限:瓶颈不是 agent,是我
同时在跑的任务有上限。这个上限不是技术限制:
1 个任务 ≈ 若干条待我拍板的决策
3 个并行 = 三倍的决策 + 三次上下文切换
真实成本是上下文切换,不是「30 秒 × N」。 一旦决策队列的膨胀速度超过我清理的速度,我就会开始逃避看它 —— 那整套就崩了。
三、定时调度:出队逻辑
调度器是个 shell 脚本,cron 每隔几分钟调一次。它干七件事:
① WIP 检查 running/ 里 ≥ 上限 → 立刻退出 ⭐ 队列空时零成本
② 取队首 queued/ 里序号最小的那个
③ 解析项目名 从文件名拿
④ 组 prompt 指向项目配置 + 通用规矩 + 这个任务文件
⑤ ⭐ 先 mv 后启动 先移进 running/,再启动进程
→ 下一个 tick 就取不到它了,天然幂等
⑥ nohup 后台跑 立刻返回
⑦ 后台守着 进程结束后看它是怎么结束的
第 ⑤ 步是关键。 先改状态再启动,所以 cron 跑多密都不会重复取同一个任务 —— 不需要锁,不需要额外的协调机制。
无人值守必须有的三个兜底
预算上限。 agent 按用量计费,而它自己不知道该停。一个卡住的任务可以反复试两小时。命令行有 --max-budget-usd,撞了就断。
撞预算不许自动重跑。 这一条是踩出来的:撞预算 → 进程死 → 任务留在 running/ → 一段时间后判定「agent 死了」→ 自动重跑 → 又撞 → 无限烧钱。所以撞预算是个特殊状态,任务进 blocked/ 并写明原因,等人决定。
死任务回收。 running/ 里的文件 mtime 超过 N 小时没变,判定它的 agent 已死,移回 queued/ 重跑。这个机制抄自一个断点续传的实现 —— 但要注意,它和上一条冲突,所以撞预算的必须走 blocked/ 而不是留在 running/。
透明度:不用自己造
我一开始打算让 agent 主动往一个 progress 文件里写进度。这是个坏主意 —— 它靠 agent 自觉,而 context 一长就会漏。
后来发现命令行本来就给了:
claude -p "..." \
--session-id "$(uuidgen)" \
--output-format stream-json \
--forward-subagent-text \
--max-budget-usd 20 \
>> logs/任务名.jsonl &
--output-format stream-json实时流出每一步--forward-subagent-text连 subagent 的内容都带出来--session-id自己指定,跑之前就知道- 另有一个子命令能列出所有活着的 session,不需要 TTY
日志是 JSON,读起来费劲,所以我写了个几十行的脚本把它翻成人话。透明度这件事,大概率不用你自己造 —— 先翻文档。
四、多 agent 规划:这块我调研得最久,因为直觉全错
❌ 别按工种拆
我最初想的是「研究 agent / 设计 agent / 实现 agent」。有人试过:
- Anthropic 做过按软件开发角色划分的实验(planner / implementer / tester / reviewer),结论是「subagent 花在协调上的 token 比花在干活上的还多」。他们给的替代原则是按上下文边界拆,不按工种拆。
- Cognition 更具体:把「做个小游戏」拆给子 agent,一个做出了某种风格的背景,另一个做的角色和整体美术对不上,负责合并的 agent 面对两个不兼容的产物无法收场。merge 这个问题他们坦承至今没解决,是直接绕开的。
判据:只有当上下文能被真正隔离时才拆。
「设计 / 实现 / 测试」拆不开,因为它们共享同一份上下文。拆了是伪隔离,只多出协调成本。
❌ 也别指望「多来几个 agent 就有多样性」
我想过一个看起来很聪明的做法:让 N 个 agent 各出一个方案再比较,总比一个 agent 自己出 A/B 强吧?
不强。同构输入下的多 agent 辩论在数学上是个鞅 —— 期望准确率不随轮次提升;3–10 个同构 agent 的各种聚合策略打不过单个 CoT agent。失败路径有三类:谄媚趋同、上下文脆化、共识坍缩(投票把已经生成出来的正确答案投掉了)。
换个 agent 但同模型、同上下文,约等于白花钱。
⭐ 多 agent 多在【输入】,不在【角色名】
这是我调研之后最重要的一条转变。
我这套里真正有效的那次,是三个「立场审」角色。但它们有效不是因为叫了三个名字:
角色 1 → 只拿到干系人 A 的表态记录
角色 2 → 只拿到干系人 B 的表态记录
角色 3 → 只拿到客户方的表态记录
三份输入互不重叠 → 这才叫异构 → 独立收敛才有信息量
如果它们共享输入,那「三个角色都指出了同一个问题」没有任何信息量 —— 那只是同一个推理被复制了三遍。
实测里它们各自抓到了不同的东西,又在一个真问题上撞到一起。这个「部分重叠」的模式才是真独立的特征:
完全一致 ⚠️ 可疑,大概率共享了上下文
完全不同 🟡 没有共识,信息量低
部分重叠 ⭐ 真独立
圆桌内部长这样:
三条不能破:主 agent【选】角色不【编】角色(编出来的没有输入隔离规则)· 并行派出看不到彼此(顺序派会互相污染)· 圆桌只读,结论交回主 agent 落盘。
输入隔离 = 现实的保真度
于是「给这个角色看什么」不再是工程细节,而是角色定义的核心:
| 角色 | 它在现实中是谁 | 只给它 | 绝不给 |
|---|---|---|---|
| 立场审 | 一个具体的干系人 | 方案 + 这一个人的表态记录 | 会议原文 · 我自己的吐槽 · 方案的推理过程 |
| 技术审 | 未来维护这段代码的人 | 方案本身 | 方案的推理过程(给了它会替作者辩护) |
| 追问者 | 会上不好糊弄的听众 | 方案本身 | 推理 · 背景材料(真听众也没看过) |
| 终审 | 刚来的验收员 | 判据 + 产出 + 轨迹 | 背景故事 |
原则一句话:每个角色只拿到它在现实中真能看到的东西。
给多了,它就在模拟一个不存在的、什么都知道的评审者 —— 那种评审没有预测力。
还有个附带效果我是踩了才知道的:如果给评审 agent 看了我对某个人的抱怨,它会把那个人描绘得比实际更不讲理。 读了几周你的吐槽之后,agent 会在所有事情上站你这边 —— 而那时候它的判断已经不值钱了。
要不要派一个新 agent:四道闸
我拿这四道闸回头验自己已有的角色:审查类的全部通过,而原本想加的「实现 agent」和「N 个方案生成 agent」在第一、二、三道闸就被拦下了。判据自洽,不是事后找理由。
最该警惕的一条
伯克利那份 MAST 研究标注了 1600+ 条多 agent 轨迹、覆盖 7 个框架:
41.8% 规格与系统设计问题 角色定义模糊 · 拆解不当 · 重复的 agent 角色 · 缺终止条件
36.9% agent 间失配 交接丢上下文 · 输出冲突 · 格式不匹配
结论是:多数失败源于组织设计,而非模型能力。
配上成本 —— 多 agent 通常 3–10 倍 token,研究型场景约 15 倍。所以我给自己的规矩是:默认不加角色。 每加一个都得先回答:它的上下文和别人隔离吗?输出契约是什么?
五、关键文件设计:什么给 agent 看,什么给人看
这一块比我预期的重要得多。因为两个进程只能通过文件说话,文件的读者是谁,决定了它该长什么样。
给 agent 看的 给人看的
───────────────────────── ─────────────────────────
PROTOCOL.md 通用规矩 DECISIONS.md 待我拍板的
PROJECT.md 项目配置 · 红线 看板 PNG 一眼扫的状态
HANDOFF.md 工作区 STATUS.md 项目当前状态
角色定义 ⭐ 人机接口是【对话】,不是文件
人机接口是【对话】,不是文件 —— 文件只是让两个进程能对上账。
HANDOFF.md:明确写「本文件不给人看」
这是 agent 之间的工作区 —— 完整推理、死胡同、失败尝试全留着。它的头部第一句就是:
⚠️ 本文件不给人看。不要为人的可读性做任何妥协。
为什么要写死这一句?因为不写的话,agent 会本能地把它整理成「给人看的样子」 —— 删掉死胡同、简化推理、加小标题。而那些恰恰是下一个 agent 最需要的东西。
死胡同尤其重要。有一条实测发现:append-only 的失败记录是个坏主意 —— agent 看到被删掉的旧方案,会跑去重建它。所以废弃的东西必须显式标记为废弃,而不是删掉。
DECISIONS.md:唯一需要人看的那份
它的设计目标只有一个:让我 30 秒能拍一条板。
### D-003 · 一句话标题 [待你拍板]
**推荐:A**
| | 好在 | 代价 |
|-------|------|------|
| **A** | … | … |
| B | … | … |
**推荐 A 的理由** 一句话
**反方意见** 圆桌说的
**不决定会怎样** ⭐ 卡住哪一步 / 不卡,可以先跑别的
---
你的选择: ____
你的理由: ____ ← 必填
两处是刻意的:
- 「不决定会怎样」 —— 告诉我这条能不能拖到明天。没这栏,每条我都得当紧急的看。
- 「你的理由」必填 —— agent 能从中学我的口味;而且三个月后我自己会想知道当时为什么这么定。
一个花了我三次蒙圈才想明白的设计
前几轮跑下来,产出的决策队列里有 12 条,我看着一片空白 —— 完全不知道怎么拍。
我一开始以为是我没时间理解项目。后来才发现是队列设计错了:
12 条里,10 条标着「需对外确认」
⚠️ 那些本来就不该由我拍,它们要拿去问别人
我把「所有未决项」都当成了「等你拍板」。 而我真正需要面对的只有 2 条。
修法很简单:看板只显示我自己能拍的,需要问别人的单独列一块,并且由 agent 汇成「按人分组的问题清单」。从 12 条变成 2 条。
这条推广开是:
写决策项之前先问 —— 接收方有没有判这条所需的上下文? 没有的话,要么翻译成他有的语言,要么根本不该进他的队列。
中途插话:一个 INBOX 文件
agent 跑起来之后,我想插一句「别做 X 了」怎么办?两个进程不能直接对话。
<项目>/INBOX.md 我往里写
agent 在【每个段边界】读一次
有内容 → 读进来,按它调整,把原文挪进 HANDOFF 存档,⭐ 清空 INBOX
没内容 → 继续
只在段边界读,不实时。 实时读会打断它的推理,而且它正在思考时读到新指令,行为不可预测。
这就是异步消息队列的最小实现 —— 一个文件。
六、凭什么算完成:两道闸
agent 说「我做完了」是不可信的,这是被反复点名的失败模式。有个描述很传神:verifier 跑一两个测试,看到通过,就宣布成功。
所以有两道闸:
闸1 脚本 量【能量化的】:产出图多高、产物目录在不在、任务有没有写判据
闸2 终审 agent 判【脚本判不了的】:比如「他看完能说出明天动哪件事」
能量化的绝不交给 agent。 让 agent 判自己的产出,它永远说「做完了」。
而终审 agent 的设计有个我一开始搞反的地方。我给它设了条约束:不给它看执行过程,只给判据和产出 —— 理由是看了过程会被「它挺努力的」影响。
查资料才发现结论相反:Meta 那篇 Agent-as-a-Judge 里,裁判看完整轨迹时与人类判断一致率是 90%;只看最终产物的传统做法只有 60–70%。
我把两件事混成了一件:
判「做完没有」 → 要看轨迹
判「写得好不好」 → 要干净上下文
同一个「审」字,底下是两种任务。
七、实证:三次被自己的系统打脸
上面的设计不是一次想出来的。这里给三个具体的翻车,它们是这套东西目前最硬的证据 —— 因为每一条都是我先拍脑袋、后被打脸。
打脸一:规矩写了,但没有执行者
我定过一条判据:产出的主干必须一屏能扫完。写了检查脚本,超了就 exit 1;规矩文档里也白纸黑字「体检不过不许标记完成」。
跑了几个任务后一查:三个产出分别是 6.9 屏、2.8 屏、7.1 屏,全部不合格,而且全都躺在「已完成」里。
脚本确实 exit 1 了。但调用它的那段流程,没有任何一行代码去读那个退出码。
规矩 ≠ 判据。判据必须有一个不讲情面的执行者。 写在文档里的约束对 agent 来说是建议;写在退出码里并且被消费的,才是边界。
打脸二:接上闸门后,判据被一行 CSS 绕过
线接上了,我以为这条结了。
然后我派了两个独立的审查 agent 去审一个方案 —— 它们看不到彼此。两个各自独立地指出同一件事:
字号 16px 调到 10px,或者加一行
body { zoom: .6 },或者单栏改三栏 —— 任何一条都能把 14000px 压进 2600px,而体检照样是绿的。
这是静默失败的教科书形态:检查通过了,产出是一张 9px 字、没人读得了的图。
我想要的是「可读」,但我能量化的只有「高度」,于是用高度代理了可读性。而只要一个指标成为目标,它就会被优化 —— 优化的方式往往不是你想要的。
修法不是找一个更聪明的指标(没有),而是把最省力的作弊路径显式禁掉,并让审查者专门查这一条:
压缩只准靠真删内容。禁止:调小字号 / zoom / transform:scale / 改多栏挤压 / 压行高
你堵不住所有作弊,但你可以让作弊比把事做对更费劲。
打脸三:审查 agent 拒绝回答我的问题
我给圆桌出了道题:「判据该怎么分档 —— 方案 A 还是方案 B」。
它没选。它绕过我的选项,直接指出:
你这个方案的前提不成立 —— 闸门根本没接上,判红的三个产物全在完成里。 在闸门接上之前,阈值定多少都不影响任何事。
它推翻的是我出题时的假设,不是我给的选项。而这恰恰是我最需要的那种反馈。
审查者最有价值的时候,是它拒绝回答你的问题。
顺带:接第二个项目时抓到的四个 bug
泛化验证的价值也在这里 —— 只跑一个项目,永远碰不到这些:
① 初始化脚本要求项目目录已存在 —— 而「接新项目」正是它该干的事
② 集中化改造时漏改了初始化脚本 —— 还在旧位置建目录
③ ⭐ 项目名解析用单横杠切分 —— 第二个项目名字里带横杠,直接崩
④ glob 模式还是旧分隔符 —— 改完命名规则后匹配不上
第 ③ 个最实质:文件名当结构化数据用时,分隔符必须和内容不冲突。
八、哪些是抄的,哪些是自己趟的
写这类文章最该说清的是每个结论的证据等级。
有出处的:按上下文拆不按工种拆(Anthropic)· 写操作单线程与合并失败(Cognition)· 同构多 agent 是个鞅(arXiv)· 裁判看轨迹一致率 90%(Meta Agent-as-a-Judge)· 41.8% 失败源于组织设计(MAST)· 人审 plan 不审代码(HumanLayer)· 用 IM 做决策队列会失败(LangChain 自用邮件 agent 的半年实践)· 多模型共识与人的判断不一致(Karpathy 的 llm-council)。
没找到前人的(也可能是我没找到):
- 入队的是原料,不是任务 —— 所有现成的任务队列都假设你已经知道任务是什么
- 「返工波及范围」判据 —— 卡住时继续还是停,判据是「假设错了会不会让已完成的部分返工」
- 多 agent 多在输入,不在角色名
- 派新 agent 的四道闸
- 判据会被视觉压缩绕过,以及对应的反作弊条款
- 审查者最有价值的时候,是它拒绝回答你的问题
还有一条,我最没底但最想留下来:
一条从来没被违反过的规矩,是废规矩。
因为它要么描述的是 agent 本来就会做的事,要么描述的是还没出现过的场景。而规矩文件一长,agent 遵守它的可靠性就下降 —— 这是有实测支撑的。
所以我留了个动作:跑够几轮之后,用审计日志统计每条规矩被违反过几次,一次都没有的删掉。
现在的状态,以及还没验的
已经验过的:
独立进程自己跑完 一次运行 $3.18 / 30 轮 / 325 秒,人没插手
圆桌不编 一次圆桌 42 处原话引用 / 0 处推测词 / 6 处标「无依据」,并把推荐改了
输入隔离有效 三个看不到彼此的角色独立收敛到同一个问题
圆桌能推翻发起人 证伪了我出题时的三个前提
泛化 接第二个项目 = 项目配置 52 行,通用规矩一字不改
还没验的:
真改代码那一段 默认禁止,要开需要三个条件同时满足
打断频率 一周被打断几次?几次是真必要的?
⚠️ 调研里明确说:全世界没有一份带实测数据的打断阈值
长期运行的腐化 规矩文件会不会越来越长、看板会不会没人看
最后那三条只能靠时间。等有数据了我再写一篇 —— 那时候才谈得上「这套东西到底有没有用」。