我用AI给挖掘机维修手册做了一个RAG搜索引擎

0 阅读14分钟

一、老张的687页难题

"小刘,PC220-6 回转马达的固定螺栓扭矩是多少?"

维修技师老张蹲在一台小松挖掘机旁,满手液压油。他翻开那本被翻得卷了边的维修手册——687页,A4开本,厚度堪比一本《新华字典》。他翻了5分钟,终于在拆装章节找到了扭矩值:PC200-6 回转马达固定螺栓 490 N·m

然后他愣住了——他的机子是 PC220-6

老张的困境不是个例。小松 PC200-6/PC220-6 系列挖掘机作为工程机械的经典机型,其官方维修手册涵盖5个衍生型号,每种型号的零件规格、液压参数、扭矩标准都有差异。维修技师在烈日下翻纸本手册找一条数据,少则3分钟,多则10分钟。更头疼的是,手册里充斥着 PPC、CLSS、LS、EPC 这类工程缩写,新人看了直接懵圈。

如果能像问ChatGPT一样问维修手册呢?

这就是我们做这件事的起点——用RAG(检索增强生成)技术,给这本旧手册装上AI引擎,让维修技师用自然语言提问,秒级拿到带页码溯源的准确答案。


二、技术方案概览:从PDF到智能问答

整个系统是一个部署在工控机上的本地Web应用,纯离线运行(工地没4G信号也能用),零成本预算(除了电脑本身)。技术架构分为四层:

┌─────────────────────────────────────────────────┐
│                  🖥️  Web 前端                    │
│   搜索输入 · 手册切换 · 图片Lightbox · 反馈收集  │
│          响应式布局 · 移动端优先                  │
└──────────────────────┬──────────────────────────┘
                       │ REST API (127.0.0.1:8765)
┌──────────────────────┴──────────────────────────┐
│              🐍  Python API Server               │
│    /api/query  /api/upload  /api/thumbnail       │
│    会话管理 · 搜索日志 · 反馈收集 · CORS          │
└──────────────────────┬──────────────────────────┘
                       │
┌──────────────────────┴──────────────────────────┐
│            🔀  HybridRetriever 混合检索           │
│   BM25 关键词(jieba) ← RRF融合 → ChromaDB 向量   │
│              Domain Reranker(领域重排序)          │
└──────────────────────┬──────────────────────────┘
                       │
┌──────────────────────┴──────────────────────────┐
│              📚 知识库构建管线                    │
│  PDF OCR → 结构化提取 → Hybrid Chunking → 向量化  │
│    378张图片提取 · 领域词汇扩展 · 多手册索引     │
└─────────────────────────────────────────────────┘

核心技术选型一览:

环节选型理由
嵌入模型all-MiniLM-L6-v2 (384维)轻量、本地运行、无需GPU
向量数据库ChromaDB嵌入式部署、元数据过滤、SQLite持久化
关键词检索BM25 (rank-bm25 + jieba分词)对零件编号/数值精确匹配效果好
融合算法RRF (Reciprocal Rank Fusion, k=60)融合语义与关键词两路召回
后端框架Python http.server零依赖,够用
前端原生 HTML/CSS/JS零框架,离线运行,移动端友好
图片处理Pillow服务端动态缩略图生成

为什么选这套方案?三个原则:零云依赖(工地没网也能用)、零成本(开源组件一条龙)、中文友好(jieba分词 + 领域词汇扩展弥补英文嵌入模型的短板)。


三、深水区一:扫描件PDF的OCR与Chunking策略

我们拿到的原始资料是实体手册扫描生成的PDF,并非可直接检索的电子文档,687页每页都需要走OCR识别流程。我们最终选用PaddleOCR,它对中文印刷体识别准确率高,原生支持多语言,刚好匹配我们的特殊场景:这本手册原本是日文手册的中文版,扫描件中残留了大量日文假名、标记字符和水印,比如"ページ""図"这类日文词汇。我们专门开发了正则规则清理模块,批量匹配并删除常见日文残留和无效水印,清理后整体OCR准确率提升了约8个百分点。

