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

1509 lines
35 KiB
Markdown
Raw 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 · 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
必须从桩逐步真实化:
- 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 命名
```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:架构边界整理,12 周
目标:不大改功能,先把概念和 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 建议保留以下能力:
```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
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 平台。