版本:v2.0(2026-10-19,纠偏后) 模块:
ai-server/src/main/java/com/zsjz/ai/module/aiclean/index/关键约束:只读模板库元数据,不扫描 DuckDB。
平台有两套模板元数据,代表两类「可被 Agent 使用的表」:
| 来源 | 元数据表 | 产生时机 |
|---|---|---|
| 既有模板库 | table_info + table_field |
上传清洗识别出的新表 / 模板管理页手工新建 |
| AI 模板库 | ai_clean_template + ai_clean_field |
AI 智能清洗出口 B 自建动态表 ai_clean_{id} |
业务约束:DuckDB 里的表必须先登记进这两套元数据,才算「可被 Agent 使用的表」;
不扫描 DuckDB 系统表(既避免把 govern_tree/raw_* 等内部表暴露给模型,也让表清单与模板库、治理树、
AI 模板匹配保持同一口径)。
向量化结果有两个用途,共用同一份索引:
search_table_schema 语义检索可选表(返回物理表名 + 物理列名 + 中文注释),
list_tables 直接列元数据目录;AiTemplateMatcher 腿 3 的向量召回(候选补充/排序,不单独判档)。table_info + table_field ──┐
├─> AiCleanTemplateVectorIndex ──> pgvector 表 rag_tpl_d{dims}
ai_clean_template + field ─┘ ↑ 增量事件 / 全量重建 / 启动自举 │
├─> searchTables(query) → Agent 查表(生成 SQL)
└─> recall(query) → AI 模板匹配召回
rag_tpl_d{dims}(按 embedding 维度隔离;换模型维度变了自然落新表)。doc_id 主键 / content 原文 / payload jsonb / embedding vector(dims)。doc_id:既有模板 t:{table_info.id},AI 模板 a:{ai_clean_template.id}。payload:{kind, templateId|aiTemplateId, tableNameEn, tableNameCn, mainId, headers[], category,
fields:[{name(物理列名), en(语义英文名), cn(中文名), type, required}]}。ON CONFLICT (doc_id) DO UPDATE;删:按 doc_id 精确删除。模板类型 + 表名(中文/英文) + 表头 + 字段清单(英文名/类型/中文注释/必填)。| 场景 | 入口 |
|---|---|
| 模板新增/更新/删除 | TemplateMetaChangedEvent(TABLE / AI_TEMPLATE × UPSERT / DELETE)→ 异步增量 |
| 启动自举 | ApplicationReadyEvent:auto-rebuild-on-startup=true 且可用、向量表不存在/为空 → 后台全量重建 |
| 存量补建 / 换嵌入模型 | POST /js/a/aiClean/templateVector/rebuild |
| 状态观测 | GET /js/a/aiClean/templateVector/status |
事件发布点:TableInfoService#create/update/delete(afterCommit)、AiCleanStore#register/deleteTemplate。
上传清洗、AI 清洗链路无需单独触发 —— 新建模板最终都走这两个入口。
| 用途 | 入口 | 行为 |
|---|---|---|
| Agent 查表 | RagSchemaService.search() / SqlAnalysisTool.listTables() |
searchTables 返回 tableName(物理表名)+ columns[].name(物理列名);list_tables 走 WorkspaceSchemaCatalog.allTables()(同一套元数据、实时) |
| AI 模板匹配 | AiTemplateMatcher.mergeRecall |
recall 只补候选/调排序,不参与档位判定(向量分不得触发 STRONG/出口 A) |
agent_model 中 type='embedding' 活跃模型 / 开关关闭:isAvailable()=false,recall/searchTables 返回空。search_table_schema 在索引为空时返回 409 且提示先 rebuild;list_tables 不依赖向量索引,仍可用。log.warn,绝不影响模板落库与清洗主流程。zsjz:
ai-clean:
vector:
enabled: true
auto-index-on-template-change: true
auto-rebuild-on-startup: true # 存量模板首次部署/升级后自动补建
top-k: 12 # 模板匹配召回条数
score-threshold: 0.30
max-documents-per-rebuild: 5000
etl:
rag:
enabled: true # 是否注册 search_table_schema 工具
top-k: 5
score-threshold: 0.3
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /js/a/aiClean/templateVector/status |
{available, vectorTable, embeddingModel, dimensions, docCount} |
| POST | /js/a/aiClean/templateVector/rebuild |
全量重建,返回写入文档数 |
RagSchemaService 已重写为统一索引的查询门面:search 调 AiCleanTemplateVectorIndex.searchTables,
reindexWorkspace 调 rebuildNow,不再维护按案件/按 DuckDB 的表结构索引。AiCleanTemplateVectorIndexTest(文档/payload 物理列名、searchTables 映射、AI 模板不过滤 ONLINE、
连续失败熔断)、WorkspaceSchemaCatalogTest(两套元数据合并、物理列名、无字段跳过、异常上抛)、
RagSchemaServiceTest(search/reindex 门面映射、索引空 409)、SqlAnalysisToolTest(list_tables 含 AI 表、兜底)、
AiTemplateMatcherTest(召回只影响候选、异常降级)。/aiClean/templateVector/rebuild,
select count(*) from rag_tpl_d1024 应 = 「有字段的 table_info + 有字段的 ai_clean_template」数量;
再新建/更新一份模板,行数应增量 +1/覆盖。rag_tpl_d{旧dims} 表需人工 drop。rag_tpl_d* 表,无需再建索引。