diff --git a/ARCHITECTURE_DESIGN.md b/ARCHITECTURE_DESIGN.md index 73d32d7..bb4e16b 100644 --- a/ARCHITECTURE_DESIGN.md +++ b/ARCHITECTURE_DESIGN.md @@ -1,239 +1,913 @@ -# sundynix-agentix · 架构设计文档(当前完整版) +# sundynix-agentix · 架构设计文档 -> 版本:2026-07-02(反映 T0–T2 全量 + T4.B/E 完成 + T4.F 部分后的现状) -> 定位:**事件驱动的 AI Agent 平台**。以 NATS 为统一总线,服务经契约解耦,Eino 为编排核心,控制面热切换。 -> 配套文档:`ARCHITECTURE_REVIEW.md`(架构评审:优缺点/技术债)、`DEPTH_ROADMAP.md`(路线图)、`EINO_ADOPTION.md`(Eino 采纳)。 +> 版本:2026-07-26 +> 定位:**事件驱动的多租户 AI Agent 平台**。以 NATS 为统一总线,服务经契约解耦,Eino 为编排核心,控制面热切换;桌面端 JARVIS 既是语音入口,也是能操作用户电脑的中枢。 +> +> **本文以代码为单一事实源。** `PROGRESS.md`(停更于 2026-06-19)与 `DEPTH_ROADMAP.md` 的汇总表已与代码脱节(详见 §9.14),仅作历史参考。 +> +> 配套文档:`ARCHITECTURE_REVIEW.md`(架构评审)、`DEPTH_ROADMAP.md`(路线图)、`EINO_ADOPTION.md`(Eino 采纳)、`SAAS_DESIGN.md` / `SAAS_P2_DESIGN.md`(多租户与计量)、`SPACE_DESIGN.md`(共享工作区)、`PAYMENT_DESIGN.md`(支付)、`VOICE_DESIGN.md`(语音)、`JARVIS_BRAIN_DESIGN.md`(JARVIS 中枢)、`LOCAL_AGENT_DESIGN.md`(本地执行)、`DEPLOY.md`(部署)。 --- ## 1. 系统概述 -sundynix-agentix 把「用户在画布上编排 Agent 图 → 平台调度执行 → 实时回流结果」做成一套可运维、可观测、可扩展的平台。核心特征: +用户在桌面端搭一张编排图(或直接说话),任务经网关落库并发到 NATS;调度集群把 DSL 编译成 Eino compose 图执行,过程中自主调用工具(检索/联网/计算/操作用户电脑),Token 与执行轨迹实时流回界面;全程带计量计费、护栏、评测与链路追踪。 -- **图编排执行**:前端画布导出 DSL(节点+连线),后端编译为可执行图,支持分支、并行、多智能体、人工审批。 -- **工具即服务**:能力(RAG/记忆/历史/报告/沙箱)以独立 MCP 服务经 NATS 暴露,加工具零改调度代码。 -- **分层韧性**:模型 failover + 熔断 + 预算 + 输入护栏 + 输出脱敏 + 自动评测纠偏(「恒温器」治理层)。 -- **控制面热切换**:模型配置、提示词经 NATS 广播热下发,不重启即生效。 -- **流式原生**:token 流与执行轨迹经 NATS→Redis Stream→SSE 实时回流,支持断点重放。 +### 1.1 Monorepo 结构 + +``` +sundynix-agentix/ +├── sundynix-shared/ 共享库:NATS 总线 / 契约 / 对象存储 / 密钥 / OTel / prompts +├── sundynix-gateway/ 第 2 层 业务网关(HTTP + WS + 平台工具 + 后台常驻) +├── sundynix-dispatcher/ 第 4 层 Agent 编排调度集群(无对外端口) +├── sundynix-mcp-go/ 第 5 层 Go I/O 型工具服务(RAG / 记忆 / 报告 / 图表) +├── sundynix-mcp-py/ 第 5 层 Python 算法型工具服务(沙箱执行 / 文档解析) +├── sundynix-desktop/ 桌面端(Wails v3 + React 19)——主产品面 +├── sundynix-admin/ 官网 + 运维控制台(同一 SPA,go:embed 进 gateway) +├── sundynix-web/ 薄 Web 面(租户自助:注册/组织/团队/账单) +├── deploy/ 三机部署配置 + NATS 集群配置 +└── scripts/ 备份/恢复/RAG 评测 +``` + +`go.work` 只纳入 4 个后端模块(shared / gateway / dispatcher / mcp-go)。 +**`sundynix-desktop` 刻意不入 workspace**(它是客户端,独立依赖),因此在该目录下跑 Go 命令必须 `GOWORK=off`。 + +### 1.2 代码规模(实测) + +| 部分 | 源码 | 测试 | +|---|---|---| +| Go · gateway | 14,395 | 2,775 | +| Go · dispatcher | 5,265 | 2,974 | +| Go · mcp-go | 3,844 | 1,319 | +| Go · shared | 2,173 | 681 | +| Go · desktop | 697 | 271 | +| TypeScript · desktop 前端 | 8,980 | 11 个前端测试文件 | +| TypeScript · admin | 9,360 | (同上) | +| TypeScript · web | 2,030 | (同上) | +| Python · mcp-py | 520 | | --- ## 2. 分层架构总览 -``` -┌── 客户端层 ───────────────────────────────────────────────┐ -│ 桌面端 Wails(:5173,用户工作产品) 管理端 React(:5174,运维控制台) │ -└──────────────┬───────────────────────────┬────────────────┘ - │ HTTP / SSE │ HTTP -┌── 接入层 ─────┴───────────────────────────┴────────────────┐ -│ gateway (gin, :8080) │ -│ JWT鉴权 · 路由 · 输入护栏 · 限流(按用户) · 审计 · SSE回流 · 导出 │ -└──────────────────────────┬─────────────────────────────────┘ - │ -┌── 总线层 ─────────────────┴─────────────────────────────────┐ -│ NATS + JetStream ——「万物总线」 │ -│ 任务 · 工具RPC · token流 · 执行轨迹 · 控制面广播 · 心跳 · 持久队列 │ -└──┬──────────────────────┬──────────────────────┬────────────┘ - │ │ │ -┌──┴────────┐ ┌────────┴────────┐ ┌────────┴────────┐ -│ dispatcher │ │ mcp-go │ │ mcp-py │ -│ 编排核心 │ │ Go I/O 工具服务 │ │ Python 算法工具 │ -│ Eino+harness│ │ RAG/记忆/历史/报告│ │ 沙箱/解析 │ -└──┬────────┘ └────────┬────────┘ └─────────────────┘ - │ LLM providers │ - │ (OpenAI 兼容) │ -┌──┴────────────────────────┴─────────────────────────────────┐ -│ 数据层:PostgreSQL(关系) · Redis(缓存/流/限流) · Milvus(向量) │ -│ Neo4j(图谱) · MinIO(对象/正文) · Jaeger(OTel 追踪) │ -└──────────────────────────────────────────────────────────────┘ +```mermaid +flowchart TB + subgraph CLIENT["客户端层"] + DESK["sundynix-desktop
Wails v3 + React 19
编排画布 · JARVIS · 本地 runner"] + ADMIN["sundynix-admin
官网 / + 控制台 /admin"] + WEB["sundynix-web
租户自助柜台"] + end + + subgraph GW["第 2 层 · gateway(唯一对外入口)"] + HTTP["HTTP API + SSE(gin)"] + WS["WebSocket
语音 /voice/stream
本地执行器 /local/runner"] + PLAT["平台工具提供方
platform_* / local_*"] + BG["后台常驻
配置控制面 · 持久消费者
入库 worker 池 · 3 个 ticker"] + end + + BUS{{"NATS + JetStream
统一总线(8MB payload)"}} + + subgraph WORK["执行层"] + DISP["第 4 层 · dispatcher
Eino compose 编排
LLM 双池 + failover + 熔断
harness 治理"] + MGO["第 5 层 · mcp-go
RAG 三路 / 记忆 / 报告 / 图表"] + MPY["第 5 层 · mcp-py
Docker 沙箱 / 文档解析"] + end + + subgraph STORE["存储层"] + PG[("PostgreSQL 16
29 张表")] + RDS[("Redis 7")] + MLV[("Milvus 2.4.13")] + BLV[("Bleve 全文")] + NEO[("Neo4j 5")] + OSS[("MinIO")] + end + + LLM["LLM Provider
OpenAI 兼容 · DeepSeek"] + VOLC["火山引擎
ASR / TTS"] + + DESK --> HTTP + ADMIN --> HTTP + WEB --> HTTP + DESK <--> WS + HTTP --> BUS + WS --> VOLC + BG <--> BUS + PLAT <--> BUS + BUS <--> DISP + BUS <--> MGO + BUS <--> MPY + DISP --> LLM + HTTP --> PG + HTTP --> RDS + HTTP --> OSS + MGO --> MLV + MGO --> BLV + MGO --> NEO + MGO --> OSS + DISP --> BUS ``` -**分层原则**:服务间**互不 import**,只依赖 `shared/contract`(数据契约)+ `shared/bus`(NATS 封装)。任何跨服务通信都过 NATS。 +### 2.1 分层职责 ---- - -## 3. 组件详细设计 - -### 3.1 gateway(接入层,gin) -唯一对外 HTTP 入口,不含业务编排。职责: -- **鉴权**:JWT 无状态(`owner` = 雪花 user.id);`RequireAuth`(登录)/ `RequireAdmin`(管理面,白名单 `ADMIN_USER_IDS`)。 -- **中间件链**(顺序):Recovery → OTel → RequestID → Observe(指标+日志) → CORS → **Auth** → **RateLimit(按 uid,未认证按 IP)** → **Guardrail(输入护栏+命中落库)**。 -- **DSL 拓扑校验**:`ParseAndAssemble` 前置拦截重复/空节点 id、悬挂边(防坏图进编排后被 compose 静默跳过)。 -- **SSE 回流**:`/tasks/:id/stream`(token 流)、`/tasks/:id/exec`(执行轨迹)、`/kb/ingest/:id/stream`(入库进度);优先读 Redis Stream(可断点续传),降级 live NATS。 -- **报告导出**:`/reports/:id/export?format=docx|md` → 经 NATS 调 mcp-go 现渲染 → `c.File` 流回。 -- **控制面 API**(`/admin/*`,RequireAdmin):模型 CRUD、计价、系统状态探活、系统聚合 overview、审计流、护栏事件流。 -- **审计**:`middleware.Audit(db)` 对变更类请求(POST/PUT/DELETE)best-effort 落库。 - -### 3.2 dispatcher(编排核心) -消费 NATS 任务,用 Eino 执行编排图 + harness 治理。详见 §5、§6。 - -### 3.3 mcp-go(Go 工具服务) -MCP 协议端点,订阅 `sundynix.tools.go.>`(queue group `mcp-go-workers`)。工具注册表是**单一事实源**(每工具声明 `cn/desc/agent/params/inject/handler`): -- **RAG**:`kb_search`(三路混合) / `wiki_search` / `kb_graph` / `kb_ingest` / `kb_delete`(级联删)。三路 = Milvus 向量 + Bleve 全文(落盘) + Neo4j 图谱,RRF 融合 + rerank。 -- **记忆**:`memory_get/upsert/delete/list`(用户长期偏好,软删)。 -- **历史**:`history_get/append`(会话历史,Redis)。 -- **报告**:`report_store`(存源) / `report_render`(渲染 docx 落盘) / `report_export`(按需导出)。 -- **元工具**:`list_tools`(自省,供 dispatcher 动态发现) / `health`(Milvus/Neo4j 就绪)。 - -### 3.4 mcp-py(Python 算法工具) -订阅 `sundynix.tools.py.>`。承载 CPU 密集/生态依赖的算法:沙箱代码执行、文档解析等。**注:算法层目前为桩,调用链路已做真。** - -### 3.5 shared(跨服务共享库) -- **bus**:NATS/JetStream 封装(Publish/Subscribe/Request-Reply、控制面广播、心跳、持久队列)。 -- **contract**:Task/ToolCall/ToolResult/ModelConfig/报告路径 等跨服务契约。 -- **prompts**:提示词注册表(内置默认 + 运行期覆盖)。 -- **secrets**:API Key AES-256-GCM 端到端加密(`SUNDYNIX_SECRET_KEY` 三服务须一致)。 -- **otel**:OTel 初始化(NATS 跨总线传播 traceparent)。 - ---- - -## 4. NATS 总线设计(架构中枢) - -NATS 不只是消息队列,而是承载 **5 类通信**: - -| 类型 | 模式 | Subject / 机制 | 用途 | +| 层 | 组件 | 职责 | 对外端口 | |---|---|---|---| -| 任务提交 | Publish | `sundynix.tasks.*` | gateway → dispatcher | -| 工具调用 | Request-Reply | `sundynix.tools.go.` / `.py.` | dispatcher/gateway → mcp-*(queue group 负载均衡) | -| token 流 | Publish/Sub | `sundynix.streams.` | dispatcher → gateway → SSE | -| 执行轨迹 | Publish/Sub | exec 事件 | 运行·观测面板 | -| 控制面广播 | Publish | 模型配置 / 提示词激活集 | 热下发 dispatcher/mcp-go(不重启) | -| 心跳探活 | Request-Reply | `sundynix.health.dispatcher` 等 | 管理端服务状态 | -| 持久队列 | JetStream | 入库工作队列(durable consumer) | 崩溃重投 / 幂等 / 背压 / AckWait 续租 | +| 1 客户端 | desktop / admin / web | 交互与呈现 | — | +| 2 接入 | **gateway** | 鉴权、租户/空间上下文、限流、护栏、审计、任务落库与发射、SSE/WS 回流、平台工具、后台常驻 | **8080**(生产 3000) | +| 3 总线 | **NATS + JetStream** | 任务队列、回流、工具 RPC、控制面广播、KV checkpoint | 4222 | +| 4 编排 | **dispatcher** | DSL→compose 图执行、ReAct 自主工具、多智能体、HITL 中断、评测纠偏 | 无(仅探针 8091) | +| 5 工具 | **mcp-go / mcp-py** | I/O 型与算法型能力,队列组水平扩 | 无(仅探针 8092) | -**⚠️ 运维要点**:单点 NATS(集群为 T3);本地开发禁止同时开 `devnats` 与 docker NATS(会脑裂)。 +**关键设计**:dispatcher 与 mcp-\* 都**没有对外端口**,只通过 NATS 通信 —— 攻击面收敛到 gateway 一个进程。 --- -## 5. 编排引擎设计(Eino compose) +## 3. 服务详解 -**Eino 只在 dispatcher 里用,是编排引擎地基。** +### 3.1 gateway —— 业务网关 / 统一接入层 -### 5.1 DSL → compose 图 -前端画布导出 `{version, nodes[{id,kind,label,config}], edges[{source,target,sourceHandle}]}`,dispatcher 编译为 Eino `compose.Graph`(`compose_compiler.go`),支持拓扑 + 连线 + 分支剪枝 + DAG 并行调度。**自研 graph.go 解释器已退役,单 compose 引擎。** +`github.com/sundynix/sundynix-gateway` · Go 1.25.8 · 入口 `cmd/server/main.go` -### 5.2 节点类型(执行器) -- `input`:用户输入 -- `retriever`:RAG 检索(Eino Retriever 包 mcp-go `kb_search`) -- `agent`:模型流式推理(拼消息 → token 流) -- `tool`:调 MCP 工具(编排直连 NATS) -- `branch`:条件路由(true/false 边标签 + **default/else 兜底** + 未匹配收口 END) -- `map`:并行 fan-out(有界并发撰写;**子项失败传播 + 全失败置 fatalErr**) -- `coordinator`:多智能体(专家=agent-as-tool,lead 分解→定制简报→并行派发→综合;**专家超时跳过**) -- `approval`:HITL 人工审批中断(暂停→waiting→批准回 running / 拒绝 rejected,checkpoint 落盘) - -### 5.3 ReAct 自主调用(`agent:true` 工具) -`agentTools()` 经 `list_tools` 动态发现 mcp 工具 → 包成 Eino `InvokableTool`(`mcpTool`)→ 模型 function-calling → ToolsNode 分发 → `InvokableRun` 转 NATS RPC。运行时注入 `user_id/session_id/kb`(不暴露给模型)。**加工具只改 mcp-go 注册表,dispatcher 零改动。** - -### 5.4 harness 治理层(「恒温器」) -- **评测**:综合分(规则+LLM质量+RAG忠实度)+ 分级(ok/warn/poor),低分**自动纠偏**重生成。 -- **输入护栏**:注入检测(网关 Tier1)+ LLM 分类器(dispatcher Tier2 灰区裁决)。 -- **输出脱敏**:有状态流式脱敏(跨分片缓冲,杜绝密钥被切断漏检)。 -- **预算**:单任务 token 计量 + 封顶(默认 `TASK_TOKEN_BUDGET`,触顶中止全图)。 -- **熔断**:后端连续失败→断开→冷却半开探测→恢复(编排层门控 + **model 层每模型熔断**)。 - ---- - -## 6. LLM 接入设计 - -- **接入策略**:开发期接第三方在线 API(OpenAI 兼容),不拉本地 Ollama(但支持 Ollama/vLLM 占位 key)。 -- **LLM Pool**(`llm/pool.go`):经 Eino ChatModel 组件;配置热更新、降级桩(未配置时)。 -- **Failover 链**(`llm/failover.go`):active=主 + 其余=按序备用,串成 `ToolCallingChatModel`,compose/ReAct/Chat 全路径透明。**每模型带熔断器**:主持续失败→熔断→跳过主直连备用;冷却半开探测自动恢复;`WithTools` 重包共享 breakers。 -- **输出缓存**(`llm/cache.go`):包在 failover 外层,缓存 Generate(非流式),键=模型+工具+消息哈希。 -- **控制面热切换**:admin 改模型 → gateway 写 DB → NATS 广播 → dispatcher `SetConfig` 热重建 pool。 - ---- - -## 7. 数据存储设计 - -| 存储 | 角色 | 关键内容 | -|---|---|---| -| **PostgreSQL** | 关系事实源 | 用户/任务/评测/模型/计价/提示词/知识库元/文档/双链/编排/审计/护栏事件(表名 `sundynix_` 前缀,AutoMigrate) | -| **Redis** | 缓存/流/限流 | token 用量计数、限流、会话历史、Redis Stream(token/exec 回放) | -| **Milvus** | 向量库 | 文档向量(语义检索),键 file_id | -| **Neo4j** | 图数据库 | 知识图谱三元组(实体 kb+name 共享,关系打 file_id) | -| **MinIO** | 对象存储 | 文档正文(大文件正文一律落 MinIO) | -| **Jaeger** | 追踪 | OTel span(节点/工具/LLM,NATS 跨总线传播) | - -**大文件入库生产化**:正文 MinIO + 切片 + 向量并发分批 + 图谱窗口化 + **JetStream 持久工作队列**(崩溃重投/幂等/背压),全 live 验证。 - ---- - -## 8. 关键数据流 - -**① 任务生命周期**:桌面端 POST DSL → gateway 解析+拓扑校验 → NATS `tasks.*` → dispatcher 编译 compose 图执行 → token 流 + 轨迹经 NATS→Redis Stream→SSE 回流 → FSM 状态机落库(submitted/running/done/failed/timeout/waiting/rejected)。 - -**② 工具调用(两条路)**: -- 编排直连:`o.tools.CallTool()` 直接 NATS→mcp-go(报告写文件走这条,**绕开 Eino**)。 -- Eino ReAct:模型 function-call → ToolsNode → `mcpTool.InvokableRun` → NATS→mcp-go。 -- 真正执行永远在 mcp-*;**Eino 把工具当黑盒(吃 JSON→吐字符串),不碰文件系统**。 - -**③ 控制面热切换**:admin 改配置 → gateway 写 DB → NATS 广播激活集 → dispatcher/mcp-go `ApplyOverrides` 热更新。 - -**④ 报告导出/写文件**:mcp-go `report_render` `os.WriteFile` 到 `SUNDYNIX_REPORTS_DIR`(默认 `$TMPDIR/sundynix-reports/{id}.docx`) → gateway `c.File(路径)` 流回 → 桌面端 Wails `SaveReportAs` 原生另存为落盘。**mcp-go 写、gateway 读须共享该目录。** PDF 目前靠前端打印(无后端 PDF 渲染)。 - ---- - -## 9. 安全与治理 - -- **鉴权**:JWT 无状态;owner 隔离(`owner_id`,单租户假设)。 -- **API Key 加密**:AES-256-GCM 端到端密文(PG + NATS 均密文)。 -- **输入护栏**:Tier1 正则/黑名单(网关,命中即拦 + 落库 `guardrail_event`)+ Tier2 LLM 分类(dispatcher 灰区)。 -- **审计**:敏感操作(改模型/密钥/激活 prompt/审批)经 `audit_log` 留痕,`/admin/audit` 可查。 -- **限流**:按登录用户(未认证按 IP),Redis 会话级。 -- **CORS**:开发 `*`,生产未显式配置则不放行任意源。 - ---- - -## 10. 可观测性 - -- **OTel 全链路**:节点/工具/LLM span,NATS 跨总线传播 traceparent,Jaeger(:16686)看瀑布。 -- **执行轨迹**:ExecEvent → 前端"运行·观测"面板(工具/专家调用、分支决策、审批等)。 -- **管理端**:`/status`(基建+服务探活+MCP 工具注册)、`/dashboard`(系统级 overview)、`/audit`(审计+安全事件)。 -- **缺口(待补)**:failover/熔断的**运行时态**只在日志、admin UI 不可见(T4.F 🔴)。 - ---- - -## 11. 前端设计 - -**双前端,严格独立、绝不合并**: -- **桌面端**(Wails 原生窗口 + React):用户工作产品——画布编排/任务运行/知识库/报告/记忆。含 Wails Go 绑定(`app.go`)提供原生能力:另存为/系统应用打开/文件读写/通知。 -- **管理端**(React,Vite):运维控制台——模型/计价/数据源/提示词/服务状态/评测/租户/护栏/审计。已接 vitest。 - ---- - -## 12. 部署形态与配置 - -- **基建**:docker compose(PG/Redis/NATS/Milvus+etcd+MinIO/Neo4j/Jaeger)。 -- **应用**:Go 服务本地编译二进制 / `go run`;启动序 = gateway → dispatcher/mcp-go(mcp-go 必须在 Milvus 后)→ mcp-py。 -- **关键配置对齐**(多进程/多机须一致):`SUNDYNIX_SECRET_KEY`、`NATS_URL`、`POSTGRES_DSN`、`SUNDYNIX_REPORTS_DIR`;`ADMIN_USER_IDS`、`CORS_ALLOW_ORIGIN`(生产)、`TASK_TOKEN_BUDGET`、`RATE_LIMIT_PER_MIN` 等。 - ---- - -## 13. 当前能力矩阵(干到哪一步) - -**✅ 已建(live 验证)** -编排引擎(compose 单引擎,分支/并行/HITL)· 多智能体协调 · RAG 三路混合 + 评测台 · 大文件入库生产化(MinIO+JetStream)· 模型 failover + 每模型熔断 · 输出缓存 · 评测纠偏闭环 · 输入护栏 + 输出脱敏 + 预算 · prompt 版本化 + DB 控制面热切换 · API Key 加密 · OTel 全链路 · **审计可溯源(audit_log + guardrail_event + admin 审计页)** · **系统级 admin overview** · **DSL 拓扑校验** · admin 控制台(含 vitest) - -**🔶 进行中(T4.F 健壮性收口)** -拆残骸/CORS/限流已做;**剩** KB 级联删事务化 · 关键 DB 写失败上浮 5xx · 审批 checkpoint 落盘重试 · 🔴模型健康/熔断态上 admin · 分页 · 配置化 - -**⬜ 规划(未动)** -T4.A 多租户/RBAC(无 tenant_id,L)· T4.C 真实计费(L)· T4.D AI 核心(记忆 Consolidate / prompt 灰度% / mcp-py 算法去桩 / 报告原生 PDF)· T3 生产硬化 ⏸(NATS 集群 / DB HA / K8s / TLS / 备份 DR,等真实流量) - ---- - -## 14. 技术栈 - -| 层 | 技术 | +| internal 包 | 职责 | |---|---| -| 编排/后端 | Go · gin · **Eino(cloudwego)** · NATS/JetStream · gorm | -| LLM | OpenAI 兼容 API(DeepSeek 等)· eino-ext openai 组件 | -| 工具服务 | Go(mcp-go) · Python(mcp-py) | -| 前端 | React 19 · Vite · Tailwind · Wails(桌面端) · vitest | -| 数据 | PostgreSQL · Redis · Milvus · Neo4j · MinIO | -| 可观测 | OpenTelemetry · Jaeger · Prometheus 指标 | +| `router` | Gin 装配:路由 + 中间件链 + CORS + admin SPA 回退 | +| `handler` | 30+ 文件:task/agent/kb/report/space/tenant/billing/payment/voice/jarvis/prompt/admin/wechat/local_runner/platform_tools | +| `middleware` | RequestID · Observe · Auth 系列 · TenantContext · SpaceContext · RateLimit · Guardrail · Audit · Require\*Role | +| `store` | Postgres(GORM) + Redis 全部访问;迁移、租户作用域插件、leader 选主 | +| `auth` | 无状态 JWT 签发/校验 + bcrypt | +| `dsl` | 前端 JSON DSL → `contract.Task` 解析组装 + 拓扑校验 | +| `guardrail` | 输入护栏 Tier1(归一化 + 注入正则 + env 黑名单) | +| `payment` | 渠道抽象 + manager + 微信支付 | +| `wechat` | 公众号带参二维码扫码登录 + 事件回调 | +| `voice` | 火山 V3 流式 ASR/TTS 客户端 + 二进制帧协议 + 攒句器 | +| `webui` | `go:embed` admin 产物打进二进制 | +| `nats` | shared/bus 薄封装 | + +**技术栈** + +| 用途 | 库 | 版本 | +|---|---|---| +| Web 框架 | `gin-gonic/gin` | v1.12.0 | +| SSE | `gin-contrib/sse` | v1.1.1 | +| ORM | `gorm.io/gorm` + `driver/postgres` | v1.31.1 / v1.6.0 | +| 缓存 | `redis/go-redis/v9` | v9.20.0 | +| 鉴权 | `golang-jwt/jwt/v5` + `x/crypto`(bcrypt) | v5.3.1 / v0.53.0 | +| ID | `bwmarrin/snowflake` | v0.3.0 | +| WebSocket | `gorilla/websocket` | v1.5.3 | +| 指标 | `prometheus/client_golang` | v1.23.2 | +| 追踪 | `otelgin` + otel | v0.69.0 / v1.44.0 | +| 支付 | `wechatpay-apiv3/wechatpay-go` | v0.2.21 | +| 测试 DB | `glebarez/sqlite`(纯 Go 无 CGO,仅测试) | v1.11.0 | + +**后台常驻组件** + +| 组件 | 作用 | +|---|---| +| `ServeConfig` ×3 | 模型配置控制面应答(kind = chat / embedding / voice) | +| `ServePrompts` | 激活 prompt 集下发 | +| `ConsumeTaskStatus` | 任务状态落库 + **报告完成时主动语音播报** | +| `ConsumeEval` / `ConsumeUsage` | 评测结果 upsert / 用量落库计费 | +| `StartIngestWorkers` | JetStream 入库作业 worker 池(有界并发背压) | +| `ServePlatformTools` | gateway 自己作为 MCP 工具提供方(见 §5.4) | +| `StartReconcile` / `StartSubscriptionTicker` / `StartScheduleTicker` | 微信掉单补偿 / 订阅周期发放 / JARVIS 定时任务 | + +三个 ticker 均带 **PG advisory lock 选主**(`store/leader.go`),多副本下只有一个实例真正扫。 + +**辅助 CLI**:`cmd/localsim`(模拟桌面本地执行器)、`cmd/voicesim`(免麦端到端语音链路模拟)、`cmd/voiceconfig`、`cmd/voicecheck`。 + +### 3.2 dispatcher —— Agent 编排调度集群 + +`github.com/sundynix/sundynix-dispatcher` · Go 1.25.8 · **无 HTTP 业务端口** + +| internal 包 | 职责 | +|---|---| +| `eino` | 编排引擎全部:orchestrator · compose_compiler/graph/callbacks · react_agent · coordinator · checkpoint · memory_extract · report | +| `harness` | 治理五件套:budget · circuitbreaker · eval · jailbreak · output | +| `llm` | pool(热更新) · failover(主备链) · cache(输出缓存) | +| `dsl` | DSL 图 → 对话计划编译 | +| `nats` | shared/bus 薄封装 | + +**技术栈**:`cloudwego/eino v0.9.12` + `eino-ext/components/model/openai v0.1.13`(OpenAI 兼容协议)、otel v1.44.0。 +另有 `cmd/loadtest`(阶梯并发压测器)。 + +### 3.3 mcp-go —— Go I/O 型工具服务 + +core NATS request-reply,订阅 `sundynix.tools.go.>`,队列组 `mcp-go-workers`,探针 `:8092`。 + +**技术栈**:`blevesearch/bleve/v2 v2.4.2`(CJK 分词)、`milvus-sdk-go/v2 v2.4.1`、`neo4j-go-driver/v5 v5.24.0`、`go-redis/v9`、`gorm.io/gorm`。 + +> ⚠️ `internal/office/unioffice.go` **名字有误导性**:并不引任何第三方 Office 库,而是用标准库 `archive/zip` + 内联 OOXML/WordprocessingML XML 手工拼 `.docx`。 + +**工具注册表**(`internal/mcp/gateway.go` 的 `buildRegistry()` 是唯一事实源,dispatcher 经 `list_tools` 动态发现,加工具零改调度代码) + +暴露给自主 agent(10 个):`wiki_search`(知识检索) · `memory_get`→`recall_user_memory` · `memory_upsert`→`remember_user_fact` · `history_get` · `web_search` · `web_fetch` · `calculator` · `current_datetime` · `sql_query`(仅 SELECT/WITH) · `chart`(bar/line/pie) + +内部/流水线(13 个):`kb_ingest` · `kb_delete` · `kb_search` · `kb_graph` · `report_render` · `report_store` · `report_export` · `external_api`(带 SSRF 校验) · `memory_delete` · `memory_list` · `history_append` · `health` · `echo` + +### 3.4 mcp-py —— Python 算法型工具服务 + +Python 3.11 · hatchling · **无 Web 框架**(纯 asyncio + `nats-py`),订阅 `sundynix.tools.py.>`,队列组 `mcp-py-workers`。 + +依赖:`nats-py>=2.7.0` · `python-docx` · `openpyxl` · `pypdf` · `docker>=7.1.0` + +| 工具 | agent 暴露 | 说明 | +|---|---|---| +| `run_code` | ✅ | Docker 沙箱 256m/10s | +| `secure_sandbox` | ❌ | 更严档 128m/5s | +| `parse_document` | ❌ | txt/md/csv 直读;docx/xlsx/pdf 按扩展名路由 | +| `echo` | ❌ | | + +两层防护:`sandbox.py` AST 静态守卫(拒 os/sys/subprocess/socket/ctypes/pickle 与 eval/exec/open)+ `interpreter.py` Docker 真隔离(禁网 / 非 root / 丢能力 / 限资源 / 一次性)。 +`mineru.py`(多模态解析)目前为桩。 + +### 3.5 shared —— 共享库 + +| 包 | 作用 | +|---|---| +| `bus` | NATS/JetStream 封装:流声明、任务收发、Token/Exec 回流、工具 RPC、5 条持久流、控制面、KV checkpoint;`trace.go` 把 W3C traceparent 塞进 NATS 头实现**跨总线链路串联**;消费侧解密 api_key | +| `contract` | 三方共享契约:subject 常量、Task/ToolCall/ToolResult/各类 Event、入库 claim-check、报告对象键 | +| `blob` | MinIO 封装;`cli==nil` 即降级回退本地盘,不阻断启动 | +| `secrets` | **AES-256-GCM**,密钥由 `SUNDYNIX_SECRET_KEY` 经 SHA-256 派生;密文 `enc:1:`+base64url(nonce‖ct);无前缀原样返回(兼容旧明文) | +| `otelx` | OTel 启动器(W3C 传播器 + OTLP/HTTP → Jaeger)+ 带 trace_id 的结构化 slog | +| `prompts` | 受管提示词注册表(内置默认 + `PROMPTS_FILE` 覆盖 + 控制面热下发) | +| `health` | 给无 HTTP 端口的服务起极小探针服务 | +| `cmd/devnats` | 内嵌 nats-server,本地开发免装 | --- -*本文档描述当前架构的「设计」;对该设计的优缺点评价、技术债与生产化建议见 `ARCHITECTURE_REVIEW.md`。* +## 4. NATS 总线契约 + +### 4.1 任务主链(JetStream 持久) + +| 流 / Subject | 值 | 用途 | +|---|---|---| +| `SUNDYNIX_TASKS` | `sundynix.tasks.` | 任务队列,消费者 `dispatchers`(队列组负载均衡) | + +### 4.2 回流(core NATS,即时不持久) + +| Subject | 用途 | +|---|---| +| `sundynix.streams.` | Token 流,结束用消息头 `X-Stream-End: 1` | +| `sundynix.exec.` | 结构化执行节点事件(seq/node/kind/phase/label/ms) | +| `sundynix.voice.event.` | JARVIS 语音事件(navigate / announce) | + +### 4.3 工具调用(request-reply + 队列组) + +| Subject | 队列组 | 提供方 | +|---|---|---| +| `sundynix.tools.go.` | `mcp-go-workers` | mcp-go | +| `sundynix.tools.py.` | `mcp-py-workers` | mcp-py | +| `sundynix.tools.platform.` | `platform-tools-workers` | **gateway 自己** | +| `sundynix.local.exec.` | — | 桌面端 runner(无订阅 = 离线,明确报不可用) | + +### 4.4 回写(JetStream 持久 + 落库幂等 → at-least-once 重投安全) + +| 流 | 消费者 | Subject | 用途 | +|---|---|---|---| +| `SUNDYNIX_STATUS` | `gateway-status` | `sundynix.status.task` | 任务生命周期状态 | +| `SUNDYNIX_USAGE` | `gateway-usage` | `sundynix.usage.task` | token 用量(计费凭据) | +| `SUNDYNIX_EVAL` | `gateway-eval` | `sundynix.eval.task` | 自动评测结果 | +| `SUNDYNIX_APPROVALS` | `approval-resumers` | `sundynix.approval.` | HITL 审批决定(抗离线) | +| `SUNDYNIX_INGEST` | `ingest-workers` | `sundynix.ingest.` | 入库作业队列(claim-check:大文件先落 MinIO,消息只带 StageKey) | + +> ⚠️ 状态流 subject **必须在 `sundynix.tasks.>` 之外**,否则会被任务流捕获成「幽灵任务」自我放大。 + +**KV**:`SUNDYNIX_CHECKPOINTS`(TTL 24h)—— 键 `task_id` 存 compose checkpoint,键 `pending:task_id` 存 resume 记录。 + +### 4.5 控制面 + +`sundynix.config..get` / `.updated`(kind = chat / embedding / voice)· `sundynix.prompts.get` / `.updated` · `sundynix.health.dispatcher`(心跳)。网关侧共用队列组 `gateway-workers`。 + +### 4.6 Task.Meta 约定 + +`user_id` · `tenant_id` · `session_id` · `safety_check` · `token_budget` · `model_profile`(=`voice`) · `intent`(=`report`) · `topic` · `kb` + +状态机:`submitted → running → done|failed|timeout`,HITL 分支 `waiting → running|rejected`。 +评测分级:`ok`(≥0.75) / `warn`(0.5~0.75) / `poor`(<0.5)。 + +--- + +## 5. 编排引擎 + +### 5.1 执行路径 + +`Orchestrator.Handle` → `executeGraph` → `runComposeGraph` → **`execComposeGraph`**(`internal/eino/compose_compiler.go`,唯一引擎): + +1. `dsl.Parse` + `dsl.Compile` → 建 `board`(黑板,进 compose 本地状态) +2. 无图/空图 → 退化为 compose 单轮对话 +3. 建 `compose.Graph`:**边只传占位信号 `flowSignal`,真实数据全走黑板**(注册 no-op merge 支持 fan-in);DAG 触发让无依赖节点自动并行 +4. `branch` 节点走 `AddBranch`;`approval` 节点在有 checkpoint 后端时编译为 `approvalInterruptLambda`(`compose.Interrupt` 落盘并释放 goroutine) +5. 编译失败 → 降级回自研 `graph.go`(已退役,保留作安全网) + +### 5.2 节点类型(12 种) + +| kind | 作用 | +|---|---| +| `input` | 输入/查询 | +| `memory` | 记忆召回 | +| `retriever` | RAG 检索 | +| `tool` | 显式工具调用 | +| `agent` | LLM 推理;`autonomous: true` → 走 **ReAct 自主工具循环** | +| `coordinator` | 多智能体:agent-as-tool 派给专家 + 并行 fan-out + 综合 | +| `aggregate` | 汇聚 | +| `approval` | **HITL 人工审批**(中断落盘) | +| `render` | 渲染(报告等) | +| `map` | 并行 fan-out | +| `output` | 输出 | +| `branch` | 条件分支(建图阶段单独处理,支持 else/default 兜底) | + +### 5.3 ReAct 自主工具 + +`react_agent.go` 用 Eino `react.NewAgent`,工具集经 `list_tools` 从**三个提供方**动态发现(mcp-go / mcp-py / gateway platform)。 + +- `MaxStep` 默认 12(`REACT_MAX_STEP` 可调) +- `StreamToolCallChecker` 扫描**整段流**判定工具调用(默认只看首片段,deepseek 等常先吐文本再给 tool call 会漏判) +- `inject` 参数(user_id / session_id / task_id / kb / tenant_id)服务端运行时绑定,**不暴露给模型** +- 工具可自报 `timeout_sec` 突破默认 3s(本地执行类要等用户点确认框) + +### 5.4 平台工具族(gateway 提供,11 个) + +平台操作的权威(提交关卡、归属校验、计费)都在 gateway,工具就长在权威所在地。 + +| 工具 | 作用 | +|---|---| +| `platform_recent_tasks` / `platform_task_status` | 查任务列表 / 单任务状态与输出 | +| `platform_gen_report` | 派发报告任务(**走 preflightCore 同一关卡**:预算/暂停/积分硬拦截) | +| `platform_open_view` | 切换客户端界面(navigate 白名单) | +| `platform_schedule_create` / `_list` / `_cancel` | 定时任务增删查 | +| `local_list_dir` / `local_read_file` | 看/读用户电脑(只读) | +| `local_write_file` / `local_exec` | 写文件 / 执行命令(三道闸,见 §8.3) | + +**安全铁律**:一律 `inject user_id` + 服务端归属校验(越权查他人任务一律回「不存在」,不泄露存在性);会烧钱的提交必须过 `preflightCore`。 + +### 5.5 harness 治理层 + +| 组件 | 作用 | +|---|---| +| `budget.go` | token 预算估算(CJK≈1 tok/字),触顶中止整图 | +| `circuitbreaker.go` | 三态熔断(阈值 3 次连续失败 / 冷却 20s / 半开 1 次探测) | +| `eval.go` | 规则 + LLM-as-judge 评测,异步 off 热路径;低分触发自动纠偏 | +| `jailbreak.go` | Tier2 越狱分类(severity ≥0.7 才拦) | +| `output.go` | 发射层逐片脱敏(sk-\* / AKIA\* / JWT / Bearer),跨分片不漏检 | + +--- + +## 6. LLM 治理 + +``` +cachingModel(输出缓存) + └─ failoverModel(主备链,每模型独立熔断器) + ├─ 主模型(active) + └─ 备用模型(其它 enabled chat 模型) +``` + +- **双池**:工作主力 `pool`(chat) + JARVIS 语音 `voicePool`(voice),语音池空则**透明回落**工作池 +- **热更新**:经 NATS 配置控制面(`SubscribeModelConfigUpdated` + 启动时 `FetchModelConfigWithRetry`) +- **接入方式**:`eino-ext/openai`,OpenAI 兼容协议(当前用 DeepSeek 在线 API,不拉本地 Ollama) +- 单次请求超时 120s;`LLM_FORCE_STUB=1` 走降级桩(压测用) +- 暴露 `ModelHealth`(provider/model/role/state/fails)供 admin 展示 +- **已知局限**:Stream 仅在建流同步报错时切备,已开始回流 token 的中途失败不切 +- **输出缓存**:`respCache` TTL 60s(`LLM_CACHE_TTL_S`,0=关)、容量 512(`LLM_CACHE_MAX`),key = sha256 + +**Prompt 版本化**:`shared/prompts` 内置默认 → `PROMPTS_FILE` 覆盖 → `sundynix_prompt` 表 + admin 热切换(含 diff 与撤销),不重编译即可改。 +Known key:`graph.extract` · `eval.quality` · `eval.refine` · `guard.jailbreak` · `coordinator.lead` · `memory.extract` + +--- + +## 7. 数据存储 + +### 7.1 中间件职责 + +| 中间件 | 版本 | 用途 | +|---|---|---| +| NATS + JetStream | 2-alpine(max_payload 8MB) | 统一总线(见 §4) | +| PostgreSQL | 16-alpine | 主业务库,29 张表 | +| Redis | 7-alpine | 会话 / 限流 / 任务输出缓存(TTL 48h) / 扫码 ticket;有内存降级兜底 | +| Milvus | v2.4.13 standalone | RAG 向量路 | +| etcd | v3.5.14 | 仅 Milvus 元数据依赖 | +| MinIO | RELEASE.2023-03-20 | 文档正文 blob、报告源/产物(bucket `sundynix-docs`)+ Milvus 段存储 | +| Neo4j | 5-community | RAG 图谱路(三元组) | +| Jaeger | all-in-one 1.60 | OTLP 收集 + trace UI | +| **Bleve** | 进程内库(非容器) | RAG 全文路,scorch 落盘 `BLEVE_PATH` —— **唯一需要给 mcp-go 挂持久卷的原因** | + +### 7.2 数据表全清单(29 张) + +命名:`TablePrefix: sundynix_` + `SingularTable: true`。公共基类 `BaseModel` = 雪花字符串 id + created/updated + 软删。 + +| # | 模型 | 表 | 职责 | +|---|---|---|---| +| 1 | `User` | `sundynix_user` | 平台用户 | +| 2 | `Task` | `sundynix_task` | 一次提交的编排任务(DSL);业务 id `task_xxx` 单列供 NATS subject | +| 3 | `Eval` | `sundynix_eval` | 自动评测结果,按 task_id upsert | +| 4 | `LLMModel` | `sundynix_model` | 模型后端配置,每 kind 同时刻仅一条 Active | +| 5 | `KB` | `sundynix_kb` | 知识库,`(space_id,name)` 唯一,分区键 `space_id/name` | +| 6 | `Doc` | `sundynix_doc` | 入库文档主表(Obsidian 式文库) | +| 7 | `Agent` | `sundynix_agent` | 编排定义,`(space_id,name)` 唯一 | +| 8 | `DocLink` | `sundynix_doc_link` | `[[双链]]` 索引,供反链/关系图 | +| 9 | `Pricing` | `sundynix_pricing` | 模型计价(每 1K token 输入/输出单价) | +| 10 | `Prompt` | `sundynix_prompt` | 受管提示词版本,`(key,version)` 唯一 | +| 11 | `AuditLog` | `sundynix_audit_log` | 敏感操作留痕,只增不改 | +| 12 | `GuardrailEvent` | `sundynix_guardrail_event` | 输入护栏命中事件 | +| 13 | `Tenant` | `sundynix_tenant` | 租户(计费/隔离单位),含物化余额列 | +| 14 | `TenantMember` | `sundynix_tenant_member` | 用户↔租户成员关系 + 角色 | +| 15 | `TenantInvite` | `sundynix_tenant_invite` | 可复用邀请码(扫码入组) | +| 16 | `Space` | `sundynix_space` | 共享工作区(资源容器) | +| 17 | `SpaceMember` | `sundynix_space_member` | 空间成员 + 空间内角色 | +| 18 | `UsageEvent` | `sundynix_usage_event` | 用量明细,task_id 唯一 → 幂等 | +| 19 | `CreditLedger` | `sundynix_credit_ledger` | 积分账本(append-only),余额 = SUM | +| 20 | `UsageRollup` | `sundynix_usage_rollup` | 用量按租户/天聚合快照 | +| 21 | `Setting` | `sundynix_setting` | 平台级键值配置 | +| 22 | `CreditPack` | `sundynix_credit_pack` | 积分包商品 | +| 23 | `PaymentOrder` | `sundynix_payment_order` | 充值订单(兑换码也写一行) | +| 24 | `RedeemCode` | `sundynix_redeem_code` | 兑换码 | +| 25 | `SubscriptionPlan` | `sundynix_sub_plan` | 订阅套餐 | +| 26 | `Subscription` | `sundynix_subscription` | 已购订阅(到期即 expired,不自动续) | +| 27 | `UserJarvis` | `sundynix_user_jarvis` | 每用户 JARVIS(名字/人设/自带火山 key,密文入库) | +| 28 | `Schedule` | `sundynix_schedule` | 定时任务 | +| 29 | `SchemaMigration` | `sundynix_schema_migration` | 已应用的版本化迁移记录 | + +### 7.3 迁移机制 + +整段迁移在 **PG advisory lock**(key `20260721`,60s 超时兜底)内串行: +`legacy(默认关) → AutoMigrate(29 模型) → 版本化 schemaSteps` + +只有 AutoMigrate 失败才返回 error;版本化步骤失败只记日志下次重试。 + +**4 个版本化步骤**:账本 grant/adjust 的**部分唯一索引**(支付入账与退款的幂等闸)· 回填租户物化余额 · 微信 openid 唯一索引。 +部分唯一索引不写在 struct tag 里 —— AutoMigrate 早于回填会撞车,必须回填后显式建。 + +破坏性 legacy 迁移需 `ALLOW_LEGACY_SCHEMA_MIGRATION=1` 显式开启(雪花 id 改造、双链改按 Doc.ID 关联)。 + +--- + +## 8. 关键链路 + +### 8.1 任务提交 → 编排 → 回流 + +```mermaid +sequenceDiagram + participant C as 桌面端 + participant G as gateway + participant N as NATS + participant D as dispatcher + participant M as mcp-go + participant L as LLM + + C->>G: POST /tasks (DSL) + G->>G: 鉴权 → 租户/空间上下文 → 限流 → 护栏 + G->>G: preflight:预算/暂停/计费租户/积分硬拦截 + G->>G: launch:落库 + 起录像器 + G->>N: publish sundynix.tasks (JetStream) + G-->>C: 202 task_id + C->>G: GET /tasks/:id/stream (SSE, token 走 query) + + N->>D: 消费任务 + D->>D: DSL 编译为 compose.Graph + loop ReAct 循环 + D->>L: 推理(failover + 熔断 + 缓存) + L-->>D: tool_call + D->>N: request sundynix.tools.go + N->>M: 队列组分发 + M-->>D: ToolResult + end + D-->>N: Token 流 sundynix.streams + N-->>G: 订阅回流 + G-->>C: SSE 逐字推送 + D->>N: status / usage / eval(JetStream 持久) + N->>G: 幂等落库 +``` + +### 8.2 语音 JARVIS 全双工 + +```mermaid +sequenceDiagram + participant U as 用户 + participant C as 桌面端 + participant G as gateway + participant V as 火山 ASR/TTS + participant D as dispatcher + + U->>C: 按住空格说话(PTT) + C->>G: WS 二进制帧:PCM 16k 上行 + G->>V: 流式 ASR(X-Api-Key 鉴权) + V-->>G: 转写(部分/最终) + G-->>C: transcript + U->>C: 松开 → end + G->>G: trySubmit:转写 → 组 DSL → 同一提交关卡 + G-->>C: task_id + G->>D: 提交任务(model_profile=voice → 走快模型) + D-->>G: Token 流 + G-->>C: reply(打字机,早于音频) + G->>V: 攒句 → 双向流 TTS + V-->>G: PCM 24k + G-->>C: speaking + 音频帧 → tts_end + C->>U: 无缝播放 +``` + +**要点** +- ASR 与 TTS 是**不同帧族**(ASR 简帧无事件号;TTS V3 事件族带 event/session/gzip) +- 下行**先订阅 token 流再建 TTS 会话** —— core NATS 无持久,订阅晚于产出会丢开头 +- 客户端播放按 `nextStart` 预约到未来时刻,收到 `tts_end` 时**不能立即停**(合成远快于语速,停了只剩前几个字) + +### 8.3 本地执行(JARVIS 操作用户电脑) + +```mermaid +sequenceDiagram + participant D as dispatcher + participant G as gateway + participant R as 桌面 Go host + participant U as 用户 + + Note over R,G: 登录后注册:WS /local/runner + R->>G: 上线(携带授权目录白名单) + G->>G: 队列组订阅 sundynix.local.exec + + D->>G: 调 local_exec(timeout_sec=160) + G->>R: WS 转发 tool + args + R->>R: 闸一:独立开关校验 + R->>R: 闸二:硬黑名单(14 条正则,命中不弹框直接拒) + R->>U: 闸三:原生确认框(默认按钮=拒绝,60s 无应答即拒) + U-->>R: 允许 / 本次会话都允许 + R->>R: 沙箱内执行(cwd 锁授权目录,60s 超时,输出 16KB 截断) + R-->>G: 结果 + G-->>D: ToolResult +``` + +**超时链必须外松内紧**:dispatcher 160s > gateway 150s > runner 转发 140s > 桌面端(审批 60s + 执行 60s)。 +任一层比内层短,用户还在看确认框就会被判超时。 + +--- + +## 9. 功能实现状态 + +图例:**✅ 已完成(live 验证)** · **🟡 部分** · **❌ 未做** + +### 9.1 多租户 / 空间 / RBAC + +| 功能 | 状态 | +|---|---| +| Tenant/TenantMember + 注册自动建默认租户 + 存量回填 | ✅ | +| **gorm 租户隔离插件**(查询/更新/删除自动加 tenant_id,创建自动填) | ✅ | +| 租户角色 RBAC(owner/admin/member/viewer)+ 路由级校验 | ✅ | +| 租户邀请码(二维码扫码入组) | ✅ | +| Space 共享工作区(KB/Agent 按空间共享 + 空间角色 + 全员空间) | ✅ | +| 用户 role 字段 + 角色表(把 `RequireAdmin` 从白名单升级为角色校验) | ❌ | +| 用户管理接口(列举/禁用/改角色) | ❌ | + +### 9.2 计量 / 计费 / 支付 + +| 功能 | 状态 | +|---|---| +| 计价配置 + usage_event 计量 + credit_ledger 账本 + daily rollup | ✅ | +| 余额硬拦截(`CREDIT_ENFORCE`) | ✅ | +| 支付渠道抽象 + 微信 Native(**真环境实测通过**) | ✅ | +| 兑换码 / 人工核销 | ✅ | +| 对账 + 退款(adjust 负分录 + 回退余额 + 审计) | ✅ | +| 订阅套餐(周期发放 + 到期失效) | ✅ | +| 发票 / Stripe | ❌ | + +### 9.3 知识库 / RAG + +| 功能 | 状态 | +|---|---| +| **三路混合检索**(Bleve 全文 + Milvus 向量 + Neo4j 图谱,RRF k=60 融合 + rerank) | ✅ | +| 中文分词修复(CJK bigram) | ✅ | +| 离线检索评测(recall@k / MRR,hybrid vs 单路) | ✅ | +| 大文件生产化(MinIO 正文 + 并发 embed + 窗口化图谱 + JetStream 持久队列,kill -9 续跑验证) | ✅ | +| KB 级联删事务化(三库 + MinIO 先删、PG 最后)+ MinIO 孤儿 GC | ✅ | +| Obsidian 式文库(Markdown + 双链 + 反链 + 关系图) | ✅ | +| 检索持久化治理 / 列表分页 | 🟡 | +| 多模态解析(MinerU/PaddleOCR) | 🟡 骨架,`mineru.py` 为桩 | + +### 9.4 报告 + +| 功能 | 状态 | +|---|---| +| 报告编排(规划 → 分章并行 → 汇聚 → 存源) | ✅ | +| Word(.docx) 渲染(自建零依赖 OOXML) | ✅ | +| Markdown 导出 | ✅ | +| PDF 导出 | 🟡 走 webview 打印;后端原生 PDF 未做 | +| 分章 map 错误传播与汇总 | ✅ | + +### 9.5 记忆 + +| 功能 | 状态 | +|---|---| +| memory CRUD + Profile 表 | ✅ | +| P1 异步攒批 Consolidate(每 3 轮 LLM 对账 ADD/UPDATE/DELETE/NOOP + 软删) | ✅ | +| P2 Score(Recency + Importance) 排序 + 衰减 + top30 截断 | ✅ | +| P3 Relevance(语义相关性) | ✅ | +| 会话历史 history_get/append | ✅ | + +### 9.6 编排引擎 + +| 功能 | 状态 | +|---|---| +| Eino Phase A/B/C/D 全部(组件 → ReAct → compose 全图 → FSM) | ✅ | +| compose 为唯一引擎(`graph.go` 已退役作降级网) | ✅ | +| HITL 人工审批中断(checkpoint 落盘 + NATS 决定回传 + 抗离线) | ✅ | +| 多智能体 coordinator(agent-as-tool + 定制 brief + 并行 fan-out + 专家超时) | ✅ | +| Branch else/default 兜底 · DSL 拓扑校验 · 工具动态发现 | ✅ | +| handoff / adk 可中断多智能体 | ❌ | +| 编译图缓存 | ❌ 判定为 premature(实测编译 ~13µs) | +| 审批 checkpoint 落盘失败重试(现只 log → 任务永卡 waiting) | ❌ | + +### 9.7 模型治理 + +| 功能 | 状态 | +|---|---| +| 模型路由 + Fallback + 每模型三态熔断 | ✅ | +| 模型健康/熔断态 surface 到 admin | ✅ | +| 输出缓存 | ✅ | +| Prompt 版本化 v1(注册表)+ v2(DB 热切换 + diff + 撤销) | ✅ | +| 工作模型与 JARVIS 语音模型分离 + 用量按实际模型计量 | ✅ | +| Prompt 灰度 % A/B | ❌ | + +### 9.8 评测 / 护栏 + +| 功能 | 状态 | +|---|---| +| 自动化评测(规则 + LLM 裁判,异步 off 热路径) | ✅ | +| 裁判校准 + 低分自动纠偏闭环(live 真触发) | ✅ | +| 输入护栏(注入检测 + 归一化 + 超大体拦截)+ 事件落库 | ✅ | +| 输出护栏(跨分片密钥脱敏) | ✅ | +| HITL 审批决定明细(理由)落库 | ❌ 现仅 who/when/status | +| 前端评测质量面板 | ❌ 后端 `/tasks/:id/eval` 已有 | + +### 9.9 语音 / JARVIS 中枢 + +| 功能 | 状态 | +|---|---| +| 火山 ASR + 双向流 TTS 全双工(新版 API Key 鉴权) | ✅ | +| 语音触发任务(复用同一提交关卡) | ✅ | +| PTT 按住说话 + 打断 + 连续对话 + 打字机文本流 | ✅ | +| 每用户 JARVIS(名字/人设/自带豆包配置回落,语音不串主记忆) | ✅ | +| 全屏钢铁侠 HUD(接真实音频电平) | ✅ | +| **P1 平台工具族**(查任务/派报告,含越权拒绝与积分硬拦截验证) | ✅ | +| **P2 动作通道**(语音让界面切页,三层白名单) | ✅ | +| **P3 主动播报**(任务终态主动开口,正朗读则排队不抢麦) | ✅ | +| 声纹 / 唤醒词 / 音色克隆 / 多语种 / ASR-TTS failover | ❌ 本期不做 | +| P5 常驻 companion session | ❌ 暂缓(现靠 session history 串联已够用) | + +### 9.10 本地执行 + +| 功能 | 状态 | +|---|---| +| **只读**:`local_list_dir` / `local_read_file` + runner 注册 + 沙箱(逃逸单测全拦) | ✅ | +| **能动的手**:`local_write_file` / `local_exec` + 三道闸(独立开关 + 14 条黑名单 + 原生审批框) | ✅ | +| 多目录白名单沙箱 + 空 path 自发现授权目录 | ✅ | +| 离线优雅降级(runner 不在线明确报不可用,不挂起) | ✅ | +| ToolPolicy 服务端策略下推 | 🟡 已有审批框 + 黑名单,下推未做 | +| **档 B**:agent 循环下沉客户端(内环不过网) | ❌ 有决策门,未做 | +| gateway LLM 代理端点(档 B 的地基) | ❌ | + +### 9.11 定时任务 + +| 功能 | 状态 | +|---|---| +| `sundynix_schedule` + leader 锁 ticker(30s) + create/list/cancel 工具 | ✅ | +| 存自然语言指令,到点走同一关卡执行 + 主动播报 | ✅ | +| 先推进后提交防重复烧钱;停机错过的**不补跑** | ✅ | + +### 9.12 可观测 / 运维 + +| 功能 | 状态 | +|---|---| +| Prometheus `/metrics`(路由模板低基数)+ 结构化日志 + X-Request-ID | ✅ | +| `/healthz` `/readyz` 探针 + 依赖聚合健康 | ✅ | +| **OTel 全链路**(otelgin + 跨 NATS traceparent 传播 → Jaeger) | ✅ | +| admin 观测面(overview/status/tasks/spaces/usage/evals/datasources/orders/审计/护栏) | ✅ | +| 检索试验台(跨租户 + 单路 mode 对比) | ✅ | +| panic 进 trace span · TTFT/token-s 指标 | ❌ | +| K8s / DB HA / TLS / DR 演练 | ❌(**例外:NATS 集群已落地**) | + +### 9.13 客户端 + +| 端 | 状态 | +|---|---| +| desktop(编排画布 · ⌘K · SSE 轨迹 · 文库 · 知识图谱 · 记忆面板 · JARVIS HUD · 本地 runner · 服务器地址运行时可配) | ✅ | +| admin(官网 + 控制台,go:embed 进 gateway) | ✅ | +| web(租户自助:注册/组织/团队/账单) | ✅ | +| 前端测试 | 🟡 11 个测试文件,集中在 lib 纯函数;视图层基本不测 | + +### 9.14 ⚠️ 已知的文档记账偏差 + +以下 4 处**文档与代码不符,一律以代码为准**: + +1. `PROGRESS.md` 停在 2026-06-19,其中「计费未做」「多租户未做」「Relevance 待 P3」均已被后续代码推翻。 +2. `DEPTH_ROADMAP.md` 进度表写「T4 后端做实 0/6 组」,但正文里 T4.A/B/E 大量条目已勾 ✅。 +3. `DEPTH_ROADMAP.md` Tier 3 写「NATS 集群未做」,实际三节点集群已在 128 落地。 +4. `PAYMENT_DESIGN.md` 标题写「不做订阅」,但订阅套餐全套(表 + ticker + admin 页)已实现。 + +--- + +## 10. 安全与治理 + +### 10.1 中间件链(顺序有意义) + +``` +Recovery → otelgin → RequestID → Observe → cors → Auth → TenantContext → SpaceContext → RateLimit → Guardrail +``` + +**Auth 必须前置于 RateLimit** —— 否则无法按用户限流(企业网多人共享出口 IP 会互相拖累)。 + +### 10.2 鉴权 + +- **JWT 无状态**(`internal/auth`),owner = 雪花 user.id;生产默认密钥 fail-fast +- `Auth()` 非阻断解析 → `RequireAuth()` / `RequireAdmin()`(当前为 `ADMIN_USER_IDS` 白名单) +- **`AuthFromHeaderOrQuery()`**:WS 与 SSE 带不了 Bearer 头,走 `?token=` —— 用于 `/tasks/:id/stream`、`/tasks/:id/exec`、`/kb/ingest/:id/stream`、`/reports/:id/export`、`/voice/stream`、`/local/runner` +- 注册/登录各带 10/min 限流;微信公众号扫码登录独立通道 +- **支付回调例外**:`/billing/callback/:channel` 无 Bearer,渠道验签是唯一的门 + +### 10.3 密钥加密 + +`shared/secrets`:**AES-256-GCM**,密钥由 `SUNDYNIX_SECRET_KEY` 经 SHA-256 派生 32 字节;密文格式 `enc:1:` + base64url(nonce‖ciphertext),带版本前缀便于轮换。 + +**全链路**:gateway 保存时 Encrypt 落 PG → 密文原样过 NATS → dispatcher/mcp-go 在 bus 层 Decrypt。**api_key 在磁盘与线缆上都非明文**,仅构建 LLM 客户端时内存短暂还原。三服务的 `SUNDYNIX_SECRET_KEY` 必须一致。 + +微信支付商户私钥不落库不进镜像 —— 宿主目录只读挂载。 + +### 10.4 租户隔离 + +`store/tenant_scope.go`:`tenantScopedMarker` 空接口(小写方法,只有本包模型能标记)+ 4 个 gorm callback(query/update/delete 前自动加 `tenant_id = ?`,create 前自动填)。 + +**两个必须记住的旁路**:系统级读(admin 聚合)与跨租户写(对账)都要 `WithoutTenant(ctx)` —— 漏了会出回归或对账崩。 + +### 10.5 限流与审计 + +- **限流**:Redis 为主后端 + **进程内固定窗口 fail-safe 兜底**(Redis 挂时不再 fail-open,改宽松本地限流);已认证按 uid、未认证按 IP +- **审计**:`Audit(db)` 只审计变更类(POST/PUT/DELETE/PATCH),best-effort 不拖垮主流程;挂在整个 `/admin` 组、prompt 激活/撤销、HITL 审批、租户成员变更、充值兑换 +- **护栏事件**:命中落 `GuardrailEvent`(actor/kind/reason/signals/path/ip)→ admin 审计页 + +--- + +## 11. 客户端形态 + +**三个独立产品面,绝不合并**: + +| 端 | 定位 | 技术栈 | dev 端口 | 路由 | +|---|---|---|---|---| +| desktop | **用户工作产品** | Wails v3.0.0-alpha2.117 + React 19 + TS 5.6 + Tailwind 3.4 + React Flow 12 | 9245 (wails3) / 5173 (纯 vite) | 无路由,`useState` | +| admin | 官网(`/`) + 平台超管控制塔(`/admin`) | React 19 + react-router-dom 7 | 5174 | **BrowserRouter** | +| web | 租户客户自助柜台 | React 19 + react-router-dom 7 | 5175 | **HashRouter**(纯静态托管即可深链) | + +**desktop 页面**(`ViewKey`):`home` 工作台 · `studio` 编排 · `kb` 知识库 · `runs` 运行 · `report` 报告 · `memory` 记忆 · `usage` 用量 + +**admin 页面**(17 条路由):仪表盘 / 服务状态 / 任务观测 / 自动评测 / 审计安全 / 模型配置 / 登录设置 / 语音设置 / 数据源RAG / 提示词 / 支付(配置·订阅·订单对账) / 租户用户 / 微信用户 / 空间 / 安全护栏 + +三端共用**自建 UI 组件**(`src/ui/`:Badge/Button/Card/Dialog/Input/Table/Tabs/Toast + cn),**未引任何组件库**。 +测试统一 Vitest 4 + jsdom + Testing Library。 + +--- + +## 12. 部署拓扑 + +### 12.1 三机内网生产 + +```mermaid +flowchart LR + USER["用户桌面端"] + FRP["frp 映射"] + + subgraph M132["192.168.100.132 · 应用机"] + GW["gateway 3000→8080
内嵌 admin UI"] + DP["dispatcher"] + MG["mcp-go"] + MP["mcp-py"] + end + + subgraph M128["192.168.100.128 · 基建机 + CI runner"] + N1["NATS 三节点集群
4222/4223/4224
REPLICAS=3"] + PG2[("PostgreSQL")] + RD[("Redis")] + ML[("Milvus + etcd")] + NJ[("Neo4j")] + JG["Jaeger"] + end + + M126[("192.168.100.126
业务 MinIO
bucket sundynix-docs")] + TX["162.14.122.200
微信 token 中控
静态 IP"] + + USER --> FRP + FRP --> GW + GW --> N1 + DP --> N1 + MG --> N1 + MP --> N1 + GW --> PG2 + GW --> RD + GW --> M126 + MG --> ML + MG --> NJ + MG --> M126 + GW --> JG + DP --> JG + GW --> TX +``` + +**要点** +- **对外只暴露 132:3000**(gateway:API + admin UI + 语音 WS + 本地 runner WS),其余全部内网 +- NATS 三节点 `cluster.name: sundynix-cluster`,routes 互指 6222;`NATS_STREAM_REPLICAS=3` 走 Raft quorum;三节点 `max_payload: 8MB` 必须一致 +- 128 上的 Redis/Milvus/NATS **无认证**,靠防火墙只放行 132/126 网段 +- 132 只读挂载两个宿主目录:微信支付商户私钥、微信域名校验文件(不进镜像、不进 git) +- 备份 `scripts/backup.sh`(PG/Neo4j/Milvus/MinIO 卷),建议 cron 每日异机存档 + +### 12.2 单机一体化 + +`docker-compose.prod.yml` 一条命令拉起应用 4 服务 + 全套基建(要求 Docker 24+/Compose v2、~8GB 内存)。 +基建端口**一律不对宿主暴露**,只留 gateway 与可选 Jaeger UI。密钥用 YAML 锚点 `${SUNDYNIX_SECRET_KEY:?}` 强制 .env 必填。 + +### 12.3 密钥模型 + +| 类别 | 内容 | 存放 | +|---|---|---| +| 引导密钥 / 基建凭据 | `SUNDYNIX_SECRET_KEY` · `JWT_SECRET` · PG/Neo4j/MinIO 密码 | `.env`,部署时填一次 | +| 业务模型 key | LLM api_key · 语音 key · 支付配置 | **不进 .env**,登录 admin 配,AES 加密存 PG | + +丢失主密钥 = 已存业务 key 全部失效。 +首次管理员:注册第一个账号 → 取 user id → 填 `ADMIN_USER_IDS` → 重启 gateway。 + +--- + +## 13. CI / CD + +### 13.1 GitHub Actions(质量门,push main + PR) + +| Job | 内容 | 红门 | +|---|---|---| +| `go` | 4 模块 `build + vet + test -race` | ✅ 一票否决 | +| `lint` | golangci-lint × 4 模块,`only-new-issues`(存量约 42 处不拦) | ✅ 仅新问题 | +| `security` | `govulncheck`(advisory 不阻断)+ **`gitleaks` 扫提交历史(命中即失败)** | 部分 | +| `web` | 三前端 `npm ci` + `tsc --noEmit` + `vitest` | ✅ | +| `desktop` | macOS runner,`GOWORK=off`,先出前端产物供 go:embed | ✅ | +| `py` | Python 3.11 pytest(含沙箱守卫测试) | ✅ | + +Go 1.25 / Node 20。所有 job 带 `if: !contains(github.server_url, 'sundynix.cn')` 避免内网 Gitea 误跑。 + +### 13.2 Gitea Actions(内网真实 CD) + +push main → 128 runner:**不用 `actions/checkout`**(内网连不上 github.com,改 `git init + fetch --depth 1`)→ 构建 4 镜像 → `docker save | gzip` scp 到 132 → `docker load` + `up -d --no-build` → **健康检查门**(循环 20 次 curl `/healthz`,失败打日志并 exit 1)。 + +132 上的 `.env` 手工预放、**部署绝不覆盖**。 + +> ⚠️ **CD 无任何测试门** —— 测试只在 GitHub 侧跑,内网推送直接部署。 + +### 13.3 Release + +tag `v*` → wails3 构建 macOS universal(lipo 合并)+ Windows amd64 → GitHub Release。旧版 App 启动查 `/releases/latest` 提示更新。 + +--- + +## 14. 技术栈总表 + +| 领域 | 选型 | +|---|---| +| 后端语言 | Go 1.25(4 模块 workspace)+ Python 3.11(算法工具) | +| Web 框架 | Gin v1.12.0 | +| 编排引擎 | **CloudWeGo Eino v0.9.12** + eino-ext/openai | +| 消息总线 | NATS 2 + JetStream(持久流 + KV) | +| 主库 | PostgreSQL 16 + GORM v1.31.1(雪花 id + 软删 + 租户插件) | +| 缓存 | Redis 7(+ 进程内降级) | +| 向量 | Milvus v2.4.13 | +| 全文 | Bleve v2.4.2(进程内,CJK bigram) | +| 图谱 | Neo4j 5-community | +| 对象存储 | MinIO(minio-go v7.2.0) | +| LLM | OpenAI 兼容协议(DeepSeek 在线 API) | +| 语音 | 火山引擎豆包 · 流式 ASR + 双向流 TTS v3 | +| 可观测 | Prometheus + OpenTelemetry v1.44 + Jaeger 1.60 | +| 桌面端 | Wails v3.0.0-alpha2.117(Go host + WKWebView) | +| 前端 | React 19 + TypeScript 5.6 + Vite 5 + Tailwind 3.4 + React Flow 12 | +| 前端测试 | Vitest 4 + jsdom + Testing Library | +| 支付 | 微信支付 APIv3(Native 扫码) | +| 密钥 | AES-256-GCM(`enc:1:` 版本化密文) | + +--- + +## 15. 已知缺口 + +按优先级归类,均为**明确未做**而非遗漏: + +**架构演进** +- 本地 agent 档 B(循环下沉客户端,内环不过网)+ 其地基 gateway LLM 代理端点 +- ToolPolicy 服务端策略下推 +- JARVIS 常驻 companion session(现靠 session history 串联) + +**治理补全** +- 用户 role 字段与角色表(`RequireAdmin` 仍是 env 白名单) +- HITL 审批决定理由落库 +- 审批 checkpoint 落盘失败重试(现只 log,任务会永卡 waiting) +- 预算硬顶兜底(budget ≤0 即无限) + +**生产硬化(Tier 3,等真实流量)** +- DB HA · K8s 编排 · TLS 终止 · DR 演练 · 备份自动化 +- panic 进 trace span · TTFT/token-s 细粒度指标 + +**产品功能** +- 报告后端原生 PDF(现走 webview 打印) +- 多模态文档解析去桩(MinerU/PaddleOCR) +- Prompt 灰度 A/B +- 前端评测质量面板(后端接口已有) +- 语音:唤醒词 / 声纹 / 音色克隆 / ASR-TTS failover + +**文档债** +- `architecture.md`(小写)是历史重复文件,内容早于本文,建议删除或改为指向本文的跳转 +- `PROGRESS.md` / `DEPTH_ROADMAP.md` 汇总表需按 §9.14 校正