三条路怎么选
目标就三件:make it work,支持 trace,在客户那边跑。三条路用的是同一个世界(twin、人、时钟、checks),差别只在两件事:谁管流程,是 Ancha 还是他们自己的测试;谁写接线,是客户的 Claude Code 还是 Ancha 自己。
建议:走第 1 条,顺手借第 3 条的一个点。Ancha 管 trial 和世界;他们的应用一直开着,每个 trial 之前重置一次;流程靠场景里的人和事件触发,需要的时候也可以拿他们自己的测试命令当驱动。
理由:从 trace 拉下来的场景,接一次线就能全部跑起来;它跑在客户的仓库和 CI 里,胶水由客户的 Claude Code 写;要新写的代码最少,因为原来的 endpoint 模式已经有了一半。第 2 条先不做,等接过两三个仓库、看清哪些胶水在重复,再搬进 Ancha。
先澄清一件事:Inbox Zero 那次走的是第 3 条
SPEC-001/002 跑通的那次,世界是我写的脚本在每个 trial 开一次,流程和判分都交给他们的 vitest:看草稿有没有写进那个线程,followUpAppliedAt 有没有值。ancha run 一次都没参与。所以"Ancha 管流程"在真实应用上还没试过,下面第 1 条的工作量就是为这个估的。
三条路放在一起
| 1 客户迁就我们 | 2 我们迁就客户 | 3 放进他们的测试 | |
|---|---|---|---|
| 谁管流程 | Ancha | Ancha | 他们的测试 |
| 谁写接线 | 客户的 Claude Code | Ancha,客户只写一份清单 | 客户,每个流程写一个测试 |
| 我们新写 | 约 450 行,1–2 session | 约 2000 行,5–8 session | 约 600 行,约 2 session |
| 客户仓库多出 | ancha.yaml 加几个脚本,约 200 行 | 约 30 行清单,加种子数据 | 每个流程一个测试,约 150 行 |
| trace 场景 | 直接跑 | 直接跑 | 每个都要测试来驱动 |
| 应用的时钟 | 应用由 Ancha 启动,有办法 | 同左 | 只有测试进程里的调用跟得上 |
| 离能用多远 | 约 3 session 能在 Inbox Zero 上跑 ancha run | 最远 | 最近,SPEC-001 已经是这个形状 |
| 像谁 | 原来的 Ancha | — | Arga、Archal、Hue |
三条都还要再加下面的"共同底座":约 1200 行,4–5 session。行数都是估算,不含测试。
不管走哪条,都要先补的世界这边
这些是 Inbox Zero 实验暴露出来的缺口,跟谁管流程无关。
- Gmail 推送搬进 twin。SPEC-001 里是 121 行的 bridge 补上的:
watch、推送、批量请求、设置。现在世界推出去的事件里没有 Gmail。约 300 行 · 1 session - 人能通过 Gmail 行动。simulated people 现在只会走 Slack 和 GitHub,Tom 没法回邮件;按消息顺序判分的 check 也只看这两种。约 350 行 · 1–1.5 session
- 前门。应用一直开着,每个 trial 换一个新世界,但应用看到的地址和 token 不变。SPEC-001 是靠每次重启 bridge 做到的。约 200 行 · 0.5–1 session
- 应用的时钟跟上世界。世界的时钟已经能快进(
advance),但应用代码里的new Date()不会跟着跳。先在 Node 上做。约 100 行 · 1 session · 没试过 - trace 支持 Gmail,并且在本地转换。现在的 trace 读取器只认 Slack、GitHub、Datadog、PagerDuty;
ancha import会把文件传到我们的服务器上转换。要在客户那边跑,就得改成在 CLI 本地转换,默认什么都不上传。约 250 行 · 1 session
trace 能重建对面的世界(邮件、人、本该发生什么),重建不了应用自己的数据库(用户的规则、设置),那部分还是要用他们的种子数据。τ² 的 E3 也说明,从失败 trace 推出来的人是最弱的一环。三条路都绕不开这两点。
1 · 客户迁就我们建议
原来的方案基本不动,客户的 Claude Code 写接线,把应用接进 ancha.yaml。下面是 Inbox Zero 用这条路时,仓库里会多出来的配置:
agent:
# 开数据库和 Redis、迁移、种数据、启动应用
start: ./ancha/start.sh
ready_url: http://localhost:3000/api/health
# 世界往这里推 Gmail 事件
url: http://localhost:3000/api/google/webhook
# 新:每个 trial 之前清掉他们的库和 Redis
reset: ./ancha/reset.sh
# 新(可选):拿他们自己的测试当驱动
command: pnpm test-e2e:flows ancha-follow-up
env:
GOOGLE_BASE_URL: "{{gmail.url}}"
# 手写的,或者 ancha pull 从 trace 拉下来的
scenes: [ancha/scenes/**/scene.yaml]
trials: 10
橙色是要新加的,其余字段今天就有。
我们要改的
- 现在
agent.start每个 trial 都把应用重启一次,启动时限默认 30 秒,适合小 bot。真实应用要开数据库、跑迁移,每次重启太慢。改成整个 run 只启动一次,每个 trial 前跑reset。 - 健康检查现在要求应用回一个
x-ancha-env-id头,真实应用不会回。应用常驻的时候,这个要求去掉。 - 允许
start和command一起用,现在这两个是互斥的。 - 场景里加一个能直接调应用接口的动作,用来触发应用自己的入口,比如定时任务、聊天框。
主要改 backend/src/runner/endpoint.ts、run.ts、config.ts 和场景的 schema。约 450 行,1–2 session。
做完之后
- 客户在自己仓库里跑
ancha run:10 个 trial、通过率,每次的世界状态和调用记录都有,和原来的 Ancha 一样。 - trace:
ancha pull拉下来的场景放进ancha/scenes/,不用写代码就能跑。接线是按应用接一次,不是按场景。 - 人和时钟:场景里的 Tom 会回信;应用是 Ancha 启动的,Ancha 有机会让它的时间跟上世界。
风险
- 每接一个仓库要写多少胶水还没量过。Chatwoot 的冷启动实验可以量。
ancha/这个文件夹要客户自己维护,而且跟他们现有的测试是两套,得单独开一个 CI job。好在 Inbox Zero 本来就有一个单独跑的ai-evals.yml。
2 · 我们迁就客户
第 1 条的基础上,把客户要写的胶水搬进 Ancha:用他们自己的 compose 或脚本开应用,自己给数据库拍快照、每个 trial 还原,自己认出 base-URL 开关,没有开关就拦截网络。
# 整块都是新的
app:
up: docker compose up -d db redis && pnpm dev
ready: http://localhost:3000/api/health
# Ancha 拍快照,每个 trial 还原
state: [postgres, redis]
# 认出 GOOGLE_BASE_URL 这类开关,没有就拦截
routes: auto
# 可选:用他们自己的测试判分
test: pnpm test-e2e:flows {{scene}}
我们要改的
- 第 1 条的全部改动,再加上:开应用、拍快照和还原、开关目录、拿他们的测试当 check。
- 网络拦截:改 DNS,加一个 TLS 中间人代理,还要把我们的证书装进各种语言的运行时(Node、Ruby、Python 各不一样)。光这一块就要 3–5 session,而且没把握。
约 2000 行,5–8 session。
做完之后
- 客户只写一份清单,连没有 base-URL 开关的应用也能接,比如 Chatwoot 的 Slack 客户端。
- trace 场景和第 1 条一样,直接跑。
风险
- 要在客户的 CI 里装我们的证书做中间人,不少团队不会接受。
- 接过两三个仓库之前,分不清哪些是通用的、哪些是个案,很容易做歪。Chatwoot 的 Slack 客户端很可能写几行只在开发环境生效的初始化就能指到 twin,用不着拦截(没试过)。
3 · 放进他们的测试里
他们的 vitest 管流程,Ancha 是测试里调用的一个库,提供世界、人、时钟和多次运行。示意:
// 新包
import { world } from '@ancha/test'
test('Tom 第二天回了信,不该再提醒', async () => {
// 每个测试一个新世界
const w = await world.up('ancha/scenes/follow-up.yaml')
// 他们自己的 helper
await sendTestReply({ to: TOM })
// 场景里的 Tom 在第 2 天回信
await w.advance('3d')
// 只有测试进程里的时间跟得上
vi.setSystemTime(w.now())
await processAccountFollowUps({ emailAccount, logger })
expect(await w.check('no_follow_up_draft')).toBe(true)
})
我们要改的
- 一个 TypeScript 库,包住现有的
world up/state/check/down,再加advance和act。以后还要一个 Python 版。 ancha test --trials 10 -- <他们的测试命令>:每次换一个种子,汇总通过率。
约 600 行,约 2 session。
做完之后
- 跟他们现有的测试放在一起,CI 不用改,最自然。SPEC-001 已经是这个形状,离能用最近。
- trace 拉下来的场景不能直接跑,每个流程都要有一个测试来驱动。
ancha run的 trial、判分、报告大多用不上。形状和 Arga、Archal、Hue 一样,能拿来比的就只剩世界本身。- 时钟:只有在测试进程里直接调用的函数(比如
processAccountFollowUps)能靠vi.setSystemTime跟上世界,应用服务器里的代码跟不上。
如果走第 1 条,接下来的顺序
- make it work:把 Inbox Zero 那次改成用
ancha run跑,在 E2B 里跑 10 个 trial。要做底座第 1、3 项,加上第 1 条的生命周期改动。约 3 session。 - 人和时钟:Tom 第 2 天回信,就不该提醒;Tom 一直不回,第 3 天要提醒。要做底座第 2、4 项。约 2 session。
- trace:用有 bug 的版本造一条失败的 trace,拉成场景,
ancha run能复现出来。要做底座第 5 项。约 1–2 session。 - 第二个仓库:用新的文档在 Chatwoot 上跑一次冷启动,量一下接第二个仓库要多少胶水。1 session,最多花 $10。
合计约 7–8 session。顺序和取舍等你定。