# 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 产品目标

将系统从当前形态：

```text
Desktop App + Admin Console + Cloud Agent Backend
```

升级为：

```text
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. 目标架构总览

```mermaid
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 重构

### 当前形态

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

### 目标形态

```text
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。

### 重构建议

目录可调整为：

```text
/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 推荐模块结构

```text
/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 请求，而是长生命周期对象。任务可能经历：

```text
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，可以用：

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

### 生产演进

后续可迁移到：

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

### 推荐状态机

```mermaid
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 --> [*]
```

### 核心表草案

```sql
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 领取任务执行：

```text
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。

### 推荐接口

```go
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 预留。

### 目标结构

```text
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 接口建议

```go
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 表

```sql
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()
);
```

### 路由策略示例

```text
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 重构

### 目标分层

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

### Tool Policy Engine 职责

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

```text
Dispatcher → Tool Policy Engine → Tool Service / Remote MCP
```

策略结果：

```text
allow
 deny
 require_approval
 quota_exceeded
 plan_required
 rate_limited
```

### Tool Policy 请求结构

```go
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 表

```sql
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

必须从桩逐步真实化：

- P0：Secure Code Sandbox 真实可用。
- P0：Code Interpreter 至少支持 Python 片段执行 + 超时 + 输出限制。
- P1：MinerU / 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 命名

```text
# 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 标准

```go
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 基础租户模型

```text
Tenant
  └── Workspace
        └── Project
              └── Task
                    └── TaskRun
```

### 5.3 推荐核心表

```sql
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 初版角色

```text
owner
admin
member
viewer
billing_admin
```

### 5.5 权限动作示例

```text
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 示例：

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

MinIO Path 示例：

```text
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 表

```sql
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 账单聚合表

```sql
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 计费阶段

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

---

## 7. 数据流重构

## 7.1 Task Run Flow

```mermaid
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

```mermaid
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

```mermaid
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

```mermaid
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 增加版本和执行策略

```json
{
  "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 增加风险和权限声明

```json
{
  "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. 目录结构重构建议

```text
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 Runtime，2–4 周

目标：让任务运行具备 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 2：Model Gateway 与 Usage Meter，2–4 周

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

任务：

- [ ] 从 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 3：Tool Policy 与 Remote MCP Gateway，2–5 周

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

任务：

- [ ] 新增 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 4：Web App 主产品化，3–6 周

目标：让 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 建议保留以下能力：

```text
必须做：
- 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 化升级：

```text
保留：
- 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 底座补齐：

```text
P0：tenant_id 全链路 + Task Runtime + Model Gateway + Usage Meter
P1：Tool Policy + Remote MCP + Web App 主产品化
P2：生产硬化 + Temporal/K8s/NATS Cluster
```

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