Files
sundynix-agentix/sundynix-agentix-saas-refactor-plan.md
T
Blizzard 55d50417a9 feat: 模型健康/熔断态 surface 到管理端(T4.F 可观测)
failover/熔断的运行时态原来只在 dispatcher 日志、admin 看不到 —— 本次接到概览可见:
- harness: CircuitBreaker.Snapshot() 只读观测访问器(state + fails,不动状态机)
- llm: Pool.ModelHealth() 上报主备链每模型 {provider,model,role,state,fails};
  buildWithFallbacks 把模型名↔breaker 配对(同包直接读 failoverModel.breakers);
  newFailoverModel 改返回具体类型以便读 breakers
- dispatcher 心跳 payload 加 models[]
- gateway /admin/overview 独立超时 Ping dispatcher,合并进 models.health
- admin 概览「模型路由」新增「运行时链路态(实时)」:逐模型状态点
  (🟢在线/🔴熔断中+失败数/🟡半开探测/单点)
- 单测:Snapshot、Pool.ModelHealth(名字↔态配对/单点/空)
- live:配坏主→提交任务打熔断→概览显示 broken-demo「熔断中·失败3」,备用在线

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 12:00:13 +08:00

35 KiB
Raw Blame History

sundynix-agentix · Web SaaS 架构重构实施文档

版本:v2.0-refactor
日期:2026-07-06
基于:ARCHITECTURE_DESIGN.md 当前完整版
目标:在保留现有事件驱动、Eino 编排、NATS 总线、MCP 工具化优势的基础上,将系统从「桌面端 + 云编排平台」升级为「可商业化的 Web SaaS Agent 平台」。


0. 重构结论

当前架构已经具备较强的 Agent 平台雏形:

  • Gateway 作为唯一 HTTP 接入层。
  • Dispatcher 使用 Eino 作为编排核心。
  • NATS + JetStream 承载任务、工具 RPC、token 流、控制面广播。
  • Redis Stream 支持 SSE 断点回放。
  • mcp-go / mcp-py 承载工具服务。
  • LLM Pool 支持 OpenAI-compatible Provider、failover、熔断、缓存。
  • Harness 层已经覆盖 guardrail、脱敏、预算、评测纠偏等能力。

但如果要做 Web SaaS 商业化,需要优先补齐以下核心能力:

  1. Client 层重构:从「桌面端主产品」调整为「Web App 主产品 + Desktop Connector 可选」。
  2. Task Runtime 独立化:把任务状态机、重试、取消、恢复、审批 checkpoint、幂等从 dispatcher/gateway 中抽象出来。
  3. Model Gateway 独立化:把 LLM Pool 升级为模型网关,承载模型路由、用量计量、成本核算、套餐权限、BYOK、fallback。
  4. 多租户/RBAC 前置:全链路补 tenant_id / workspace_id / project_id,避免后期大规模重构。
  5. Tool / MCP 分层:拆分 Internal Tools、Remote MCP Gateway、Tool Policy Engine。
  6. NATS Subject 规范化:把 Command、Event、Stream、Tool RPC、Control、Health 分区,避免“万物总线”失控。
  7. Billing Usage Meter 先落事件:不必马上接 Stripe/支付,但必须先把用量事件和成本账本做出来。
  8. 生产化迁移机制:逐步替换 GORM AutoMigrate,补充 migration、幂等、审计、数据隔离策略。

1. 重构目标

1.1 产品目标

将系统从当前形态:

Desktop App + Admin Console + Cloud Agent Backend

升级为:

Web SaaS Agent Platform
+ Optional Desktop / Local Connector
+ Admin Console

目标产品能力:

  • 用户可在 Web App 中创建工作区、项目、任务、Agent 图和知识库。
  • Agent 编排默认在服务端执行。
  • 远程 MCP、内部工具、模型调用、计费全部由服务端统一管控。
  • 桌面端不再是唯一用户入口,而是可选的本地能力扩展。
  • 后续可支持 Local Agent / Desktop Connector 处理本地文件、Shell、本地 MCP。

1.2 技术目标

  • 保留现有 Go + Eino + NATS + MCP 方向。
  • 降低 Gateway 和 Dispatcher 的职责耦合。
  • 增加 SaaS 必备的租户、计费、权限、审计、模型成本能力。
  • 让 Agent 任务支持暂停、恢复、取消、重试、审批、断点回放。
  • 让模型和工具调用都可计量、可审计、可限流、可降级。
  • 为后续微服务拆分、K8s、NATS 集群、DB HA 做边界准备。

2. 当前架构问题清单

2.1 Client 层定位偏桌面端

当前文档中用户工作产品是 Wails 桌面端,Web 管理端仅用于运维控制台。这个定位更适合桌面 Agent,而不是 Web SaaS。

风险:

  • SaaS 用户入口不清晰。
  • 本地文件能力和云端 Agent 能力边界混合。
  • 后续做团队协作、组织计费、浏览器任务面板会被桌面端假设限制。

重构方向:

  • Web App 成为主用户产品。
  • Admin Console 保留为管理端。
  • Desktop/Wails 改为 Optional Local Connector。

