跳至正文
浏览章节

架构

一个极小的内核、其余一切都是插件,以及一个由构建来断言的依赖方向。

一句话

一个极小的内核——会话 actor、有序的事件日志、回合状态机、权限闸门、插件宿主——其余 一切都是躲在稳定 trait 后面的插件 crate,而每一个界面都是一个提交入口和一个订阅的 客户端。

分层

bingo (bin)          composes Vec<Box<dyn Plugin>>, picks a Surface
  ↑
plugins / surfaces   depend on bingo-sdk only
  ↑
bingo-core           depends on bingo-sdk only
  ↑
bingo-sdk            serde, schemars, thiserror, async-trait, tokio(sync),
                     futures, ulid, jiff — nothing heavier

依赖方向严格向下。有五条边被彻底禁止,而 scripts/check_discipline.sh 在 cargo metadata 上断言它们(ADR-0001):

  1. 一个插件或界面 crate 依赖 bingo-core;
  2. bingo-core 依赖一个插件或界面 crate;
  3. bingo-surface-tui 以外的任何东西依赖 ratatui 或 crossterm;
  4. bingo-core 或 bingo-sdk 在它们的常规依赖树的任何地方解析出 reqwest、 rmcp、ratatui、crossterm、image 或 syntect;
  5. 一个插件依赖另一个插件。 跨插件的需求走一个经 sdk 注册的服务 trait。

后果才是重点。内核是在进程内、对着 sdk 自己的假实现测试的,没有子进程。动一个工具 crate 绝不会重新链接终端界面。sdk——外部插件作者下载的东西——拉不进 ratatui 或 reqwest。而终端界面够不到引擎,因为根本没有一条边让它沿着够过去。

这些 crate

内核与契约

crate它装着什么
bingo-sdkid、Message/ContentPart、Frame/Event/Item、SessionState 与 apply、每一个 trait、HostApi、服务注册表、测试用的假实现
bingo-core会话 actor、日志与广播、回合状态机、权限闸门、工具执行器、插件宿主、上下文标尺、ContextView::fold

插件

种类crate
提供方bingo-provider-anthropic、bingo-provider-openai(openai 与 codex)、bingo-provider-fake
工具bingo-tool-fs、bingo-tool-bash、bingo-tool-web、bingo-mcp、bingo-agents
策略bingo-permissions、bingo-hooks-shell
会话bingo-store-jsonl、bingo-context
功能bingo-skills、bingo-rooms、bingo-tasks、bingo-experience、bingo-schedule
扩展bingo-plugin-rpc —— 跨进程桥
界面bingo-surface-print、bingo-surface-rpc、bingo-surface-tui、bingo-channels
demobingo-demo-ui —— 除非 --demo-ui 否则关着,也是插件作者最先该读的那个完整例子

库

一个声明了 [package.metadata.bingo] tier = "library" 的 crate 什么都不注册,只依赖 bingo-sdk 和外部 crate,而任何插件都可以依赖它。bingo-auth-oauth 是第一个、目前 也是唯一一个:PKCE loopback、设备码、auth.json、单飞刷新(ADR-0012)。

二进制

bingo 显式地组装插件列表——在插件必须不由 bin 点名就能加载之前,不会有一个自注册 crate——并根据这次运行是什么来挑界面:管道两端都是人时是终端界面,否则是 print 界面, serve 时是 RPC 界面,channels 和 gateway run 时是频道界面。

一个回合

客户端调用 SessionHandle::submit(intent, input)——同步,且什么也不返回。会话 actor 追加一个 user Item,生成一个 seq,把这个回合交给状态机。循环向贡献者要上下文, 把提供方流式取回,把提供方的事件折进一个个 Item,让每一次工具调用过钩子和策略 (需要人回答时是一个 Interaction),执行这些工具,在屏障处吸收排队的转向输入,并 以恰好一个 TurnCompleted 收尾。每一帧都先进日志再广播到每个订阅者的有界通道。每个 客户端都用 SessionState::apply 折叠这些帧。

会话是唯一的对话名词

子代理是带一条 parent 链接的会话。房间是没有模型的会话。两者都经由同一个 reducer 和同一套绘制代码渲染,而内核两个名词都不拥有:room、team、hire、task、 experience 和 schedule 在 bingo-sdk 或 bingo-core 里一处也不出现。它们是插件 的词,装在内核并不去枚举的 Event::Extension 载荷上。

接下来读什么

在仓库自身里:ARCHITECTURE.md 是地图,docs/adr/ 是那些决定,docs/plans/ 是眼下 在建的东西,docs/design/ 是那些提案和库选型背后的调研。