一个 Python 脚本 + LLM 怎么替代向量检索?ProjQA 技能架构原理深度解析

一个 Python 脚本 + LLM 怎么替代向量检索?ProjQA 技能架构原理深度解析

ProjQA 不用 Embedding、不用向量数据库、不用检索后端,却能让 AI Agent 精准回答项目知识问题。本文从架构层面拆解它的检索原理、数据流、设计取舍,以及为什么这套方案在中小规模场景下比传统 RAG 更优。

整体架构

复制代码
┌──────────────────────────────────────────────────┐
│                 AI Agent (LLM)                    │
│                                                   │
│  ┌───────────────┐    ┌────────────────────┐     │
│  │  别名映射表    │    │     索引文件        │     │
│  │ _aliases.md   │    │   _index.md        │     │
│  └───────┬───────┘    └─────────┬──────────┘     │
│          │                       │                │
│          ▼                       ▼                │
│  ┌─────────────────────────────────────────┐     │
│  │         检索与匹配引擎(LLM)             │     │
│  │  别名映射 → 关键词匹配 → 语义理解        │     │
│  │  → 候选排序 → 取 Top-K                   │     │
│  └──────────────────┬──────────────────────┘     │
│                     │                             │
│                     ▼                             │
│  ┌─────────────────────────────────────────┐     │
│  │         文档读取与定位(LLM)             │     │
│  │  多格式解析 → 章节定位 → 内容提取        │     │
│  └──────────────────┬──────────────────────┘     │
│                     │                             │
│                     ▼                             │
│  ┌─────────────────────────────────────────┐     │
│  │         回答生成(LLM)                  │     │
│  │  综合内容 + 来源标注 + 兜底机制          │     │
│  └─────────────────────────────────────────┘     │
│                                                   │
└──────────────────────────────────────────────────┘
         ▲
         │ 调用
         │
┌────────┴─────────────────────────────────────────┐
│            scripts/scan_projqa.py                 │
│  文件扫描 → 增量比对 → 索引更新 → 待补清单        │
│  纯确定性操作,无 LLM 参与                         │
└──────────────────────────────────────────────────┘
         ▲
         │ 扫描
         │
┌────────┴─────────────────────────────────────────┐
│              projqa-docs/                         │
│  ├── 电商平台/                                    │
│  │   ├── FAQ/                                     │
│  │   ├── 运维手册/                                 │
│  │   └── 服务资料/                                 │
│  ├── 技术中台/                                    │
│  │   └── 架构设计/                                 │
│  ├── _index.md                                   │
│  └── _aliases.md                                 │
└──────────────────────────────────────────────────┘

三层结构,各司其职:

  • 底层数据层:知识库目录,存放原始文档和元数据文件
  • 中间脚本层:Python 脚本,做确定性的文件系统操作
  • 上层智能层:LLM,做所有需要理解的检索与回答

核心数据结构:索引

索引(_index.md)是整个技能的检索枢纽。每条索引对应一个文档:

复制代码
- [电商平台/FAQ/支付超时排查.md] | 关键词: 支付, 超时, 504, payment-gateway, Redis, 熔断 | 简述: 支付网关504超时的排查链路 | 提问摘要: 支付超时怎么排查? Redis连接池配置多少? 熔断怎么触发? | mtime: 2026-08-27 10:00:00 | size: 612

六个字段的职责分工:

字段 优先级 由谁生成 作用
相对路径 --- 脚本 文件定位
关键词 P0 必补 LLM 实体检索匹配
简述 P0 必补 LLM 内容概览判断
提问摘要 P1 增强 LLM 语义精准匹配
mtime --- 脚本 增量检测
size --- 脚本 增量检测

关键设计:语义字段由 LLM 生成,元数据字段由脚本维护。两套字段互不干涉,各自由最适合的执行者处理。

检索链路详解

第 1 步:别名映射

复制代码
用户提问:"库存中心的数据挂了怎么办"

LLM 读取 _aliases.md,将口语称呼映射到标准服务名:

复制代码
库存中心 -> inventory-center
数据挂了 -> 数据库连接失败 / 数据库宕机

