Electric Agent 运行时 DSL 覆盖率全景:从测试矩阵到编排模式验证指南
【免费下载链接】electricThe agent platform built on sync.项目地址: https://gitcode.com/GitHub_Trending/el/electric
导读:本文围绕 packages/agents-runtime/test/runtime-dsl-ideas.md 展开,这是一份服务于
packages/agents-runtime运行时黑盒测试套件的"规范规划与覆盖率追踪"文档。它通过 A–N 十二组场景编号,把 Electric Agent 运行时(基于 durable streams 的行为栈)的实体生命周期、派生机制、共享状态、观察回放、协调编排、Map-Reduce、流水线、同行评审、辩论、Wiki 自治与响应式观察流全部映射为可断言的流历史测试。读完本文,你将理解这套 DSL 测试的编写纪律、83 个场景覆盖了什么、哪些是待办缺口、哪些模式被刻意否决,并能据此为自己的 Agent 编排功能设计同等级的回归测试。
一、这份文档是什么:DSL 覆盖矩阵而非普通笔记
runtime-dsl-ideas.md的定位在文件头写得非常清楚:它是packages/agents-runtime/test/runtime-dsl.test.ts的权威规划/覆盖率追踪文件(canonical planning/coverage tracker)。它的核心数据是:
runtime-dsl.test.ts当前包含83 个场景测试;- 完整运行时套件全绿,共256 个测试;
- 文档追踪三件事:已覆盖什么、还缺什么、哪些想法因编码了错误的编排模式而刻意不"加持"(not blessing)。
也就是说,这不是一篇教程,而是一张"测试军规 + 覆盖地图 + 待办看板"三合一的技术文档。作为文章骨架,它最适合用来回答三个问题:Electric Agent 运行时的行为契约是什么?用什么纪律去验证?哪些行为边界还没有被验证?
二、DSL 编写纪律:七条铁律
文档在 "DSL Rules" 一节给出了黑盒场景测试的七条编写纪律,这是理解整个矩阵的方法论前提:
- 尽量断言完整流历史,而不是抽查(Assert full stream history wherever possible, not spot checks);
- 优先使用过滤快照(filtered snapshots),而不是对调度敏感(scheduler-sensitive)的原始历史快照;
- 从流开头匹配,禁止固定 sleep(Match from the beginning of the stream; no fixed sleeps)——测试必须靠语义事件驱动,而不是靠等时间;
- 断言 spawn 参数、manifest 行与初始 inbox 消息的 eager durability(主动持久化);
- 测试回放/再水合(replay/rehydration)行为时强制 wake 边界(Force wake boundaries);
- 使用确定性的假助手/假工具,而不是 playground 特有的工具蔓延;
- 不要因为某些错误编排模式"容易测"就把它们洗白进 DSL。
这七条纪律在 runtime-dsl.test.ts 中有直接体现。例如测试基础设施runtimeTest()在 runtime-dsl.ts 中统一管理ElectricAgentsServer+DurableStreamTestServer+ 本地 webhook 处理器,并提供waitForRun、waitForRunCount、waitForSettled等语义等待原语;waitForSettled依赖runtime.drainWakes()让运行时达到静默(quiescence),而不是轮询固定次数。这正是"从流开头匹配、禁止固定 sleep"纪律的实现基础。
三、已覆盖场景全解:A–N 十二组矩阵
A. 独立助手(Standalone Assistant,A1–A14)
这组场景回答"单个实体如何从 spawn 到产生完整 run 历史"。核心断言包括:
A1:spawn 立即写入entity_created,并携带 spawn 参数(见 A1 测试,快照确认args与entity_type进入历史);A2:spawn 携带initialMessage时,inbox 历史先于任何 run 写入(快照中entity_created在inbox之前,且无 run 记录);A3:单条消息产生完整的 run 历史——快照展示run insert → step insert → text insert → text_delta insert → text update(completed) → step update(completed) → run update(completed)的全链路(见 快照文件);A5:多条消息产生两条 completed run;A5b/A5c分别验证entity.waitForSettled与全局t.waitForSettled两种静默等待路径;A6:无 agent 的实体只记录入站消息(只写 inbox);A7:setup 状态写入出现在 run 历史之前;A9–A11:同步/异步工具调用在单个 run 内按序出现、多次工具调用顺序稳定且取最后一次结果;A12:有状态 note 写入跨 wake 持久、后续可读;A13/A14:失败工具干净地关闭 run 并保留持久失败历史,且实体能在后续 run 中恢复。
其中A13对应的fail_tool命令与createFakeToolAssistant(runtime-dsl.test.ts)体现了"确定性假工具"纪律:工具行为全部由测试内联命令解析器驱动,不依赖任何外部服务。
B. 派生机制(Spawn Mechanics,B1–B4)
验证父实体 spawn 子实体的流历史形态:
B1:spawn 创建可接收消息的子实体;B2:带initialMessage的 spawn 写入子实体历史;B3:spawn 的 manifest 历史包含解析后的entityUrl;B4:spawn 自动创建 observe manifest 条目(对应测试标题 "spawn marks the child manifest row as observed")。
C. 状态集合(State Collections,C1–C3)
C1:ctx.db上的集合写入反映到完整流历史;C2:setup 初始化的状态在最终历史中仍然可见;C3:实体自己写自己的状态不会触发第二次 run——这是防止"状态回环"(self-triggered loop)的关键行为契约。
D. 共享状态(Shared State,D1–D12)
共享状态是跨实体协作的地基。createSharedState/ctx.mkdb+ctx.observe(db(...))的组合(见d1–d4实体定义,runtime-dsl.test.ts)驱动了 12 个场景:
D1:createSharedState在实体历史中产生 manifest 条目;D2:共享状态流在任何写入之前就已存在;D3:对共享状态的写入同时反映在写入方与被观察方两个历史中;D4:第二个实体可连接既有共享状态并读到先前行;D5:共享状态的 update/delete 事件跨 wake 持久;D6:多集合共享状态在 writer/reader 实体间保持一致;D7:多个实体可向同一共享集合贡献持久行;D8:后写入者可覆盖共享行,新 reader 读到最新值;D9:setup 注册的共享状态 effect 在首次 wake 写入时触发并在后续 wake 存活;D10:不同实体可在同一共享状态中向不同集合写入;D11:同一共享 key 的相邻写入者保留完整历史且后写覆盖(last-write-wins);D12:变更一个共享集合不干扰另一集合的读取。
E. 观察回放(Observation Replay,E1–E3)
E1:observed effect 在父实体重新 wake 时不重复旧子行;E2:更新被观察行保持单一派生行 key;E3:被观察行更新被回放为 update 而非第二次 insert(E0 还额外验证了"不带 wake 的 observe 在后续子写入时不会重新 wake")。
F. 协调编排(Coordination Orchestration,F1–F12)
这是文档篇幅最大的编排组,覆盖 dispatcher 与 manager-worker 两种经典模式:
F1:dispatcher 路由到指定专家类型并记录子实体;F2:manager-worker spawn、observe 并以稳定顺序收集全部 perspectives(测试中optimist/pessimist/pragmatist三种视角,runtime-dsl.test.ts);F3:dispatcher 递增 dispatch 计数并在多次 wake 后保留两个子行;F4:dispatcher 记录 dispatch 过程中的期望状态迁移(idle → classifying → dispatching → waiting,见createDispatcherAssistant);F5/F7:子实体无文本输出时使用文档化占位符(placeholder);F6:wait_for_all在未 spawn 前调用返回文档化错误路径;F8:重复spawn_perspectives复用同一批子实体,只返回最新输出;F9/F10:manager-worker 对定向子失败只对该 perspective 用占位符,且可在失败后重试并最终收集完整结果;F11/F12:dispatcher 在专家失败(甚至反复失败)时保留计数器与子行。
从实现看,子完成通过wake: { on:runFinished, includeResponse: true }注册唤醒,父实体在非 inbox wake 中读取finished_child载荷并更新childStatus集合(见f2Manager的applyWakeStatus,runtime-dsl.test.ts)。
G. Map-Reduce(G1–G4)
G1:即使各 chunk 完成时间不同,结果仍按 chunk 顺序返回(测试用delayMs打乱完成时序,见createMapReduceAssistant);G2:单 chunk 的 map-reduce 仍走编排路径;G3:后续 run 复用 chunk 子实体且不泄漏上一次的 chunk 输出;G4:仅对失败 chunk 使用占位符,其余保留。
H. 流水线(Pipeline,H1–H6)
H1:流水线在首次 wake、阶段执行之前就写入状态行;H2:每个阶段消费上一阶段的输出并持久化最终状态;H3:状态上限封顶于stage_5,更长流水线仍能完成——这与实现中Math.min(stageNumber, 5)的写法一致(runtime-dsl.test.ts);H4:逐阶段持久化currentInput更新;H5:后续 run 复用阶段子实体但重置到最新输入链;H6:失败阶段以占位符输入继续传给后续阶段。
I. 同行评审(Peer Review,I1–I4)
I1:评审者通过共享状态聚合 reviewer 写入(测试含 3 名评审者:clarity/correctness/completeness,见 runtime-dsl.test.ts);I2:summarize_reviews在无评审时返回空状态错误路径;I3/I4:分别配置 1 名与 2 名评审者时,只汇总对应的持久行——评审者数量必须精确对应聚合结果。
J. 辩论(Debate,J1–J3)
J1:辩论父实体在裁决前从共享状态读取正反双方论点;J2:end_debate在无论点时返回空状态路径;J3:只有一方论点时辩论保持 partial,直到缺失一方到达才 resolve。
K. Wiki 自治(Wiki,K1–K10)
K1:Wiki 专家累积共享文章,后续查询可读;K2:重复create_wiki复用既有专家,只 spawn 缺失的子主题;K3:get_wiki_status在专家文章落地后报告完整覆盖;K4:create_wiki拒绝切换既有 wiki 的主题;K5/K7:未创建任何文章/未创建 wiki 时返回空状态消息;K6:同主题同子主题的重复create_wiki幂等;K8:wiki 镜像持久化的子实体/文章通知元数据(含 topic/author);K9:幂等重建不重复共享文章行;K10:同主题扩展只新增缺失文章并更新后续查询覆盖。
L. 响应式观察流(Reactive Observation Flows,L1–L5)
L1:显式observe + createEffect转发 insert/update/delete 三类通知;L2:子实体无新变更时重新 wake watcher 不重复旧通知;L3:watcher 休眠期间发生的子删除在回放时只产生一条 delete 通知;L4:同一子实体被观察两次保持去重(deduped);L5:一个 watcher 可观察多个子实体并保留来源归属(source attribution)。
从实现看,l1Watcher实体通过_mirror集合记录每个子 URL 的镜像行,在每次 wake 时做"增/改/删"三向比对并生成insert:/update:/delete:通知行(runtime-dsl.test.ts),是响应式观察流的参考实现。
M/N 附加组:Deep Researcher 与 Wake 语义
文档的 Active Backlog 提到 Deep Researcher(M 组)已覆盖 spawn-timeinitialMessage、wait_for_results前调用错误路径、多子研究员跨 wake 隔离。测试文件中对应的M1–M3(runtime-dsl.test.ts)验证了"研究员从 spawn 的 initialMessage 启动,无需额外 send"。此外N1–N5组专门验证 wake 语义:runFinished唤醒时父实体收到wake类型事件、spawn wake 与子 manifest 共享同一条runFinished注册、ctx.agent.run可携带 wake 载荷执行第二次 run。
四、待办缺口:Active Backlog 逐项解读
文档明确区分"已覆盖"与"待办"。待办中有一部分已打勾,有一部分仍是缺口,其中值得关注的有:
共享状态(第 1 节)
- 已覆盖:相邻 wake 下两个实体对同一 key 的写竞争、reader 观察一个集合时 writer 变更另一集合;
- 缺口:setup 时间与动态 update/delete 组合作用于可变行时的 effect 覆盖。
协调失败与恢复(第 2 节)——全部为缺口
- dispatcher 子失败路径上的
runFinished延续聚合; - 跨多次 wake 的反复失败需保留子行与计数器(注:
F12已在测试文件中实现为 "dispatcher preserves counters and child rows across repeated failing dispatches"); - 子失败后在同一父实体上替换子实体。
Map-Reduce 与流水线边界(第 3 节)——已全部覆盖
- 单 chunk 失败占位符、跨 wake 的重复 chunk id 复用、阶段失败语义、
done后的重跑重置语义。
同行评审与辩论(第 4 节)
- 缺口:评审已持久后跨 wake 边界的汇总;
- 已覆盖:评审者数量变体(1/2/3)、辩论双方向缺失前的 partial 状态。
Deep Researcher(第 5 节)——已全部覆盖。
Wiki 自治(第 6 节)——全部为缺口
- 无需第二次用户干预的 build-and-answer 流程;
- 文章足够后自动 resolve 的 pending query 状态;
- 只有部分专家写完时的 partial wiki 答案;
- 同一实体上的显式子主题扩展与后续跟进查询;
- 面向观察者的更清晰的父侧高层进度面。
观察 / Effects(第 7 节)——全部为缺口,且文档给出了关键判断
- 无效 observe 目标的错误路径;
- observe-once 与 first-write-wins 配置不匹配的固定化;
- 多种编排模式下的
live .send()观察句柄; - 可变行 effect 回放/update/delete(需上游 effect gating 落地);
- 多源 joined effects(依赖上游
createEffect语义稳定)。
文档在 第 7 节末尾 给出一条重要的前置依赖说明:ElectricAgents 现在已经有了按源命名空间隔离的 StreamDB collection id 与逐消息 offset,但上游createEffect的稳定回放语义仍不够可靠,因此**暂时不"加持"(bless)**可变行与多源 joined 这两类用例。
Trading Floor(第 8 节)——全部为缺口
- 开放市场播种与时钟初始化;
- 新闻注入扇出(fanout);
- 跨会话的时钟推进;
- 由持久共享状态派生的市场摘要。
横切压力(第 9 节)——全部为缺口
- 混合 spawn + observe + shared state + replay + 多子实体的组合场景;
- 带反复再水合与过滤快照的长多 wake 场景。
五、Import Idea Triage:想法分级与"刻意否决"清单
2026-03-21 的一次头脑风暴被映射进 DSL 计划,文档给出三种归宿:
1. 已由 DSL 覆盖(如 1–8 由 A1–A14/C1–C3 覆盖,9–19 由 B1–B4/A1–A2/F3/F8 覆盖,80–85 由 F/G/H/I/J/K 各组合覆盖)。
2. 更适合放在 DSL 之外的低层测试(setup-context、wake-handler、process-wake的单元/集成测试),包括:spawn manifest 的entityUrl、createSharedState/connectSharedState的 manifest 条目、createEffect的 manifest/functionRef/dedupe 规则、agent factory 与 re-wake 回放内部机制、spawn/setup 失败的 crash-only 行为。这类条目不是行为洞,而是测试层级选择。
3. 明确不在当前产品方向内:"子完成通过runFinished唤醒父实体,编排应表示为持久化的延续唤醒(durable continuation wakes)"。
真实遗留 DSL 待办包括:状态删除流覆盖(15)、受保护状态迁移示例(28)、agent factory 中创建共享状态与幂等创建(39–40)、协调实体存活直到所有子完成(86)、trading-floor 场景(94–95)、无效 guard 迁移错误路径(97)。
**Explicitly Rejected Patterns(明确否决的模式)**是本文档的独特价值——它警告了三种"看似能测但方向错误"的测试:
- 依赖同步子输出读取的同实体重复 map-reduce 再聚合;
- 假设立即子复用与聚合的同实体重复流水线重跑;
- 依赖偶然调度顺序而非持久语义历史的测试。
这与 DSL Rules 第 7 条"不要将坏编排模式洗白进 DSL"形成闭环:测试矩阵不仅测"对的",也明确拒绝"错的"。
六、落地运行与仓库证据索引
如果你想亲自验证这套矩阵,仓库提供了完整的运行路径:
- 包名:
@electric-ax/agents-runtime,版本0.6.3,见 packages/agents-runtime/package.json; - 运行命令:在
packages/agents-runtime下执行pnpm test(vitest run)或pnpm test:watch;完整运行时套件 256 个测试全绿(文档记录),其中runtime-dsl.test.ts贡献 83 个场景; - 测试基础设施:
runtimeTest()在 packages/agents-runtime/test/runtime-dsl.ts 中自动拉起ElectricAgentsServer、DurableStreamTestServer与本地 webhook 处理器,并注入system:runtime-dsl-test主体验证 spawn 权限; - 快照断言:流历史快照集中在 packages/agents-runtime/test/snapshots/runtime-dsl.test.ts.snap,
A1–A4的entity_created → inbox → run → step → text → text_delta事件链是理解"完整流历史断言"的最佳样本; - 相关运行时代码:
ctx.spawn/ctx.observe/ctx.mkdb等上下文能力对应 packages/agents-runtime/src/setup-context.ts,wake 分发逻辑见 packages/agents-runtime/src/process-wake.ts,实体定义与注册见 packages/agents-runtime/src/define-entity.ts 与 packages/agents-runtime/src/index.ts。
七、结论:一张"活的"编排行为契约
runtime-dsl-ideas.md的价值不在于罗列测试数量,而在于它把"行为契约"工程化了:用可断言的流历史替代时序猜测,用语义等待替代固定 sleep,用确定性假助手替代外部依赖,用"明确否决"清单守住编排模式的正确方向。对于在 Electric Agent 运行时之上构建协调类功能的开发者,这张矩阵既是回归测试的规格说明书,也是判断"某个行为该不该进入 DSL"的决策框架——当你想给一个新编排模式补测试时,先问自己:它能断言完整流历史吗?它依赖偶然调度吗?它是不是把坏模式洗白了?答案会引导你走向 A–N 中正确的那个分组,或者走向setup-context级别的低层测试,又或者直接进入"Not Current Product Direction"清单。
【免费下载链接】electricThe agent platform built on sync.项目地址: https://gitcode.com/GitHub_Trending/el/electric
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考