进阶 A · 多模态 RAG:图片与表格检索
知识库里不只有文字------截图、架构图、Excel 表格都藏着关键信息。多模态 RAG 让这些"非文字知识"也能被检索。
1. 多模态 RAG 的两种路线
| 路线 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 文本描述法 | 用 Vision LLM 把图片转文字,再走标准 RAG | 简单,复用现有架构 | 描述质量决定检索上限 |
| 多模态 Embedding | 用 CLIP 等模型直接对图片编码 | 精度更高 | 需要额外的向量存储和模型 |
推荐起步姿势:文本描述法。先用 Vision LLM 跑通,再逐步升级。
2. 图片 RAG:文本描述法
typescript
// multimodal/image.ts
import { ChatOpenAI } from "@langchain/openai";
class ImageDescriber {
private visionLLM = new ChatOpenAI({ model: "gpt-4o" });
async describeImage(imageBase64: string): Promise<ImageDescription> {
const response = await this.visionLLM.invoke([
{
type: "text",
text: "请详细描述这张图片的内容,包括:1. 整体内容 2. 关键文字/数据 3. 图表类型(如果是图表)4. 与前端开发相关的信息"
},
{
type: "image_url",
image_url: { url: `data:image/png;base64,${imageBase64}` }
}
]);
return {
description: response.content as string,
imageBase64,
timestamp: Date.now(),
};
}
}
// 图片摄入流水线
async function ingestDocumentWithImages(filePath: string) {
const { text, images } = await parseDocument(filePath);
const chunks: Document[] = [];
// 标准文本分块
chunks.push(...await textChunker.split(text));
// 图片转文本描述
for (const img of images) {
const desc = await imageDescriber.describeImage(img.base64);
const imageChunk = new Document({
pageContent: `[图片描述]: ${desc.description}`,
metadata: {
type: "image",
originalImage: img.base64, // 保留原图供 LLM 查看
position: img.position, // 在文档中的位置
}
});
chunks.push(imageChunk);
}
await vectorStore.addDocuments(chunks);
}
检索增强:当检索到的 Chunk 带有图片元数据时,把原图也传给 LLM:
javascript
async function generateWithImages(results: Document[], query: string) {
const messages = [{ type: "text", text: `根据以下内容回答问题:\n\n${query}` }];
for (const doc of results) {
messages.push({ type: "text", text: `文档:${doc.pageContent}` });
if (doc.metadata.type === "image" && doc.metadata.originalImage) {
messages.push({
type: "image_url",
image_url: { url: `data:image/png;base64,${doc.metadata.originalImage}` }
});
}
}
return visionLLM.invoke(messages);
}
3. 表格 RAG
表格的问题在于:把表格当成普通文本会丢失结构化信息。
3.1 表格摘要法
typescript
// multimodal/table.ts
class TableProcessor {
async summarizeTable(tableData: { headers: string[]; rows: string[][] }) {
const tableMarkdown = [
"| " + tableData.headers.join(" | ") + " |",
"|" + tableData.headers.map(() => "---").join("|") + "|",
...tableData.rows.map(row => "| " + row.join(" | ") + " |"),
].join("\n");
const summary = await llm.invoke(`
总结以下表格的关键信息(50-100字),保留具体的数字和名称:
${tableMarkdown}
总结:
`);
return {
markdown: tableMarkdown,
summary: summary.content,
};
}
}
// 检索:对摘要做向量匹配,返回时对 LLM 展示完整表格
async function tableRAG(query: string) {
// 检索命中摘要
const results = await vectorStore.similaritySearch(query, 5);
const context = results.map(r => {
if (r.metadata.type === "table") {
return `表格:\n${r.metadata.tableMarkdown}`;
}
return r.pageContent;
}).join("\n\n");
return llm.invoke(`根据以下内容回答问题:\n\n${context}\n\n问题:${query}`);
}
4. Node.js 多模态文档处理工具链
bash
# 图片转 base64
npm i sharp
# PDF 提取图片和文本
npm i pdf-parse pdf2pic
# Office 文档处理
npm i mammoth # .docx → HTML (含图片)
npm i xlsx # .xlsx 表格解析
typescript
// 完整的多模态文档处理器
import sharp from "sharp";
import * as XLSX from "xlsx";
class MultimodalDocumentProcessor {
async process(filePath: string, mimeType: string) {
switch (mimeType) {
case "application/pdf":
return this.processPDF(filePath);
case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":
return this.processExcel(filePath);
case "image/png":
case "image/jpeg":
const buffer = await fs.readFile(filePath);
const desc = await imageDescriber.describeImage(buffer.toString("base64"));
return [{ pageContent: desc.description, metadata: { type: "image" } }];
default:
return this.processText(filePath);
}
}
}
5. 关键决策点
- 不需要一开始就上多模态:先验证纯文本 RAG 的价值,再逐步加图片/表格支持
- 图片描述的质量决定了检索质量:用最好的 Vision 模型做描述,这笔钱值得花
- 表格保留 Markdown 格式给 LLM:LLM 对 Markdown 表格的理解远好于 JSON
- 架构图/流程图目前效果有限:Vision 模型描述复杂架构图容易出错,建议人工标注关键信息
上一篇:06 · 从开发到生产 下一篇:B · Graph RAG:知识图谱增强检索