这一步是传统向量检索做不到的------向量只能算"库存"和"inventory"的 Embedding 距离,而 LLM 能理解它们指的是同一个实体。

第 2 步:索引扫描匹配

LLM 读取 _index.md 全文,在每个条目的语义字段上做多级匹配:

匹配优先级(从高到低)

优先级 匹配字段 逻辑 示例
1 提问摘要 语义高度匹配 "库存......数据......" ↔ 命中"数据库连接失败怎么排查?"
2 关键词 核心实体命中 "inventory-center" ↔ 关键词含 inventory-center
3 简述 语义相关 "数据库连接失败" ↔ 简述含"数据库"
4 文件路径 路径包含关键词 "库存" ↔ 路径含 /服务资料/
5 文件名 名称包含关键词 "inventory" ↔ 文件名含 inventory

候选数量控制

  • 默认取 1-3 个最相关文档
  • 跨主题问题(如"订单服务依赖库存和支付,怎么排查链路")放宽到 3-5 个
  • 单文档命中充分时不读多余文档

第 3 步:文档读取与定位

命中候选后,按格式选择读取方式:

复制代码
.md / .txt  → 直接读取
.docx       → 平台 Word 解析能力
.xlsx       → 平台 Excel 解析能力
.pdf        → 平台 PDF 解析能力

大文档(>200 行)的分章节策略:

复制代码
1. 先读前 50 行获取标题结构
2. 根据标题定位相关章节的行号范围
3. 只读取该范围内容
4. 正文无序时用关键词位置定位

这里有一个和入库时的联动设计:大文档入库时,提问摘要里会追加章节索引信息:

复制代码
提问摘要: §部署步骤: 怎么部署? §环境变量: 需要配置什么? §常见故障: 启动失败怎么办?

检索时 LLM 能直接从提问摘要判断应该读哪个章节,跳过无关部分。500 行的文档可能只需要读 30 行。

第 4 步:回答生成

复制代码
综合文档内容 → 结构化输出 → 来源标注

回答必须包含来源标注:来源:电商平台/FAQ/支付超时排查.md

第 5 步:兜底机制

无匹配时的处理链路:

复制代码
别名兜底(尝试同义词/缩写)
  → 扩宽检索(放宽关键词、读文件名和开头段落)
  → 明确告知"未找到"
  → 给出最接近的相关文档(若有)
  → 提示用户补充文档

核心原则:禁止编造。找不到就明说,引导用户补文档,形成"提问 → 缺失 → 补文档 → 下次能答"的闭环。

入库链路详解

入库是知识库质量的保障线。完整流程:

复制代码
放文档到目录
  │
  ▼
运行 scan_projqa.py --check-similarity    ← 第一次扫描
  │
  ├──→ 增量比对 mtime/size
  ├──→ 新文件追加索引条目(语义字段留空)
  ├──→ 变更文件清空旧语义信息
  ├──→ 输出分级待补清单(P0/P1)
  └──→ 相似检测提醒(≥60% 提醒可能重复)
  │
  ▼
LLM 根据待补清单补充语义信息              ← 智能体参与
  │
  ├──→ P0: 生成关键词(5-10个)+ 简述(一句话)
  ├──→ P1: 生成提问摘要(2-3个提问式描述)
  └──→ 大文档追加章节索引
  │
  ▼
运行 scan_projqa.py                       ← 第二次扫描(验证)
  │
  └──→ 确认"待补清单: 无" → 入库完成

分级待补机制

级别 判定条件 标记 必要性
P0 关键词或简述为空 !! 必须完成
P1 提问摘要为空 ? 建议完成

这个分级机制确保了入库质量可控------P0 不完成就不算入库完成,P1 是检索质量的增量优化。

增量比对原理

脚本通过 mtime(修改时间)和 size(文件大小)两个字段做增量检测:

python 复制代码
# 伪代码
if old_mtime != new_mtime or old_size != new_size:
    # 文件变更了,清空语义信息,需要重新补充
    entry.keywords = ""
    entry.summary = ""
    entry.questions = ""

这个设计的精妙之处:脚本不需要读文件内容就能判断文件是否变更。比对文件系统元数据是 O(1) 操作,上千文件也秒级完成。

