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>
35 KiB
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 商业化,需要优先补齐以下核心能力:
- Client 层重构:从「桌面端主产品」调整为「Web App 主产品 + Desktop Connector 可选」。
- Task Runtime 独立化:把任务状态机、重试、取消、恢复、审批 checkpoint、幂等从 dispatcher/gateway 中抽象出来。
- Model Gateway 独立化:把 LLM Pool 升级为模型网关,承载模型路由、用量计量、成本核算、套餐权限、BYOK、fallback。
- 多租户/RBAC 前置:全链路补
tenant_id / workspace_id / project_id,避免后期大规模重构。 - Tool / MCP 分层:拆分 Internal Tools、Remote MCP Gateway、Tool Policy Engine。
- NATS Subject 规范化:把 Command、Event、Stream、Tool RPC、Control、Health 分区,避免“万物总线”失控。
- Billing Usage Meter 先落事件:不必马上接 Stripe/支付,但必须先把用量事件和成本账本做出来。
- 生产化迁移机制:逐步替换 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
必须从桩逐步真实化:
- 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 命名
# 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.mdshared/contract/envelope.goshared/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-runtimemigrations/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-gatewaysundynix_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-policysundynix_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 建议保留以下能力:
必须做:
- 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 底座补齐:
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 平台。