博客
Eva-CLI 的模块划分边界
Eva-CLI 如何把架构决策落实为 Rust crate 边界、依赖方向、运行时交接和受控副作用归属。
Eva-CLI 的模块划分不是给 crate 起名字。它真正要固定的是架构边界:权力归属在哪里,副作用在哪里发生,热更新 Agent 行为如何演进,同时又不把运行时变成一个没有结构的脚本宿主。
简短结论是:Rust 托管可信运行时边界,Lua 承载可替换业务行为,EventBus 与 Scheduler 负责协作,Adapter 和服务 crate 负责副作用,eva-runtime 是唯一组合具体实现的位置。
先划职责,再排目录
推荐的 workspace 使用 crate 承载稳定职责域,crate 内部再用 module 组织实现细节。这个区别很重要:目录结构可以较低成本调整,职责边界一旦被错误依赖穿透,后续修复成本会高很多。
第一条规则是底层 crate 不反向依赖应用组合根。eva-core、eva-eventbus、eva-scheduler、eva-agent、eva-lua-host、eva-adapter、eva-storage 和服务 crate 都应保持可独立测试。eva-runtime 负责装配它们,但不能把自身泄漏回这些模块。
核心依赖方向
依赖方向应把契约留在中心,把具体 transport 留在边缘。核心类型、Topic 规则、结构化错误、权限形状和观测字段可以向内共享。provider 协议、shell 执行、MCP transport、HTTP、硬件 I/O 和 release mutation 必须藏在 adapter 或服务边界之后。
| 边界 | 负责 | 不负责 |
|---|---|---|
eva-core |
Event、Topic、ID、schema 形态契约、通用错误形态。 | 运行时编排、文件系统访问、provider 协议、策略决策。 |
eva-eventbus 与 eva-scheduler |
发布订阅、投递、路由规则、队列、死信。 | 业务决策、Lua 执行、外部 capability 调用。 |
eva-agent 与 eva-lua-host |
Agent 生命周期、私有 inbox、沙箱 Lua state、受控 host API。 | 原始 shell、原始网络、密钥、设备句柄、release pointer mutation。 |
eva-adapter 与服务 crate |
capability、provider 路由、副作用边界、storage、memory、discovery、lifecycle。 | 未经校验的 Lua 权力,或跨模块共享的隐式全局状态。 |
运行时交接必须显式
好的模块边界会在运行时交接上显形。用户输入通过 ingress surface 进入并变成 typed Event。EventBus 持久化并发布事件。Scheduler 决定投递。AgentRuntime 隔离每个 Agent inbox。Lua 通过窄 host context 处理业务逻辑。AdapterRouter 和服务 crate 只有在 schema 与 policy 校验通过后才执行副作用。
横切规则
有些规则不属于单个 crate,但每个 crate 都必须保留。跨模块错误需要稳定字段,例如 kind、message、retryable、provider_code 和 correlation 标识。权限只能沿着 system policy、manifest permissions、user/session policy、request constraints、effective permissions 逐层收紧。状态归属也必须显式:Agent 局部状态归 Agent 与 Lua generation,业务持久状态归 storage 与 memory,事件恢复状态归 EventBus,Adapter 运行状态归 Adapter,generation 状态归 lifecycle。
基础契约
-> 事件与路由内核
-> 隔离的 Agent 与 Lua 执行边界
-> 受控 capability 与 Adapter 边界
-> Discovery 与 Runtime services
-> CLI、Supervisor 和运维工作流
为什么这会影响实现
Eva-CLI 后续会接入 Skills、MCP、外部 Agent、记忆、硬件、备份、Release Snapshot 和进程 generation 切换。每个功能都天然想直接拿到某种强权力。模块划分的价值就是把这些权力保持在可审查路径上:Discovery 可以发现候选,Policy 可以授权,Adapter 可以执行,Observability 可以记录,Runtime 可以组合,而 Lua 或 Scheduler 不会变成隐藏系统 API。
完整技术方案见 模块划分方案。