ai-template-vector-index.md 6.8 KB

模板元数据向量索引(统一服务:Agent 查表 + AI 模板匹配)

版本:v2.0(2026-10-19,纠偏后) 模块:ai-server/src/main/java/com/zsjz/ai/module/aiclean/index/ 关键约束:只读模板库元数据,不扫描 DuckDB。


1. 要解决的问题

平台有两套模板元数据,代表两类「可被 Agent 使用的表」:

来源 元数据表 产生时机
既有模板库 table_info + table_field 上传清洗识别出的新表 / 模板管理页手工新建
AI 模板库 ai_clean_template + ai_clean_field AI 智能清洗出口 B 自建动态表 ai_clean_{id}

业务约束:DuckDB 里的表必须先登记进这两套元数据,才算「可被 Agent 使用的表」; 不扫描 DuckDB 系统表(既避免把 govern_tree/raw_* 等内部表暴露给模型,也让表清单与模板库、治理树、 AI 模板匹配保持同一口径)。

向量化结果有两个用途,共用同一份索引:

  1. Agent 问数 / 生成 SQL:search_table_schema 语义检索可选表(返回物理表名 + 物理列名 + 中文注释), list_tables 直接列元数据目录;
  2. AI 模板匹配:AiTemplateMatcher 腿 3 的向量召回(候选补充/排序,不单独判档)。

2. 设计结论

table_info + table_field ──┐
                           ├─> AiCleanTemplateVectorIndex ──> pgvector 表 rag_tpl_d{dims}
ai_clean_template + field ─┘        ↑ 增量事件 / 全量重建 / 启动自举      │
                                                                        ├─> searchTables(query) → Agent 查表(生成 SQL)
                                                                        └─> recall(query)      → AI 模板匹配召回

2.1 存储

  • 表名 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 精确删除。

2.2 文档与查询文本

  • 文档(建索引时嵌入):模板类型 + 表名(中文/英文) + 表头 + 字段清单(英文名/类型/中文注释/必填)。
  • 查询:Agent 用自然语言(如「包含手机号和身份证号的表」);模板匹配用主块表头。
  • 由于文档同时包含中英文表名、表头、字段语义,两种查询都能命中。

2.3 什么时候建

场景 入口
模板新增/更新/删除 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 清洗链路无需单独触发 —— 新建模板最终都走这两个入口。

2.4 两个用途的接入方式

用途 入口 行为
Agent 查表 RagSchemaService.search() / SqlAnalysisTool.listTables() searchTables 返回 tableName(物理表名)+ columns[].name(物理列名);list_tables 走 WorkspaceSchemaCatalog.allTables()(同一套元数据、实时)
AI 模板匹配 AiTemplateMatcher.mergeRecall recall 只补候选/调排序,不参与档位判定(向量分不得触发 STRONG/出口 A)

2.5 失败语义(fail-open)

  • 没配 agent_model 中 type='embedding' 活跃模型 / 开关关闭:isAvailable()=false,recall/searchTables 返回空。
  • 全量重建连续嵌入失败 ≥5 次自动中止(通常是模型/Key 不可用),避免刷屏;返回已写入数。
  • search_table_schema 在索引为空时返回 409 且提示先 rebuild;list_tables 不依赖向量索引,仍可用。
  • 事件发布/索引更新失败只 log.warn,绝不影响模板落库与清洗主流程。

3. 配置

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

4. 接口

方法 路径 说明
GET /js/a/aiClean/templateVector/status {available, vectorTable, embeddingModel, dimensions, docCount}
POST /js/a/aiClean/templateVector/rebuild 全量重建,返回写入文档数

5. 与问数链路的关系

  • RagSchemaService 已重写为统一索引的查询门面:search 调 AiCleanTemplateVectorIndex.searchTables, reindexWorkspace 调 rebuildNow,不再维护按案件/按 DuckDB 的表结构索引。
  • 因此「Agent 能查的表」= 两套模板元数据里登记的表(既有模板 + AI 动态表), 不再包含「库里存在但未登记元数据」的表。

6. 验证

  • 单测(离线):AiCleanTemplateVectorIndexTest(文档/payload 物理列名、searchTables 映射、AI 模板不过滤 ONLINE、 连续失败熔断)、WorkspaceSchemaCatalogTest(两套元数据合并、物理列名、无字段跳过、异常上抛)、 RagSchemaServiceTest(search/reindex 门面映射、索引空 409)、SqlAnalysisToolTest(list_tables 含 AI 表、兜底)、 AiTemplateMatcherTest(召回只影响候选、异常降级)。
  • 真库(需 dev PG + pgvector + 有效 embedding 模型):调一次 /aiClean/templateVector/rebuild, select count(*) from rag_tpl_d1024 应 = 「有字段的 table_info + 有字段的 ai_clean_template」数量; 再新建/更新一份模板,行数应增量 +1/覆盖。

7. 已知边界 / 后续

  • 索引最终一致:异步构建,模板刚落库的极短窗口内可能检索不到(不影响指纹/打分腿)。
  • 换嵌入模型且维度变化后旧 rag_tpl_d{旧dims} 表需人工 drop。
  • 向量召回只解决“召回”不解决“判对”;AI 模板匹配的出口硬门保持不变。
  • 若后续做「AI 模板相似合并去重 / 别名词典」,可直接复用这套 rag_tpl_d* 表,无需再建索引。