Files
sundynix-agentix/ARCHITECTURE_REVIEW.md
T
Blizzard da04beadf5 docs: 架构评审报告 ARCHITECTURE_REVIEW.md
基于通读代码 + 真机验证的全栈架构评审:系统全景/核心决策取舍/关键数据流/
优缺点/技术债「税单」映射路线图/上生产前必修清单/总体判断。
一手依据:控制面热切换 live、failover+熔断 live、双 NATS 脑裂实测、审计误报核实。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 16:35:21 +08:00

173 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# sundynix-agentix · 架构评审报告
> 评审日期:2026-07-02 评审范围:全栈架构(gateway / dispatcher / mcp-go / mcp-py / shared / 双前端 / 基建)
> 评审方式:通读代码 + 真机运行验证(本次会话中实跑并踩到若干真实问题,见文末「证据索引」)
> 定性:这不是「一个应用」,而是一套 **事件驱动的 Agent 平台**。请按「平台」而非「应用」的标准衡量它。
---
## 0. 摘要(一句话结论)
**架构 factored 得相当好、也相当有野心,但当前是「分布式系统的复杂度,由接近单人的规模在扛」。**
解耦干净、控制面热切换、韧性分层——这些是真资产;代价是运维面大、一致性靠自觉、单租户假设焊死。
复杂度**是否值得,取决于目标**:多租户平台产品 → 值;单用户工具/demo → 偏重。
| | Top 3 |
|---|---|
| 最强 | ① 契约+NATS 解耦(加工具/换模型/改 prompt 热生效,零重编译)② 分层韧性(failover+熔断+预算+护栏+评测)③ 流式原生 |
| 最痛 | ① NATS 万物单点 + 运维尖角(双 NATS 脑裂真踩到)② 一致性靠 best-effort(非事务级联删等)③ 单租户假设焊死 |
---
## 1. 系统全景
```
┌─────────────┐ ┌─────────────┐
│ 桌面端 Wails │ │ 管理端 React │ ← 两个独立产品面(绝不合并)
└──────┬──────┘ └──────┬──────┘
│ HTTP/SSE │ HTTP
┌──────┴──────────────────┴──────┐
│ gateway (gin) │ 接入层:JWT鉴权/路由/护栏/限流/审计/SSE回流/导出
└───────────────┬────────────────┘
│ NATSRPC + 流 + 控制面广播 + 心跳)——万物总线
┌──────────────────┼───────────────────────────────┐
│ │ │
┌────┴─────┐ ┌──────┴──────┐ ┌──────┴──────┐
│dispatcher│ │ mcp-go │ │ mcp-py │
│ 编排核心 │ │ Go 工具服务 │ │ Python 算法 │
│ (Eino) │ │ RAG/记忆/ │ │ 沙箱/解析 │
│ +harness │ │ 历史/报告 │ │ │
└────┬─────┘ └──────┬──────┘ └─────────────┘
│ │
└── LLM providers └── PG / Redis / Milvus / Neo4j / MinIO
(OpenAI 兼容) 关系 / 缓存 / 向量 / 图谱 / 对象
```
**模块职责边界(清晰、无循环 import)**
- **gateway**HTTP 接入、JWT、路由、输入护栏、限流、审计、SSE 回流、报告导出服务。不含业务编排。
- **dispatcher**Eino 编排引擎(DSL→compose 图)+ harness 治理层(评测/护栏/预算/熔断/纠偏/脱敏)+ LLM 池(failover+缓存)+ 多智能体协调。
- **mcp-go / mcp-py**MCP 工具服务,经 NATS 被调。真正的 I/O 与算法执行在这里。
- **shared**busNATS/JetStream)、contract(跨服务契约)、prompts 注册表、secretsAES-GCM)、otel。**服务间只认 contract + bus,互不 import。**
---
## 2. 核心架构决策与取舍
| 决策 | 好处 | 代价 |
|---|---|---|
| **NATS 为统一总线**(RPC/流/控制面/心跳全在上) | 极致解耦;广播式控制面热更新 | 万物单点;一旦异常以诡异方式全面降级;单点 NATS(集群是 T3) |
| **MCP 工具作为独立服务** | 加工具只改注册表、dispatcher 自动发现;多语言(Go+Py | 每次 tool call 是网络 RPC(多 `no responders`/超时故障类);要整链在线 |
| **Eino 做编排核心** | DSL→compose、ReAct、checkpoint(HITL) 白嫖框架 | 框架耦合/塑形;限制传导(如流式中途失败不切) |
| **控制面热切换**(模型/prompt 经 NATS 广播) | 不重启改配置/换 provider/改提示词 | 权威态在 DB、下发靠广播,广播失败需兜底 |
| **契约解耦**shared/contract | 服务独立演进、独立部署 | 契约变更需跨服务协调 |
| **多存储专用化**PG/Redis/Milvus/Neo4j/MinIO | 各司其职、检索质量高 | 5 个存储要起/连/备份/保一致 |
| **双前端**desktop/admin 独立) | 用户产品与运维面互不干扰 | 两套前端 + 两套构建要维护 |
---
## 3. 关键数据流(架构最能说明问题的四条)
**A. 任务生命周期**:桌面端 POST DSL → gateway 解析+拓扑校验 → NATS 发布 → dispatcher 编译为 Eino compose 图执行 → token 流 + 执行轨迹经 NATS→Redis Stream→SSE 回流 → 状态机 FSM 落库。
**B. 工具调用(两条路,易混淆)**
- **编排直连**dispatcher 编排代码 `o.tools.CallTool()` 直接 NATS→mcp-go。**报告写文件(report_render)走这条,绕开 Eino。**
- **Eino ReAct 自主调用**:仅 `agent:true` 工具。模型 function-call → Eino ToolsNode → `mcpTool.InvokableRun`react_agent.go)→ NATS→mcp-go。
- **共同点**:真正执行永远在 mcp-go;**Eino 把工具当黑盒(吃 JSON→吐字符串),从不碰文件系统**。
**C. 报告导出/写文件**dispatcher 生成 → mcp-go `report_store` 写源 JSON → 用户导出 → gateway `report_export` → mcp-go 渲染 docx 并 `os.WriteFile``SUNDYNIX_REPORTS_DIR`(默认 `$TMPDIR/sundynix-reports/{id}.docx`)→ gateway `c.File(路径)` 流回 → 桌面端 Wails `SaveReportAs` 原生另存为落盘。**⚠️ mcp-go 写、gateway 按路径读,二者必须共享该目录。**
**D. 控制面热切换**admin 改配置 → gateway 写 DB → NATS 广播激活集 → dispatcher/mcp-go `ApplyOverrides` 热更新(不重启)。本次会话 live 验证 prompt/模型 均即时生效。
---
## 4. 优点(有据,非纸面)
1. **解耦干净、扩展成本低 —— 最大亮点。** 服务互不 import,只认契约。加工具只改 mcp-go 注册表、dispatcher 经 `list_tools` 自动发现;换 provider/改 prompt 控制面热下发、零重编译。本次会话亲见 live 生效。
2. **韧性分层叠进架构,非事后补。** model 层 failover + 编排层熔断 + 预算 + 护栏 + 评测纠偏(「恒温器」)。单 provider 挂平台不宕——本次真机演示过完整 failover+熔断+半开恢复。
3. **流式原生。** token 流 + 执行轨迹 NATS→SSE + Redis Stream 断点重放,实时体验是架构级支持。
4. **Eino 省造轮子。** 编排图/ReAct/checkpoint(HITL) 白嫖框架,自研 graph.go 已退役。
5. **合理的多语言/多存储。** Go 走热路径,Python 做算法/沙箱;存储各司其职。
6. **可观测与水平扩展底子在。** OTel 全链路 + 结构化日志 + 轨迹;NATS queue group + JetStream 持久消费,worker 无状态可扩;入库走 JetStream 持久队列(崩溃重投/幂等/背压,已 live 验证)。
---
## 5. 缺点 / 风险 / 技术债(诚实的一半)
### 5.1 运维面大、故障以诡异方式出现
- **NATS 万物单点。** 它是 RPC/流/控制面/心跳,一旦异常全面降级。本次真踩到**双 NATS 脑裂**(`make devnats` 与 docker NATS 都占 4222,客户端被劈成两半、静默半失败)。当前是**单点 NATS**(集群为 T3,未上)。
- **分布式跑/调都难。** 4 服务 + NATS + 5 存储要全起对连对。`local-run-gotchas` 那条 memory 长不是没原因(mcp-go 必须在 Milvus 后、ReportsDir 共享、token 过期静默 401、JetStream 重投…)。
### 5.2 一致性/正确性靠自觉,非机制强制
- 大量 **best-effort**:审计失败吞掉、部分关键 DB 写失败仍返 200、**KB 级联删跨三库非事务**(删一半失败仍删 PG → 不一致)。
- 预算/护栏是「计量+兜底」式,非硬约束(不过已有默认顶,见「审计误报」)。
### 5.3 与「干净解耦」自相矛盾的隐性耦合
- **共享本地文件系统**ReportsDir):mcp-go 写、gateway 按路径读——单机能对上,一旦容器化拆开即断,除非挂共享卷。这是整套 NATS 解耦里一处**潜在耦合**。
### 5.4 扩展性/多租户
- **单租户假设焊死。** 无 RBAC/tenant_id`owner_id` 当隔离键。变多租户是**动表结构 + 鉴权边界的手术**(T4.A,L 级)。
- **RequireAdmin 开发期放行任意登录用户**(生产靠 `ADMIN_USER_IDS` 白名单,无角色矩阵)。
### 5.5 框架耦合 / 取舍不一致
- 编排核心 Eino-native,框架抽象塑形代码,限制会传导(流式中途失败不切,部分是 Eino stream 语义)。
- 又有「该用库却手搓」的地方:`office/unioffice.go` 其实是零依赖手搓 docx(非 unioffice 库);`mcp-py` 算法层目前是**桩**(解析未接 OCR)。
### 5.6 可观测的盲区
- failover/熔断的**运行时态只在 dispatcher 日志,admin UI 看不到**(本次 demo 暴露,已记 T4.F 待办)。分层韧性在运维上偏不透明。
---
## 6. 「税单」:复杂度的代价何时到期(映射路线图)
这套架构的痛点,本质是「分布式平台迟早要付的税」。项目已把它们编入路线图,且**克制地把纯部署硬化推迟**(T3 标 ⏸「等有真实流量再上」——这是对的判断,别在没用户前先付税):
| 债 | 何时爆发 | 对应项 |
|---|---|---|
| 单点 NATS / DB 无 HA | 第一次真实并发/宕机 | T3NATS 集群 + DB HA)⏸ |
| 单租户 → 多租户 | 第一个多租户客户 | T4.ARBAC/tenant_idL|
| 共享文件系统耦合 | 第一次容器化/多机 | 见 §7 必修清单 |
| 非事务级联删 / best-effort 写 | 数据量上来后的静默不一致 | T4.F(级联删事务化/关键写上浮 5xx)|
| 无审计/安全溯源 | 合规/事故复盘 | ✅ T4.B(已做:审计+护栏事件+审计页)|
| 运行时韧性态不可见 | 生产排障 | T4.F(🔴 模型健康/熔断态 surface 到 admin,本次新增)|
**已还的债(本次会话)**:T4.B 审计可溯源整组 ✅、T4.E 编排边角整组 ✅(Map 错误传播/专家超时/Branch else/DSL 校验/熔断接回 failover)、T4.F 部分(拆 search 残骸/CORS+限流收紧)。
---
## 7. 「上生产前必修清单」(当你要容器化 / 多机部署时,这些会先断)
1. **报告文件落盘 → 去本地盘耦合。** mcp-go 写/gateway 读同一本地目录在多机即断。改:报告直传 MinIO(正文已走 MinIO,导出产物同理),gateway 从对象存储取,而非 `c.File(本地路径)`
2. **单点 NATS → 集群。** 3 节点 + JetStream Raft,否则总线宕=平台宕。(T3)
3. **多进程共享配置对齐。** `SUNDYNIX_SECRET_KEY`(三服务须一致)、`SUNDYNIX_REPORTS_DIR``NATS_URL` 等,容器化时用统一 env/secret,别靠 `$TMPDIR` 默认对齐(会不一致)。
4. **DB HA + 备份/DR 演练。**T3
5. **CORS/TLS 严格化。** CORS 生产已收紧(本次做);TLS 待上(T3)。
6. **RBAC/多租户**若面向多客户则前置(T4.A)。
7. **一致性收口**:KB 级联删事务化、关键 DB 写失败上浮 5xx(T4.F)——多机下静默不一致更难查。
8. **可观测补齐**:模型/熔断运行时态、各服务指标上报 admin(T4.F)。
---
## 8. 总体判断与建议
**判断**:一套 factored 良好、有平台野心的架构。解耦与控制面是真正的差异化资产;当前阶段(单租户、无真实流量)复杂度相对偏重,但**方向自洽**——痛点都已被识别并编入路线图,且硬化被克制地推迟。
**最该警惕的一条**:解耦很干净,但**一致性与运维的「暗债」在攒**(非事务级联删、best-effort 写、双 NATS 这类环境坑、共享文件系统)。这些不会在 demo 里咬你,会在「第一个真实多用户 / 容器化部署」时集中爆发。
**建议(按优先级)**
1. **继续还 T4.F 的正确性债**(级联删事务化、关键写上浮)——低成本、防未来静默 bug。
2. **补运行时可观测**(模型/熔断态上 admin)——让分层韧性在生产可见。
3. **在容器化之前,先解「共享文件系统 + 配置对齐」两处**(§7 #1#3)——这是"看起来能跑、一拆就断"的典型。
4. **T3NATS 集群/HA/TLS)维持 ⏸,直到有真实流量**——不提前付税是对的。
5. **多租户(T4.A)按商业化节奏推**——不确定要多租户就先别动这个大手术。
---
## 附:证据索引(本次评审的一手依据)
- **控制面热切换 live 有效**:prompt 建版→激活→图谱抽取即时变;模型配置热下发(dispatcher 日志 `model config set`)。
- **failover+熔断 live**:配坏主模型→任务经备用出答案;连败 3 次熔断→跳过坏主;冷却半开探测→重试→重熔断(完整三态)。
- **双 NATS 脑裂真踩到**`devnats`(127.0.0.1:4222) 与 docker NATS(*:4222) 并存,客户端劈成两半、`/admin/status` 误报服务离线;`curl :8222/connz` 定位、`pkill devnats` 收敛。
- **审计清单需逐项核实**(原始审计有噪声):ListModels「O(N²)」实为 O(N) 单次;budget「无限烧」实为 `taskBudget` 已默认 20 万顶;hybrid「删文件」实为拆残骸接线;failover「卡在备用」实为每次都重试主。**教训:改前必读码核实。**
- **关键文件**`gateway/internal/{router,handler,middleware,store}``dispatcher/internal/{eino,llm,harness}``mcp-go/internal/{mcp,rag,office}``shared/{bus,contract,prompts,secrets}`
- **配套文档**`DEPTH_ROADMAP.md`T0T4 路线)、`EINO_ADOPTION.md`Eino 采纳 A/B/C✅ D 基本)、`production_readiness.md``architecture.md`