基于通读代码 + 真机验证的全栈架构评审:系统全景/核心决策取舍/关键数据流/ 优缺点/技术债「税单」映射路线图/上生产前必修清单/总体判断。 一手依据:控制面热切换 live、failover+熔断 live、双 NATS 脑裂实测、审计误报核实。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
14 KiB
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回流/导出
└───────────────┬────────────────┘
│ NATS(RPC + 流 + 控制面广播 + 心跳)——万物总线
┌──────────────────┼───────────────────────────────┐
│ │ │
┌────┴─────┐ ┌──────┴──────┐ ┌──────┴──────┐
│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:bus(NATS/JetStream)、contract(跨服务契约)、prompts 注册表、secrets(AES-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. 优点(有据,非纸面)
- 解耦干净、扩展成本低 —— 最大亮点。 服务互不 import,只认契约。加工具只改 mcp-go 注册表、dispatcher 经
list_tools自动发现;换 provider/改 prompt 控制面热下发、零重编译。本次会话亲见 live 生效。 - 韧性分层叠进架构,非事后补。 model 层 failover + 编排层熔断 + 预算 + 护栏 + 评测纠偏(「恒温器」)。单 provider 挂平台不宕——本次真机演示过完整 failover+熔断+半开恢复。
- 流式原生。 token 流 + 执行轨迹 NATS→SSE + Redis Stream 断点重放,实时体验是架构级支持。
- Eino 省造轮子。 编排图/ReAct/checkpoint(HITL) 白嫖框架,自研 graph.go 已退役。
- 合理的多语言/多存储。 Go 走热路径,Python 做算法/沙箱;存储各司其职。
- 可观测与水平扩展底子在。 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 | 第一次真实并发/宕机 | T3(NATS 集群 + DB HA)⏸ |
| 单租户 → 多租户 | 第一个多租户客户 | T4.A(RBAC/tenant_id,L) |
| 共享文件系统耦合 | 第一次容器化/多机 | 见 §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. 「上生产前必修清单」(当你要容器化 / 多机部署时,这些会先断)
- 报告文件落盘 → 去本地盘耦合。 mcp-go 写/gateway 读同一本地目录在多机即断。改:报告直传 MinIO(正文已走 MinIO,导出产物同理),gateway 从对象存储取,而非
c.File(本地路径)。 - 单点 NATS → 集群。 3 节点 + JetStream Raft,否则总线宕=平台宕。(T3)
- 多进程共享配置对齐。
SUNDYNIX_SECRET_KEY(三服务须一致)、SUNDYNIX_REPORTS_DIR、NATS_URL等,容器化时用统一 env/secret,别靠$TMPDIR默认对齐(会不一致)。 - DB HA + 备份/DR 演练。(T3)
- CORS/TLS 严格化。 CORS 生产已收紧(本次做);TLS 待上(T3)。
- RBAC/多租户若面向多客户则前置(T4.A)。
- 一致性收口:KB 级联删事务化、关键 DB 写失败上浮 5xx(T4.F)——多机下静默不一致更难查。
- 可观测补齐:模型/熔断运行时态、各服务指标上报 admin(T4.F)。
8. 总体判断与建议
判断:一套 factored 良好、有平台野心的架构。解耦与控制面是真正的差异化资产;当前阶段(单租户、无真实流量)复杂度相对偏重,但方向自洽——痛点都已被识别并编入路线图,且硬化被克制地推迟。
最该警惕的一条:解耦很干净,但一致性与运维的「暗债」在攒(非事务级联删、best-effort 写、双 NATS 这类环境坑、共享文件系统)。这些不会在 demo 里咬你,会在「第一个真实多用户 / 容器化部署」时集中爆发。
建议(按优先级):
- 继续还 T4.F 的正确性债(级联删事务化、关键写上浮)——低成本、防未来静默 bug。
- 补运行时可观测(模型/熔断态上 admin)——让分层韧性在生产可见。
- 在容器化之前,先解「共享文件系统 + 配置对齐」两处(§7 #1、#3)——这是"看起来能跑、一拆就断"的典型。
- T3(NATS 集群/HA/TLS)维持 ⏸,直到有真实流量——不提前付税是对的。
- 多租户(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(T0–T4 路线)、EINO_ADOPTION.md(Eino 采纳 A/B/C✅ D 基本)、production_readiness.md、architecture.md。