而文件变更后清空语义信息,是因为内容变了,旧的关键词/简述/提问摘要可能已经不准确了。宁可重新生成,也不用过时信息。

与传统 RAG 的深度对比

检索原理对比

传统 RAG

复制代码
Query → Embedding → 计算与所有文档向量的余弦相似度 → 排序 → Top-K

检索质量取决于 Embedding 模型的质量。两个语义相同但词面不同的句子,如果 Embedding 模型不够好,向量距离可能很远。

ProjQA

复制代码
Query → 别名映射 → LLM 读取索引 → 语义理解 + 关键词匹配 → 候选排序 → Top-K

检索质量取决于 LLM 的理解能力。LLM 能理解"库存中心"和"inventory-center"是同一实体,能理解"挂了"等于"宕机"------这是向量检索做不到的。

维护成本对比

操作 传统 RAG ProjQA
新增文档 切块 → Embedding → 写入向量库 放文件 → 跑脚本 → LLM 补语义
更新文档 重新切块 → 重新 Embedding → 更新向量库 改文件 → 跑脚本(自动清旧语义)→ 重新补充
删除文档 从向量库删除对应 chunk 删文件 → 跑脚本(自动移除)
检索调优 换 Embedding 模型 / 调 chunk 大小 / 加 reranker 修改索引的关键词/提问摘要(即时生效,无需重新处理)

可解释性对比

传统 RAG :检索结果是一组向量距离分数,比如 0.873。这个数字代表什么?很难解释。为什么这篇文档排第一?不透明。

ProjQA:检索依据是索引字段,完全可读。比如某文档被选中是因为提问摘要里有"支付超时怎么排查?",和用户提问高度匹配------这个匹配链路是可追溯、可人工修正的。

适用边界

ProjQA 的优势区间

  • 文档规模:百级以下(索引 100 行左右,LLM 扫描无压力)
  • 文档组织:以项目/团队为单位,结构清晰
  • 检索场景:运维问答、排障查询、服务信息查找
  • 理解需求:需要别名映射、口语化理解(LLM 的强项)

传统 RAG 的优势区间

  • 文档规模:万级以上(索引大小超出上下文窗口)
  • 检索场景:开放域问答、海量知识检索
  • 响应要求:毫秒级检索延迟
  • 技术栈:已有成熟的 Embedding + 向量库基础设施

设计哲学总结

ProjQA 的架构设计可以用三句话概括:

  1. 脚本做脏活,LLM 做判断------确定性操作用脚本,语义理解交 LLM
  2. 索引是桥梁------脚本维护结构,LLM 维护语义,索引是两者的交接点
  3. 简单到不需要解释------没有 Embedding,没有向量库,没有后端,一个脚本 + 一份索引就够了

不是所有问题都需要工程化解决。在中小规模项目知识问答这个场景里,LLM 自身的理解能力就是最好的检索引擎。


相关推荐
隔窗听雨眠16 分钟前
ARM架构下Logstash与Elasticsearch集群部署完全指南:从环境适配到生产验证
arm开发·elasticsearch·架构
SamChan9021 分钟前
Python+PyMuPDF提取PDF表格并翻译:表格结构检测与双语重建实战
windows·python·ai·pdf·wpf
weixin_3077791322 分钟前
PySpark根据输入的表名和过滤条件生成 INSERT 语句
python·spark·云计算·big data
赛博三把手23 分钟前
DeepSeek Harness (dsh) 国内网络接入第三方大模型聚合平台 API:以 Claude Opus 5 /Fable 5为例
人工智能·架构·开源
GodSure091428 分钟前
Java单一职责原则SRP详解
java·python·单一职责原则
探数API小喇叭32 分钟前
基站查询 API 怎么用?LAC、CELLID 参数详解与实战】
java·开发语言·api·基站定位
RestCloud33 分钟前
行业数据集成架构对比:金融、制造、政务三大场景的差异化设计
金融·架构·制造·etl·etlcloud·数据传输·数据集成平台
Mr.Lu ‍34 分钟前
C++开发,使用openCV API对图片进行压缩
开发语言·c++·opencv
z落落36 分钟前
C# WinForm NModbus 串口通信+WinForm Modbus4 串口通信
开发语言·单片机·c#