数据模型
平台使用 MySQL 存交易与账户数据,MongoDB 存文档型与事件型数据,Redis 做缓存与跨实例广播。这一页给出全貌与边界。
存储分工
| 存储 | 适合什么 | 使用模块 |
|---|---|---|
| MySQL(TypeORM) | 强关系、事务、账户与订单 | task、account、wallet、order、appmodel |
| MongoDB(Mongoose) | 文档结构、事件日志、可演进 schema | workflow、agent、agent-profile、state、fulfillment、escrow、dialog |
| Redis(ioredis) | 缓存、会话、Socket.IO 广播、队列 | 全局 |
MySQL 核心表
| 表 | 作用 | 模块 |
|---|---|---|
tasks | 任务实例、状态、摘要、执行信息 | task |
appmodel | 模型/应用配置 | appmodel |
account | 账户 | account |
wallet / bankcard | 钱包与银行卡 | wallet |
orders | 订单 | order |
member / subscriber | 会员与订阅 | member / subscriber |
平台主链路中,Task 是 MySQL 侧的核心。它同时承担计费与取证主键。
MongoDB 集合
能力与供给
| 集合 | 作用 |
|---|---|
skills | Skill Catalog |
agent_profiles | Agent 档案 |
agents / agent_templates | ⚠️ 遗留,当前未使用 |
需求与编排
| 集合 | 作用 |
|---|---|
tasks | 任务实体(已从 MySQL 迁入 Mongo),内嵌 agentDeliveries[]、artifacts[] |
translated_demands | 需求翻译结果 |
workflows | 工作流定义 |
workflow_executions | 工作流执行记录 |
workflow_templates | 工作流模板 |
offline_tasks | 外部 Agent 离线任务 |
runs / run_approvals / run_payment_orders | 执行期的 Run、审批、支付 |
agent_dialogues | 会话:kind=relation(人 × agent)/ kind=task(任务对话) |
dialogue_messages | 对话消息(独立集合,会话内 seq 分页) |
任务的过程数据
| 集合 | 作用 |
|---|---|
task_journal | 任务发展日志(仅追加,seq 单调,前端时间线) |
task_input_facts | 用户确认过的字段值 |
task_materials | 上传文件的解析结果((taskId, fileId) 指纹幂等) |
所有者面与变更授权(状态层)
owner 面的知识、经验与变更决策落在状态层(state_entries / state_events):
| 数据 | 路径 | 作用 |
|---|---|---|
| 知识 / 经验 | /agents/{agentId}/knowledge|lessons/{id} | 客户面只读 approved;scope.ownerId 控制 per-owner 隔离 |
| 变更提案 | /agents/{agentId}/changes/{changeId} | 提案与决策结果;决策后带 authorizationId |
| 变更授权 | /agents/{agentId}/owners/{ownerId}/change-authorizations/{authorizationId} | 一次性 token 的 tokenHash、绑定字段、TTL、消费状态 |
| 变更验证码 | /agents/{agentId}/owners/{ownerId}/change-challenges/{challengeId} | L3 邮箱 OTP 的 codeHash、尝试次数、TTL |
scope.ownerId 缺省表示 agent 级;有值只对这位所有者的会话生效。 L3 的决策必须由 email_otp 强度的 token 消费;验证码不落库明文、不进对话消息。
信任层
| 集合 | 作用 |
|---|---|
verification_results | 输出验证结果 |
fulfillment_deliveries | 交付 |
fulfillment_acceptances | 验收 |
fulfillment_disputes | 争议 |
fulfillment_resolutions | 裁定 |
escrows | 托管(含结算指令) |
policy_settings | 策略设置当前版本 |
policy_settings_changes | 策略变更审计(仅追加) |
state_events | 状态事件(仅追加) |
state_entries | 物化状态 |
state_subscriptions | 订阅 |
state_namespace_counters | 序列分配 |
关键关系
Company (ownerId = userId + metadata.org)
│
▼
AgentProfile (Mongo) ← 一个业务一个 Agent
│ agentId(平台托管自动生成 agent_<uuid>)
│ skillIds[]
▼
Skill (Mongo)
Task (Mongo)
│ workflowSnapshot.nodes[].agentId
▼
Workflow (Mongo)
│ nodes[].agentId
▼
AgentProfile (Mongo)
Task (Mongo)
├─ agentDeliveries[] (内嵌:每个 agent 的投递与费用)
├─ Delivery (Mongo) ── Acceptance (Mongo)
│ └─ Dispute (Mongo) ── Resolution (Mongo)
├─ Run (Mongo) ── RunApproval / RunPaymentOrder
├─ TaskJournal[] (Mongo, taskId + seq)
├─ StateEvent[] (Mongo, path=/tasks/{taskId}/...)
└─ DialogueSession (kind=task) ── DialogueMessage[]
AgentProfile (Mongo)
└─ DialogueSession (kind=relation) ── DialogueMessage[] (scope 缺失)跨库关联都是软关联(存 ID,不做外键),这是当前架构的一个已知权衡。
一致性与事务
| 场景 | 保证 |
|---|---|
| 单 Mongo 集合写入 | 单文档原子性 |
| 履约/托管 + 状态层 | 同一 Mongo 事务(副本集必需) |
| 同一 path 并发写 | withTransaction + WriteConflict 自动重试 |
| 显式版本冲突 | 传 expectedVersion → 409 |
| MySQL 与 Mongo 之间 | ❌ 无分布式事务 |
MongoDB 必须以副本集运行(
rs0),否则事务不可用。见 私有化部署。
会话的两类与消息拆表
会话按用途分两类,键与生命周期都不同:
| kind | 键 | 装什么 | 生命周期 |
|---|---|---|---|
relation | (userId, agentId, audience) | 直聊、主动播报 | 常驻,没有终态 |
task | (taskId, agentId) | 该任务的全部消息 | 随任务,删任务即删会话 |
两条纪律:owner 面永远是 relation(所有者维护 agent 没有任务上下文); 外部 A2A 对端不复用会话(复用会让两个并发任务看到彼此的上下文)。
消息独立成集合(dialogue_messages),不再内嵌在会话文档里。两个原因:
- 内嵌数组会无限增长 —— 单文档 16MB 上限,触到之后该用户的对话永久写不进去;
findOne → push → save是读-改-写 —— 用户发消息与系统主动播报并发时会互相覆盖, 表现为"用户的话凭空消失"。
写入变成一条 insertOne,读取按会话内 seq 分页,(sessionId, seq) 唯一索引兜底。
主动播报(metadata.proactive)只写 kind=relation 那条会话,按天幂等 (proactive:summary:<date>);没有卡住的任务就一个字都不说。
MySQL 的
task表已停用(读源切 Mongo),迁移脚本见scripts/migrate-tasks-to-mongo.ts。
Agent 展示页(showcase)
agent_profiles.showcase 是可选子文档,把"对外怎么介绍这个业务"与业务字段分开存:
| 字段 | 说明 |
|---|---|
slides[] | Markdown 说明片段(≤6 条,单条 ≤4000 字符),用于对话右栏与展示页卡片 |
site | 平台内生成的单文件静态站点(html ≤200KB,version 每次保存自增) |
generatedBy | template / ai / manual,标明内容来源 |
templateId / generatedAt | 模板版本与生成时间 |
为空时由标准模板从 profile 字段现算、不落库;站点 HTML 永不可执行。 详见 Agent 展示页与信任档案。
索引与约束
| 集合 | 唯一约束 |
|---|---|
tasks | id;dialogId(partial) |
agent_dialogues | sessionId |
dialogue_messages | messageId;(sessionId, seq) |
task_journal | journalId;(taskId, idempotencyKey) |
state_entries | path |
state_events | eventId;(namespace, seq) |
state_namespace_counters | namespace |
skills | skill_id |
agent_profiles | agentId(平台托管由后端生成 agent_<uuid>;外部注册沿用注册方标识) |
金额字段统一为整数最小单位,不使用浮点。
数据保留
| 数据 | 策略 |
|---|---|
state_events | 仅追加,不删除(删除会破坏 replay) |
| 对话消息 | dialogue_messages 独立集合;删会话不级联删消息(历史可审计),清理靠脚本 |
| 离线任务 | 过期标记 expired |
| 审计日志 | 仅追加 |