本文引用的文件
本设计文档梳理本项目在 PostgreSQL 主库中的持久化数据结构,覆盖网关路由、用户与授权、Agent 专家与工具、聊天附件、清洗模板与同义词、行统计、案件动态表、外部数据表、研判会话消息、平台元数据等。同时说明 pgvector 向量表与 DuckDB 临时数据源的用途差异,给出 ER 关系图、关键字段定义、主外键、索引约束和业务规则,并补充初始化顺序、迁移规范和回滚注意事项。
项目 SQL 脚本集中在 sql/ 目录,按业务功能拆分:
gateway.sqluser.sqlagent_expert.sql、agent_tool.sqlchat_attachment.sqlai_clean.sql、ai_clean_field_short_names.sql、ai_clean_row_stats.sql、ai_clean_synonym_init.sql、ai_clean_template_drop_case_src.sqlcase_table.sql、create_table_2.sqlexternal_tables.sql、external_table_field.sqlinsight_agent_tables.sql、insight_agent_tables_align.sql平台元数据:plat.sql
graph TB
PG["PostgreSQL 主库"]
subgraph "系统配置"
GW["gateway_service / gateway_node<br/>gateway_user_route"]
SYS["sys_user"]
AG["agent<br/>agent_tool"]
end
subgraph "对话与附件"
ATT["chat_attachment"]
INS_S["agent_insight_session"]
INS_M["agent_insight_message"]
end
subgraph "AI 清洗"
TPL["ai_clean_template / ai_clean_field"]
RULE["ai_clean_rule"]
JOB["ai_clean_job / ai_clean_batch"]
SYN["ai_clean_synonym"]
end
subgraph "案件数据"
CT["case_table.*<br/>DuckDB 动态表"]
EXT["external_tables.*<br/>DuckDB 外部数据"]
end
subgraph "平台元数据"
PLAT["plat.sql 中的 table_info / table_field<br/>chat_session / chat_model 等"]
end
SYS --> GW
SYS --> INS_S
SYS --> JOB
SYS --> ATT
TPL --> JOB
RULE --> JOB
SYN --> TPL
ATT --> INS_M
CT --> PLAT
EXT --> PLAT
图表来源
章节来源
| 表名 | 主键 | 关键字段 | 约束与索引 | 业务含义 |
|---|---|---|---|---|
gateway_service |
id VARCHAR(64) |
name、path_prefix、login_path、health_path、default_node_id、enabled |
path_prefix 唯一;enabled 默认 1 |
一个对外路径前缀对应一组后端节点,首次启动可从配置播种 |
gateway_node |
id VARCHAR(64) |
service_id、name、base_url、enabled |
service_id + base_url 唯一;外键指向 gateway_service.id |
一台后端 ai-server 实例 |
gateway_user_route |
id BIGSERIAL |
service_id、user_id、username、node_id |
ck_gateway_user_route_owner 保证至少一个用户标识;部分唯一索引分别约束 user_id 和 lower(username);node_id 有普通索引;两个外键分别指向服务和节点 |
用户粘性绑定到某个节点,不可用时返回 503,不自动切换 |
业务规则:
path_prefix 全局唯一,不能以 / 结尾,且不是 /gateway。base_url 不含上下文路径,同一服务内地址必须唯一。章节来源
| 表名 | 主键 | 关键字段 | 约束与索引 | 业务含义 |
|---|---|---|---|---|
sys_user |
id BIGSERIAL |
username、pwd_hash、display_name、is_admin、status、last_login_time |
lower(username) 唯一;is_admin 仅控制是否可调用用户管理接口;status 默认启用 |
系统认证用户,不实现角色菜单权限体系 |
case_info |
id INTEGER(自增) |
owner_id BIGINT(新增列) |
对 owner_id 建索引 |
案件归属用户,仅创建者可见;存量未归属案件统一归给内置管理员 |
注意:
sys_role、sys_menu、sys_permission、sys_user_role 等完整权限体系。case_info.owner_id 是应用层“允许同名案件”的归属边界,数据库侧只加索引,不加唯一约束。章节来源
| 表名 | 主键 | 关键字段 | 约束与索引 | 业务含义 |
|---|---|---|---|---|
agent |
row_id(通过脚本补主键) |
user_id、is_builtin、skills_allow_json、tools_allow_json |
user_id 有索引;内置专家全局可见不可编辑;非内置专家按创建者隔离 |
智能体配置,内置标记区分平台级和用户级 |
agent_tool |
id BIGINT |
tool_name、tool_class、tool_method、group_code、parameters、status |
tool_name 唯一;status 取 ACTIVE 或 DELETED |
启动时反射扫描 @Tool 后 upsert 的工具清单,页面只读 |
业务规则:
is_builtin=1,归属平台管理员;用户自建专家 is_builtin=0,按 user_id 过滤。章节来源
| 表名 | 主键 | 关键字段 | 约束与索引 | 业务含义 |
|---|---|---|---|---|
chat_attachment |
id BIGINT |
user_id、case_id、session_id、file_name、file_path、sidecar_path、mime、kind、size_bytes、char_count、row_count、truncated、inline_text、index_blocks、index_status、link_message_id、fail_reason |
索引:session_id、status+link_message_id |
附件问答三路的元数据锚点:内联文本、Python 直读文件、RAG 分块向量化 |
业务规则:
user_id/case_id/session_id 三重归属。sessionId。章节来源
| 表名 | 主键 | 关键字段 | 约束与索引 | 业务含义 |
|---|---|---|---|---|
ai_clean_template |
id BIGINT |
table_name_cn、table_name_en、headers、header_md5、func_regex、struct_ops、needs_struct、category、match_tier、status、ver、confidence |
header_md5 唯一;status、category 有索引 |
AI 模板,等价于既有 table_info,但完全独立存储 |
ai_clean_field |
id BIGINT |
ai_template_id、column_name、field_name_cn、field_name_en、field_type、required、field_len、field_sort、col_index、matched |
按 ai_template_id 索引 |
动态表 DDL 真源,物理列固定为 c0/c1/… |
ai_clean_rule |
id BIGINT |
ai_template_id、col_index、field_name_en、hints、ref_template_id |
按 ai_template_id 索引 |
扁平枚举 hint 的规则快照,避免反序列化任意类 |
ai_clean_job |
id BIGINT |
case_id、file_id、sheet_id、block_no、ver、status、exit_code、token_in/out、cost_ms、row_total/loaded/rejected/warned/unprocessed |
唯一键 (case_id, file_id, sheet_id, block_no, ver);status、file_id 有索引 |
幂等游标 + 状态线 + 成本埋点 |
ai_clean_batch |
id BIGINT |
case_id、batch_id、stage、file_count、sheet_total、matched_count、ai_judged_count、loaded_rows、rejected_rows、unprocessed_rows、self_built_count |
唯一键 (case_id, batch_id);stage 有索引 |
智能清洗批次状态机 |
ai_clean_synonym |
id BIGINT |
std_word、alias_word、remark、status |
(std_word, alias_word) 唯一 |
表头同义词星型词组,用于评分和绑定域 |
重要变更:
ai_clean_field.column_name 与 field_name_en 统一为 c{位置},宽度收窄为 VARCHAR(5)。job.row_rejected、job.row_warned、job.row_unprocessed,以及 batch.loaded_rows、rejected_rows、unprocessed_rows、self_built_count。case_id_src,跨案件共享。章节来源
case_table.sql 中定义了多个案件相关表,例如:
trans_record、trans_record_all:交易流水call_record、call_record_all:通话记录use_open_info_call_record:开户信息通话记录account_opening_info:账户开户信息这些表通常作为 DuckDB 临时数据源使用,包含高频定位列的索引约定,如姓名、身份证号、卡号、手机号、证件号等。
章节来源
external_tables.sql 描述来自“表结构信息.txt”的外部数据表,包括:
cert_info:出入境证件信息entry_rec:出入境记录vehicle_info:机动车信息lic_info:驾驶证信息veh_violation:机动车违章household:同户人co_resident:同住址co_veh_viol:同车违章criminal:违法犯罪人员信息这些表与 external_table_field.sql 一起,映射到平台 table_info 与 table_field,用于外部数据语义检索。
章节来源
| 表名 | 主键 | 关键字段 | 约束与索引 | 业务含义 |
|---|---|---|---|---|
agent_insight_session |
id BIGINT |
case_id、biz_type、scope、agent_row_id、model_id、title、context、context_hash、sys_prompt、message_count、last_seq、total_tokens、last_message_at、pinned、status |
case_id+biz_type+last_message_at 列表索引;case_id+context_hash 复用索引 |
独立于通用对话表的研判会话,强制案件级隔离 |
agent_insight_message |
id BIGINT |
session_id、case_id、seq、role、content、content_type、message_type、metadata、tool_events、output_json、token_count、reply_to_message_id、starred、status |
唯一键 (session_id, seq);外键级联删除会话;case_id+create_at 部分索引过滤结构化产物 |
研判消息,排序列必须是 seq |
注意:
insight_agent_tables.sql;已有库需用 insight_agent_tables_align.sql 补齐约束与索引。seq 通过 UPDATE ... RETURNING last_seq 原子分配,禁止 SELECT MAX(seq)+1。章节来源
plat.sql 包含一些平台基础表和旧版聊天元数据,例如:
bank_card_issuer_info:银行卡发卡方case_info:案件基础信息phone_isp:手机号运营商归属system_info:系统激活信息table_info、table_field:表与字段元数据chat_message、chat_session、chat_model、chat_provider、knowledge_attach、knowledge_fragment、knowledge_info这些表与 AI 清洗、外部数据、图谱等业务配合,但不直接承载主业务流程的核心行数据。
章节来源
erDiagram
sys_user {
bigint id PK
varchar username
varchar pwd_hash
smallint is_admin
smallint status
timestamp last_login_time
}
gateway_service {
varchar id PK
varchar name
varchar path_prefix UK
varchar login_path
varchar health_path
varchar default_node_id
smallint enabled
}
gateway_node {
varchar id PK
varchar service_id FK
varchar name
varchar base_url
smallint enabled
}
gateway_user_route {
bigint id PK
varchar service_id FK
bigint user_id
varchar username
varchar node_id FK
}
agent {
bigint row_id PK
bigint user_id
smallint is_builtin
text skills_allow_json
text tools_allow_json
}
agent_tool {
bigint id PK
varchar tool_name UK
varchar tool_class
varchar tool_method
varchar group_code
jsonb parameters
varchar status
}
chat_attachment {
bigint id PK
bigint user_id
bigint case_id
bigint session_id
varchar file_name
varchar file_path
varchar kind
integer row_count
varchar status
}
ai_clean_template {
bigint id PK
varchar header_md5 UK
varchar table_name_en
varchar status
numeric confidence
}
ai_clean_field {
bigint id PK
bigint ai_template_id
varchar column_name
varchar field_name_en
varchar field_type
}
ai_clean_rule {
bigint id PK
bigint ai_template_id
integer col_index
text hints
}
ai_clean_job {
bigint id PK
bigint case_id
bigint file_id
bigint sheet_id
integer block_no
integer ver
varchar status
integer row_rejected
integer row_warned
integer row_unprocessed
}
ai_clean_batch {
bigint id PK
bigint case_id
bigint batch_id
varchar stage
bigint loaded_rows
bigint rejected_rows
bigint unprocessed_rows
}
ai_clean_synonym {
bigint id PK
varchar std_word
varchar alias_word
}
agent_insight_session {
bigint id PK
bigint case_id
varchar biz_type
bigint model_id
jsonb context
varchar context_hash
bigint last_seq
varchar status
}
agent_insight_message {
bigint id PK
bigint session_id FK
bigint case_id
bigint seq
varchar role
jsonb output_json
varchar status
}
sys_user ||--o{ gateway_user_route : "绑定"
gateway_service ||--o{ gateway_node : "包含"
gateway_service ||--o{ gateway_user_route : "路由范围"
agent ||--o{ agent_insight_session : "可选使用"
ai_clean_template ||--o{ ai_clean_field : "包含字段"
ai_clean_template ||--o{ ai_clean_rule : "包含规则"
ai_clean_job ||--o{ ai_clean_batch : "属于批次"
agent_insight_session ||--o{ agent_insight_message : "包含消息"
chat_attachment ||--o{ agent_insight_message : "可选关联"
图表来源
sequenceDiagram
participant Client as "客户端"
participant Gateway as "ai-gateway"
participant Service as "gateway_service"
participant Node as "gateway_node"
participant Route as "gateway_user_route"
participant Server as "ai-server 实例"
Client->>Gateway: "请求 /js/a/... 路径"
Gateway->>Service: "根据 path_prefix 查找服务"
Service-->>Gateway: "返回服务配置"
Gateway->>Route: "按 user_id 或 lower(username) 查绑定"
Route-->>Gateway: "返回 node_id"
Gateway->>Node: "检查节点可用"
alt "节点可用"
Gateway->>Server: "转发原始路径"
Server-->>Client: "响应"
else "节点不可用"
Gateway-->>Client: "返回 503"
end
图表来源
章节来源
flowchart TD
Start(["上传文件"]) --> Parse["解析表头与内容"]
Parse --> Fingerprint["计算 header_md5"]
Fingerprint --> Match{"命中既有模板?"}
Match -->|是| Existing["走出口 A:复用既有模板"]
Match -->|否| LLM["调用模型判定结构"]
LLM --> SaveTpl["保存 ai_clean_template / ai_clean_field"]
SaveTpl --> Rule["生成 ai_clean_rule"]
Rule --> Job["写入 ai_clean_job"]
Existing --> Batch["写入 ai_clean_batch"]
Rule --> Batch
Batch --> Validate["逐行校验类型/必填/长度"]
Validate --> Load{"是否通过"}
Load -->|是| Insert["写入 DuckDB 动态表"]
Load -->|否| Reject["计入 rejected_rows / row_rejected"]
Insert --> Done(["批次完成"])
Reject --> Partial["部分成功"]
Partial --> Done
图表来源
章节来源
sequenceDiagram
participant User as "用户"
participant Session as "agent_insight_session"
participant Message as "agent_insight_message"
participant DB as "PostgreSQL"
User->>Session: "发起研判会话"
Session->>DB: "INSERT session (case_id, biz_type, context)"
DB-->>Session: "返回 session.id"
User->>Message: "发送消息"
Message->>DB: "UPDATE session SET last_seq = last_seq + 1 RETURNING last_seq"
DB-->>Message: "返回 seq"
Message->>DB: "INSERT message (session_id, case_id, seq, role, content)"
DB-->>User: "返回消息结果"
图表来源
章节来源
graph LR
SYS["sys_user"] --> GWU["gateway_user_route"]
GWS["gateway_service"] --> GWN["gateway_node"]
GWS --> GWU
GWN --> GWU
AG["agent"] --> INS_S["agent_insight_session"]
TPL["ai_clean_template"] --> FL["ai_clean_field"]
TPL --> RU["ai_clean_rule"]
TPL --> JB["ai_clean_job"]
SY["ai_clean_synonym"] --> TPL
JB --> BA["ai_clean_batch"]
ATT["chat_attachment"] --> INS_M["agent_insight_message"]
INS_S --> INS_M
CT["case_table.*"] --> PT["platform meta"]
EX["external_tables.*"] --> PT
图表来源
章节来源
plat.sql
table_info、table_field、case_info、chat_*、knowledge_* 等基础表。user.sql
sys_user,插入内置管理员,为 case_info 添加 owner_id 并回填。gateway.sql
gateway_service、gateway_node、gateway_user_route,并建立外键与索引。agent_expert.sql
agent.user_id、is_builtin、主键、序列和对齐逻辑。agent_tool.sql
ai_clean.sql
ai_clean_field_short_names.sql
field_name_en 与 column_name 对齐为 c{位置},收窄列宽。ai_clean_row_stats.sql
ai_clean_job 和 ai_clean_batch 增加行统计列。ai_clean_synonym_init.sql
ai_clean_template_drop_case_src.sql
ai_clean_template.case_id_src。insight_agent_tables.sql
insight_agent_tables_align.sql
uk_aim_session_seq、fk_aim_session、列表索引、部分索引、注释。case_table.sql、create_table_2.sql
external_tables.sql、external_table_field.sql
table_info、table_field。CREATE TABLE IF NOT EXISTS、ALTER TABLE ... ADD COLUMN IF NOT EXISTS、DROP CONSTRAINT IF EXISTS、CREATE INDEX IF NOT EXISTS。lock_timeout,避免长事务阻塞。DROP TABLE 脚本;现有脚本多为测试或演示用途,实际部署应改用增量迁移。agent_tool,DDL 由人工执行一次,运行时只 upsert 数据。insight_agent_tables,已有库必须执行 insight_agent_tables_align.sql,否则缺失唯一约束和外键会导致孤儿消息。ON DELETE RESTRICT,可直接阻止危险删除。agent 主键修复和序列对齐脚本会清理脏数据,回滚前需备份。case_id_src 后,不应再通过该列做案件归属判断。on conflict do nothing,重复执行不会破坏数据。seq 的唯一约束由 UPDATE ... RETURNING last_seq 和唯一索引共同保障,回滚后重新执行对齐脚本即可。章节来源
gateway_service.path_prefix 唯一索引保证路径分发稳定。gateway_user_route 使用部分唯一索引分别约束 user_id 和 lower(username),避免并发冲突。node_id 普通索引加速节点可用性查询。sys_user.username 使用 lower() 唯一索引,兼容大小写不敏感登录。case_info.owner_id 索引支持按归属用户过滤。agent_tool.tool_name 唯一索引防止模型调用落到错误实现。ai_clean_template.header_md5 唯一索引用于零模型重放。ai_clean_job 唯一键保证同一文件块只处理一次。ai_clean_batch 唯一键保证重复点击返回同一批次。ai_clean_synonym 唯一键防止标准词与别名组合重复。agent_insight_session 列表索引按 case_id、biz_type、last_message_at 排序。context_hash 索引用于同条件复用。agent_insight_message 唯一键 (session_id, seq) 防重号。idx_aim_output 只索引有结构化产出的消息。chat_att_vec_{caseId}_d{dims}。sessionId,避免跨会话泄露。章节来源
sys_user.pwd_hash 使用 BCrypt 哈希,明文不得进入脚本或日志。chat_attachment.file_path、sidecar_path、inline_text 可能包含隐私数据,应限制文件系统访问权限。chat_att_vec_{caseId}_d{dims} 包含文档切片向量,应按会话隔离访问。agent_tool.parameters 存 JSON Schema,不应直接拼接执行。ai_clean_rule.hints 使用枚举 hint,避免反序列化任意 Java 类。agent_insight_session.sys_prompt 仅运维追溯,不通过接口下发。is_admin=1 可调用用户管理接口,前端静态菜单仍由本地配置生成。case_id,消息表冗余 case_id 以强化 SQL 层隔离。user_id/case_id/session_id,任一不匹配即拒绝。sys_user、gateway_*、agent、agent_tool、chat_attachment、ai_clean_*、agent_insight_*。pwd_hash、context、sys_prompt、tool_events、output_json 等高价值或高敏感字段。d{dims} 维度的向量表。章节来源
sys_user.status 是否为启用。sys_user.pwd_hash 是否为有效 BCrypt 值。gateway_user_route 是否存在对应用户绑定。章节来源
agent_expert.sql 清理重复行、对齐序列并补主键。agent.row_id 已有主键约束。章节来源
agent_tool.sql。agent_tool.tool_name 是否有重复值。章节来源
ai_clean_job 唯一键是否命中。ai_clean_batch 唯一键是否重复触发。row_rejected、row_warned、row_unprocessed 定位失败原因。章节来源
insight_agent_tables_align.sql。uk_aim_session_seq 是否存在。fk_aim_session 是否级联删除。seq 通过 UPDATE ... RETURNING last_seq 原子分配。章节来源
chat_attachment.session_id、user_id、case_id。index_status 是否为 READY。章节来源
本项目 PostgreSQL 主库采用多业务域分离设计:网关路由、用户认证、Agent 专家与工具、聊天附件、AI 清洗链路、研判会话消息、案件动态表和外部数据表各司其职。PG 负责持久化、一致性、会话隔离和审计追踪;DuckDB 承担案件级临时分析和外部数据快速查询;pgvector 向量表按会话和维度动态组织,服务于 RAG 检索。迁移应以幂等脚本为主,严格遵循初始化顺序,并在生产环境谨慎执行 DROP 操作。安全方面重点保护密码、向量数据、提示词、工具参数和案件归属关系,备份恢复应覆盖主库、向量表和 DuckDB 临时数据。