deepseek原稿 23:12 2026/7/10
好的,我们开始架构评审。
???
总体设计原则
- 单向依赖:上层依赖下层,下层不感知上层
- 策略可替换:模型调用、降级策略均通过接口隔离
- 上下文传递:请求级上下文(含强制标志)贯穿全链路,不依赖闭包捕获
- 文件即模块:每个文件独立可替换,符合 AI 全量替换开发模式
一、模块拆分方案(Q1 答复)
建议文件清单(共 7 个核心文件 + 1 个入口)
项目根目录/
├── server.js # 入口文件(~80行)—— 仅启动服务、挂载路由
├── src/
│ ├── config/
│ │ └── index.js # 配置中心(~50行)
│ ├── core/
│ │ ├── app.js # Express + Socket.IO 初始化(~100行)
│ │ └── context.js # 请求上下文管理(~80行)—— 核心!
│ ├── layers/
│ │ ├── router.js # 指令路由层(~150行)
│ │ ├── l0-fast.js # L0极速层(~200行)
│ │ └── tools.js # 工具搜索层(~150行)
│ ├── models/
│ │ ├── model-service.js # 模型调度层(~250行)—— 核心!
│ │ ├── local-model.js # 本地模型适配器(~150行)
│ │ └── cloud-model.js # 云端模型适配器(~150行)
│ ├── coordinator/ # 预留调度层
│ │ └── index.js # 占位接口(~30行)
│ └── utils/
│ └── logger.js # 日志工具(~50行)
各文件职责说明
文件 职责 依赖方向 server.js 读取配置、启动服务、捕获全局异常 → config, app config/index.js 集中管理环境变量、超时阈值、模型端点 无依赖 core/app.js 初始化 Express + Socket.IO,挂载中间件 → config, context core/context.js 核心:管理每个请求的上下文(reqId、prefix、forceLocal、abortSignal),贯穿全链路 无依赖 layers/router.js 解析前缀,将请求分发给 L0/模型/工具/Coordinator → context, l0-fast, model-service, tools, coordinator layers/l0-fast.js 关键词匹配 + 纯 JS 极速处理 → context layers/tools.js 必应搜索、文件存储等工具函数 → config models/model-service.js 核心:统一模型调用入口,根据策略选择本地/云端,执行降级管控 → context, local-model, cloud-model models/local-model.js Ollama 14B 调用封装,仅处理流式通信 → config models/cloud-model.js DeepSeek API 调用封装,仅处理流式通信 → config coordinator/index.js 520 Agent 占位接口,返回 NotImplementedError 无依赖 utils/logger.js 统一日志格式 无依赖
二、强制本地/云端 + 降级链路设计方案(Q2 答复)
核心设计思路:策略模式 + 上下文驱动
不再使用「传参控制降级」 ,而是使用策略选择 + 上下文标志:
- 路由层(router.js) 根据前缀决定调用哪个策略: · /本 → 调用 modelService.executeLocalOnly() · /云 → 调用 modelService.executeCloudOnly() · 无前缀 → 调用 modelService.executeWithFallback()
- 策略函数本身已经决定了“能不能降级” ,无需在底层传递 allowFallback 参数。
- 底层只依赖 context 中的 reqId、abortSignal 做中断控制,不感知强制标志。
调用流程
用户请求
│
▼
┌─────────────────────────────────────────────────────────┐
│ router.js │
│ 1. 解析前缀 → 得到 routeType: 'local'|'cloud'|'auto' │
│ 2. 创建请求上下文 context (含 reqId, abortSignal) │
│ 3. 根据 routeType 调用 model-service 的对应方法 │
└─────────────────────────────────────────────────────────┘
│
├── routeType = 'local' ( /本 )
│ ▼
│ ┌─────────────────────────────────────┐
│ │ modelService.executeLocalOnly() │
│ │ · 只调用 local-model │
│ │ · 不设置降级定时器 │
│ │ · 异常直接抛给 router 返回错误 │
│ └─────────────────────────────────────┘
│
├── routeType = 'cloud' ( /云 )
│ ▼
│ ┌─────────────────────────────────────┐
│ │ modelService.executeCloudOnly() │
│ │ · 只调用 cloud-model │
│ │ · 无降级逻辑 │
│ └─────────────────────────────────────┘
│
└── routeType = 'auto' ( 无前缀 )
▼
┌─────────────────────────────────────┐
│ modelService.executeWithFallback() │
│ 1. 先调 local-model │
│ 2. 设置超时定时器 (4min) │
│ 3. 超时或异常 → 自动切 cloud-model│
│ 4. 仅此一处有降级逻辑 │
└─────────────────────────────────────┘
关键设计要点
设计点 实现方式 降级逻辑只有一处 仅在 modelService.executeWithFallback() 中存在,executeLocalOnly() 和 executeCloudOnly() 不含降级代码 强制本地无遗漏风险 路由层直接选择 executeLocalOnly(),该函数内部没有降级路径,底层 local-model 不感知降级,异常直接上抛 底层模型适配器纯净 local-model.js 只负责调用 Ollama 并推送 chunk,不包含任何降级判断 超时控制分层 超时定时器在 model-service 层设置,而非 local-model 内部,统一管理
三、Coordinator 多 Agent 调度中枢分层定位(Q3 答复)
定位:编排调度层,位于路由层与模型层之间
用户请求
│
▼
┌──────────────┐
│ router.js │ ← 路由层:解析前缀,决定走普通模式还是 Agent 模式
└──────┬───────┘
│ (当启用 Coordinator 且匹配 Agent 意图时)
▼
┌──────────────┐
│ coordinator/ │ ← 调度中枢:分析意图,拆解任务,编排执行计划
│ index.js │ · 决定调用哪个 Agent
│ │ · Agent 内部可调用 model-service / tools
└──────┬───────┘
│
├──→ ┌──────────────┐
│ │ model-service│ ← 模型层:仍负责具体模型调用
│ └──────────────┘
│
└──→ ┌──────────────┐
│ tools.js │ ← 工具层:搜索等能力
└──────────────┘
完整流转链路
用户输入: "/搜 最新AI新闻"
│
▼
router.js: 解析前缀 /搜 → 调用 tools.search()
│ (若不匹配任何前缀,进入普通对话)
▼
router.js: 未匹配特殊前缀 → 检查 Coordinator 是否启用
│
├── 未启用 → 直接调用 modelService.executeWithFallback()
│
└── 已启用 → 调用 coordinator.handle()
│
▼
coordinator/index.js:
1. 分析用户意图(可用简单关键词匹配或小型模型判断)
2. 决定执行计划: [工具搜索, 模型总结]
3. 编排执行:
a. 调用 tools.search() 获取结果
b. 调用 modelService.executeCloudOnly() 生成总结
4. 聚合结果返回前端
Coordinator 与模型层的关系
关系 说明 不是替代 Coordinator 不取代 model-service,而是编排它 可组合 Coordinator 可以多次调用 model-service 和 tools,实现多步骤任务 可选启用 通过 global.enableCoordinator 开关控制,关闭时走原有普通流程 占位实现 2.0 阶段仅提供占位接口,返回 NotImplementedError,不阻塞启动
四、Coordinator 过渡方案(明确)
采用 「占位接口 + 开关控制」 方案:
// coordinator/index.js (2.0 占位)
const ENABLED = false; // 默认关闭
function handle(userInput, context) {
if (!ENABLED) {
throw new Error('Coordinator not enabled');
}
// 占位:返回未实现错误,但不会导致启动失败
throw new Error('Coordinator not implemented yet');
}
module.exports = { handle, ENABLED };
· server.js 启动时 不主动 require coordinator,仅在 router.js 中动态 require · 若文件不存在,router.js 捕获错误并降级到普通模型调用 · 完全不影响核心业务启动
五、后续落地步骤(建议)
- 确认文件清单:确认上述 7+1 文件结构是否接受
- 按顺序生成代码: · 第一批:config/、utils/、core/context.js(无依赖基础层) · 第二批:models/local-model.js、models/cloud-model.js(独立适配器) · 第三批:models/model-service.js(依赖前两者) · 第四批:layers/ 各文件(依赖 model-service 和 context) · 第五批:core/app.js、server.js(组装启动) · 第六批:coordinator/index.js(占位)
- 迁移验证:逐文件替换,每替换一个验证一次核心功能
- 清理:确认全部正常后删除原 seven.js
六、与 1.0 的差异对照
1.0 痛点 2.0 解决方案 降级点分散在 2 个定时器 + 3 个 catch 降级逻辑仅 1 处:executeWithFallback() /本 强制本地靠传参 + 手动判断 路由层直接选择 executeLocalOnly(),该函数无降级路径 Coordinator require 报错阻塞启动 动态 require + 开关控制,不存在则不加载 单文件 1500+ 行,难以定位修改 单文件 150~250 行,职责清晰 AI 改代码需逐行找位置 AI 输出完整模块文件,全量替换
七、待确认事项
- 文件拆分颗粒度:上述 7+1 文件是否合适?是否需要进一步合并(如将 local-model.js 和 cloud-model.js 合并为 model-adapters.js)?
- L0 极速层独立:是否保留独立的 l0-fast.js?还是合并到 router.js 中作为优先匹配?
- 超时阈值:本地 4 分钟超时是否保留?云端是否有独立超时?
- Coordinator 开关位置:global.enableCoordinator 是否保留,还是迁移到 config/index.js?
以上为完整架构方案,请评审确认方向是否一致,确认后我们逐文件生成代码。