ai-template-vector-index.md 7.7 KB

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

版本:v2.1(2026-10-22,向量模型本地/在线开关) 模块: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{tag}_d{dims}
ai_clean_template + field ─┘        ↑ 增量事件 / 全量重建 / 启动自举      │
                                                                        ├─> searchTables(query) → Agent 查表(生成 SQL)
                                                                        └─> recall(query)      → AI 模板匹配召回

2.1 存储

  • 表名 rag_tpl{tag}_d{dims}(按 embedding 维度 + 模型隔离;换模型自然落新表)。
    • zsjz.embedding.enabled=true(本地模型):tag=_bge_m3,如 rag_tpl_bge_m3_d1024;
    • false(在线模型):tag 为空串,沿用 rag_tpl_d{dims},保持存量索引可用。
  • 列: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)

  • 向量模型统一由 EmbeddingProvider 路由:zsjz.embedding.enabled=true 走本地 ONNX bge-m3, false 回退 agent_model 中 type='embedding' 的活跃模型。本地开关打开但模型目录缺失时不静默回退在线模型。
  • 模型不可用 / 开关关闭:isAvailable()=false,recall/searchTables 返回空。
  • 全量重建连续嵌入失败 ≥5 次自动中止(通常是模型不可用),避免刷屏;返回已写入数。
  • search_table_schema 在索引为空时返回 409 且提示先 rebuild;list_tables 不依赖向量索引,仍可用。
  • 事件发布/索引更新失败只 log.warn,绝不影响模板落库与清洗主流程。

3. 配置

zsjz:
  embedding:
    enabled: true                        # true = 本地 bge-m3;false = 在线 embedding 模型
    model-path: bge-m3
    dimensions: 1024                     # 本地模型声明维度(建表/状态用,不触发加载)

  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 + 可用向量模型):调一次 /aiClean/templateVector/rebuild, select count(*) from rag_tpl_bge_m3_d1024(本地模式)或 rag_tpl_d1024(在线模式) 应 = 「有字段的 table_info + 有字段的 ai_clean_template」数量; 再新建/更新一份模板,行数应增量 +1/覆盖。

7. 已知边界 / 后续

  • 索引最终一致:异步构建,模板刚落库的极短窗口内可能检索不到(不影响指纹/打分腿)。
  • 换嵌入模型(含本地/在线开关切换)会落到新表名;旧 rag_tpl* 表需人工 drop(不会自动删除数据)。 附件 RAG 同理:切换后旧模型索引的附件在新表名下无召回,按「无片段」降级,不会串用旧向量。
  • 向量召回只解决“召回”不解决“判对”;AI 模板匹配的出口硬门保持不变。
  • 若后续做「AI 模板相似合并去重 / 别名词典」,可直接复用这套 rag_tpl* 表,无需再建索引。