适用版本:ChengOS v0.1.0+ | 最后核对:2026-08-12 | 来源:
crates/cheng-core/src/ports/mod.rs、crates/cheng-core/src/ports/executor.rs、crates/cheng-engine/src/executor、chengflow/CLAUDE.md
ChengOS 的后端是一套六边形架构(端口与适配器)加领域驱动设计。在这个项目里这不是口号——它由 crate 依赖图强制保证:cheng-core 不依赖数据库、不依赖 HTTP、不依赖文件系统,因此一条领域规则不可能悄悄伸手去够连接池。
分层
┌─────────────────────────────────────────────┐
│ 领域层(cheng-core) │
│ ┌─────────────────────────────────────┐ │
│ │ 业务逻辑与领域模型 │ │
│ └──────────────┬──────────────────────┘ │
│ │ 使用 │
│ ┌──────────────▼──────────────────────┐ │
│ │ 端口(trait) │ │
│ │ WorkflowRepository、ExecutionRepo、 │ │
│ │ Executor、EventPublisher…… │ │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
▲
│ 实现
┌─────────────────┴───────────────────────────┐
│ 适配器层(基础设施) │
│ DieselWorkflowRepository(cheng-storage) │
│ WorkflowEngine (cheng-engine) │
│ EventBusAdapter / WsManager(cheng-api) │
└─────────────────────────────────────────────┘
| Crate | 层次 | 拥有 |
|---|---|---|
cheng-core |
领域 | 模型、端口、领域服务、领域事件。不含基础设施。 |
cheng-storage |
基础设施 | Diesel 仓储、迁移、领域 ↔ 数据行转换 |
cheng-engine |
应用 | WorkflowEngine、DAG 分析、调度、生命周期 |
cheng-api |
表现 | Axum REST、WebSocket,以及 main.rs 里的依赖注入 |
依赖注入只发生一次,在 main.rs。没有全局状态,这正是引擎和所有服务都能用内存假实现测试的原因,也是同一个 Executor 能同时交给 REST 处理器、CLI、调度器和子流程节点的原因。
端口
端口是 cheng-core/src/ports/ 里的 trait。这份清单很长——四十多个——而通读它们的名字是了解这个系统最快的地图:WorkflowRepository、ExecutionRepository、ConversationRepository、DocumentRepository、DocumentShadowRepository、TableRepository、KnowledgeBaseRepository、ArtifactStore、ExecutionArchiveStore、TraceSnapshotStore、ScheduledTaskRepository、CredentialRepository、ChannelRepository、ChannelAdapter、AdapterRegistry、SkillRepository、McpServerRepository、ApprovalEventBus、ShadowEventBus、EventPublisher、Executor、NodeRegistry、WorkflowFileImporter、WorkspaceToolBridge 等等。
最该先弄懂的是 Executor 端口。execute(workflow, options) 启动一次运行并立即返回一个 ExecutionId,运行在后台继续。execute_by_id 会先从仓储加载——工作流保存之后前端用的就是它。
执行管线
1. REST 收到执行请求
2. WorkflowEngine.execute(),带上工作流 id
3. 从 WorkflowRepository 加载
4. 由节点和连线构建 DAG(petgraph)
5. 创建 Execution 记录
6. 后台任务:
拓扑排序 → 就绪节点
TaskScheduler spawn 异步任务
node.execute(ExecutionContext)
结果持久化
EventPublisher → WebSocket
重复直到完成或失败
7. 更新终态
8. WebSocket 广播完成
引擎干净地一分为二:静态那一半(DAG 规划器、校验、拓扑排序、阶段选择)是纯的、无状态的;动态那一半(任务调度器)在运行时工作。因为执行计划是推导出来而非存储的,修改工作流绝不会留下过期计划。执行模型对此有更深入的讲解。
关键领域类型
- 强类型 id:
WorkflowId、NodeId、ExecutionId——UUID 新类型,因此你无法把执行 id 传到需要工作流 id 的地方。 NodeValue:字符串、数字、布尔、数组、对象、文件、流的带标签联合。File和Stream这两种让大载荷能按引用传递,也让模型节点能在 token 到达时就向下游发出。ExecutionState:pending、running、paused、waiting_for_review、completed、failed、cancelled、timeout——共八个状态,后四个是终态。- 组件标识:节点类型为
category/name,例如io/input_text、ai/llm。
节点体系
节点在编译期由宏注册。#[derive(Node)] 提供元数据;register_node! / register_async_node! 把类型加入一个 inventory,注册表在启动时收集它。没有需要维护的注册清单——加一个文件并重新构建就够了。
三种实现形态:
| Trait | 适用于 |
|---|---|
TypedNode |
同步、类型安全的纯计算 |
AsyncTypedNode |
异步,I/O 密集或依赖上下文 |
AsyncNode |
裸 HashMap<String, Value>——遗留写法 |
引擎从 NodeRegistry 取到定义,按节点声明的规格校验输入,带着 ExecutionContext 调用 execute,并把输出保存给下游节点。
Schema 驱动的界面
后端为每个节点发出带 x-* 界面扩展的 JSON Schema;前端的字段控件注册表按优先级匹配把它变成表单控件。对开发的直接影响是:在后端改动节点的输入或输出,界面会自动跟着变,前端不需要改代码。
优先级链是有序的——已连接端口状态(100)、文件路径(90)、敏感字段名(85)、形如 model_AC 的组件标识(65)、枚举(45)、基础类型(40)、兜底(0)。理解它就能解释某个字段为何长成那样。见节点配置。
支持的扩展包括 x-control、x-provider-options、x-model-options、x-depends-on、x-allow-custom、x-hidden 和 x-port。
横切模式
- 事件驱动——
EventBusAdapter把引擎事件桥接到 WebSocket。 - 全异步——Tokio;所有 I/O 都是
async。 - 流式——节点可通过流式发布器逐步产出结果。
- 错误——应用错误用
anyhow::Result,领域错误用thiserror。 - 凭证——
EncryptionService加CredentialRepository;一个 OAuth2 后台服务负责刷新令牌。 - 渠道鉴权——以
chk_为前缀的 API 密钥作为provider = "channel_adapter"的凭证存储,由中间件校验。 - 环境上下文——
workspace_id、conversation_id、document_id由引擎注入,并在工具调用中强制覆盖模型给出的值。
前端架构
chengflow-ui 是 React 18 + TypeScript + Vite,配合 TanStack Router(文件式路由)、Zustand + Immer 管理客户端状态、React Query 管理服务端状态、React Flow 承载画布,以及 Tailwind + Radix + shadcn/ui 负责呈现。
状态被拆分到若干专用 store,而不是一坨全局对象:editorStore(节点与连线增删改查、视口、剪贴板、校验)、workflowTabStore(标签页与后端同步)、historyStore(撤销/重做)、executionStore(WebSocket 执行状态)、workspaceStore(当前工作区)、shadowStore(可评审的 AI 修改)。
应用是工作区范围的。路由有两种形态——旧的全局路由(/chat、/editor/$id)和工作区范围路由(/w/$workspaceId/...)。workspaceStore 是 activeWorkspaceId 的权威来源,功能模块读它而不是去碰 localStorage。启动时的解析顺序:URL → localStorage 缓存 → 后端 /me/preferences → 第一个可访问的工作区。该 store 绝不自动创建工作区。
与编辑器相独立,src/runtime/ 把 PageLayout JSON——工作流的产出——渲染成实时页面:一个微型的应用渲染引擎,其构建期对应物是 scripts/generate-pages.ts。
云服务不变量
chenghub 和 skill-registry 位于本树之外,遵守它们自己的规则:PostgreSQL 是业务真相的唯一来源,Redis 只是加速器,两个服务在它宕机时都优雅降级;各自只有一个二进制,其 migrate 子命令是唯一会改动 schema 的路径;技能注册中心从不接收 ChengFlow 凭证、从不执行技能,并且只按 SHA-256 对产物做内容寻址,已发布的产物不可变。

暂无评论内容