代码架构分层与执行管线

适用版本:ChengOS v0.1.0+ | 最后核对:2026-08-12 | 来源:crates/cheng-core/src/ports/mod.rscrates/cheng-core/src/ports/executor.rscrates/cheng-engine/src/executorchengflow/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。这份清单很长——四十多个——而通读它们的名字是了解这个系统最快的地图:WorkflowRepositoryExecutionRepositoryConversationRepositoryDocumentRepositoryDocumentShadowRepositoryTableRepositoryKnowledgeBaseRepositoryArtifactStoreExecutionArchiveStoreTraceSnapshotStoreScheduledTaskRepositoryCredentialRepositoryChannelRepositoryChannelAdapterAdapterRegistrySkillRepositoryMcpServerRepositoryApprovalEventBusShadowEventBusEventPublisherExecutorNodeRegistryWorkflowFileImporterWorkspaceToolBridge 等等。

最该先弄懂的是 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 规划器、校验、拓扑排序、阶段选择)是纯的、无状态的;动态那一半(任务调度器)在运行时工作。因为执行计划是推导出来而非存储的,修改工作流绝不会留下过期计划。执行模型对此有更深入的讲解。

关键领域类型

  • 强类型 idWorkflowIdNodeIdExecutionId——UUID 新类型,因此你无法把执行 id 传到需要工作流 id 的地方。
  • NodeValue:字符串、数字、布尔、数组、对象、文件、流的带标签联合。FileStream 这两种让大载荷能按引用传递,也让模型节点能在 token 到达时就向下游发出。
  • ExecutionStatependingrunningpausedwaiting_for_reviewcompletedfailedcancelledtimeout——共个状态,后四个是终态。
  • 组件标识:节点类型为 category/name,例如 io/input_textai/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-controlx-provider-optionsx-model-optionsx-depends-onx-allow-customx-hiddenx-port

横切模式

  1. 事件驱动——EventBusAdapter 把引擎事件桥接到 WebSocket。
  2. 全异步——Tokio;所有 I/O 都是 async
  3. 流式——节点可通过流式发布器逐步产出结果。
  4. 错误——应用错误用 anyhow::Result,领域错误用 thiserror
  5. 凭证——EncryptionServiceCredentialRepository;一个 OAuth2 后台服务负责刷新令牌。
  6. 渠道鉴权——以 chk_ 为前缀的 API 密钥作为 provider = "channel_adapter" 的凭证存储,由中间件校验。
  7. 环境上下文——workspace_idconversation_iddocument_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/...)。workspaceStoreactiveWorkspaceId 的权威来源,功能模块读它而不是去碰 localStorage。启动时的解析顺序:URL → localStorage 缓存 → 后端 /me/preferences → 第一个可访问的工作区。该 store 绝不自动创建工作区。

与编辑器相独立,src/runtime/ 把 PageLayout JSON——工作流的产出——渲染成实时页面:一个微型的应用渲染引擎,其构建期对应物是 scripts/generate-pages.ts

云服务不变量

chenghubskill-registry 位于本树之外,遵守它们自己的规则:PostgreSQL 是业务真相的唯一来源,Redis 只是加速器,两个服务在它宕机时都优雅降级;各自只有一个二进制,其 migrate 子命令是唯一会改动 schema 的路径;技能注册中心从不接收 ChengFlow 凭证、从不执行技能,并且只按 SHA-256 对产物做内容寻址,已发布的产物不可变。

下一步

© 版权声明
THE END
喜欢就支持一下吧
点赞14 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容