2.2 NATS 承载语义过多

当前 NATS 承载任务、工具 RPC、token 流、执行轨迹、控制面广播、心跳、持久队列。

风险:

  • Subject 命名膨胀。
  • Command/Event/Stream/Query 混用。
  • 任务状态散落在消息链路中。
  • 调试和权限治理变复杂。

重构方向:

  • 保留 NATS 作为事件和工具总线。
  • 明确 Command Bus、Event Bus、Stream Bus、Tool RPC、Control Plane、Health 的命名规范。
  • 引入 Task Runtime 作为任务生命周期事实源。

2.3 Task Runtime 不够独立

当前任务生命周期分散在 Gateway、NATS、Dispatcher、Redis Stream、DB FSM、approval checkpoint 中。

风险:

  • 取消、暂停、恢复、审批重试、Worker 崩溃恢复逻辑分散。
  • 很难统一做任务幂等和任务审计。
  • 后续引入 Temporal 或多 Worker 会迁移困难。

重构方向:

  • 增加 Task Runtime Layer。
  • MVP 阶段用 PostgreSQL FSM + NATS/JetStream。
  • 生产阶段可演进到 Temporal。

2.4 LLM Pool 应升级为 Model Gateway

当前 LLM Pool 已经具备 failover、缓存、热配置、breaker,但仍偏模型池实现,而不是 SaaS 计费核心。

风险:

  • 模型能力、价格、用户套餐、token 用量、成本账本分散。
  • 不利于接入多 Provider、BYOK、企业模型网关。
  • 不利于真实计费。

重构方向:

  • 独立 Model Gateway / Model Runtime 概念。
  • 统一 Provider Adapter、Usage Meter、Cost Calculator、Routing Policy、Capability Registry。

2.5 多租户和 RBAC 尚未进入核心数据模型

当前文档明确是 owner_id 单租户假设,多租户/RBAC 尚未启动。

风险:

  • 表结构、Redis key、NATS envelope、MinIO object path、Milvus/Neo4j namespace 后期都要重构。
  • 计费、审计、权限无法天然支持组织/团队。

重构方向:

  • 立即在核心 Contract 和数据表中引入 tenant_id
  • 同步引入 workspace_id / project_id / user_id / role / permission

2.6 Tool / MCP 层边界需要重拆

当前 mcp-go 同时承载 RAG、记忆、历史、报告、元工具;mcp-py 承载算法工具,但部分为桩。

风险:

  • 内部工具和远程 MCP 混合。
  • Tool 权限、审计、套餐限制没有独立中枢。
  • Python 算法层如果长期为桩,会影响产品真实可用性。

重构方向:

  • 拆成 Internal Tool Services、Remote MCP Gateway、Tool Policy Engine。
  • Python 工具必须从“链路已通”走向“至少一条真实可用核心能力”。

3. 目标架构总览

flowchart TB

subgraph CLIENT["1. Client Layer"]
  WEB["User Web App\nNext.js/React + React Flow\nTask Console / DSL Designer / Artifact Viewer"]
  ADMIN["Admin Console\nModel / Billing / Tenant / Audit / Ops"]
  LOCAL["Optional Local Connector\nWails/Tauri\nLocal FS / Shell / Local MCP"]
end

subgraph GATEWAY["2. Gateway Layer"]
  GW["Go Gateway\nAuth / RateLimit / Guardrail Tier1\nTask API / SSE / Admin API"]
end

subgraph TASK["3. Task Runtime Layer"]
  FSM["Task FSM\nsubmitted/running/waiting/done/failed"]
  APPROVAL["Approval Checkpoint"]
  IDEMP["Idempotency / Retry / Cancel / Resume"]
  EVENTLOG["Task Event Log"]
end

subgraph BUS["4. Event Bus Layer"]
  NATS["NATS + JetStream"]
  REDISSTREAM["Redis Stream\nSSE Replay"]
end

subgraph DISPATCHER["5. Agent Dispatcher Layer"]
  EINO["Eino Compose Engine"]
  PLANNER["Planner / Router / Context Manager"]
  TOOLRUN["Tool Executor"]
  HARNESS["Harness\nGuardrail Tier2 / Eval / Budget / Desensitize"]
end

subgraph MODEL["6. Model Gateway Layer"]
  ROUTER["Routing Policy"]
  ADAPTER["Provider Adapters\nOpenAI-compatible / vLLM / Ollama / Future Claude"]
  USAGE["Usage Meter / Cost Calculator"]
  BREAKER["Failover / Breaker / Cache"]
end

subgraph TOOL["7. Tool & MCP Layer"]
  POLICY["Tool Policy Engine\nAllow/Deny/Approval/Quota/Audit"]
  GOTOOLS["Internal Go Tools\nRAG / Memory / History / Report / External API"]
  PYTOOLS["Internal Python Tools\nSandbox / Parser / OCR / Code Interpreter"]
  REMOTEMCP["Remote MCP Gateway\nOAuth / Registry / Remote Servers"]
end

