长篇文学平台同时包含文学模型、CLI 状态机、Agent Runtime、Web API、Vue 前端、2.5D 渲染、桌面安装和 DOCX 交付。若这些能力通过互相直接导入连接,一个小需求就会要求理解整个仓库。
ArcVellum 采用面向接口的分层。模块化的目标是让需求定位到明确所有者,使代码、测试、文档与依赖围绕同一能力收敛。
六个主要层级
| 层级 | 核心职责 | 不应拥有的内容 |
|---|---|---|
| Engine | 文学规则、路线、任务、Gate、项目格式 | FastAPI、Vue、Tauri、Provider SDK |
| Studio Application | 用例编排、事务、投影、Runtime 调度 | 页面状态、具体 HTTP 细节 |
| Runtime | Worker、沙箱、进程、Provider、恢复 | 作品事实资格、Canon Policy |
| API | 请求解析、认证、SSE、DTO 适配 | 文学决策与直接文件写入 |
| Client | 用户交互、读模型、空间表现 | CLI 规则和正式状态推导 |
| Desktop | 安装、窗口、本地服务、更新、安全桥接 | 文学工作流实现 |
这些层级通过公开接口连接。Engine 可以独立测试,Studio 可以替换 Runtime,Client 可以重新布局,Desktop 可以更新打包链而不改变作品规则。
Engine 内部模块
文学工程内核进一步拆分为:
foundation:Schema、身份、路径、时间与基础值对象;tasking:TaskPackage、任务注册、事件和 completion;routes:七条正式路线与阶段定义;workflow:派生状态、Gate 与状态推进;literary:场景、人物、Canon、节奏、文风和审查规则;prompting:Prompt Asset、Recipe 与编译;orchestration:计划、任务图和受控编排;projections:面向产品的只读读模型;projects:项目初始化、迁移和资产目录;command_line:CLI 参数与用例适配;public:稳定对外入口。
外部代码只通过 public 或声明端口使用 Engine。这样可以继续拆分内部大文件,而不迫使 Studio 和测试批量修改导入。
Studio 的组合根
application/container.py::build_application_container 是依赖装配的唯一组合根。默认适配器在 infrastructure/composition.py 创建,业务用例依赖 Protocol 或 Port。
组合根负责选择:
- 项目仓储;
- Engine gateway;
- Runtime supervisor;
- Provider catalog;
- event bus;
- projection builder;
- export service;
- 本地持久化与时钟。
业务模块不在函数内部随意实例化真实适配器。测试可以注入 fake,桌面版与 Web 开发版也能使用不同宿主实现。
Runtime 端口
Runtime 通过小接口暴露:
- stage/open/run/cancel task;
- 查询 session 与 event;
- 预检和事务写回;
- Provider 与模型诊断;
- 资源、租约和健康状态。
Pi Worker、外部 CLI Agent 或未来本地模型 Worker 作为适配器实现这些端口。它们共享 TaskPackage 与结果合同,不要求应用层了解各自内部命令。
前端 Feature 边界
Vue 客户端按产品能力组织 Feature,例如 project、orrery、reader、archive、style、advisor、runtime、decisions、delivery 和 settings。每个 Feature 拥有:
- API client interface;
- 状态 store 或 composable;
- 视图组件;
- 格式化与投影适配;
- 单元测试和用户文案。
跨 Feature 组合由 shell、router 和 registry 完成。星仪通过 action port 请求打开正文或决策,不直接导入另一个 Feature 的内部 store。
需求定位流程
面向接口开发遵循固定步骤:
用户需求或故障 -> 确定事实所有者 -> 确定用例所有者 -> 找到公开端口和适配器 -> 写合同测试 -> 在单模块内实现 -> 运行局部测试 -> 运行架构与端到端棘轮例子:
| 需求 | 主要模块 |
|---|---|
| 修改正文晋升条件 | Engine workflow/literary |
| Agent 读取文件越界 | Runtime sandbox/preflight |
| 决策卡不消失 | Application choice use case + Client decisions |
| 星仪节点过密 | Client orrery layout,Projection 只补语义数据 |
| 模型选择不持久 | Provider settings repository |
| DOCX 混入草稿标记 | Export manifest 与 renderer |
先定位所有者能够避免“前端加一条判断暂时挡住”的跨层补丁。
大文件拆分原则
文件大小只是信号。可靠拆分依据变更原因:
- CLI parser 与命令 handler 分开;
- Route 定义、Gate 计算和恢复建议分开;
- API endpoint、DTO 与 use case 分开;
- 星仪布局、相机、渲染、窗口和交互分开;
- Provider catalog、凭证和连接诊断分开;
- Prompt Recipe、证据编译和渲染分开。
拆分后保留兼容 facade 和公开导入,先迁移测试,再删除旧内部实现。单纯建立大量一行子类会增加导航成本,只有在替换策略、共享合同或独立测试确有价值时才引入抽象。
架构棘轮
自动审计持续检查:
- forbidden dependencies;
- 模块循环依赖;
- 跨层私有导入;
- composition root 之外的具体适配器构造;
- 大文件和复杂函数趋势;
- API 和 Prompt Schema 兼容性;
- Engine 对 Studio、FastAPI、Vue 或 Provider 的反向依赖。
当前模块目录记录仍有 16 个超大文件和 120 个复杂函数的工程债,同时保持 0 条禁止依赖和 0 个模块环。这个状态说明边界已经稳定,内部复杂度仍需要按热点逐步收敛。
类、协议与函数的取舍
值对象适合表示 task id、candidate identity、revision、word target 与路径集合;Protocol 适合隔离 Runtime、仓储、时钟和 Provider;纯函数适合 Lint、布局、Gate 计算和投影转换。
继承只在存在稳定“是一种”关系和可替换行为时使用。许多执行策略更适合组合:TaskStrategy 可以由证据策略、推理档位和提交策略组合,而无需为每种文学任务建立一个深层子类。
文档作为开发接口
模块目录为每个模块记录职责、入口、公开端口、依赖、测试、常见故障和禁止修改范围。Agent 接到需求后先读取目标模块说明,不必把整个仓库塞入上下文。
Change Packet 进一步提供:问题、事实所有者、受影响合同、允许文件、测试命令和回滚点。它让大型项目仍能进行局部开发,也减少 Coding Agent 因上下文不足而自行跨层搜索。
模块化验收
一次架构优化应证明:
- 公共行为和项目格式保持兼容;
- 禁止依赖和循环仍为零;
- 目标模块拥有清晰测试;
- 调用方通过接口,不依赖私有实现;
- 文件与类数量的增长有明确职责收益;
- 真实端到端闭环仍能完成;
- 开发者或 Agent 可以仅阅读模块说明完成局部修改。
模块化不会让系统自然变简单。它让复杂性停留在正确位置,使每次开发都能在一个可理解范围内完成。