OCR完成后,我们先做了结构化章节提取:通过识别文字大小、缩进特征来判断标题层级,从OCR结果中重建出和原文档一致的目录结构,把每页文本归属到对应章节,既保证后续chunk的语义完整性,也方便最终结果溯源到具体章节。

在Chunking策略上,我们对比测试了三套方案:第一套固定token切分,实现简单但经常把完整技术参数说明从中间切开,造成语义破碎;第二套按自然段落切分,语义完整但段落长度差异极大,长段落超出嵌入窗口,短段落语义信息不足;第三套就是我们最终采用的Hybrid Chunking混合策略:先按章节语义块切分出约800字符的大chunk,保证完整技术点不被切割,再用200字符的重叠滑动窗口对大chunk二次切分,重叠设计保证关键参数不会因为切分丢失边界信息。

我们做了一个小创新:由于OCR输出经常存在碎片化和换行乱序,直接用原文做embedding效果不佳。我们给每个chunk额外生成一句100字以内的通顺摘要,整理碎片化信息,用这段摘要做embedding,同时保留原始OCR文本作为可追溯引用。这个改进让语义检索Top1准确率提升了15个百分点,效果超出预期。


四、深水区二:BM25 + 向量检索 + RRF 混合检索

工程机械维修手册的检索需求天生分成两类,一类是语义型问题,比如"液压系统怎么工作""回转无力怎么排查",这类问题需要理解用户意图做语义匹配;另一类是精确型查询,比如"34.8 MPa""DB60001""PC220-6回转螺栓扭矩",这类问题核心是精确匹配特定数值或零件编号,语义匹配很容易错配。

单一向量检索很难同时满足两种需求,因此我们采用了双路并行召回架构:用rank-bm25结合jieba中文分词做关键词检索,专门处理精确匹配需求;用ChromaDB做向量检索,专门处理语义匹配需求。两路检索各召回Top-20候选结果,再用RRF(Reciprocal Rank Fusion)算法做分数融合,RRF的核心思想是,一个chunk如果在两路检索中的排名都比较高,它的最终得分就更高,公式是 score = sum(1/(rank + k)),我们这里选择k=60,这是社区经过验证的常用值,适配我们两路Top-20召回的场景。

融合之后我们加了一层领域专属的重排序Domain Reranker,做两个核心优化:第一是机型加权,用户查询里提到PC220-6,我们就给所有标注了PC220相关的chunk提升20%权重,这样同系列不同机型参数不会互相干扰;第二是缩写扩展,工程机械领域有大量行业缩写,比如PPC就是泵功率控制,CLSS是闭式负荷感应系统,我们预先构建了领域缩写词典,检索时自动把缩写扩展为全称作为同义词加入查询,同时匹配原缩写和全称,解决了新人搜索找不到答案的痛点。

这套混合检索方案比单纯向量检索,在我们的测试集上精确匹配准确率提升了22个百分点,特别是零件编号和技术参数这类查询的提升尤其明显,同时兼顾了语义检索的灵活性和关键词检索的精确性。


五、深水区三:从单手册到多手册的架构演进

第一个版本我们只支持单手册索引,上线后很快遇到痛点:用户有PC200-6、PC220-6、PC200LC-6等多本不同机型手册,每次更换机型都需要重新跑一遍OCR→Chunking→向量化整个索引管线,不仅耗时,还没法做多手册跨册检索,用户想查多个手册共有的故障也只能分开搜,体验很差。

因此我们演进到Phase 2多手册架构,核心设计思路是每个PDF独立构建索引,对应ChromaDB里一个独立collection:这样新增手册只需要构建新的collection,不需要修改已有索引,删除手册也只需要删除对应collection,完全隔离互不影响。API层新增了manual_ids查询参数,前端通过手册选择器控制检索范围,用户可以选单本手册,也可以选全部手册跨册检索。

