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

42 KiB
Raw Permalink Blame History

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.mdEino 采纳)、SAAS_DESIGN.md / SAAS_P2_DESIGN.md(多租户与计量)、SPACE_DESIGN.md(共享工作区)、PAYMENT_DESIGN.md(支付)、VOICE_DESIGN.md(语音)、JARVIS_BRAIN_DESIGN.mdJARVIS 中枢)、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. 分层架构总览

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),多副本下只有一个实例真正扫。

辅助 CLIcmd/localsim(模拟桌面本地执行器)、cmd/voicesim(免麦端到端语音链路模拟)、cmd/voiceconfigcmd/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.13OpenAI 兼容协议)、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.2CJK 分词)、milvus-sdk-go/v2 v2.4.1neo4j-go-driver/v5 v5.24.0go-redis/v9gorm.io/gorm

⚠️ internal/office/unioffice.go 名字有误导性:并不引任何第三方 Office 库,而是用标准库 archive/zip + 内联 OOXML/WordprocessingML XML 手工拼 .docx

工具注册表internal/mcp/gateway.gobuildRegistry() 是唯一事实源,dispatcher 经 list_tools 动态发现,加工具零改调度代码)

暴露给自主 agent10 个):wiki_search(知识检索) · memory_getrecall_user_memory · memory_upsertremember_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 checkpointtrace.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.> 之外,否则会被任务流捕获成「幽灵任务」自我放大。

KVSUNDYNIX_CHECKPOINTSTTL 24h)—— 键 task_id 存 compose checkpoint,键 pending:task_id 存 resume 记录。

4.5 控制面

sundynix.config.<kind>.get / .updatedkind = 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|timeoutHITL 分支 waiting → running|rejected。 评测分级:ok(≥0.75) / warn(0.5~0.75) / poor(<0.5)。


5. 编排引擎

5.1 执行路径

Orchestrator.HandleexecuteGraphrunComposeGraphexecComposeGraphinternal/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 节点走 AddBranchapproval 节点在有 checkpoint 后端时编译为 approvalInterruptLambdacompose.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 默认 12REACT_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
  • 单次请求超时 120sLLM_FORCE_STUB=1 走降级桩(压测用)
  • 暴露 ModelHealthprovider/model/role/state/fails)供 admin 展示
  • 已知局限:Stream 仅在建流同步报错时切备,已开始回流 token 的中途失败不切
  • 输出缓存respCache TTL 60sLLM_CACHE_TTL_S0=关)、容量 512LLM_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 lockkey 2026072160s 超时兜底)内串行: 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 任务提交 → 编排 → 回流

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 全双工

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 操作用户电脑)

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/secretsAES-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.gotenantScopedMarker 空接口(小写方法,只有本包模型能标记)+ 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 审批、租户成员变更、充值兑换
  • 护栏事件:命中落 GuardrailEventactor/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 三机内网生产

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:3000gatewayAPI + admin UI + 语音 WS + 本地 runner WS),其余全部内网
  • NATS 三节点 cluster.name: sundynix-clusterroutes 互指 6222NATS_STREAM_REPLICAS=3 走 Raft quorum;三节点 max_payload: 8MB 必须一致
  • 128 上的 Redis/Milvus/NATS 无认证,靠防火墙只放行 132/126 网段
  • 132 只读挂载两个宿主目录:微信支付商户私钥、微信域名校验文件(不进镜像、不进 git)
  • 备份 scripts/backup.shPG/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 govulncheckadvisory 不阻断)+ gitleaks 扫提交历史(命中即失败) 部分
web 三前端 npm ci + tsc --noEmit + vitest
desktop macOS runnerGOWORK=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-GCMenc: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 校正