subgraph DATA["8. Data Layer"]
  PG["PostgreSQL"]
  REDIS["Redis"]
  MINIO["MinIO"]
  MILVUS["Milvus"]
  NEO4J["Neo4j"]
  JAEGER["Jaeger / OTel"]
end

WEB --> GW
ADMIN --> GW
LOCAL --> GW
GW --> FSM
FSM --> NATS
NATS --> EINO
EINO --> ROUTER
EINO --> POLICY
POLICY --> GOTOOLS
POLICY --> PYTOOLS
POLICY --> REMOTEMCP
ROUTER --> ADAPTER
ADAPTER --> USAGE
USAGE --> PG
EINO --> NATS
NATS --> REDISSTREAM
REDISSTREAM --> GW
GW --> WEB
GW --> ADMIN
FSM --> PG
EVENTLOG --> PG
GOTOOLS --> MILVUS
GOTOOLS --> NEO4J
GOTOOLS --> MINIO
PYTOOLS --> MINIO
GW --> REDIS

4. 分层重构设计

4.1 Client Layer 重构

当前形态

Desktop Wails = 用户工作产品
Admin React = 运维控制台

目标形态

User Web App = 主产品
Admin Console = 运维/商业后台
Optional Local Connector = 本地能力扩展

User Web App 职责

  • 工作区 / 项目 / 任务管理。
  • Agent DSL 画布编排。
  • 任务运行控制台。
  • Plan 审核、HITL 审批。
  • Token stream / exec event 实时展示。
  • Artifact / Report / Knowledge Base 预览。
  • 团队成员和权限入口。
  • 套餐、用量、账单入口。

Admin Console 职责

  • 模型配置。
  • Provider 配置。
  • 价格配置。
  • 租户管理。
  • 用户管理。
  • 审计日志。
  • Guardrail 事件。
  • 模型健康和熔断状态。
  • MCP 工具注册与状态。
  • 系统运行 overview。

Optional Local Connector 职责

  • 本地文件系统访问。
  • 本地 Shell 执行。
  • 本地 Git 操作。
  • 本地 MCP Server 连接。
  • 本地另存为、系统通知、应用打开。
  • 将本地能力以受控 Tool 形式暴露给服务端或本地 Agent。

重构建议

目录可调整为:

/apps
  /web                 # 用户 Web App
  /admin               # 管理后台
  /desktop-connector   # 可选本地连接器,原 Wails 逐步迁移

4.2 Gateway Layer 重构

Gateway 应保留职责

  • HTTP API 入口。
  • Auth / JWT / Session。
  • Rate Limit。
  • CORS。
  • RequestID / OTel / Audit middleware。
  • Guardrail Tier1。
  • DSL Schema Validation。
  • Task API。
  • SSE/WebSocket Fanout。
  • Admin API。
  • Report download/export endpoint。

Gateway 不应承担职责

  • 不做 Agent 编排。
  • 不做复杂 DSL execution plan assembly。
  • 不做工具路由。
  • 不做模型路由。
  • 不直接访问内部工具实现。

Gateway 推荐模块结构

/services/gateway
  /cmd
  /internal
    /api
      /task
      /workspace
      /project
      /billing
      /admin
      /sse
    /middleware
      auth.go
      ratelimit.go
      guardrail.go
      audit.go
      observe.go
    /validator
      dsl_schema.go
    /client
      task_runtime_client.go
      event_stream_client.go

4.3 Task Runtime Layer 新增

为什么要新增

Agent SaaS 的任务不是普通 HTTP 请求,而是长生命周期对象。任务可能经历:

submitted → queued → running → waiting_approval → running → done
submitted → queued → running → failed → retrying → running → done
submitted → queued → running → cancelled
submitted → queued → running → timeout

这些状态不应该散落在 Gateway、Dispatcher 和 NATS 消息中。

Task Runtime 职责

  • Task FSM 状态机。
  • TaskRun 实例管理。
  • StepRun 状态管理。
  • Approval checkpoint。
  • Retry / Cancel / Resume / Timeout。
  • 幂等键管理。
  • Worker lease / heartbeat。
  • Event Log 落库。
  • Task 级并发控制。
  • Tenant 级并发控制。

MVP 实现

MVP 不必马上引入 Temporal,可以用:

PostgreSQL tasks/task_runs/task_events
+ NATS JetStream durable consumer
+ Dispatcher worker heartbeat
+ Redis lock 可选

生产演进

后续可迁移到:

Temporal Workflow
+ Go Activity Worker
+ Eino Agent Activity
+ NATS Event Fanout

推荐状态机

stateDiagram-v2
  [*] --> submitted
  submitted --> queued
  queued --> running
  running --> waiting_approval
  waiting_approval --> running: approved
  waiting_approval --> rejected: rejected
  running --> done
  running --> failed
  running --> timeout
  running --> cancelling
  cancelling --> cancelled
  failed --> retrying
  retrying --> queued
  done --> [*]
  rejected --> [*]
  cancelled --> [*]
  timeout --> [*]

核心表草案