当用户选择跨册检索时,我们采用多collection并行查询,每个collection各自返回Top-20结果,再统一用RRF做分数合并。这里有个公平性问题:不同collection的chunk总数不同,直接用原始排名合并会让chunk多的大手册占据优势,因此我们对每个collection的排名做了归一化处理,保证不同大小手册的结果公平竞争。

落地时几个细节值得一提:一是collection命名规范统一用{prefix}_{model}格式,方便程序识别和管理;二是每个chunk元数据都添加了manual_id字段,就算做单collection多文档存储也能支持元数据过滤;三是对不同手册中重复的内容,我们通过文本哈希做检索结果去重,避免同一内容占据多个结果位置。这套架构现在可以平滑支持10+本不同机型手册并行检索,新增一本手册只需要2-3分钟,完全满足工程场景的扩展需求。


六、不止后端:9个前端交互细节让"AI查手册"真正可用

后端做了混合检索和领域重排序,但如果前端只是套一个文本输入框+裸文本输出,老张在工地上绝对用不下去。技术产品落地的关键往往在交互细节——下面分享我们打磨的9个前端UX设计。

1. 响应式布局:移动端优先

维修技师的场景是什么?蹲在挖掘机旁边,掏手机。所以前端优先适配竖屏移动端(375px宽度起),PC端自适应。

/* 移动端优先的单列布局 */
.container {
  display: flex;
  flex-direction: column;
  padding: 12px;
  max-width: 800px;
  margin: 0 auto;
}

/* 搜索输入区域固定在顶部 */
.search-bar {
  position: sticky;
  top: 0;
  z-index: 10;
  background: var(--bg);
  padding: 8px 0;
}

没有用任何CSS框架,纯原生的 display: flex + @media 查询,整个前端不到500行CSS。在泥地里也能看清的配色方案:高对比度白色背景 + 深色文字,字号最小14px,按钮触摸目标至少44×44px(符合移动端触控标准)。

2. 快捷提问卡片:降低使用门槛

新用户面对一个空白的搜索框,往往不知道问什么。我们在搜索框下方放了6个预置问题卡片:

<div class="quick-asks">
  <button class="quick-ask-chip" data-query="PC220-6 回转马达扭矩多少">
    🔧 回转马达扭矩
  </button>
  <button class="quick-ask-chip" data-query="液压泵LS压差怎么调节">
    ⚙️ LS压差调节
  </button>
  <button class="quick-ask-chip" data-query="主安全阀设定压力是多少">
    📐 主安全阀压力
  </button>
  <!-- ...更多 -->
</div>

每个卡片既是一个快捷入口,也是引导——让用户直观理解"这个系统能回答什么类型的问题"。点击后自动填入搜索框并触发检索,减少两次点击。

3. 多手册选择器:一本 or 全部?

当系统加载了多本手册后,顶部会出现一个下拉选择器:

┌─────────────────────────────┐
│ 📚 全部手册(跨册搜索)      │ ← 默认
│    PC200-6 装修手册          │
│    PC220-6 装修手册          │
│    PC200LC-6 装修手册        │
└─────────────────────────────┘

选中"全部手册"时,后端并行检索所有手册的索引,按 RRF 分数合并后取 Top-K。选中单本手册时,只在该手册的索引中检索。切换手册时,用 URLSearchParams 同步到地址栏,刷新不丢状态:

// 手册选择持久化到 URL
const params = new URLSearchParams(location.search);
const manualId = params.get('manual') || 'all';
document.getElementById('manual-select').value = manualId;

// 选择变更时同步
manualSelect.addEventListener('change', (e) => {
  params.set('manual', e.target.value);
  history.replaceState(null, '', `?${params}`);
});

4. 打字指示器:消除等待焦虑

