Files
sundynix-agentix/ARCHITECTURE_DESIGN.md
Blizzard 5c50c87a88 docs: 重写架构文档——工程级完整版(5 张 Mermaid 图 + 功能状态总表)
原文停在 2026-07-02 已严重失真:能力矩阵还写「多租户/RBAC 未动、真实计费
未做、NATS 集群未做」,而这三项均已完成并 live 验证。它又被 5 份设计文档
引用为配套文档,不能放着不管。

重写为 15 章 913 行,以代码为单一事实源:
- 分层架构图 + 3 张关键链路时序图(任务提交/语音全双工/本地执行三道闸)
  + 三机部署拓扑图,全部 Mermaid(已用 mermaid-cli 逐张渲染验证)
- 5 个服务逐个拆:internal 包职责 + 技术栈依赖表含版本
- NATS 契约总表:1 任务流 + 3 回流 + 4 组工具 RPC + 5 条持久回写流 + KV
- 编排引擎:12 种节点 kind、ReAct、11 个平台工具、harness 五件套
- 29 张表全清单 + 9 个中间件 + 迁移机制
- 13 域功能实现状态总表(/🟡/)
- 安全治理/可观测/客户端三产品面/CI-CD/技术栈速查/已知缺口

顺带记账:§9.14 点名 4 处文档与代码不符(PROGRESS.md 停更、DEPTH_ROADMAP
汇总表自相矛盾、Tier3 说 NATS 集群没做但已落地、PAYMENT_DESIGN 说不做订阅
但已实现),明确以代码为准,避免后来人被误导。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 11:46:02 +08:00