CREATE TABLE sundynix_tasks (
  id UUID PRIMARY KEY,
  tenant_id UUID NOT NULL,
  workspace_id UUID NOT NULL,
  project_id UUID,
  owner_user_id UUID NOT NULL,
  title TEXT NOT NULL,
  status TEXT NOT NULL,
  dsl JSONB NOT NULL,
  current_run_id UUID,
  idempotency_key TEXT,
  created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
  updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
  deleted_at TIMESTAMPTZ
);

CREATE TABLE sundynix_task_runs (
  id UUID PRIMARY KEY,
  tenant_id UUID NOT NULL,
  task_id UUID NOT NULL,
  status TEXT NOT NULL,
  attempt INT NOT NULL DEFAULT 1,
  started_at TIMESTAMPTZ,
  finished_at TIMESTAMPTZ,
  error_code TEXT,
  error_message TEXT,
  worker_id TEXT,
  heartbeat_at TIMESTAMPTZ,
  created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE sundynix_task_events (
  id UUID PRIMARY KEY,
  tenant_id UUID NOT NULL,
  task_id UUID NOT NULL,
  run_id UUID,
  event_type TEXT NOT NULL,
  event_payload JSONB NOT NULL,
  trace_id TEXT,
  created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE sundynix_approval_checkpoints (
  id UUID PRIMARY KEY,
  tenant_id UUID NOT NULL,
  task_id UUID NOT NULL,
  run_id UUID NOT NULL,
  node_id TEXT NOT NULL,
  status TEXT NOT NULL,
  request_payload JSONB NOT NULL,
  response_payload JSONB,
  requested_by UUID,
  approved_by UUID,
  created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
  resolved_at TIMESTAMPTZ
);

4.4 Agent Dispatcher Layer 重构

保留能力

当前 dispatcher 的这些能力建议保留:

  • Eino compose 单编排引擎。
  • DSL → compose 图编译。
  • 分支、并行、map、coordinator、approval。
  • ReAct 工具调用。
  • Token stream 和 exec event。
  • Harness 治理层。

需要调整的边界

Dispatcher 不应该直接承担完整任务生命周期,而应该从 Task Runtime 领取任务执行:

Task Runtime 发布 RunCommand
→ Dispatcher 执行 Agent Graph
→ Dispatcher 发布 TaskEvent / StreamEvent
→ Task Runtime 更新状态

Dispatcher 推荐职责

  • 编译 DSL 为 Eino compose graph。
  • 执行 graph。
  • 管理上下文。
  • 调用 Model Gateway。
  • 调用 Tool Policy Engine。
  • 产出 token stream / exec events。
  • 产出节点级执行结果。
  • 产出评测结果。

Dispatcher 不应职责

  • 不直接做计费结算。
  • 不直接管理租户权限。
  • 不直接存储模型密钥。
  • 不直接暴露 HTTP API。

推荐接口

type AgentExecutor interface {
    Execute(ctx context.Context, req ExecuteRequest) (<-chan AgentEvent, error)
    Cancel(ctx context.Context, runID string) error
    Resume(ctx context.Context, req ResumeRequest) error
}

type ExecuteRequest struct {
    TenantID    string
    WorkspaceID string
    ProjectID   string
    TaskID      string
    RunID       string
    UserID      string
    DSL         DSLGraph
    Input       map[string]any
    Policy      ExecutionPolicy
}

4.5 Model Gateway Layer 新增

为什么独立

模型调用是 SaaS 成本、体验和商业化的核心,不应该只是 dispatcher 内部的 llm/pool.go

Model Gateway 要统一处理:

  • Provider Adapter。
  • 模型能力注册。
  • 模型路由。
  • 套餐可用模型。
  • BYOK。
  • Usage metering。
  • Cost calculation。
  • Failover。
  • Circuit breaker。
  • Cache。
  • Structured output 适配。
  • Tool calling 适配。
  • Prompt caching 预留。

目标结构

Model Gateway
├── Provider Registry
├── Capability Registry
├── Routing Policy
├── Provider Adapter
├── Usage Meter
├── Cost Calculator
├── Failover Manager
├── Breaker Manager
├── Cache Layer
└── Admin Runtime State

Provider Adapter 接口建议

type ModelProvider interface {
    Name() string
    Chat(ctx context.Context, req ChatRequest) (*ChatResponse, error)
    Stream(ctx context.Context, req ChatRequest) (<-chan ChatStreamEvent, error)
    CountTokens(ctx context.Context, req TokenRequest) (*TokenUsage, error)
    Capabilities(ctx context.Context, model string) ModelCapabilities
}

type ModelCapabilities struct {
    SupportsTools       bool
    SupportsVision      bool
    SupportsJSONMode    bool
    SupportsReasoning   bool
    SupportsEmbeddings  bool
    MaxContextTokens    int
    MaxOutputTokens     int
}

Usage Event 表

CREATE TABLE sundynix_model_usage_events (
  id UUID PRIMARY KEY,
  tenant_id UUID NOT NULL,
  user_id UUID NOT NULL,
  task_id UUID,
  run_id UUID,
  provider TEXT NOT NULL,
  model TEXT NOT NULL,
  input_tokens BIGINT NOT NULL DEFAULT 0,
  output_tokens BIGINT NOT NULL DEFAULT 0,
  cached_tokens BIGINT NOT NULL DEFAULT 0,
  cost_usd NUMERIC(18,8) NOT NULL DEFAULT 0,
  latency_ms BIGINT,
  status TEXT NOT NULL,
  error_code TEXT,
  created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

路由策略示例

route_policy:
  tenant_plan: pro
  task_type: report_generation
  preferred_models:
    - deepseek-chat
    - qwen-plus
  fallback_models:
    - local-vllm-qwen
  max_cost_usd: 0.20
  max_latency_ms: 30000
  require_tool_calling: true

4.6 Tool / MCP Layer 重构

目标分层

Tool & MCP Layer
├── Tool Policy Engine
├── Internal Go Tools
├── Internal Python Tools
└── Remote MCP Gateway

Tool Policy Engine 职责

每一次工具调用前,都必须经过 Tool Policy Engine

Dispatcher → Tool Policy Engine → Tool Service / Remote MCP

策略结果:

allow
 deny
 require_approval
 quota_exceeded
 plan_required
 rate_limited

Tool Policy 请求结构

type ToolPolicyRequest struct {
    TenantID    string
    WorkspaceID string
    ProjectID   string
    UserID      string
    TaskID      string
    RunID       string
    ToolName    string
    ToolType    string // internal_go, internal_py, remote_mcp, local_connector
    Arguments   map[string]any
    RiskLevel   string // low, medium, high, critical
}

Tool Audit 表

CREATE TABLE sundynix_tool_call_events (
  id UUID PRIMARY KEY,
  tenant_id UUID NOT NULL,
  user_id UUID NOT NULL,
  task_id UUID,
  run_id UUID,
  tool_name TEXT NOT NULL,
  tool_type TEXT NOT NULL,
  risk_level TEXT,
  policy_decision TEXT NOT NULL,
  input_hash TEXT,
  output_hash TEXT,
  latency_ms BIGINT,
  status TEXT NOT NULL,
  error_code TEXT,
  created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

Internal Go Tools

适合继续由 Go 承载:

  • RAG search。
  • Wiki search。
  • Memory。
  • History。
  • Report render/export。
  • External API connectors。
  • Metadata tools。
  • Health/list_tools。

Internal Python Tools

必须从桩逐步真实化:

  • P0Secure Code Sandbox 真实可用。
  • P0Code Interpreter 至少支持 Python 片段执行 + 超时 + 输出限制。
  • P1MinerU / OCR 文档解析真实可用。
  • P1:多模态解析任务异步化。

Remote MCP Gateway

Remote MCP Gateway 单独负责:

  • MCP Server Registry。
  • OAuth token vault。
  • Tool discovery。
  • Tool allowlist / denylist。
  • Per-tenant tool policy。
  • Per-plan tool availability。
  • Remote tool audit。
  • Remote tool quota。

4.7 NATS Subject 规范化

当前问题

当前 NATS 是统一总线,但 subject 分类需要更严格,否则后期难以运维。

推荐 Subject 命名

# Command
sundynix.cmd.task.submit
sundynix.cmd.task.cancel
sundynix.cmd.task.resume
sundynix.cmd.task.approve
sundynix.cmd.task.reject

# Event
sundynix.events.task.created
sundynix.events.task.queued
sundynix.events.task.started
sundynix.events.task.waiting_approval
sundynix.events.task.completed
sundynix.events.task.failed
sundynix.events.task.cancelled

# Stream
sundynix.streams.task.<task_id>.token
sundynix.streams.task.<task_id>.exec
sundynix.streams.kb.<ingest_id>.progress

# Tool RPC
sundynix.tools.go.<tool_name>
sundynix.tools.py.<tool_name>
sundynix.tools.remote_mcp.<server_id>.<tool_name>

# Control Plane
sundynix.control.model.updated
sundynix.control.prompt.activated
sundynix.control.tool.registry_updated
sundynix.control.policy.updated

# Health
sundynix.health.gateway
sundynix.health.dispatcher
sundynix.health.mcp_go
sundynix.health.mcp_py
sundynix.health.model_gateway

Envelope 标准

type Envelope[T any] struct {
    ID          string    `json:"id"`
    Type        string    `json:"type"`
    Version     string    `json:"version"`
    TenantID    string    `json:"tenant_id"`
    WorkspaceID string    `json:"workspace_id,omitempty"`
    ProjectID   string    `json:"project_id,omitempty"`
    UserID      string    `json:"user_id,omitempty"`
    TaskID      string    `json:"task_id,omitempty"`
    RunID       string    `json:"run_id,omitempty"`
    TraceID     string    `json:"trace_id,omitempty"`
    CreatedAt   time.Time `json:"created_at"`
    Payload     T         `json:"payload"`
}

使用原则

  • Command:表示“请求系统执行某动作”。
  • Event:表示“某事实已经发生”。
  • Stream:表示“高频、可回放、面向前端展示的事件流”。
  • Tool RPC:表示“内部工具请求-响应”。
  • Control:表示“运行时配置变更广播”。
  • Health:表示“探活和状态查询”。

5. 多租户与 RBAC 重构

5.1 为什么必须前置

Web SaaS 的最小商业单位不是 user,而是 tenant / organization。

如果后期再补多租户,会影响:

  • PostgreSQL 表结构。
  • Redis key。
  • NATS envelope。
  • MinIO object path。
  • Milvus collection/partition。
  • Neo4j node/edge namespace。
  • MCP token。
  • 模型 Key。
  • 审计日志。
  • 计费账本。

5.2 基础租户模型

Tenant
  └── Workspace
        └── Project
              └── Task
                    └── TaskRun

5.3 推荐核心表

CREATE TABLE sundynix_tenants (
  id UUID PRIMARY KEY,
  name TEXT NOT NULL,
  slug TEXT UNIQUE NOT NULL,
  plan TEXT NOT NULL DEFAULT 'free',
  status TEXT NOT NULL DEFAULT 'active',
  created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE sundynix_tenant_members (
  id UUID PRIMARY KEY,
  tenant_id UUID NOT NULL,
  user_id UUID NOT NULL,
  role TEXT NOT NULL,
  status TEXT NOT NULL DEFAULT 'active',
  created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
  UNIQUE(tenant_id, user_id)
);

CREATE TABLE sundynix_workspaces (
  id UUID PRIMARY KEY,
  tenant_id UUID NOT NULL,
  name TEXT NOT NULL,
  created_by UUID NOT NULL,
  created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE sundynix_projects (
  id UUID PRIMARY KEY,
  tenant_id UUID NOT NULL,
  workspace_id UUID NOT NULL,
  name TEXT NOT NULL,
  description TEXT,
  created_by UUID NOT NULL,
  created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

5.4 RBAC 初版角色

owner
admin
member
viewer
billing_admin

5.5 权限动作示例

tenant.manage
billing.manage
workspace.create
project.create
task.create
task.run
task.cancel
task.approve
model.use
model.manage
tool.use
tool.manage
kb.read
kb.write
audit.read

5.6 数据隔离规则

  • 所有核心表必须有 tenant_id
  • 所有查询必须默认带 tenant_id
  • 所有 Redis key 带 tenant 前缀。
  • 所有 NATS envelope 带 tenant_id
  • 所有 MinIO path 带 tenant_id/workspace_id/project_id
  • Milvus 推荐使用 tenant partition 或 metadata filter。
  • Neo4j 节点和关系必须带 tenant_id

Redis Key 示例:

sundynix:{tenant_id}:task:{task_id}:stream
sundynix:{tenant_id}:rate:{user_id}
sundynix:{tenant_id}:session:{session_id}

MinIO Path 示例:

tenants/{tenant_id}/workspaces/{workspace_id}/projects/{project_id}/artifacts/{artifact_id}

6. Billing 与 Usage Meter 重构

6.1 原则

第一阶段不一定要接支付,但必须先做 Usage Meter。

真实计费依赖以下用量:

  • 模型 input/output/cached token。
  • 模型成本。
  • 工具调用次数。
  • 远程 MCP 调用次数。
  • Code Sandbox 执行时长。
  • 文档解析页数。
  • 向量入库 token/条数。
  • 存储空间。
  • 任务运行时长。

6.2 Billing Event 表

CREATE TABLE sundynix_usage_events (
  id UUID PRIMARY KEY,
  tenant_id UUID NOT NULL,
  user_id UUID,
  task_id UUID,
  run_id UUID,
  event_type TEXT NOT NULL,
  resource_type TEXT NOT NULL,
  resource_name TEXT,
  quantity NUMERIC(18,6) NOT NULL,
  unit TEXT NOT NULL,
  unit_price NUMERIC(18,8),
  cost NUMERIC(18,8),
  currency TEXT DEFAULT 'USD',
  metadata JSONB,
  created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

6.3 账单聚合表

CREATE TABLE sundynix_usage_daily_rollups (
  id UUID PRIMARY KEY,
  tenant_id UUID NOT NULL,
  date DATE NOT NULL,
  resource_type TEXT NOT NULL,
  quantity NUMERIC(18,6) NOT NULL,
  cost NUMERIC(18,8) NOT NULL,
  created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
  UNIQUE(tenant_id, date, resource_type)
);

6.4 计费阶段

阶段 1:仅记录 usage_events。
阶段 2:按天 rollup,后台展示成本。
阶段 3:套餐 quota + 超额限制。
阶段 4:接支付系统。
阶段 5:企业账单、发票、成本中心。

7. 数据流重构

7.1 Task Run Flow

sequenceDiagram
  participant U as Web App
  participant G as Gateway
  participant T as Task Runtime
  participant N as NATS
  participant D as Dispatcher
  participant M as Model Gateway
  participant P as Tool Policy
  participant Tool as Tool/MCP
  participant R as Redis Stream

  U->>G: POST /tasks with DSL
  G->>G: Auth + RateLimit + Guardrail Tier1 + DSL Schema Validation
  G->>T: Create Task
  T->>T: Create TaskRun + FSM=submitted/queued
  T->>N: Publish cmd.task.run
  D->>N: Subscribe cmd.task.run
  D->>D: Compile DSL to Eino Graph
  D->>M: Stream Chat / Tool Calling
  D->>P: Check Tool Policy
  P->>Tool: Allow and Call Tool
  D->>N: Publish task events/token stream
  N->>R: Persist stream events
  G->>R: Read Redis Stream
  G->>U: SSE token/exec events
  D->>T: Report final status
  T->>T: FSM=done/failed/timeout

7.2 Approval Flow

sequenceDiagram
  participant D as Dispatcher
  participant T as Task Runtime
  participant R as Redis Stream
  participant G as Gateway
  participant U as Web App

  D->>T: Create Approval Checkpoint
  T->>T: FSM=waiting_approval
  T->>R: Event approval.required
  G->>R: SSE read
  G->>U: Show approval card
  U->>G: POST /tasks/:id/approvals/:approval_id/approve
  G->>T: Approve checkpoint
  T->>T: FSM=queued/running
  T->>D: Resume run command

7.3 Model Usage Flow

sequenceDiagram
  participant D as Dispatcher
  participant M as Model Gateway
  participant P as Provider
  participant B as Usage Meter
  participant DB as PostgreSQL

  D->>M: Chat/Stream request
  M->>M: Route model + check plan + budget
  M->>P: Provider API
  P-->>M: Stream/Response + usage
  M->>B: Emit model_usage_event
  B->>DB: Store usage/cost
  M-->>D: Return response

7.4 Tool Call Flow

sequenceDiagram
  participant D as Dispatcher
  participant P as Tool Policy Engine
  participant A as Approval Runtime
  participant Tool as Internal/Remote Tool
  participant Audit as Tool Audit

  D->>P: tool_call_request
  P->>P: Check tenant/plan/risk/quota
  alt allow
    P->>Tool: Call tool
    Tool-->>P: Tool result
    P->>Audit: Write allow event
    P-->>D: result
  else require approval
    P->>A: Create approval checkpoint
    P-->>D: wait approval
  else deny
    P->>Audit: Write deny event
    P-->>D: policy denied
  end

8. DSL 重构建议

8.1 DSL 增加版本和执行策略

{
  "version": "2.0",
  "kind": "agent_graph",
  "metadata": {
    "name": "Research Report Agent",
    "description": "Generate report from KB and web tools"
  },
  "execution_policy": {
    "timeout_seconds": 1800,
    "max_retries": 2,
    "max_cost_usd": 0.5,
    "require_approval_for_high_risk_tools": true
  },
  "nodes": [],
  "edges": []
}

8.2 Node 增加风险和权限声明

{
  "id": "tool_1",
  "kind": "tool",
  "label": "Search Wiki",
  "config": {
    "tool_name": "kb_search",
    "risk_level": "low",
    "requires_approval": false,
    "timeout_seconds": 30
  }
}

8.3 DSL 校验分层

Gateway 只做:

  • JSON schema 校验。
  • 节点 ID 重复。
  • 悬挂边。
  • 节点数量限制。
  • 边数量限制。
  • 不允许的节点 kind。

Dispatcher 做:

  • 编译为 Eino graph。
  • execution plan assembly。
  • branch default 检查。
  • map 并发策略。
  • tool availability 检查。
  • model capability 检查。

9. 目录结构重构建议

sundynix-agentix/
  apps/
    web/
    admin/
    desktop-connector/

  services/
    gateway/
    task-runtime/
    dispatcher/
    model-gateway/
    tool-policy/
    mcp-go/
    mcp-py/

  shared/
    contract/
      envelope.go
      task.go
      dsl.go
      model.go
      tool.go
      billing.go
      tenant.go
    bus/
      nats.go
      subjects.go
      jetstream.go
    auth/
    otel/
    secrets/
    prompts/
    errors/

  deployments/
    docker-compose/
    k8s/

  migrations/
    postgres/

  docs/
    architecture/
    api/
    dsl/
    operations/

MVP 阶段不一定物理拆成所有服务,但代码目录应按目标边界组织。


10. 分阶段实施计划

Phase 0:架构边界整理,1–2 周

目标:不大改功能,先把概念和 contract 对齐。

任务:

  • 在架构文档中新增 Task Runtime Layer。
  • 在架构文档中新增 Model Gateway Layer。
  • 在架构文档中拆分 Internal Tools / Remote MCP Gateway / Tool Policy Engine。
  • 统一 NATS Subject 命名规范。
  • 定义 Envelope v1。
  • 核心 contract 增加 tenant_id/workspace_id/project_id 字段。
  • 明确 Web App、Admin Console、Desktop Connector 三类客户端边界。

交付物:

  • docs/architecture/ARCHITECTURE_DESIGN_V2.md
  • shared/contract/envelope.go
  • shared/bus/subjects.go

Phase 1:多租户骨架与 Task Runtime24 周

目标:让任务运行具备 SaaS 数据隔离和统一状态机。

任务:

  • 新增 tenant/workspace/project 表。
  • 核心任务表补 tenant_id/workspace_id/project_id
  • Redis key 加 tenant namespace。
  • NATS envelope 强制带 tenant_id。
  • 新增 task_runs/task_events/approval_checkpoints。
  • Gateway 创建任务改为写 Task Runtime。
  • Dispatcher 从 Task Runtime command 中领取任务。
  • Task 状态变更统一落 task_events。
  • SSE 从 Redis Stream + task_events 恢复历史。

交付物:

  • services/task-runtime
  • migrations/postgres/001_tenant_task_runtime.sql
  • 任务运行状态机测试。

Phase 2Model Gateway 与 Usage Meter24 周

目标:模型调用从“能用”升级为“可商业化”。

任务:

  • 从 dispatcher 中抽出 model runtime。
  • 定义 ModelProvider 接口。
  • 实现 OpenAI-compatible Provider。
  • 实现 vLLM/Ollama Provider。
  • 增加 model_capabilities 表。
  • 增加 model_usage_events 表。
  • 增加价格配置表。
  • 增加 route policy。
  • Admin 展示模型健康、熔断状态、成本。
  • 每次模型调用写 usage event。

交付物:

  • services/model-gateway
  • sundynix_model_usage_events
  • Admin Model Runtime 页面。

Phase 3Tool Policy 与 Remote MCP Gateway25 周

目标:工具调用可管控、可审批、可审计、可计费。

任务:

  • 新增 Tool Policy Engine。
  • 每次工具调用前经过 policy check。
  • 高风险工具支持 require_approval。
  • 工具调用写 tool_call_events
  • mcp-go 工具注册增加 risk_level、required_plan、permissions。
  • mcp-py 至少真实化一个核心工具。
  • Remote MCP Gateway 独立出 registry、OAuth、audit。
  • Admin 增加 Tool Registry / Tool Audit 页面。

交付物:

  • services/tool-policy
  • sundynix_tool_call_events
  • Tool risk policy 文档。

Phase 4Web App 主产品化,36 周

目标:让 Web App 成为主产品入口。

任务:

  • 新建或迁移 User Web App。
  • React Flow DSL Designer Web 化。
  • Task Run Console。
  • Artifact Viewer。
  • Approval UI。
  • Usage / Billing 页面。
  • Workspace / Project / Team 页面。
  • Desktop Connector 改为可选本地能力。

交付物:

  • /apps/web
  • 用户任务闭环:创建 → 执行 → 审批 → 查看结果 → 查看用量。

Phase 5:生产硬化,持续

目标:上线准备。

任务:

  • AutoMigrate 替换为 migration 工具。
  • NATS 集群。
  • PostgreSQL 备份/恢复。
  • Redis HA。
  • MinIO bucket policy。
  • TLS。
  • Secret rotation。
  • OTel 指标完善。
  • Prometheus/Grafana。
  • Admin 熔断态和失败原因可视化。
  • 数据删除和租户注销流程。

11. MVP 收敛范围

为了避免重构过度,MVP 建议保留以下能力:

必须做:
- Web App 任务入口
- Gateway
- Task Runtime DB FSM
- Dispatcher + Eino
- Model Gateway 基础版
- OpenAI-compatible Provider
- NATS + Redis Stream
- mcp-go 基础工具
- tenant_id 全链路
- usage_events 记录
- SSE 任务流

暂缓:
- Temporal
- NATS 集群
- 完整 K8s
- 完整计费支付
- 完整 mcp-py 算法集群
- 完整多模型市场
- 完整 prompt 灰度实验
- 完整企业审计

12. 风险与规避

风险 影响 规避
继续以桌面端为主产品 Web SaaS 定位不清 Web App 主产品化,Desktop Connector 可选
NATS 语义过载 后期难维护 subject 分区 + envelope 标准
任务状态散落 取消/恢复/审批困难 Task Runtime 独立
模型调用不可计量 无法商业化 Model Gateway + usage_events
没有 tenant_id SaaS 后期重构巨大 立即补全 tenant/workspace/project
工具调用无策略 安全和套餐不可控 Tool Policy Engine
mcp-py 长期为桩 产品能力虚 P0 真实化 sandbox 或 parser
AutoMigrate 上生产 数据迁移不可控 goose/atlas/migrate
Guardrail 只在入口 工具风险失控 Tool-call 前置 policy + output guard

13. 最终建议

这次重构不是推翻当前架构,而是做一次 SaaS 化升级:

保留:
- Go 主后端
- Gateway 接入层
- Eino Dispatcher
- NATS + JetStream
- Redis Stream 回放
- mcp-go/mcp-py 工具化
- Harness 治理层
- 控制面热切换

新增/强化:
- User Web App 主产品
- Task Runtime
- Model Gateway
- Tenant/RBAC
- Usage Meter
- Tool Policy Engine
- Remote MCP Gateway
- NATS Subject 规范
- Migration 生产化

优先级最高的不是继续堆 Agent 能力,而是先把 SaaS 底座补齐:

P0tenant_id 全链路 + Task Runtime + Model Gateway + Usage Meter
P1Tool Policy + Remote MCP + Web App 主产品化
P2:生产硬化 + Temporal/K8s/NATS Cluster

完成这次重构后,sundynix-agentix 将从一个能力很强的内部/桌面混合 Agent 平台,升级为一个具备商业化基础的 Web SaaS Agent 平台。