0. 结论先行
- Curator 真实存在,但不在安装包里。它在仓库的
experimental/curator/下,README 自述”随仓库提供,不包含在安装包中”。本机 uv 工具里的 raven 0.2.4 没有任何 curator 模块,~/.raven/config.json里也搜不到相关键——这不是配置疏漏,是官方就没发。 - 它对 Raven 源码零侵入。
raven/没有为它改动一行,也不引用它。Curator 的产物只落在 Raven 本来就开放的扩展点上:home 文件、配置、钩子、插件贡献、ACP 子代理,由真实的 Raven AgentLoop 执行。依赖方向是单向的experimental/ → raven/。 - 它改写的是 harness,不是提示词。Raven 把 Agent 的运行循环拆成四个相互解耦的策略模块——Memory 决定本轮能看到什么,Planning 决定如何着手,Capability 决定本轮开放哪些工具,Action 决定做什么并在执行前加以判断。Curator 正是在这四个位置上持续改写。
- 七轮试跑,七种死法:缺第三方搜索 key、单次调用超时(300s / 900s / 1800s 三次)、网关停机连带全死、curator 调用预算用尽。每一轮都往前推进一截,最后一轮员工真的上场,交付了一份行程方案 PPT 并逐页自检。
- 小时级是这套设计的常态,不是故障。官方展示案例 3 轮 14 回合用了 4.4 小时(claude-opus-5.5);我们用 step-5-preview,单次 curator 调用分钟级。把失败请求原样重放,low effort 下 237 秒完成、产出 16,749 个 completion token。
- 自进化不处理基础设施错误。网络超时靠 provider 自带重试吸收;四次都失败则 iteration 转 paused,而 simulation CLI 没有 resume 入口,没有任何机制会自动恢复它。这是硬边界。
- 为了”看见运行”,我们自建了一条 SSE 事件流。试跑是脱离网关的独立进程,而 Raven 界面只建模会话、子代理和文件,看不见它。把
run.log里员工 AgentLoop 的结构化事件推成实时流,延迟约 0.4 秒。
一、Curator 是什么:四个策略面与一个改写者
官方中文 README 的「运行时自进化」小节把机制说得很紧凑,值得先原文引用:
Raven 从架构设计之初就支持运行时自进化。它将 Agent 的运行循环拆分为四个相互解耦的策略模块——Memory 决定本轮能看到什么,Planning 决定如何着手,Capability 决定本轮开放哪些工具,Action 决定做什么并在执行前加以判断。Curator 正是在这四个位置上持续改写:可以是一项配置变更,也可以是为该 Agent 编写的一段判断逻辑。改动仅作用于对应的 Agent,装配完成后即按新的实现运行。
四个策略面是理解整套东西的钥匙。它不是”给 Agent 换段 system prompt”,而是在四个正交的位置上换实现:
| 策略面 | 管什么 | 改写它意味着 |
|---|---|---|
| Memory | 本轮能看到什么 | 换检索范围、换注入的历史、换技能呈现 |
| Planning | 如何着手 | 换任务分解与流程规范 |
| Capability | 本轮开放哪些工具 | 换工具集、换外部服务、换子代理编组 |
| Action | 做什么,执行前如何判断 | 换行动前的检查规则与判断逻辑 |
experimental/README.md 把这套机制的定位说成”数字员工的 Worker RSI“——让 Curator 通过多轮交互,把一个 Raven 数字员工的 Harness 改造到能满足交互方提出的核心要求,”像培养一个新员工那样”。改进来自两个方向:
- 按资料定制:交互方交出的资料(流程、规范、模板)被落到 Harness 的四个策略面上,员工从第一轮起就按这些规矩工作;
- 按反馈改进:员工工作后,评价方说出哪里不对,Curator 修订 Harness,下一轮再检验。
质量由两点保障:装配前必须通过校验,未通过则返工;常规轮次的信号在交给它之前会剥掉参考答案,减少答案的直接暴露。
代码结构也照这个分工展开:
1 | experimental/ |
二、它怎么嵌进 Raven:零侵入与单向依赖
这是最容易被忽略、但设计上最讲究的一层。
experimental/README.md 第一句就划清了边界:”整个目录对 Raven 源码零侵入:raven/ 没有为它改动,也不引用它。Curator 生成的东西落在 Raven 本来就开放的扩展点上(home 文件、配置、钩子、插件贡献、ACP 子代理),由真实的 Raven AgentLoop 执行。”
我们做了双向实证。导入层面,experimental/simulation/__main__.py 里是 from raven.config.mode_catalogue import build_mode_catalogue、from raven.providers.factory import make_lazy_provider——它 import 的是 raven 包本体;simulation/employee.py 里还有 from raven.agent.subagent.builtin_agents import GENERIC_AGENT。运行时层面,试跑日志里能看到员工是被真实拉起的 AgentLoop:
1 | raven.acp_client.client:launch - acp agent 'Raven': started ['.../raven-src/.venv/bin/python'] (pid 5026) |
docs/design.md 第 5 节甚至自列了它依赖的 Raven 内部成员:AgentLoop._playbooks、_disabled_tools、_connect_mcp、_started_services、_mcp_tool_notices、_now_fn、_skill_hub_client、_provider_pool、_skill_blocklist_reader,以及 raven.acp.server._answer/_drain、raven.cli.acp_commands._open_stdin。这是一份诚实的依赖清单——它踩的是私有成员,所以官方说它实验性、不随安装包发布,是有道理的。
还有一个值得记的事实:0.2.4 的运行时其实已经为 Curator 留了挂点。agent/context/builder.py 写着”Curator working state (those are owned by the assembler’s …)”,agent/loop/turn_path.py 提到”curator candidate view”,agent/harness/memory.py 说”context engine 是负责装配的一方,持有 Curator 的工作状态”。也就是说四个策略面的扩展位在运行时里是认的,缺的只是那个”改写者”的实现。
最后要和 Evolver 区分开,这是两个不同的东西:Evolver 是离线基准测试驱动、以库方式调用 Raven、官方已标记计划退役;Curator 是对话式多轮、运行时改写 harness、官方标记实验性。
三、试跑准备
- 仓库:
github.com/EverMind-AI/Raven@ commita2d391ea(2026-10-18 21:23 +0800,main tip)。第一次git clone --depth 1以 “unexpected disconnect while reading sideband packet” 失败、留下空.git,重试即成功。 - 独立 venv:181 个包全部安装成功、0 失败(raven 可编辑安装指向仓库树,外加 ppt-engine、design-engine、everos-memory 等 plugins-dist workspace 成员)。
- 仓库自带测试:
tests/test_harness_curator_*.py、test_analyst_*.py、test_iteration_*.py、test_simulation_*.py共 368 个全部通过——四层机制在部件级可用。 - 模型通路:用 trial 配置经 Raven 自己的 provider 工厂做了一次最小真实调用,stepfun/step-5-preview 返回正常。
- 隔离:trial 配置里
memory.backend=null(禁用 everos,不碰生产库)、RAVEN_HOME指向 trial 目录,全程没动生产状态。
入口命令形如:
1 | python -m experimental.simulation \ |
四、七轮试跑实录
| 轮次 | 死法/进展 | 位置 |
|---|---|---|
| run1 | 员工启动第一步即失败:Raven-Research 的网页搜索默认 vendor 是 serper,仓库检出无 .env、无 SERPER_API_KEY |
hire → prepare_children |
| run2 | select 阶段第 4 次调用超过 300s,asyncio.TimeoutError 被包装成空消息 |
select |
| run3 | 超时提到 900s 仍不够;同期做了关键取证 | select |
| run4 | 1800s 预算下冲到 implement 第 15 次调用,员工真的上场(写占位申请、跟学生客户对话、逐条自查 S5.3/S6.7/G6/G8 红线)——随后网关停机,全死 | implement |
| run5 | setsid 脱离网关;design 阶段第 12 次调用撞上 1800s | design |
| run6 | low effort + 3600s;冲到 implement,24 次调用预算用尽 | implement |
| run7 | 72 次调用预算;员工交付行程方案 PPT 并逐页解析 pptx 自检 | implement → 员工上场 |
几个值得单独说的结论:
run1 的缺 key 问题用配置层绕开,没动生产凭据。 Raven 的 schema 支持 switchOnly 存根行,merge_product_seeds 读该 flag 把已发现产品置 disabled。代价是员工作演练时没有 Research/PPT 子代理,但 Curator 闭环本身不受影响——而那正是要验证的东西。
超时是预算问题,不是机制故障。 三次试跑每次抬高预算都能走得更远(select → select → design → implement),说明调用只是慢。run3 的取证把这一点钉死了:从暂停状态提取第 5 次调用的完整 22 条消息(约 82K prompt tokens),用同一 provider 原样重放——不带 tools、effort 取 low 时 237 秒完成,completion_tokens=16,749;而真实调用跑在 high effort 且带 select 提交工具,超过 900s。官方展示案例 s0925c 的 provenance 显示全程 4.4 小时。curator 单次调用分钟级本身就是这套设计的常态。
run4 的死法教会我们最重要的一课。 它当时是作为 Raven 子代理跑在网关里的,网关一停,子代理和它拉起的模拟进程一起死。所以后续轮次全部改成 setsid 脱离网关会话——进程的 SID 与网关不同,网关重启不再连带杀死它。代价是它跳出了 Raven 界面的模型,这是下一节的主题。
run6 与 run7 是两个参数的博弈。 run6 把 effort 降到 low(CLI 注释明确该开关只作用于 Curator/Analyst/评审,员工自身 effort 不受影响),38 分钟冲到 implement,然后 24 次调用预算用尽——它自己说了 “resume with a higher total max_calls”。run7 把预算提到 72,员工终于完整上场。
五、事件流:把”看不见的运行”变成一条推送
试跑脱离网关之后,出现一个真问题:它跑了什么,看不见。Raven 的 Web 界面只建模三样东西——会话、子代理、文件。一个独立操作系统进程不属于其中任何一样,界面就没有可绑定的对象。文件类接口也只有 fs.dirs(浏览目录)、fs.open/fs.reveal(作用在网关主机上,远程用户用不上)、fs.upload;内嵌查看器只渲染 markdown / PDF / 图片,不渲染 .log。
但数据是全的。员工是真实的 Raven AgentLoop,run.log 里每一步都记了:Tool call: 连参数、Tool result: 连耗时与结果、Assistant reasoning 连思考全文、Assistant ( 连输出、Iteration N/40 连迭代序号。第一版状态页只取了日志末尾 8 行原文,等于把金矿糊住了。
于是做成一条推送式事件流:
- 服务:Python 标准库
ThreadingHTTPServer,一个后台线程每 0.4 秒尾随run.log,把新行分类成tool_call/tool_result/reasoning/assistant/iteration/warn/system/info,通过 Server-Sent Events 推给页面; - 暴露:WSL 侧
raven-tunnel.service增加第二条-R 18793:127.0.0.1:18793反向转发,ECS nginx 加一个location /trial/反代过去,和 Raven 本体共用同一个域名、同一套 Basic Auth; - 脱敏:所有推向页面的日志文本都过一道替换,40 字符以上的连续 token 一律打成
***REDACTED***,防止密钥随日志外泄。
计数器区给出阶段、调用数、查询数、校验/返工与运行时长;下面就是事件流本体。
这张图是员工真实工作的切片,值得逐条看:
tool_call带完整参数——{"request": {"stage": "S7"}, "name": "curator_planning"}这样的结构化调用,以及一次参数不合规(Invalid parameters for tool 'tool_call': arguments should be object)后的自我纠正;Assistant reasoning是思考全文,包括它对自己质检记录的说明:”本次由我完成独立质检;封面因未提供官方标识素材使用了文字标志。另:调研中 4 项’未查到的’内容(远期天气预报、酒店国庆实时房价、钟鼓索道一票价、2026 年国庆单日限流具体数字)在方案中均如实标注,未以确定信息呈现”;Assistant是给客户的正式回复,包含质检结论、报价编号 HL-Q-1004-3P(第 2 版)、产品、日期、人数、单价与总价 2,940 元,以及付款节点与占位规则;Iteration 10/40、Iteration 11/40是迭代分隔;- 末尾两条
⚠ LLM error [network]是 provider 的重试告警,随后自动恢复。
这一层踩的坑比试跑本身还密。
- JS 里的绝对路径。页面写
fetch('/status.json'),从/trial/打开会被 nginx 的location /转去网关,自然拿不到 JSON。改成相对路径status.json才对。 - server 级
sub_filter会污染页面。nginx 在 server 段把移动优化层注入所有 HTML 响应,其中那段 JS 的 S8 自愈逻辑会清掉 Raven 界面的桌面状态键。在/trial/里用一条不匹配的 pattern 覆盖掉它。 - SSE 必须关缓冲。
proxy_buffering off,否则事件被 nginx 攒着不推;读超时也要从 60s 提到 3600s。 - 计数器刷屏挤掉真实事件。把每秒变化的”已运行时长”放进了变更判定,结果每 0.4 秒吐一个事件,把 600 条的缓冲全刷成计数器——首屏变空。改成只对有意义字段的变更发事件。
- cron 的静默去重。新任务的下次触发时刻与同一 channel/chat 下某个已存在任务相差 15 分钟以内(:00 前后都算),
add_job直接返回旧任务,工具回显却写着 “Created job”。更麻烦的是 30 分钟节奏与每小时 :00 的任务根本不相容。最后改用系统 crontab 每小时 :37。 tui不是通道适配器。channels/adapters/下没有 tui;而message工具的channel/chat_id参数基本是装饰性的,真正投递走当前会话自己的send_callback。cron 那一轮的投递目标tui:default没有适配器可落,消息凭空消失,回执却报 “Message sent”。改由 crontab 驱动raven agent --session <用户会话键>,让 headless 回合直接写进用户会话。
六、代价与边界
小时级。 官方展示案例 4.4 小时;我们七轮试跑累计约 5 小时 API 时间。单次 curator 调用分钟级是常态,--timeout 给到 3600s 才够用。
没有 resume。 simulation/__main__.py 里 grep 不到任何 resume/continue/checkpoint 参数。暂停点虽有 checkpoint 文件,但除了换个新 state-dir 重跑,没有办法从暂停点继续。我们建的 Raven 任务级持久化(raven-task + raven agent --session/--resume + crontab tick)救不了它——那套续的是”agent 回合”,而 curation 是一个多小时的 OS 进程,带着自己的状态机。模型不匹配,不是能力不足。
自进化不处理基础设施错误。 网络超时由 raven.providers.base 的自带重试吸收(最多 4 次、退避递增);四次都失败则调用抛异常、curator 阶段中断、iteration 转 paused,没有任何机制自动恢复。Curator 改写 harness 的依据是评价者对员工工作质量的反馈,基础设施错误不是它的输入。
员工上场才是看点。 前六轮全部死在 curation 阶段,员工从未被装配出来。直到 run7,日志里才出现真正的工具调用、思考与输出——它交付 PPT、逐页解析 pptx 自检报价编号与产品信息、按 SOP 规则回复客户、对自己的质检范围如实说明。这套机制最终交付的不是一段提示词,是一个会干活、能被评价、能按反馈修订的数字员工。
七、结语
Curator 能不能动?能。但它不是”装上去就自动变强”的魔法,而是一个小时级、需要观测、没有断点续跑、对基础设施错误零自愈的工程系统。它的价值也不在提示词工程,而在那个很少被认真对待的设计决定:对 Raven 源码零侵入,产物只落在既有的扩展点上,由真实的 AgentLoop 执行。
而我们为了看见它运行所做的那条 SSE 事件流,某种程度上比试跑本身更接近日常——把一个脱离界面的长跑进程,变成一条能盯着看的推送,这大概是每个搭过 agent 系统的人都会遇到的题。