检索耗时 ~250-400ms,但加上LLM生成答案,总响应时间可能到2-3秒。在AI产品中,等待是焦虑的放大器。我们加了一个经典的"三个跳动圆点"打字指示器:

<div class="typing-indicator">
  <span class="dot"></span>
  <span class="dot"></span>
  <span class="dot"></span>
</div>
.typing-indicator .dot {
  width: 8px; height: 8px;
  background: #666;
  border-radius: 50%;
  animation: bounce 1.4s infinite ease-in-out both;
}
.typing-indicator .dot:nth-child(1) { animation-delay: -0.32s; }
.typing-indicator .dot:nth-child(2) { animation-delay: -0.16s; }

纯CSS动画,零JS。用户看到圆点在跳,就知道系统在工作,而不是卡死了。这个设计在小破站上用了十年,老但靠谱。

5. 来源标签:页码溯源 + 相关度分数

RAG最怕"答案看起来合理但不知道从哪来的"。每个答案下方附带来来源标签,告诉用户信息来自手册第几页、相关度多少:

<div class="source-tag">
  📖 PC200-6 装修手册 · 第 30-12 页 · 相关度 87%
</div>

来源标签使用不同颜色区分高相关(>80% 绿色)和中等相关(50-80% 黄色),一眼可判断"这条答案靠不靠谱"。点击标签还能跳转到该页的扫描件图片。

6. 图片懒加载 + 缩略图 + Lightbox

手册中有 378 张技术插图——液压回路图、拆装示意图、零件分解图。直接嵌入全分辨率图片(每张可能 2-3MB)会炸掉移动端内存。

我们做了三级图片处理:

  1. 服务端动态缩略图/api/thumbnail?img=img_0123.png&size=200,Pillow 按宽度等比缩放,磁盘缓存,重复请求秒返。
  2. 前端懒加载<img loading="lazy"> + IntersectionObserver 兜底,只加载进入视口的图片。
  3. Lightbox 全屏查看:点击缩略图弹出高清原图,ESC 或点击遮罩关闭。
// Lightbox — 纯CSS+JS,60行搞定
function openLightbox(imgSrc) {
  const overlay = document.createElement('div');
  overlay.className = 'lightbox-overlay';
  overlay.innerHTML = `<img src="${imgSrc}" alt="原图">`;
  overlay.addEventListener('click', () => overlay.remove());
  document.body.appendChild(overlay);
  // ESC关闭
  document.addEventListener('keydown', function esc(e) {
    if (e.key === 'Escape') { overlay.remove(); document.removeEventListener('keydown', esc); }
  });
}

7. 反馈按钮:👍👎 驱动持续改进

每个答案后面跟两个小按钮:👍 有用 / 👎 没用。点击后静默记录到服务端 feedback_log.jsonl

{"timestamp": "2026-06-15T08:30:00", "query": "回转马达扭矩", "rating": "up", "session_id": "abc123"}
{"timestamp": "2026-06-15T08:31:00", "query": "PC阀拆装步骤", "rating": "down", "session_id": "abc123"}

反馈数据是改进检索策略的金矿——哪些查询的召回差?哪些回答被踩了?我们计划积累500+条反馈后,用这些数据做检索结果的自动化调优(调整BM25权重、优化chunk大小、甚至用反馈数据微调嵌入模型)。

8. PDF上传:拖拽 + 进度 + 自动索引

系统支持上传新的维修手册PDF,一键扩展知识库:

<div class="upload-zone" id="uploadZone">
  <div class="upload-hint">
    📤 拖拽PDF到此处 或 点击选择文件
  </div>
  <progress id="uploadProgress" value="0" max="100"></progress>
  <div id="uploadStatus"></div>