914 lines
42 KiB
Markdown
Raw Permalink 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-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. 系统概述
用户在桌面端搭一张编排图(或直接说话),任务经网关落库并发到 NATS;调度集群把 DSL 编译成 Eino compose 图执行,过程中自主调用工具(检索/联网/计算/操作用户电脑),Token 与执行轨迹实时流回界面;全程带计量计费、护栏、评测与链路追踪。
### 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/ 官网 + 运维控制台(同一 SPAgo: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. 分层架构总览
```mermaid
flowchart TB
subgraph CLIENT["客户端层"]
DESK["sundynix-desktop<br/>Wails v3 + React 19<br/>编排画布 · JARVIS · 本地 runner"]
ADMIN["sundynix-admin<br/>官网 / + 控制台 /admin"]
WEB["sundynix-web<br/>租户自助柜台"]
end
subgraph GW["第 2 层 · gateway(唯一对外入口)"]
HTTP["HTTP API + SSEgin"]
WS["WebSocket<br/>语音 /voice/stream<br/>本地执行器 /local/runner"]
PLAT["平台工具提供方<br/>platform_* / local_*"]
BG["后台常驻<br/>配置控制面 · 持久消费者<br/>入库 worker 池 · 3 个 ticker"]
end
BUS{{"NATS + JetStream<br/>统一总线(8MB payload"}}
subgraph WORK["执行层"]
DISP["第 4 层 · dispatcher<br/>Eino compose 编排<br/>LLM 双池 + failover + 熔断<br/>harness 治理"]
MGO["第 5 层 · mcp-go<br/>RAG 三路 / 记忆 / 报告 / 图表"]
MPY["第 5 层 · mcp-py<br/>Docker 沙箱 / 文档解析"]
end
subgraph STORE["存储层"]
PG[("PostgreSQL 16<br/>29 张表")]
RDS[("Redis 7")]
MLV[("Milvus 2.4.13")]
BLV[("Bleve 全文")]
NEO[("Neo4j 5")]
OSS[("MinIO")]
end
LLM["LLM Provider<br/>OpenAI 兼容 · DeepSeek"]
VOLC["火山引擎<br/>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
```
### 2.1 分层职责
| 层 | 组件 | 职责 | 对外端口 |
|---|---|---|---|
| 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) |
**关键设计**dispatcher 与 mcp-\* 都**没有对外端口**,只通过 NATS 通信 —— 攻击面收敛到 gateway 一个进程。
---
## 3. 服务详解
### 3.1 gateway —— 业务网关 / 统一接入层
`github.com/sundynix/sundynix-gateway` · Go 1.25.8 · 入口 `cmd/server/main.go`
| internal 包 | 职责 |
|---|---|
| `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` 动态发现,加工具零改调度代码)
暴露给自主 agent10 个):`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,本地开发免装 |
---
## 4. NATS 总线契约
### 4.1 任务主链(JetStream 持久)
| 流 / Subject | 值 | 用途 |
|---|---|---|
| `SUNDYNIX_TASKS` | `sundynix.tasks.<id>` | 任务队列,消费者 `dispatchers`(队列组负载均衡) |
### 4.2 回流(core NATS,即时不持久)
| Subject | 用途 |
|---|---|
| `sundynix.streams.<id>` | Token 流,结束用消息头 `X-Stream-End: 1` |
| `sundynix.exec.<task_id>` | 结构化执行节点事件(seq/node/kind/phase/label/ms |
| `sundynix.voice.event.<user_id>` | JARVIS 语音事件(navigate / announce |
### 4.3 工具调用(request-reply + 队列组)
| Subject | 队列组 | 提供方 |
|---|---|---|
| `sundynix.tools.go.<tool>` | `mcp-go-workers` | mcp-go |
| `sundynix.tools.py.<tool>` | `mcp-py-workers` | mcp-py |
| `sundynix.tools.platform.<tool>` | `platform-tools-workers` | **gateway 自己** |
| `sundynix.local.exec.<user_id>` | — | 桌面端 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.<task_id>` | HITL 审批决定(抗离线) |
| `SUNDYNIX_INGEST` | `ingest-workers` | `sundynix.ingest.<job_id>` | 入库作业队列(claim-check:大文件先落 MinIO,消息只带 StageKey |
> ⚠️ 状态流 subject **必须在 `sundynix.tasks.>` 之外**,否则会被任务流捕获成「幽灵任务」自我放大。
**KV**`SUNDYNIX_CHECKPOINTS`TTL 24h)—— 键 `task_id` 存 compose checkpoint,键 `pending:task_id` 存 resume 记录。
### 4.5 控制面
`sundynix.config.<kind>.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-alpinemax_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 / evalJetStream 持久)
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: 流式 ASRX-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_exectimeout_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,创建自动填) | ✅ |
| 租户角色 RBACowner/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 / MRRhybrid 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 决定回传 + 抗离线) | ✅ |
| 多智能体 coordinatoragent-as-tool + 定制 brief + 并行 fan-out + 专家超时) | ✅ |
| Branch else/default 兜底 · DSL 拓扑校验 · 工具动态发现 | ✅ |
| handoff / adk 可中断多智能体 | ❌ |
| 编译图缓存 | ❌ 判定为 premature(实测编译 ~13µs |
| 审批 checkpoint 落盘失败重试(现只 log → 任务永卡 waiting | ❌ |
### 9.7 模型治理
| 功能 | 状态 |
|---|---|
| 模型路由 + Fallback + 每模型三态熔断 | ✅ |
| 模型健康/熔断态 surface 到 admin | ✅ |
| 输出缓存 | ✅ |
| Prompt 版本化 v1(注册表)+ v2DB 热切换 + 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 callbackquery/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<ViewKey>` |
| 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<br/>内嵌 admin UI"]
DP["dispatcher"]
MG["mcp-go"]
MP["mcp-py"]
end
subgraph M128["192.168.100.128 · 基建机 + CI runner"]
N1["NATS 三节点集群<br/>4222/4223/4224<br/>REPLICAS=3"]
PG2[("PostgreSQL")]
RD[("Redis")]
ML[("Milvus + etcd")]
NJ[("Neo4j")]
JG["Jaeger"]
end
M126[("192.168.100.126<br/>业务 MinIO<br/>bucket sundynix-docs")]
TX["162.14.122.200<br/>微信 token 中控<br/>静态 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**gatewayAPI + 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 universallipo 合并)+ Windows amd64 → GitHub Release。旧版 App 启动查 `/releases/latest` 提示更新。
---
## 14. 技术栈总表
| 领域 | 选型 |
|---|---|
| 后端语言 | Go 1.254 模块 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 |
| 对象存储 | MinIOminio-go v7.2.0 |
| LLM | OpenAI 兼容协议(DeepSeek 在线 API |
| 语音 | 火山引擎豆包 · 流式 ASR + 双向流 TTS v3 |
| 可观测 | Prometheus + OpenTelemetry v1.44 + Jaeger 1.60 |
| 桌面端 | Wails v3.0.0-alpha2.117Go host + WKWebView |
| 前端 | React 19 + TypeScript 5.6 + Vite 5 + Tailwind 3.4 + React Flow 12 |
| 前端测试 | Vitest 4 + jsdom + Testing Library |
| 支付 | 微信支付 APIv3Native 扫码) |
| 密钥 | 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 校正