Files
group_fqcd_jr/docs/02-数据库建表设计.md
T

928 lines
43 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.
# 数据库建表设计
> 版本:v3.1
> 修订日期:2026-09-09
> 演进原则:只允许新增表和新增字段,不修改已有表名、已有字段名、字段类型及既有业务含义
## 1. 文档目的
本文定义通用 Agent 平台、记忆系统和智能客服专项能力的数据库建设方案。数据库业务基线已固化为项目内的《00-新数据库基线设计.md》,本文只在该基线上补充字段、表、实施顺序和迁移规则。
## 2. 数据库范围
- 数据库:MySQL 8.0。
- 默认字符集:`utf8mb4`。
- 默认排序规则:`utf8mb4_0900_ai_ci`。
- 基础业务表:33 张。
- 智能客服专项表:6 张。
- 平台底座增量表:12 张(原设计 10 张、outbox_delivery、api_request_receipt)。
- 建成后总表数:51 张(包含会话补表及公共 HTTP 写操作幂等回执)。
- Redis、Milvus 和 Neo4j 不计入 MySQL 表数量。
当前交易范围是场内基金模拟交易。数据库不建设充值、提现、银行流水匹配或场外基金申购赎回交易表。
### 2.1 不可变基线规则
新版《00-新数据库基线设计.md》中的表名和已有字段是不可变基线。后续迁移只能创建新表、在已有表中增加新字段,或为新增字段增加索引和约束。禁止重命名表,禁止删除、重命名或复用已有字段,禁止改变已有字段的类型、可空性和既有业务含义。历史问题无法兼容解决时,新增语义明确的字段或新表,并由应用执行双读、回填和切换。
## 3. 通用建表约定
数据库实体和迁移属于 MVC+S 的 Model 层。Controller 不访问 ORM;Service 通过 Repository 读写 Model。Alembic 迁移是表结构变更的唯一入口,运行时业务代码不执行 `CREATE TABLE` 或 `ALTER TABLE`。
| 项目 | 约定 |
|---|---|
| 主键 | `BIGINT UNSIGNED AUTO_INCREMENT` |
| 业务编号 | 单独字段并建立唯一索引 |
| 时间 | 使用 `DATETIME(6)`,统一以 `_at` 结尾 |
| 金额 | `DECIMAL(18,2)` |
| 价格 | `DECIMAL(18,6)` |
| 数量 | `DECIMAL(18,4)` |
| 置信度 | `DECIMAL(5,4)`,范围 0 至 1 |
| JSON | 使用 MySQL 原生 `JSON`,禁止用逗号字符串代替数组 |
| 删除 | 交易、预警、审计、对话和通知不物理删除 |
| 审计 | 关键状态变化追加写入 `interaction_audit` |
| 外键 | MySQL 核心实体使用外键;Milvus/Neo4j 关系由应用校验 |
| 时间基准 | 应用和数据库统一使用 UTC,展示层转换时区 |
## 4. 51 张表总览
### 4.1 基础业务表
| 域 | 表 | 主要职责 | Agent 访问边界 |
|---|---|---|---|
| 身份 | `sys_user` | 客户与员工统一身份 | 按字段策略读取 |
| 权限 | `sys_role` | 角色定义 | 只读 |
| 权限 | `sys_permission` | 资源、动作、数据范围 | 只读 |
| 权限 | `sys_user_role` | 用户角色关系 | 只读 |
| 权限 | `sys_role_permission` | 角色权限关系 | 只读 |
| 归属 | `sys_customer_assignment` | 客户与投顾/运营归属 | 只读 |
| 客户 | `fin_customer_profile` | 客户金融画像 | 只读 |
| 客户 | `fin_risk_assessment` | 正式风险测评历史 | 只读、权威事实 |
| 软事实 | `user_facts` | 现有代码兼容软事实 | 只读 |
| 产品 | `fin_product` | 场内产品及适当性信息 | 只读 |
| 行情 | `fin_market_price` | 场内日线行情 | 只读 |
| 净值 | `fin_nav_history` | 基金净值兼容数据 | 只读 |
| 账户 | `fin_sim_account` | 虚拟资金账户 | 只读 |
| 资金 | `fin_cash_ledger` | 虚拟资金明细 | 只读 |
| 资金 | `fin_capital_flow` | 风控兼容标准化流水 | 只读 |
| 费用 | `fin_fee_rule` | 模拟交易费率 | 只读 |
| 委托 | `fin_sim_order` | 场内模拟委托 | 交易服务写,Agent 只读 |
| 成交 | `fin_transaction` | 模拟成交 | 交易服务写,Agent 只读 |
| 持仓 | `fin_holding` | 当前持仓汇总 | 交易服务写,Agent 只读 |
| 登录 | `sys_login_record` | 逐次登录结果 | 只读 |
| 工单 | `biz_work_order` | 风险调查和处置工单 | 风控流程读写 |
| 风控 | `fin_risk_alert` | 规则命中与人工处置 | 风控服务读写 |
| 通知 | `fin_risk_notification` | 站内和邮件通知 | 风控服务读写 |
| 会话 | `conversation_message` | 完整对话留存 | 公共基础层写 |
| 知识 | `fin_knowledge_meta` | 知识元数据及正文镜像 | 知识服务写、Agent 只读 |
| Episode | `episodes` | 会话片段摘要 | 记忆服务写 |
| 记忆 | `memory_unit` | 中期记忆单元 | 记忆服务写 |
| 证据 | `memory_evidence` | 记忆来源证据 | 记忆服务写 |
| 冲突 | `memory_conflict` | 新旧记忆冲突 | 记忆服务写 |
| 画像 | `profile_snapshots` | 长期画像版本投影 | 画像服务写 |
| 同步 | `memory_sync_outbox` | Milvus/Neo4j 同步事件 | 记忆服务写、消费者更新状态 |
| 审计 | `interaction_audit` | 全系统追加审计 | 公共基础层追加写 |
| 内容 | `client_facing_content` | 对客内容审核发布 | 投顾流程读写 |
### 4.2 智能客服专项表
| 表 | 优先级 | 主要职责 |
|---|---|---|
| `agent_negative_word` | P0 | 禁止表达与变体规则配置 |
| `svc_handover_ticket` | P0 | 客服转人工工单 |
| `agent_faq_synonym` | P1 | FAQ 同义问法和口语表达 |
| `conversation_feedback` | P1 | 用户对回复的反馈 |
| `agent_reply_template` | Phase 2 | 兜底、拒答、免责声明等模板 |
| `agent_intent_config` | Phase 2 | 意图、阈值、路由和工具配置 |
### 4.3 平台底座增量表
| 表 | 主要职责 | 写入方 |
|---|---|---|
| `svc_conversation_session` | 会话状态、澄清轮次和配置快照 | 会话服务 |
| `outbox_delivery` | 已有 Outbox 消费幂等记录 | Outbox Worker |
| `api_request_receipt` | 公共 HTTP 写操作幂等回执 | ApiTransactionService |
api_request_receipt 是独立新增表,不复用只用于 Agent 运行的 request_idempotency。
其字段为 id BIGINT UNSIGNED 自增主键、user_id BIGINT UNSIGNED 非空、scope_hash CHAR(64)
非空、idempotency_key VARCHAR(128) 非空、request_hash CHAR(64) 非空、response_json JSON
可空、created_at DATETIME(6) 非空默认 CURRENT_TIMESTAMP(6),唯一键
(user_id, scope_hash, idempotency_key)。操作结果、业务状态和审计在同一事务完成;失败整体回滚。
迁移 20260909_api_receipt 仅 CREATE TABLE;既有 49 表字段指纹比对未变化,会话表保持 §8.1。
| `domain_event_outbox` | 通用领域事件可靠投递 | 事务内事件发布器 |
| `request_idempotency` | 请求占位、结果定位和并发防重 | AgentService |
| `agent_run` | 可恢复 Agent 运行状态、租约和结果定位 | AgentRunService/Worker |
| `config_release` | 配置发布、审核、激活和回滚批次 | 配置中心 |
| `platform_config_item` | 记忆、关系及运行参数的版本化配置项 | 配置中心 |
| `model_endpoint_config` | 模型端点能力、限制和密钥引用 | 模型平台管理员 |
| `model_routing_rule` | 按 Agent 和任务类型选择模型及备用链 | 模型路由服务 |
| `model_routing_fallback` | 路由规则的有序备用端点及可重试错误 | 模型路由服务 |
| `prompt_template_version` | 分类、生成、摘要和提取 Prompt 版本 | Prompt 管理服务 |
## 5. 核心关系
```text
sys_user 1 --- 1 fin_customer_profile
sys_user 1 --- N fin_risk_assessment
sys_user 1 --- N conversation_message
sys_user 1 --- N svc_handover_ticket
sys_user 1 --- N svc_conversation_session
conversation_message N --- 1 session_id(逻辑会话)
conversation_message 1 --- N conversation_feedback
fin_knowledge_meta 1 --- N agent_faq_synonym
fin_knowledge_meta -- 正文和向量镜像 --> Milvus 知识集合
episodes 1 --- N memory_unit
memory_unit 1 --- N memory_evidence
memory_unit N --- N memory_unit(通过 memory_conflict)
memory_unit/profile_snapshots 1 --- N memory_sync_outbox
svc_conversation_session 1 --- N conversation_message
svc_conversation_session 1 --- N request_idempotency
svc_conversation_session 1 --- N agent_run
request_idempotency 1 --- 1 agent_run
conversation_message 1 --- N agent_run(用户消息)
conversation_message 1 --- N agent_run(助手结果)
conversation_message/interaction_audit 1 --- N domain_event_outbox
config_release 1 --- N platform_config_item
config_release 1 --- N model_routing_rule
config_release 1 --- N prompt_template_version
model_endpoint_config 1 --- N model_routing_rule
model_routing_rule 1 --- N model_routing_fallback
model_endpoint_config 1 --- N model_routing_fallback
fin_risk_alert 1 --- N biz_work_order
fin_risk_alert 1 --- N fin_risk_notification
svc_handover_ticket 与 biz_work_order 相互独立
```
## 6. 现有表字段增补
### 6.1 conversation_message
新版基线已经包含 `tool_calls JSON`,保持其名称和定义不变。本次只新增以下三个字段:
| 字段 | 类型 | 约束 | 用途 |
|---|---|---|---|
| `intent` | VARCHAR(32) | 可空、索引 | 助手消息的意图结果 |
| `confidence` | DECIMAL(5,4) | 可空、索引、0 至 1 | 意图置信度 |
| `source_references` | JSON | 可空 | RAG 来源集合与文档 ID |
用户原始消息的三个新增字段可以为空;助手最终消息记录实际采用的意图、置信度和来源。既有 `tool_calls` 字段继续保存脱敏后的工具调用;未使用知识或工具时对应 JSON 字段使用 `NULL`。
迁移示例:
```sql
ALTER TABLE conversation_message
ADD COLUMN intent VARCHAR(32) NULL COMMENT '识别的意图' AFTER trace_id,
ADD COLUMN confidence DECIMAL(5,4) NULL COMMENT '意图置信度' AFTER intent,
ADD COLUMN source_references JSON NULL COMMENT '检索来源' AFTER confidence,
ADD CONSTRAINT chk_conversation_confidence
CHECK (confidence IS NULL OR confidence BETWEEN 0 AND 1),
ADD INDEX idx_conversation_intent (intent),
ADD INDEX idx_conversation_confidence (confidence);
```
### 6.2 fin_knowledge_meta
最终结构以新版表定义为准,必须包含 `content_text`、`tags`、`effective_date`、`expire_date`、`version`、`reviewer_id` 和 `review_status`。
实际迁移前使用 Alembic Inspector 检查现库。若 `version`、`effective_date`、`expire_date` 已存在,只新增以下四个缺失字段:
```sql
ALTER TABLE fin_knowledge_meta
ADD COLUMN content_text MEDIUMTEXT NULL COMMENT 'Milvus知识正文MySQL镜像,保持可空兼容旧数据',
ADD COLUMN tags JSON NULL COMMENT '知识标签数组',
ADD COLUMN reviewer_id BIGINT UNSIGNED NULL COMMENT '审校人ID',
ADD COLUMN review_status VARCHAR(16) NOT NULL DEFAULT 'pending'
COMMENT 'pending/approved/published/archived',
ADD CONSTRAINT fk_knowledge_reviewer
FOREIGN KEY (reviewer_id) REFERENCES sys_user(id),
ADD INDEX idx_knowledge_review_status (review_status),
ADD INDEX idx_knowledge_reviewer (reviewer_id);
```
`content_text` 保持新增时的可空定义,后续不得更改已有字段约束。应用通过发布校验保证 `published` 知识必须存在正文。
有效知识的统一过滤条件为:
```sql
review_status = 'published'
AND status = 'active'
AND (effective_date IS NULL OR effective_date <= UTC_DATE())
AND (expire_date IS NULL OR expire_date > UTC_DATE())
```
## 7. 智能客服专项表 DDL
以下 DDL 面向全新数据库。若应用于已有数据库,必须通过 Alembic 生成版本迁移并在测试库演练。
### 7.1 agent_negative_word
禁止表达不写死在业务 Agent 中。规则变更必须经过审核并保留版本。
```sql
CREATE TABLE agent_negative_word (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
rule_code VARCHAR(64) NOT NULL,
word_pattern VARCHAR(256) NOT NULL,
match_type VARCHAR(16) NOT NULL DEFAULT 'contains',
category VARCHAR(32) NOT NULL,
severity VARCHAR(16) NOT NULL DEFAULT 'block',
applicable_agents JSON NULL,
safe_reply_template_code VARCHAR(64) NULL,
status VARCHAR(16) NOT NULL DEFAULT 'draft',
version INT UNSIGNED NOT NULL DEFAULT 1,
created_by BIGINT UNSIGNED NOT NULL,
reviewer_id BIGINT UNSIGNED NULL,
reviewed_at DATETIME(6) NULL,
created_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
updated_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6)
ON UPDATE CURRENT_TIMESTAMP(6),
PRIMARY KEY (id),
UNIQUE KEY uk_negative_rule_code (rule_code),
KEY idx_negative_status_category (status, category),
KEY idx_negative_reviewer (reviewer_id),
CONSTRAINT fk_negative_created_by FOREIGN KEY (created_by) REFERENCES sys_user(id),
CONSTRAINT fk_negative_reviewer FOREIGN KEY (reviewer_id) REFERENCES sys_user(id),
CONSTRAINT chk_negative_match_type
CHECK (match_type IN ('exact', 'contains', 'regex')),
CONSTRAINT chk_negative_severity
CHECK (severity IN ('warn', 'regenerate', 'block')),
CONSTRAINT chk_negative_status
CHECK (status IN ('draft', 'active', 'disabled', 'archived'))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='Agent禁止表达配置';
```
### 7.2 svc_handover_ticket
本表只表示客服会话向人工坐席的转接,不替代风险预警处置工单 `biz_work_order`。
```sql
CREATE TABLE svc_handover_ticket (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
ticket_no VARCHAR(32) NOT NULL,
session_id VARCHAR(64) NOT NULL,
customer_id BIGINT UNSIGNED NULL,
source_agent VARCHAR(32) NOT NULL DEFAULT 'customer_service',
source_message_id BIGINT UNSIGNED NULL,
intent VARCHAR(32) NULL,
confidence DECIMAL(5,4) NULL,
priority VARCHAR(8) NOT NULL DEFAULT 'P1',
reason_code VARCHAR(64) NOT NULL,
reason_detail VARCHAR(500) NULL,
conversation_summary MEDIUMTEXT NULL,
source_references JSON NULL,
status VARCHAR(16) NOT NULL DEFAULT 'pending',
assigned_to BIGINT UNSIGNED NULL,
assigned_at DATETIME(6) NULL,
accepted_at DATETIME(6) NULL,
resolved_at DATETIME(6) NULL,
closed_at DATETIME(6) NULL,
resolution TEXT NULL,
created_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
updated_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6)
ON UPDATE CURRENT_TIMESTAMP(6),
PRIMARY KEY (id),
UNIQUE KEY uk_handoff_ticket_no (ticket_no),
KEY idx_handoff_session (session_id, created_at),
KEY idx_handoff_customer (customer_id, created_at),
KEY idx_handoff_queue (status, priority, created_at),
KEY idx_handoff_assignee (assigned_to, status),
CONSTRAINT fk_handoff_customer FOREIGN KEY (customer_id) REFERENCES sys_user(id),
CONSTRAINT fk_handoff_source_message
FOREIGN KEY (source_message_id) REFERENCES conversation_message(id),
CONSTRAINT fk_handoff_assignee FOREIGN KEY (assigned_to) REFERENCES sys_user(id),
CONSTRAINT chk_handoff_confidence
CHECK (confidence IS NULL OR confidence BETWEEN 0 AND 1),
CONSTRAINT chk_handoff_priority CHECK (priority IN ('P0', 'P1', 'P2')),
CONSTRAINT chk_handoff_status
CHECK (status IN ('pending', 'assigned', 'processing', 'resolved', 'closed', 'cancelled'))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='客服转人工工单';
```
状态流转:
```text
pending -> assigned -> processing -> resolved -> closed
| | |
`----------+-------------+-> cancelled
```
`cancelled` 只允许在未解决状态进入。每次分配、接单、解决、关闭或取消都要写入 `interaction_audit`。
### 7.3 agent_faq_synonym
```sql
CREATE TABLE agent_faq_synonym (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
knowledge_id BIGINT UNSIGNED NOT NULL,
phrase VARCHAR(500) NOT NULL,
normalized_phrase VARCHAR(500) NOT NULL,
phrase_hash CHAR(64) NOT NULL,
language_code VARCHAR(16) NOT NULL DEFAULT 'zh-CN',
source_type VARCHAR(24) NOT NULL DEFAULT 'manual',
hit_count BIGINT UNSIGNED NOT NULL DEFAULT 0,
status VARCHAR(16) NOT NULL DEFAULT 'pending',
created_by BIGINT UNSIGNED NOT NULL,
reviewer_id BIGINT UNSIGNED NULL,
reviewed_at DATETIME(6) NULL,
created_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
updated_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6)
ON UPDATE CURRENT_TIMESTAMP(6),
PRIMARY KEY (id),
UNIQUE KEY uk_faq_synonym (knowledge_id, phrase_hash),
KEY idx_faq_synonym_status (status),
KEY idx_faq_synonym_normalized (normalized_phrase(191)),
CONSTRAINT fk_synonym_knowledge FOREIGN KEY (knowledge_id) REFERENCES fin_knowledge_meta(id),
CONSTRAINT fk_synonym_created_by FOREIGN KEY (created_by) REFERENCES sys_user(id),
CONSTRAINT fk_synonym_reviewer FOREIGN KEY (reviewer_id) REFERENCES sys_user(id),
CONSTRAINT chk_synonym_source
CHECK (source_type IN ('manual', 'conversation', 'ticket', 'import')),
CONSTRAINT chk_synonym_status
CHECK (status IN ('pending', 'approved', 'disabled', 'archived'))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='FAQ同义问法';
```
只有 `approved` 的同义问法参与意图识别和检索扩展。会话自动提取内容必须先进入 `pending`。
`phrase_hash` 由应用对完整 `normalized_phrase` 计算 SHA-256,防止仅使用索引前缀时不同长文本被误判为重复。
### 7.4 conversation_feedback
```sql
CREATE TABLE conversation_feedback (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
feedback_no VARCHAR(32) NOT NULL,
session_id VARCHAR(64) NOT NULL,
message_id BIGINT UNSIGNED NOT NULL,
customer_id BIGINT UNSIGNED NULL,
rating TINYINT NOT NULL,
feedback_type VARCHAR(32) NULL,
feedback_content VARCHAR(1000) NULL,
status VARCHAR(16) NOT NULL DEFAULT 'open',
handler_id BIGINT UNSIGNED NULL,
handled_at DATETIME(6) NULL,
handling_note VARCHAR(1000) NULL,
created_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
updated_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6)
ON UPDATE CURRENT_TIMESTAMP(6),
PRIMARY KEY (id),
UNIQUE KEY uk_feedback_no (feedback_no),
UNIQUE KEY uk_feedback_message_customer (message_id, customer_id),
KEY idx_feedback_session (session_id, created_at),
KEY idx_feedback_status_type (status, feedback_type),
CONSTRAINT fk_feedback_message FOREIGN KEY (message_id) REFERENCES conversation_message(id),
CONSTRAINT fk_feedback_customer FOREIGN KEY (customer_id) REFERENCES sys_user(id),
CONSTRAINT fk_feedback_handler FOREIGN KEY (handler_id) REFERENCES sys_user(id),
CONSTRAINT chk_feedback_rating CHECK (rating IN (-1, 1)),
CONSTRAINT chk_feedback_status CHECK (status IN ('open', 'reviewed', 'resolved', 'ignored'))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='对话回复反馈';
```
匿名或未登录反馈的 `customer_id` 可以为空,但必须由 API 限流并保存安全审计信息,防止重复刷票。
### 7.5 agent_reply_template
```sql
CREATE TABLE agent_reply_template (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
template_code VARCHAR(64) NOT NULL,
scene VARCHAR(32) NOT NULL,
title VARCHAR(128) NOT NULL,
content_text MEDIUMTEXT NOT NULL,
variables JSON NULL,
locale VARCHAR(16) NOT NULL DEFAULT 'zh-CN',
version INT UNSIGNED NOT NULL DEFAULT 1,
status VARCHAR(16) NOT NULL DEFAULT 'draft',
active_key VARCHAR(128) GENERATED ALWAYS AS
(IF(status = 'active', CONCAT(template_code, ':', locale), NULL)) STORED,
effective_at DATETIME(6) NULL,
expire_at DATETIME(6) NULL,
created_by BIGINT UNSIGNED NOT NULL,
reviewer_id BIGINT UNSIGNED NULL,
reviewed_at DATETIME(6) NULL,
created_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
updated_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6)
ON UPDATE CURRENT_TIMESTAMP(6),
PRIMARY KEY (id),
UNIQUE KEY uk_reply_template_version (template_code, version),
UNIQUE KEY uk_reply_template_active_one (active_key),
KEY idx_reply_template_active (scene, status, effective_at, expire_at),
CONSTRAINT fk_template_created_by FOREIGN KEY (created_by) REFERENCES sys_user(id),
CONSTRAINT fk_template_reviewer FOREIGN KEY (reviewer_id) REFERENCES sys_user(id),
CONSTRAINT chk_template_scene
CHECK (scene IN ('disclaimer', 'low_confidence', 'compliance_block',
'transfer', 'model_failure', 'system_busy', 'clarification')),
CONSTRAINT chk_template_status
CHECK (status IN ('draft', 'approved', 'active', 'archived')),
CONSTRAINT chk_template_window
CHECK (expire_at IS NULL OR effective_at IS NULL OR expire_at > effective_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='Agent回复模板';
```
相同 `template_code + locale` 可以有多个历史版本,但数据库通过 `active_key` 唯一索引保证同一组合最多一个 `active` 版本。发布服务仍须在事务中完成状态切换并写入审计记录。
### 7.6 agent_intent_config
```sql
CREATE TABLE agent_intent_config (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
agent_type VARCHAR(32) NOT NULL,
intent_code VARCHAR(64) NOT NULL,
intent_name VARCHAR(128) NOT NULL,
description VARCHAR(500) NULL,
examples JSON NOT NULL,
classifier_instruction TEXT NULL,
confidence_threshold DECIMAL(5,4) NOT NULL DEFAULT 0.6000,
max_clarification_rounds TINYINT UNSIGNED NOT NULL DEFAULT 2,
transfer_on_failure TINYINT(1) NOT NULL DEFAULT 1,
collection_routes JSON NULL,
allowed_tools JSON NULL,
priority INT NOT NULL DEFAULT 100,
version INT UNSIGNED NOT NULL DEFAULT 1,
status VARCHAR(16) NOT NULL DEFAULT 'draft',
active_key VARCHAR(128) GENERATED ALWAYS AS
(IF(status = 'active', CONCAT(agent_type, ':', intent_code), NULL)) STORED,
effective_at DATETIME(6) NULL,
expire_at DATETIME(6) NULL,
created_by BIGINT UNSIGNED NOT NULL,
reviewer_id BIGINT UNSIGNED NULL,
reviewed_at DATETIME(6) NULL,
created_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
updated_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6)
ON UPDATE CURRENT_TIMESTAMP(6),
PRIMARY KEY (id),
UNIQUE KEY uk_intent_config_version (agent_type, intent_code, version),
UNIQUE KEY uk_intent_config_active_one (active_key),
KEY idx_intent_config_active (agent_type, status, priority),
CONSTRAINT fk_intent_created_by FOREIGN KEY (created_by) REFERENCES sys_user(id),
CONSTRAINT fk_intent_reviewer FOREIGN KEY (reviewer_id) REFERENCES sys_user(id),
CONSTRAINT chk_intent_confidence
CHECK (confidence_threshold BETWEEN 0 AND 1),
CONSTRAINT chk_intent_status
CHECK (status IN ('draft', 'approved', 'active', 'archived')),
CONSTRAINT chk_intent_window
CHECK (expire_at IS NULL OR effective_at IS NULL OR expire_at > effective_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='Agent意图路由配置';
```
客服初始意图至少包含:`faq`、`product_inquiry`、`policy_explain`、`chitchat` 和 `transfer_human`。
## 8. 平台底座增量表 DDL
本节所有对象均为新增表,不替换、不重命名原有表。
### 8.1 svc_conversation_session
```sql
CREATE TABLE svc_conversation_session (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
session_id VARCHAR(64) NOT NULL,
user_id BIGINT UNSIGNED NOT NULL,
portal VARCHAR(32) NOT NULL,
agent_type VARCHAR(32) NULL,
status VARCHAR(16) NOT NULL DEFAULT 'active',
clarification_round TINYINT UNSIGNED NOT NULL DEFAULT 0,
message_count INT UNSIGNED NOT NULL DEFAULT 0,
last_intent VARCHAR(32) NULL,
config_version VARCHAR(64) NULL,
started_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
last_active_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
ended_at DATETIME(6) NULL,
created_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
updated_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6)
ON UPDATE CURRENT_TIMESTAMP(6),
PRIMARY KEY (id),
UNIQUE KEY uk_session_id (session_id),
KEY idx_session_user_active (user_id, last_active_at),
KEY idx_session_status_active (status, last_active_at),
CONSTRAINT fk_session_user FOREIGN KEY (user_id) REFERENCES sys_user(id),
CONSTRAINT chk_session_status
CHECK (status IN ('active', 'ended', 'transferred', 'expired')),
CONSTRAINT chk_session_clarification CHECK (clarification_round <= 10)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='Agent会话状态与澄清轮次';
```
澄清次数使用带旧值条件的原子更新;Redis 只缓存该记录,MySQL 是权威来源。会话结束、转人工和过期必须写明确状态。
### 8.2 domain_event_outbox
```sql
CREATE TABLE domain_event_outbox (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
event_id CHAR(36) NOT NULL,
event_type VARCHAR(64) NOT NULL,
aggregate_type VARCHAR(32) NOT NULL,
aggregate_id VARCHAR(64) NOT NULL,
trace_id VARCHAR(64) NOT NULL,
payload JSON NOT NULL,
status VARCHAR(16) NOT NULL DEFAULT 'pending',
retry_count INT UNSIGNED NOT NULL DEFAULT 0,
next_retry_at DATETIME(6) NULL,
last_error VARCHAR(500) NULL,
occurred_at DATETIME(6) NOT NULL,
published_at DATETIME(6) NULL,
created_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
updated_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6)
ON UPDATE CURRENT_TIMESTAMP(6),
PRIMARY KEY (id),
UNIQUE KEY uk_domain_event_id (event_id),
KEY idx_domain_outbox_pending (status, next_retry_at, id),
KEY idx_domain_outbox_aggregate (aggregate_type, aggregate_id),
KEY idx_domain_outbox_trace (trace_id),
CONSTRAINT chk_domain_outbox_status
CHECK (status IN ('pending', 'published', 'failed', 'dead'))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='通用领域事件Outbox';
```
### 8.3 request_idempotency
```sql
CREATE TABLE request_idempotency (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
user_id BIGINT UNSIGNED NOT NULL,
session_id VARCHAR(64) NOT NULL,
agent_type VARCHAR(32) NOT NULL,
idempotency_key VARCHAR(64) NOT NULL,
request_hash CHAR(64) NOT NULL,
trace_id VARCHAR(64) NOT NULL,
status VARCHAR(16) NOT NULL DEFAULT 'processing',
result_message_id BIGINT UNSIGNED NULL,
error_code VARCHAR(64) NULL,
locked_until DATETIME(6) NULL,
expire_at DATETIME(6) NOT NULL,
created_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
updated_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6)
ON UPDATE CURRENT_TIMESTAMP(6),
PRIMARY KEY (id),
UNIQUE KEY uk_idempotency_scope (user_id, agent_type, idempotency_key),
KEY idx_idempotency_expire (expire_at),
KEY idx_idempotency_trace (trace_id),
KEY idx_idempotency_session (session_id, created_at),
CONSTRAINT fk_idempotency_user FOREIGN KEY (user_id) REFERENCES sys_user(id),
CONSTRAINT fk_idempotency_session
FOREIGN KEY (session_id) REFERENCES svc_conversation_session(session_id),
CONSTRAINT fk_idempotency_result
FOREIGN KEY (result_message_id) REFERENCES conversation_message(id),
CONSTRAINT chk_idempotency_status
CHECK (status IN ('processing', 'completed', 'failed'))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='Agent请求幂等记录';
```
相同幂等键但 `request_hash` 不同必须返回冲突;`processing` 未过租约返回处理中,`completed` 返回原结果,只有租约超时后才能由新请求接管。
### 8.4 agent_run
```sql
CREATE TABLE agent_run (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
run_id CHAR(36) NOT NULL,
idempotency_id BIGINT UNSIGNED NOT NULL,
session_id VARCHAR(64) NOT NULL,
user_id BIGINT UNSIGNED NOT NULL,
agent_type VARCHAR(32) NOT NULL,
trace_id VARCHAR(64) NOT NULL,
request_message_id BIGINT UNSIGNED NOT NULL,
result_message_id BIGINT UNSIGNED NULL,
status VARCHAR(24) NOT NULL DEFAULT 'queued',
attempt_count INT UNSIGNED NOT NULL DEFAULT 0,
worker_id VARCHAR(128) NULL,
locked_until DATETIME(6) NULL,
result_version INT UNSIGNED NULL,
error_code VARCHAR(64) NULL,
cancel_requested_at DATETIME(6) NULL,
started_at DATETIME(6) NULL,
completed_at DATETIME(6) NULL,
created_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
updated_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6)
ON UPDATE CURRENT_TIMESTAMP(6),
PRIMARY KEY (id),
UNIQUE KEY uk_agent_run_id (run_id),
UNIQUE KEY uk_agent_run_idempotency (idempotency_id),
UNIQUE KEY uk_agent_run_trace (trace_id),
KEY idx_agent_run_session (session_id, created_at),
KEY idx_agent_run_user (user_id, created_at),
KEY idx_agent_run_worker_lease (status, locked_until),
CONSTRAINT fk_agent_run_idempotency
FOREIGN KEY (idempotency_id) REFERENCES request_idempotency(id),
CONSTRAINT fk_agent_run_session
FOREIGN KEY (session_id) REFERENCES svc_conversation_session(session_id),
CONSTRAINT fk_agent_run_user FOREIGN KEY (user_id) REFERENCES sys_user(id),
CONSTRAINT fk_agent_run_request_message
FOREIGN KEY (request_message_id) REFERENCES conversation_message(id),
CONSTRAINT fk_agent_run_result_message
FOREIGN KEY (result_message_id) REFERENCES conversation_message(id),
CONSTRAINT chk_agent_run_status CHECK
(status IN ('queued', 'running', 'succeeded', 'failed',
'cancel_requested', 'cancelled'))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='可恢复Agent运行状态';
```
`run_id` 是对外运行主键,`trace_id` 只用于可观测性。Worker 通过 `locked_until` 租约接管过期运行;同一 `run_id` 安全重试,不承诺从模型中间 Token 续写。最终消息、审计、幂等完成状态和 Outbox 必须与运行成功状态同一事务提交。
### 8.5 config_release
```sql
CREATE TABLE config_release (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
release_no VARCHAR(64) NOT NULL,
title VARCHAR(128) NOT NULL,
change_summary VARCHAR(1000) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'draft',
created_by BIGINT UNSIGNED NOT NULL,
reviewer_id BIGINT UNSIGNED NULL,
reviewed_at DATETIME(6) NULL,
activated_at DATETIME(6) NULL,
supersedes_release_id BIGINT UNSIGNED NULL,
rollback_of_release_id BIGINT UNSIGNED NULL,
active_slot TINYINT GENERATED ALWAYS AS
(IF(status = 'active', 1, NULL)) STORED,
created_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
updated_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6)
ON UPDATE CURRENT_TIMESTAMP(6),
PRIMARY KEY (id),
UNIQUE KEY uk_config_release_no (release_no),
UNIQUE KEY uk_config_release_active_one (active_slot),
KEY idx_config_release_status (status, created_at),
CONSTRAINT fk_config_release_creator FOREIGN KEY (created_by) REFERENCES sys_user(id),
CONSTRAINT fk_config_release_reviewer FOREIGN KEY (reviewer_id) REFERENCES sys_user(id),
CONSTRAINT fk_config_release_supersedes
FOREIGN KEY (supersedes_release_id) REFERENCES config_release(id),
CONSTRAINT fk_config_release_rollback
FOREIGN KEY (rollback_of_release_id) REFERENCES config_release(id),
CONSTRAINT chk_config_release_status
CHECK (status IN ('draft', 'validating', 'pending_review', 'approved',
'active', 'superseded', 'rejected', 'rolled_back'))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='平台配置发布批次';
```
> **变更记录**:原 `CONSTRAINT chk_config_release_separation CHECK (reviewer_id IS NULL OR reviewer_id <> created_by)`
> (双人复核)已于迁移 `20260910_drop_review_separation` 撤下。原因是本平台为单管理员部署,
> 该约束使配置发布在数据库层永远无法完成(错误 3819)。撤下的只是"审核人必须不同于创建人"这一
> 身份限制:状态机仍要求 `pending_review` → `approved` 经审核接口并写入 `reviewer_id`/`reviewed_at`,
> `reviewer_id` 列本身的类型、可空性与业务含义均未改变。`docs/00-新数据库基线设计.md` 未修改。
> **变更记录(2026-09-11 合并说明)**:两条并行分支各自补了一次撤约束迁移——
> `20260910_drop_review_separation`(风控线)与 `20260911_drop_review_separation`(yc 线),
> 两者都先查 `information_schema` 再决定是否 `ALTER`,重复执行不会报错,因此合并后全部保留。
> `chk_config_release_status`、全部外键、唯一键与索引不变,表名与字段定义未做任何改动;
> `docs/00-新数据库基线设计.md` 未修改。
### 8.6 platform_config_item
```sql
CREATE TABLE platform_config_item (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
release_id BIGINT UNSIGNED NOT NULL,
namespace VARCHAR(64) NOT NULL,
config_key VARCHAR(128) NOT NULL,
value_json JSON NOT NULL,
schema_version VARCHAR(32) NOT NULL,
checksum CHAR(64) NOT NULL,
created_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
PRIMARY KEY (id),
UNIQUE KEY uk_config_item_release (release_id, namespace, config_key),
KEY idx_config_item_lookup (namespace, config_key, release_id),
CONSTRAINT fk_config_item_release
FOREIGN KEY (release_id) REFERENCES config_release(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='版本化平台配置项';
```
`namespace` 首版限定为 `agent_runtime`、`memory_policy`、`relationship_view`、`compliance_policy` 和 `tool_policy`;`value_json` 必须先通过对应 Pydantic Schema 校验。
### 8.7 model_endpoint_config
```sql
CREATE TABLE model_endpoint_config (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
endpoint_code VARCHAR(64) NOT NULL,
provider VARCHAR(32) NOT NULL,
model_name VARCHAR(128) NOT NULL,
base_url VARCHAR(500) NOT NULL,
secret_ref VARCHAR(256) NOT NULL,
capabilities JSON NOT NULL,
allowed_data_levels JSON NOT NULL,
context_window INT UNSIGNED NOT NULL,
timeout_ms INT UNSIGNED NOT NULL DEFAULT 15000,
cost_input_per_1k DECIMAL(12,6) NULL,
cost_output_per_1k DECIMAL(12,6) NULL,
status VARCHAR(16) NOT NULL DEFAULT 'disabled',
created_by BIGINT UNSIGNED NOT NULL,
reviewer_id BIGINT UNSIGNED NULL,
reviewed_at DATETIME(6) NULL,
created_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
updated_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6)
ON UPDATE CURRENT_TIMESTAMP(6),
PRIMARY KEY (id),
UNIQUE KEY uk_model_endpoint_code (endpoint_code),
KEY idx_model_endpoint_status (status, provider),
CONSTRAINT fk_model_endpoint_creator FOREIGN KEY (created_by) REFERENCES sys_user(id),
CONSTRAINT fk_model_endpoint_reviewer FOREIGN KEY (reviewer_id) REFERENCES sys_user(id),
CONSTRAINT chk_model_endpoint_status
CHECK (status IN ('draft', 'approved', 'active', 'disabled', 'archived'))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='模型端点配置';
```
`secret_ref` 只保存环境变量名或密钥服务路径,禁止保存明文密钥。
### 8.8 model_routing_rule
```sql
CREATE TABLE model_routing_rule (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
release_id BIGINT UNSIGNED NOT NULL,
rule_code VARCHAR(64) NOT NULL,
agent_type VARCHAR(32) NOT NULL,
task_type VARCHAR(48) NOT NULL,
model_policy VARCHAR(32) NOT NULL,
primary_endpoint_id BIGINT UNSIGNED NOT NULL,
fallback_endpoint_ids JSON NULL,
condition_json JSON NULL,
max_attempts TINYINT UNSIGNED NOT NULL DEFAULT 2,
latency_budget_ms INT UNSIGNED NOT NULL DEFAULT 15000,
cost_budget DECIMAL(12,6) NULL,
priority INT NOT NULL DEFAULT 100,
created_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
PRIMARY KEY (id),
UNIQUE KEY uk_model_route_release (release_id, rule_code),
KEY idx_model_route_match
(release_id, agent_type, task_type, model_policy, priority),
CONSTRAINT fk_model_route_release FOREIGN KEY (release_id) REFERENCES config_release(id),
CONSTRAINT fk_model_route_primary
FOREIGN KEY (primary_endpoint_id) REFERENCES model_endpoint_config(id),
CONSTRAINT chk_model_route_attempts CHECK (max_attempts BETWEEN 1 AND 3)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='多模型路由规则';
```
`fallback_endpoint_ids` 是为旧版读取方保留的兼容字段,可为空;它不再作为备用链权威来源。若迁移期继续填充该字段,其端点顺序必须与 `model_routing_fallback` 按 `fallback_order` 排序后的结果完全一致,发布服务在同一配置发布事务中校验。
### 8.9 model_routing_fallback
```sql
CREATE TABLE model_routing_fallback (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
routing_rule_id BIGINT UNSIGNED NOT NULL,
endpoint_id BIGINT UNSIGNED NOT NULL,
fallback_order TINYINT UNSIGNED NOT NULL,
retryable_error_codes JSON NULL,
created_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
PRIMARY KEY (id),
UNIQUE KEY uk_model_fallback_order (routing_rule_id, fallback_order),
UNIQUE KEY uk_model_fallback_endpoint (routing_rule_id, endpoint_id),
KEY idx_model_fallback_endpoint (endpoint_id),
CONSTRAINT fk_model_fallback_rule
FOREIGN KEY (routing_rule_id) REFERENCES model_routing_rule(id),
CONSTRAINT fk_model_fallback_endpoint
FOREIGN KEY (endpoint_id) REFERENCES model_endpoint_config(id),
CONSTRAINT chk_model_fallback_order CHECK (fallback_order BETWEEN 1 AND 2)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='模型路由有序备用端点';
```
`model_routing_fallback` 是备用链的权威关系表。发布服务必须校验主端点不出现在备用链中、每条路由的总端点数不超过 3,且 `retryable_error_codes` 只能包含平台登记的错误码。
### 8.10 prompt_template_version
```sql
CREATE TABLE prompt_template_version (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
release_id BIGINT UNSIGNED NOT NULL,
prompt_code VARCHAR(64) NOT NULL,
task_type VARCHAR(48) NOT NULL,
agent_type VARCHAR(32) NULL,
version INT UNSIGNED NOT NULL,
system_prompt MEDIUMTEXT NOT NULL,
user_prompt_template MEDIUMTEXT NOT NULL,
input_schema JSON NULL,
output_schema JSON NULL,
checksum CHAR(64) NOT NULL,
created_by BIGINT UNSIGNED NOT NULL,
created_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
PRIMARY KEY (id),
UNIQUE KEY uk_prompt_version (prompt_code, version),
KEY idx_prompt_release_task (release_id, task_type, agent_type),
CONSTRAINT fk_prompt_release FOREIGN KEY (release_id) REFERENCES config_release(id),
CONSTRAINT fk_prompt_creator FOREIGN KEY (created_by) REFERENCES sys_user(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='Prompt不可变版本';
```
## 9. 创建与迁移顺序
### 9.1 空库创建顺序
1. 创建数据库和 Alembic 版本表。
2. 创建 `sys_user`、角色、权限及用户角色关系。
3. 创建客户画像、风险测评和客户归属。
4. 创建产品、行情、账户、费用、委托、成交、资金和持仓。
5. 创建预警、通知、风险工单、内容和审计表。
6. 创建对话、知识、Episode 和记忆相关表。
7. 创建 `agent_negative_word` 和 `svc_handover_ticket`。
8. 创建 `agent_faq_synonym` 和 `conversation_feedback`。
9. 创建 `agent_reply_template` 和 `agent_intent_config`。
10. 创建会话、幂等、`agent_run` 和通用事件 Outbox 表。
11. 创建配置发布、平台配置、模型端点、模型路由、路由备用端点和 Prompt 表。
12. 写入初始角色、权限、意图、模板、模型路由和禁止表达。
13. 建立 Milvus 集合、Neo4j 约束和 Redis Key 规范。
### 9.2 已有库升级
1. 备份结构和关键表数据。
2. 使用 Inspector 获取真实列、索引和外键。
3. 只增加缺失字段,不重建或覆盖现有业务表。
4. 新字段先允许兼容旧代码,再部署双版本兼容应用。
5. 回填知识正文、审核状态和对话分类信息。
6. 创建专项表和平台底座增量表。
7. 发布首个完整配置快照。
8. 部署新代码并验证双版本兼容读写。
`content_text` 在已有知识数据未回填前保持可空。即使回填完成,也不修改该已有字段定义;由应用校验和发布条件保证已发布知识正文非空。
## 10. 初始数据
### 10.1 内置角色
- `customer`
- `advisor`
- `operator`
- `risk_operator`
- `admin`
- `super_admin`
### 10.2 客服初始禁止表达
至少录入:保本、稳赚、无风险、保证收益、预期收益率、年化收益率、安全,以及经合规确认的变体。初始状态必须经过审核后才可以设置为 `active`。
### 10.3 客服初始模板
- 固定免责声明。
- AI 生成标识。
- 低置信兜底。
- 合规拒答。
- 转人工提示。
- 模型故障和系统繁忙提示。
## 11. 事务和并发要求
- 模拟成交、账户余额、资金明细和持仓必须在一个事务内提交。
- 工单状态更新采用乐观锁或条件更新,禁止重复接单覆盖。
- 画像版本和 Outbox 事件在同一事务提交。
- 助手消息、执行审计和 `domain_event_outbox` 在同一短事务提交。
- 幂等记录通过唯一键抢占,完成状态与结果消息在同一事务更新。
- 配置发布批次和全部配置项校验通过后才能原子激活。
- 模板和意图新版本发布时,旧版本失效与新版本生效必须原子完成。
- 审计记录与受监管的业务状态变更应在同一事务写入。
- 外部模型、邮件、Milvus 和 Neo4j 调用不得放入 MySQL 长事务。
## 12. 数据安全
- `password_hash` 不向 Agent、风控或 NL2SQL 开放。
- 身份证、手机号、银行卡号和精确账户余额不得写入 Milvus 或 Neo4j。
- `source_references` 只保存必要的来源标识,不复制整份文档。
- SQL 查询必须应用 `data_scope` 和客户归属过滤。
- `interaction_audit` 仅追加,不允许普通业务接口更新或删除。
- 生产备份、迁移导出和错误日志均执行敏感字段保护。
- 模型端点表只保存 `secret_ref`,不得保存 API 明文密钥。
## 13. 验证清单
- 51 张业务表全部存在(另有 `alembic_version`),原 39 张表的名称和已有字段定义保持不变。
- 主键、唯一键、外键和检查约束生效;唯一键必须与 `docs/00` 声明一致,由 `tools/audit_constraints.py` 逐表比对。
- 2026-09-09 约束纠偏:`fin_market_price`、`fin_nav_history`、`fin_holding`、`sys_customer_assignment` 四张表的联合唯一键曾被基线生成器错误拆成单列唯一键,已由迁移 `20260909_constraint_fix` 修正(删 8 个错误唯一键、加 4 个联合唯一键),`docs/00` 未做修改。证据见 `docs/evidence/` 与 `docs/08`。
- `conversation_message` 三个 Agent 增量字段和既有 `tool_calls` 字段可正常读写。
- 知识检索只命中已发布且在有效期内的条目。
- Milvus 故障时 MySQL 正文降级检索可用。
- 客服转人工工单与风险工单完全独立。
- 同义问法只有审核通过后参与检索。
- 一个客户对同一回复不能重复反馈。
- 模板和意图配置可版本化发布和回滚。
- 每个模板或意图组合最多一个 `active` 版本,备用模型端点均受外键约束且顺序唯一。
- 越权客户查询被数据库访问层和服务层共同拒绝。
- Redis 丢失可以从 MySQL 恢复必要记忆。
- Milvus 或 Neo4j 单边失败不会丢失 Outbox 事件。
- 重复请求不会重复生成消息、工单或领域事件。
- 配置发布失败时旧版本继续有效,回滚不修改历史版本。
- 模型路由最多尝试三个已审核端点,并完整记录采用版本。
- 所有迁移在空库、旧版本库和回滚路径各执行一次。
## 14. 已知实施前提
6 张专项表的字段由本文补全并作为建议实施结构。若团队已有《智能客服Agent新增表详细设计.md》,合并前必须逐字段比较;业务编号、状态枚举、审核流和外键语义不一致时,由数据库负责人组织评审,不允许两个定义同时进入迁移脚本。