</div>
// 拖拽上传
uploadZone.addEventListener('drop', async (e) => {
  e.preventDefault();
  const file = e.dataTransfer.files[0];
  if (!file.name.endsWith('.pdf')) return;

  const formData = new FormData();
  formData.append('file', file);

  const xhr = new XMLHttpRequest();
  xhr.upload.addEventListener('progress', (e) => {
    const pct = Math.round((e.loaded / e.total) * 100);
    uploadProgress.value = pct;
    uploadStatus.textContent = `正在上传... ${pct}%`;
  });

  xhr.addEventListener('load', () => {
    uploadStatus.textContent = '✅ 上传完成,正在构建索引...';
    // 后端异步构建索引,轮询状态
    pollIndexingStatus(xhr.response.id);
  });

  xhr.open('POST', '/api/upload');
  xhr.send(formData);
});

上传完成后,后端自动走一遍OCR→Chunking→向量化的完整管线,2-3分钟后新手册可检索。进度条消除了"我上传了然后呢?"的迷茫感。

9. 搜索日志:持续优化闭环

每次查询都记录到 search_log.jsonl

{
  "timestamp": "2026-06-15T08:30:01",
  "query": "回转马达扭矩",
  "manuals": ["pc200-6"],
  "results_count": 5,
  "latency_ms": 248,
  "session_id": "abc123"
}

这些日志是产品迭代的数据基础——高频查询可以做成预设问题卡片,零结果查询暴露知识库盲区,慢查询指导性能优化方向。我们把这部分暴露了一个简单的管理面板(/admin/logs),用表格展示最近的搜索记录,方便离线时也能分析。


七、当前效果与改进方向

在10个测试用例上的评估:

指标数值说明
关键词覆盖率96.7%检索能覆盖绝大多数查询意图
平均响应时间248ms单手册检索,秒级返回
多手册响应时间~400ms3手册并行检索合并
页面准确率30%受限于英文嵌入模型的中文语义能力
知识库规模~150个chunks单手册模式,多手册可线性扩展

当前最大的短板是页面准确率只有30% ——这意味着搜索返回的Top-5结果中,真正匹配的不到2条。根因是我们用了英文为主的 all-MiniLM-L6-v2 嵌入模型来处理中文工程文本。

改进路径很明确:

  1. 嵌入模型升级:切换到 BGE-small-zh-v1.5(中文专用,512维),预期将页面准确率提升到 60%+。之前没选它只是因为网络环境从 HuggingFace 下载不稳定——后续可以考虑离线下载后打包到部署包里。
  2. 反馈驱动调优:积累500+条用户反馈后,用踩赞比标注高质量和低质量查询,针对性调整 chunking 策略和 BM25 权重。
  3. 混合嵌入:对零件编号(如"DB60001")这类确定性高的查询,直接用精确匹配兜底,跳过向量检索环节。

八、写在最后

这个项目没有用任何"高大上"的技术——没有 LangChain、没有 Pinecone、没有大语言模型 API。就是用 Python 自带 http.server 搭了个服务,ChromaDB 存了向量,BM25 做了关键词检索,jieba 分了中文词,再用原生 HTML/CSS/JS 写了响应式前端。

但就是这样一套"土法炼钢"的组合,把一个687页的扫描件PDF变成了能在工地上用手机秒查的AI问答系统。

值得做的方向还很多:

  • Docker 一键部署,让任何维修厂都能开箱即用
  • 扩展到更多机型(PC300、PC400 系列)
  • 接入本地 LLM(如 Qwen2.5-7B),从"检索结果罗列"升级为"带推理的完整回答"
  • 知识库众包——让维修师傅贡献自己的经验笔记,自动索引到系统里

最后一句话:AI 落地的真正价值不在论文里,而在老张蹲在挖掘机旁边掏出手机的那一瞬间。


项目地址:[GitHub (待开源)]
技术栈:Python · ChromaDB · BM25 · jieba · Sentence-Transformers · 原生 HTML/CSS/JS
标签:#RAG #向量检索 #ChromaDB #BM25 #工程机械 #PDF数字化