AI应用开发四:RAG多模态数据处理

RAG 做到文本问答,只是第一步。真实业务中的知识并不只存在于纯文本里。一个客服知识库可能同时有 Word、PDF、网页、表格、活动海报、产品图片、视频说明;一个运营分析场景里,可能有 Excel、截图、合同扫描件、录音、会议视频;一个财务对账场景里,可能还有托管行 PDF、日文材料和内部系统数据。

所以多模态 RAG 要解决的核心问题是:

text 复制代码
不同模态的数据
  -> 解析成可处理的内容
  -> 转成统一的语义表示
  -> 建立索引
  -> 用户提问时统一检索
  -> 把文本、图片、视频等结果一起交给 LLM
  -> 输出答案,并附上相关媒体

这里有两条主线:

  1. 多模态大模型可以直接理解图像、音频、视频、PDF、代码等输入。
  2. 多模态 Embedding 可以把文本、图片、视频映射到统一向量空间,支撑跨模态检索。

这两条线结合起来,才能做出"既能查文档,又能找图片,还能关联视频"的 RAG 应用。

一、多模态 RAG 的背景与模型能力

多模态不只是"看图说话"

多模态能力常被理解成"上传一张图,让模型描述图片"。这只是最表层的能力。

从应用角度看,多模态可以覆盖以下几类任务:

  • 文本:制度、说明书、问答、网页、代码。
  • 图片:海报、截图、票据、照片、图表。
  • 音频:语音讲解、播客、会议录音。
  • 视频:监控片段、操作录屏、课程视频、活动视频。
  • PDF / Word / PPT / Excel:混合了文本、表格、图片和版式结构的文档。

如果只做文本 RAG,那么图片里的文字、表格结构、视频内容、页面位置关系都会丢失。多模态 RAG 的价值就在于把这些非文本内容也纳入知识系统。

比如一个跨境基金运营智能对账场景,业务痛点可能是:

  • 托管行材料是 PDF,而且可能是日文。
  • 内部系统数据在 Excel 里。
  • 每天都要人工核对大量数据。
  • 流程繁琐、耗时,而且容易因为语言隔阂产生疏漏。

这类问题不是简单问答,而是要把 PDF、Excel、截图、业务规则一起理解,再进一步生成对账方案、流程说明、汇报材料,甚至演示内容。

用多模态模型做快速研究和材料生成

多模态模型不仅能回答问题,也能把一个领域的资料加工成不同形式的材料。

常见输出包括:

  • 汇报材料。
  • 信息图。
  • 思维导图。
  • 音频讲解。
  • 视频讲解。
  • PPT 演示。

如果要把一段内容做成视频,大致可以拆成几个工程步骤:

text 复制代码
原始材料
  -> LLM 生成视频脚本
  -> 按脚本拆分成 6-8 张配图
  -> 每张图和脚本段落对应
  -> 生成口播音频
  -> 生成字幕
  -> 合成视频

这也是多模态应用和普通聊天应用的重要区别:普通聊天只输出一段文字,多模态应用会把文字、图片、音频、视频串成一个完整工作流。

工具选择上,可以这样理解:

  • NotebookLM 更适合把资料整理成 summary、标签、问答和音频式讲解。
  • Gemini 这类多模态模型适合处理图像、视频、PDF 等复杂输入。
  • 国内图像和视频生成场景中,可以考虑即梦、MiniMax 等工具。
  • 多模态理解可以使用 Qwen、Gemini 等支持视觉输入的模型。

对于工作汇报,图像生成也很实用。常见风格包括白板书、视觉笔记、未来科技风、3D 黏土风等。

比如讲 CNN 的由来,可以把文字材料转成"教授板书照片"的样子:用箭头、方框、图表和中文说明来解释 S 细胞、C 细胞、卷积层和池化层之间的关系。这类图的重点不只是美观,而是把抽象概念转成更容易理解的视觉结构。

Gemini 的多模态处理能力

Gemini 这类多模态模型的特点在于:可以把文本、图像、音频、视频、PDF、代码等不同形态的数据一次性喂给模型,让模型在同一套神经网络里完成理解、推理和生成,不一定要先把所有东西转成文本。

它的核心能力可以概括成四点。

第一,原生统一架构。

从预训练开始就把多种模态放在同一个表征空间里学习,而不是先训练一个大语言模型,再外挂视觉模块或语音模块。这样做的好处是信息损耗更小,也更容易保留时序、细节和跨模态关系。

第二,端到端推理。

同一组 Transformer 参数可以直接处理任意组合输入。例如:

  • 给一张 CT 影像和病历文本,模型可以同步给出诊断建议。
  • 给一段手写食谱视频,模型可以直接整理成数字菜谱。

第三,长上下文。

长窗口模型可以一次性读入很长的视频或 PDF,再输出结构化报告。长上下文不能替代 RAG,但它让"直接理解完整材料"变得更容易。

第四,生成能力。

多模态模型不仅能看和听,还能画和说。例如文本生成图像、编辑图像、图像修复、流式生成音频等。

Gemini API 的基本用法

如果用 Gemini API,第一步是申请 API Key,然后设置环境变量:

text 复制代码
GEMINI_API_KEY

文本生成的代码可以写成:

python 复制代码
from google import genai

client = genai.Client()

response = client.models.generate_content(
    model="gemini-3-flash-preview",
    contents="用中文解释AI大模型是如何工作的",
)

print(response.text)

图像理解时,关键变化是:contents 变成一个列表,同时放图片对象和文字问题。

python 复制代码
from PIL import Image

image = Image.open("dog_and_girl.jpeg")

response = client.models.generate_content(
    model="gemini-3-flash-preview",
    contents=[image, "帮我解释下这张照片"],
)

print(response.text)

视频理解要多一个上传和等待处理的过程。视频上传后,服务端需要几秒钟进行转码和处理:

python 复制代码
import time
from google import genai

client = genai.Client()

print("正在上传视频...")
video_file = client.files.upload(file="car.mp4")
print(f"上传成功: {video_file.name}")

while video_file.state.name == "PROCESSING":
    print("视频处理中,请稍候...")
    time.sleep(2)
    video_file = client.files.get(name=video_file.name)

if video_file.state.name == "FAILED":
    raise ValueError("视频处理失败")

print("视频就绪,开始推理...")

response = client.models.generate_content(
    model="gemini-3-flash-preview",
    contents=[
        video_file,
        "详细描述视频里发生了什么?如果有对话,请把关键对话提取出来。",
    ],
)

print(response.text)

这类多模态 LLM 更适合"直接理解和生成"。但如果要在大量资料中做检索,还需要引入 Embedding 和向量索引。

二、迪士尼 RAG 助手与多模态 Embedding 路线

迪士尼 RAG 助手要解决什么

可以用一个迪士尼客服助手来说明多模态 RAG。

目标是构建一个 7×24 小时在线的 AI 客服助手,能力包括:

  • 自动回答票务、入园须知、会员权益等高频问题。
  • 所有回答尽量来自官方知识库,避免信息错误或过时。
  • 不仅回答文本问题,也能处理图片相关问题,比如活动海报。
  • 遇到视频相关问题时,可以关联视频素材或视频理解结果。

这个场景的难点主要体现在三方面。

第一,知识来源多样。

知识库里可能有 PDF 格式的官方规定、Word 格式的内部 FAQ、网页公告,以及包含图片和表格的活动介绍文件。

第二,非结构化数据处理复杂。

PDF 和 Word 里不仅有纯文本,还有表格、图片、版式。图片里可能有文字,表格里有结构,PDF 页面的顺序和层次也会影响理解。

第三,回答必须基于检索结果。

客服场景最怕模型乱编。RAG 系统要让最终答案严格基于检索到的内容,同时在合适的时候附上图片或视频。

两种技术路线

处理多模态知识库有两种常见路线。

方案一:不同模态分开处理

这种方式比较传统:

  • PDF 用 PyMuPDF 解析。
  • Word 用 python-docx 解析。
  • 图片里的文字用 OCR,比如 pytesseract、PaddleOCR、DeepSeek-OCR、MinerU 等。
  • 文本用文本 Embedding 模型。
  • 图片用 CLIP 或图像 Embedding 模型。
  • 向量检索用 FAISS,生产环境也可以用 Milvus、ChromaDB 或 Elasticsearch。
  • 最终回答用 Qwen、DeepSeek、Gemini 等 LLM。

这条路线的优点是可控,每一步都很清楚。缺点是模块较多,文本、图片、视频可能不在同一个语义空间里,跨模态检索需要额外处理。

方案二:统一多模态 Embedding

另一种方式是:文本 Embedding、图像 Embedding、视频 Embedding 都使用同一个多模态 Embedding 模型。

也就是把文本、图片、视频都转换成同一语义空间中的向量。这样可以实现:

  • 以文搜图。
  • 以图搜图。
  • 以文搜视频。
  • 以图搜视频。
  • 文本、图片、视频之间做统一相似度计算。
  • 对内容进行分类、聚类、打标签。

这也是迪士尼 RAG 助手更适合使用的路线。

Multimodal Embedding 怎么理解

多模态 Embedding 的关键点是:不同模态生成的向量位于同一个语义空间。

表中几个模型的差异,主要体现在向量维度、文本长度限制、图片限制、视频限制和价格上。

例如:

  • qwen2.5-vl-embedding 支持较长文本,向量维度可选。
  • tongyi-embedding-vision-plus 输出 1152 维向量,支持文本、图片和视频。
  • tongyi-embedding-vision-flash 成本更低,维度为 768。
  • multimodal-embedding-v1 输出 1024 维向量。

从 RAG 的角度看,最重要的不是"模型名字",而是以下几个问题:

  • 文本、图片、视频是否真的在同一向量空间里?
  • 向量维度是多少?
  • 图片是否需要 Base64?
  • 视频是本地上传还是 URL 输入?
  • 一次可以传多少张图片?
  • 输入 token 成本能不能接受?
  • 检索效果是否能覆盖自己的业务问题?

文本、图片、视频如何向量化

文本向量化相对直接,把文本放到 input 中即可:

python 复制代码
import dashscope
import json
from http import HTTPStatus

text = (
    "上海迪士尼乐园门票分为一日票、两日票和特定日票三种类型。"
    "一日票可在购买时选定日期使用,价格根据季节浮动,平日成人票475元起"
)

input_data = [{"text": text}]

resp = dashscope.MultiModalEmbedding.call(
    model="tongyi-embedding-vision-plus",
    input=input_data,
)

if resp.status_code == HTTPStatus.OK:
    result = {
        "status_code": resp.status_code,
        "request_id": getattr(resp, "request_id", ""),
        "code": getattr(resp, "code", ""),
        "message": getattr(resp, "message", ""),
        "output": resp.output,
        "usage": resp.usage,
    }
    print(json.dumps(result, ensure_ascii=False, indent=4))

图片向量化通常需要先读取为 Base64,再组装成 data URL:

python 复制代码
import base64
import dashscope
import json
from http import HTTPStatus

image_path = "./disney_knowledge_base/images/1-聚在一起说奇妙.jpg"

with open(image_path, "rb") as image_file:
    base64_image = base64.b64encode(image_file.read()).decode("utf-8")

image_format = "jpg"
image_data = f"data:image/{image_format};base64,{base64_image}"

input_data = [{"image": image_data}]

resp = dashscope.MultiModalEmbedding.call(
    model="tongyi-embedding-vision-plus",
    input=input_data,
)

if resp.status_code == HTTPStatus.OK:
    result = {
        "status_code": resp.status_code,
        "request_id": getattr(resp, "request_id", ""),
        "code": getattr(resp, "code", ""),
        "message": getattr(resp, "message", ""),
        "output": resp.output,
        "usage": resp.usage,
    }
    print(json.dumps(result, ensure_ascii=False, indent=4))

视频向量化要注意:有些多模态向量模型目前只支持视频 URL,不支持直接传本地视频文件。

python 复制代码
import dashscope
import json
from http import HTTPStatus

video = "https://dataset-1255932437.cos.ap-nanjing.myqcloud.com/mp4/car.mp4"

input_data = [{"video": video}]

resp = dashscope.MultiModalEmbedding.call(
    model="tongyi-embedding-vision-plus",
    input=input_data,
)

if resp.status_code == HTTPStatus.OK:
    result = {
        "status_code": resp.status_code,
        "request_id": getattr(resp, "request_id", ""),
        "code": getattr(resp, "code", ""),
        "message": getattr(resp, "message", ""),
        "output": resp.output,
        "usage": resp.usage,
    }
    print(json.dumps(result, ensure_ascii=False, indent=4))

这里需要区分两个概念:

  • 多模态 LLM:直接理解图片或视频,然后生成文字回答。
  • 多模态 Embedding:把图片或视频压成向量,用来做相似度检索。

前者偏向"理解和生成",后者偏向"召回和匹配"。

图片一定要转文字吗

不一定。图片通常有两种处理方式。

第一种,把图片转成文本:可以用 OCR 提取图片里的文字,也可以用多模态 LLM 生成图片描述。这样所有模态最终都统一成文本,再进入文本 Embedding 或文本 RAG 流程。优点是可解释性强,结果容易调试,也方便和文件名、标签、业务字段一起进入检索。缺点是部分信息会丢失。图片里的构图、颜色、人物动作、视觉风格、空间关系,不一定能完整转成文字。

第二种,直接做图片 Embedding:图片直接进入多模态 Embedding 模型,输出向量。这样可以实现以文搜图、以图搜图,也能保留一些视觉语义。优点是跨模态检索能力强。缺点是向量不可读,命中错误时不如文本结果容易解释。

实际项目中通常会两种方式结合使用:

text 复制代码
图片原文件
  -> OCR / 多模态 LLM 生成文字描述
  -> 图片 Embedding
  -> 文本描述 + 图片向量 + metadata 一起保存

这样召回时,既可以依赖图片向量,也可以依赖文本描述和关键词。

三、文档解析、FAISS 索引与元数据

Word 和 PDF 怎么解析

多模态 RAG 的数据层首先要把文件拆开。

Word 文档

Word 里可能有段落,也可能有表格。一个 parse_docx 函数需要遍历 .docx 文件中的所有元素,把段落和表格提取成独立内容块。

段落处理时:

  • 找出所有段落。
  • 提取段落内纯文本。
  • 去掉多余空白。
  • 标记为 type: text

表格处理时:

  • 读取表头。
  • 逐行读取单元格。
  • 转成 Markdown 表格。
  • 标记为 type: table

简化代码如下:

python 复制代码
def parse_docx(file_path):
    doc = DocxDocument(file_path)
    content_chunks = []

    for element in doc.element.body:
        if element.tag.endswith("p"):
            paragraph_text = ""
            for run in element.findall(
                ".//w:t",
                {"w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"},
            ):
                paragraph_text += run.text if run.text else ""

            if paragraph_text.strip():
                content_chunks.append({
                    "type": "text",
                    "content": paragraph_text.strip(),
                })

        elif element.tag.endswith("tbl"):
            md_table = []
            table = [t for t in doc.tables if t._element is element][0]

            if table.rows:
                header = [cell.text.strip() for cell in table.rows[0].cells]
                md_table.append("| " + " | ".join(header) + " |")
                md_table.append("|" + "---|" * len(header))

                for row in table.rows[1:]:
                    row_data = [cell.text.strip() for cell in row.cells]
                    md_table.append("| " + " | ".join(row_data) + " |")

                table_content = "\n".join(md_table)
                if table_content.strip():
                    content_chunks.append({
                        "type": "table",
                        "content": table_content,
                    })

    return content_chunks

PDF 文档

PDF 更复杂,因为它可能同时包含文本、扫描图、嵌入图片和复杂版式。

使用 PyMuPDF 时,可以逐页提取文本,也可以提取每页嵌入的图片:

python 复制代码
import os
import fitz

def parse_pdf(file_path, image_dir):
    doc = fitz.open(file_path)
    content_chunks = []

    for page_num, page in enumerate(doc):
        text = page.get_text("text")
        content_chunks.append({
            "type": "text",
            "content": text,
            "page": page_num + 1,
        })

        for img_index, img in enumerate(page.get_images(full=True)):
            xref = img[0]
            base_image = doc.extract_image(xref)
            image_bytes = base_image["image"]
            image_ext = base_image["ext"]

            image_path = os.path.join(
                image_dir,
                f"{os.path.basename(file_path)}_p{page_num + 1}_{img_index}.{image_ext}",
            )

            with open(image_path, "wb") as f:
                f.write(image_bytes)

            content_chunks.append({
                "type": "image",
                "path": image_path,
                "page": page_num + 1,
            })

    return content_chunks

这一步的结果不是最终答案,而是为后续索引做准备。

FAISS 索引怎么构建

构建多模态知识库时,可以把 Word、图片、视频都放到同一个 FAISS 索引里。

整体流程可以拆成五步:

  1. 解析 Word 文档。
    parse_docx() 遍历 DOCX 元素,提取段落文本和表格,并把表格转成 Markdown。

  2. 文本切分。
    split_text() 按固定长度切分,例如 chunk_size=500overlap=50

  3. 多模态 Embedding。

    使用 tongyi-embedding-vision-plus 统一处理文本、图片和视频。

  4. 构建 FAISS 索引。

    可以使用 IndexFlatL2,也就是基于 L2 距离的精确搜索索引。

  5. 持久化存储。

    索引保存为 disney_index.faiss,元数据保存为 disney_metadata.json

多模态统一索引的结构可以概括为:

这里有几个关键点:

文本可以直接编码。

图片需要先 Base64 编码后发送。

视频可以先提取多帧,再计算平均向量;如果模型直接支持视频 URL,也可以把 URL 传给模型。

最终都进入统一向量空间,再写入同一个 FAISS Index。

元数据要保存什么

FAISS 本身更像一个向量检索库,它重点管理向量和索引。真实业务里,不能只存向量,还要单独维护元数据。

典型 metadata 可以这样设计。

文本类型:

json 复制代码
{
  "id": 0,
  "source": "退票政策.docx",
  "type": "text",
  "content": "退票内容..."
}

图片类型:

json 复制代码
{
  "id": 10,
  "source": "图片: poster.jpg",
  "type": "image",
  "path": "images/poster.jpg",
  "content": "[图片] poster.jpg"
}

视频类型:

json 复制代码
{
  "id": 15,
  "source": "视频: 汽车剐蹭",
  "type": "video",
  "url": "https://...",
  "description": "汽车剐蹭视频"
}

也就是说,向量数据库或 FAISS 索引负责"找相似向量",metadata 负责"回到原始业务内容"。

有一个常见问题是:向量数据库中是否只存向量,而不存原始文本和图片?

答案是:可以存,也可以不存,取决于具体数据库。FAISS 只是库,不会像完整数据库那样帮你管理业务数据。如果用 FAISS,原始文本、图片路径、视频 URL、来源文件、页码、权限标签这些都需要自己维护。

Query 查询怎么处理

用户提问时,系统需要先把问题转换成向量,再到 FAISS 中检索。

完整流程如下:

  1. 加载索引。
    load_index() 从文件加载 FAISS 索引和元数据 JSON。

  2. Query Embedding。

    把用户问题转成向量,和索引里的向量放到同一个空间比较。

  3. 相似度检索。

    检索全部记录,按 L2 距离排序,再转换成相似度:

text 复制代码
sim = 1 / (1 + distance)
  1. 媒体意图检测。
    用关键词判断用户是否需要图片或视频。
python 复制代码
IMAGE_KEYWORDS = ["图片", "海报", "照片", "看看", "长什么样"]
VIDEO_KEYWORDS = ["视频", "录像", "播放"]
  1. 结果筛选。

    文本取 Top-K,例如默认 k=3。图片和视频只有在距离小于阈值时才采纳,例如 MEDIA_DISTANCE_THRESHOLD = 3.0

  2. LLM 生成。

    构建 prompt,把背景知识和用户问题交给 qwen-flash 生成答案,并在需要时附上匹配到的图片或视频链接。

关键配置可以这样写:

python 复制代码
CHUNK_SIZE = 500
CHUNK_OVERLAP = 50

EMBEDDING_MODEL = "tongyi-embedding-vision-plus"
LLM_MODEL = "qwen-flash"

MEDIA_DISTANCE_THRESHOLD = 3.0

统一向量空间到底是什么意思

统一向量空间可以理解为:文本、图片、视频都被映射到同一个坐标系里。

图里蓝色点代表文本 chunk,绿色点代表图片,红色点代表视频。用户 query 也会被编码成一个向量。如果 query 离某张图片更近,就可以实现"以文搜图";如果 query 离某段视频更近,就可以实现"以文搜视频"。

这个能力的前提是:这些模态必须由同一个多模态 Embedding 模型映射到统一空间,或者至少经过对齐训练。

还有一个常见问题:图片 embedding 把图片压缩成向量后,能否和纯文本 query 的向量匹配?

答案是可以,前提是模型本身支持跨模态对齐。多模态 Embedding 的训练目标之一,就是让"描述同一语义的文本和图片"在向量空间里距离更近。

统一索引 + 后筛选

多模态 RAG 可以采用"统一索引 + 后筛选"的检索策略。

伪代码如下:

python 复制代码
# 1. 统一检索:一次查询返回所有类型
results = search_with_details(query, index, metadata)

# 2. 意图检测:关键词判断是否需要媒体
want_image, want_video = detect_media_intent(query)

# 3. 文本无条件取 Top-K
top_results = [
    r for r in results
    if r["type"] == "text"
][:k]

# 4. 图片仅当用户需要图片且距离足够近时才返回
if want_image:
    image_results = [
        r for r in results
        if r["type"] == "image" and r["distance"] < 3.0
    ]
    matched_image = min(image_results, key=lambda x: x["distance"])

这个策略背后的考虑包括:

  • 单索引架构:文本、图片、视频共享统一向量空间,索引维护简单。
  • 意图驱动:通过关键词检测用户意图,按需触发媒体检索,避免无关媒体干扰。
  • 文本优先:文本结果无条件进入 prompt,媒体作为附件补充,保证回答质量。
  • 阈值过滤:媒体需要满足距离阈值,防止低相关性媒体误匹配。

完整工作流可以概括成:

更细的查询处理流程如下:

索引构建结果怎么看

构建知识库后,日志大概长这样:

text 复制代码
--- 构建多模态知识库 ---
切分参数: chunk_size=500, overlap=50

处理文档: 1-上海迪士尼门票规则.docx
  文档长度: 1342 字符, 切分为 3 个 chunk

处理文档: 2-迪士尼老人票价规定.docx
  文档长度: 879 字符, 切分为 2 个 chunk

处理文档: 3-迪士尼乐园游玩攻略清单.docx
  文档长度: 641 字符, 切分为 2 个 chunk

处理文档: 4-上海迪士尼乐园酒店会员制度.docx
  文档长度: 790 字符, 切分为 2 个 chunk

处理图片...
  - 1-聚在一起说奇妙.jpg
  - 2-万圣节.jpeg

处理视频...
  - 汽车剐蹭视频

向量维度: 1152
索引已保存: disney_index.faiss
元数据已保存: disney_metadata.json
完成! 文本:9, 图片:2, 视频:1

从这段日志中可以看出几个信息:

  • 文档中的文字内容被切成 9 个 chunk。
  • 图片有 2 张。
  • 视频有 1 个。
  • 所有内容统一生成 1152 维向量。
  • FAISS 索引和 metadata 分开保存。

四、查询处理、案例验证与媒体结果处理

用户问题 1:门票退款流程

用户问:

text 复制代码
我想了解一下迪士尼门票的退款流程

系统会先做相似度排名,然后发现不需要图片,也不需要视频:

text 复制代码
意图检测: 需要图片=False, 需要视频=False

接着选取 Top-3 文本构建 prompt,并调用 LLM 生成回答。

生成结果里包含:

  • 退改时间限制:入园前至少 48 小时操作才可修改或退票。
  • 不足 48 小时通常无法办理退改。
  • 特殊情况如突发疾病、意外事故,可提交相关证明申请处理。
  • 门票过期后 30 天内可能可以补差价延期。
  • 操作方式包括官方 App、官网、微信公众号或小程序。
  • 退款一般原路退回,可能需要 3-7 个工作日。

这个例子说明:当用户只问文本政策时,媒体内容不需要进入答案。否则会增加噪声。

用户问题 2:万圣节活动海报

用户问:

text 复制代码
最近万圣节的活动海报是什么

这个问题里有"海报",所以意图检测结果是:

text 复制代码
需要图片=True
需要视频=False

系统除了检索 Top-K 文本,还会从图片类型结果里找距离小于阈值的匹配:

text 复制代码
匹配到图片: disney_knowledge_base/images/2-万圣节.jpeg
距离: 1.5501
相似度: 0.3921

最终回答不仅会描述活动内容,还会附上相关图片路径。

这里有一个容易误解的地方:用户问题里出现"海报",并不意味着一定要把所有图片都返回。仍然要经过相似度、阈值和类型筛选。

用户问题 3:汽车被剐蹭了,能看到视频吗

用户问:

text 复制代码
我的汽车被剐蹭了,你能看到视频么?

这个问题里有"视频",所以系统会触发视频意图:

text 复制代码
需要图片=False
需要视频=True

检索结果里命中了一个视频:

text 复制代码
匹配到视频: https://dataset-1255932437.cos.ap-nanjing.myqcloud.com/mp4/car.mp4
距离: 1.5424
相似度: 0.3933

最终回答需要注意业务边界:客服助手不能假装自己能查看监控,也不能直接给出不存在的权限。更合理的回答是:

  • 抱歉听到车辆被剐蹭。
  • 建议联系游客服务中心。
  • 保留现场照片或视频证据。
  • 如果有目击者,留下联系方式。
  • 如涉及保险理赔,及时通知保险公司。
  • 可以附上相关视频链接,但不能夸大自己对监控系统的访问能力。

这就是多模态 RAG 的边界:可以检索媒体,但不能把检索到的媒体当成模型实际拥有的业务权限。

为什么文本结果有时排在图片前面

笔记里提到一个现象:问图片内容时,文本相关结果可能还排在前面。

这是正常现象。

因为统一索引返回的是所有类型的向量结果,文本、图片、视频一起排序。如果文本 chunk 的语义和 query 更接近,它就会排在前面。

因此才需要"意图检测 + 后筛选":

  • 用户只是问政策,就只用文本。
  • 用户明确问海报、图片、照片,就从图片结果中再挑。
  • 用户明确问视频、录像、播放,就从视频结果中再挑。

如果只看全局相似度,不看用户意图,可能会出现媒体误匹配。

也可能有人会问:是不是因为图片文件名中带有"万圣节",所以才命中?

如果 FAISS 检索只用图片 embedding,本身不会看文件名。但如果把文件名、图片描述、OCR 文本也一起做了 embedding,那么文件名和描述会影响文本侧召回。这并不是坏事,关键是要清楚自己到底把哪些信息放进了向量化输入。

图片和视频向量能还原原图原视频吗

不能。

Embedding 是特征提取,不是可逆压缩。它把原始内容映射成一串浮点数,用来做相似度计算。

所以:

  • 图片转成 embedding 后,不能从 embedding 还原原图。
  • 视频转成 embedding 后,也不能从 embedding 还原原视频。
  • 原始图片和视频仍然要保存。

这也是 metadata 重要的原因。FAISS 命中向量以后,要通过 metadata 找回原始图片路径、视频 URL、文件名、页码或业务 ID。

视频怎么做 Embedding

视频可以理解为多帧图像与时间关系的组合。

常见方式有几种:

  1. 直接把视频 URL 交给支持视频输入的多模态 Embedding 模型。
  2. 对视频抽关键帧,每一帧做图片 embedding。
  3. 对多个帧 embedding 做平均或加权平均,得到视频级向量。
  4. 先用多模态 LLM 描述视频内容,再对描述文本做 embedding。

可以用下面这种方式直观理解:

text 复制代码
8 个关键帧
  -> 8 个 embedding
  -> 加权平均
  -> 视频 embedding

如果视频内容很长,单一向量可能会丢失细节。更稳妥的方式是分段抽帧,每段保留一个向量和一个时间戳,命中以后可以回到具体片段。

五、切片策略、页面结构与质量控制

切片策略为什么重要

知识切片是 RAG 系统的核心环节,直接影响检索质量和回答准确性。

常见切片方法有五种:

  1. 固定长度切片。
  2. 句子边界切片。
  3. LLM 语义切片。
  4. 层次切片。
  5. 滑动窗口切片。

固定长度切片

按固定字符数切分文本,可以带 overlap,也可以优先在句子边界附近切,避免强行切断句子。

优点是实现简单、速度快、长度统一,适合批量处理技术文档和规范文件。

缺点是语义不一定完整,容易把一个完整知识点切断。

句子边界切片

按句号、段落等自然语言边界切分,再合并成 chunk。

优点是语义保持好,适合自然语言文本和问答系统。

缺点是长度可能不均匀。如果文档里长句很多,chunk 会变得不好控制。

LLM 语义切片

让 LLM 理解内容后按语义切分。

Prompt 可以这样写:

text 复制代码
请将以下文本按照语义完整性进行切片,每个切片不超过 {max_chunk_size} 字符。

要求:
1. 保持语义完整性。
2. 在自然的分割点切分。
3. 返回 JSON 格式的切片列表。

文本内容:
{text}

请返回 JSON:
{
  "chunks": [
    "第一个切片内容",
    "第二个切片内容"
  ]
}

优点是语义完整性最好,适合复杂结构和高质量要求。

缺点是成本高、速度慢,不适合大规模文档全部这么处理。

层次切片

按标题、章节、段落切分,保留文档结构。

适合手册、规范、API 文档等结构清晰的材料。

缺点是依赖文档本身有标题层级。如果是一坨无标题纯文本,就不太适合。

滑动窗口切片

使用固定大小窗口在文本上滑动,产生大量重叠内容。

优点是上下文连续,长文档召回时不容易漏信息。

缺点是冗余多、存储成本高、去重压力大。

切片大小怎么选

没有通用的固定答案。

笔记里提到 300800 这样的数字,本质上是在提醒:chunk 大小要测试。

可以这样理解:

  • chunk 太小:召回更细,但上下文不完整,LLM 可能缺背景。
  • chunk 太大:上下文完整,但噪声更多,检索命中不够精确。
  • overlap 太小:容易断上下文。
  • overlap 太大:重复内容太多,索引变大,召回结果也可能重复。
  • step_size 不能太小,否则滑动窗口会产生大量高度重复的 chunk。

实际项目中,最好准备一组测试问题,比较不同 chunk_size、overlap 和切分策略下的命中率与回答质量。

PageIndex 和 RAG 是替代关系吗

PageIndex 更像是对 RAG 的增强,而不是替代。

普通 RAG 只把文档切成文本块,容易丢掉页面结构、图片位置、表格关系和页码信息。

PageIndex 更关注页面级组织:这一页有哪些区域、哪些文本、哪些图片、哪些表格,它们之间的位置关系是什么。对于 PDF、扫描件、报告、论文、说明书这类版式复杂的资料,页面结构非常重要。

可以这样理解:

text 复制代码
RAG:更关注知识片段能不能被检索到。
PageIndex:更关注页面结构、版面位置和引用回溯。

两者可以结合使用:先用 PageIndex 保存页面结构和定位信息,再把文本块、图片块、表格块送进 RAG 检索系统。

知识冲突和噪声怎么处理

如果检索内容中混入虚假信息、过时信息或噪声,LLM 本身的防御能力有限。

更可靠的做法是在 RAG 流程里增加控制:

  • 来源分级:官方文档优先,用户评论和普通网页降权。
  • 时间过滤:过期文档不要进入检索,或在 metadata 里标记版本。
  • 规则校验:关键字段用程序校验,不只靠模型判断。
  • 多来源交叉验证:多个来源冲突时,提示冲突而不是强行合并。
  • LLM 自检:让模型列出依据、冲突点和不确定项。
  • 人工审核:高风险答案不要自动执行。

知识冲突可以用 prompt 让 LLM 辅助发现,但不要只靠 prompt。真正稳定的系统,需要依赖数据治理、权限控制、版本管理和来源可信度。

专利检索为什么效果不佳

专利检索如果只做普通 embedding 相似度,效果可能不稳定。

原因是专利文本通常有这些特点:

  • 句子长。
  • 术语多。
  • 表达绕。
  • 同义改写很多。
  • 法律边界和技术边界都很重要。

可以从以下几个方向优化:

  • 使用更适合技术文本的 embedding 模型。
  • 做 query 改写,把用户问题改成专利检索式。
  • 做混合检索,结合关键词、BM25、向量检索。
  • 增加 rerank 模型。
  • 保存 IPC 分类号、申请人、年份、领域等 metadata。
  • 用结构化字段过滤后再做向量召回。

只依赖一个向量库,很难把专利检索做好。

六、工程实现、概念辨析与最终链路

Skills 和 Function Call 的关系

笔记里提到一个说法:

text 复制代码
skills = 高阶版的 function call

可以这样理解:

Function Call 更像是"模型可以调用一个函数"。函数通常是代码里预先注册好的工具。

Skill 更像是"一个可复用任务能力包",通常包括:

  • 独立脚本。
  • 使用说明。
  • 输入输出约定。
  • 何时触发。
  • 任务流程。

所以 Skill 不只是一个函数,而是把一类任务的流程和工具封装起来,供模型按需加载。

用 AI 编程工具写这类代码,应该怎么提需求

不需要把所有 RAG 和 Agent 代码都手敲一遍。

更实际的做法是:先理解整体流程,再让 AI 编程工具辅助实现。

给 AI 的提示词需要包含技术关键词,并明确输入和输出要求。例如:

text 复制代码
我打算构造一个多模态向量数据库,使用 FAISS。

数据源在 disney_knowledge_base 文件夹中,里面有 Word 文档、图片和视频 URL。

请参考这些已有代码:
@1-文本embedding.py
@2-图片embedding.py
@3-视频embedding.py

要求:
1. 解析 Word 文档,提取段落和表格。
2. 文本按 chunk_size=500、overlap=50 切分。
3. 图片转 Base64 后调用 tongyi-embedding-vision-plus。
4. 视频用 URL 调用多模态 embedding。
5. 所有向量写入 FAISS。
6. metadata 单独保存为 JSON。
7. 最后持久化为 disney_index.faiss 和 disney_metadata.json。

如果已经有旧代码,可以直接引用旧代码,让 AI 在旧代码基础上修改,这通常比从零描述所有细节更稳。

几个容易混淆的问题

Tongyi 只能用 DashScope 上的模型吗

调用方式取决于平台和 SDK。DashScope 是阿里云提供的模型服务接口,ModelScope 则更偏模型托管和开源模型生态。实际项目里要区分"模型在哪里""通过什么 API 调用""是否本地部署"这几件事。

多模态向量是不是固定 768 维

不一定。

不同模型的向量维度不同,例如 768、1024、1152、2048 都可能出现。维度不是越高越好,最终要看检索效果、成本、速度和存储压力。

图片 embedding 会有 chunk 过程吗

文本通常需要 chunk,因为文本可能很长。

图片一般一张图可以看成一个 chunk。如果图片本身很复杂,也可以先做 OCR、区域切分、图像描述,再把不同区域或描述作为多个 chunk。

视频是否可以理解为多张图片

可以这样近似理解:

视频包含多个帧,也包含时间顺序。工程上经常抽关键帧做 embedding,再聚合成视频向量。但如果模型支持视频输入,也可以直接让模型处理视频 URL。

Base64 会不会太大

Base64 是原始图片的一种编码方式,体积确实会比较大。但最终进入向量库的是 embedding,不是 Base64 字符串本身。

Base64 常用于把图片传给接口。原始图片仍然要作为文件保存,向量库只负责检索。

图像转文本是不是更好

不一定。

图像转文本的可解释性更好,也能提取关键词,但一定会丢失部分视觉信息。图片 embedding 保留视觉语义更好,但不好解释。实际项目里可以两者结合。

意图识别怎么做

简单方式是关键词匹配:

python 复制代码
IMAGE_KEYWORDS = ["图片", "海报", "照片", "看看", "长什么样"]
VIDEO_KEYWORDS = ["视频", "录像", "播放"]

更复杂的方式是让 LLM 输出结构化判断:

json 复制代码
{
  "want_image": true,
  "want_video": false,
  "reason": "用户明确询问活动海报"
}

关键词方式便宜、稳定,但覆盖不全。LLM 方式灵活,但成本更高,也要防止误判。

FAISS 里存 metadata 吗

FAISS 本身不负责完整业务 metadata 管理。一般做法是:

python 复制代码
metadata_store = []

向量进 FAISS,metadata 进 JSON 或数据库。检索命中向量 ID 后,再用 ID 找 metadata。

为什么相似度越大,答案反而不准

相似度只说明"向量空间里接近",不等于业务答案一定正确。

可能原因包括:

  • query 本身不清楚。
  • chunk 切得不好。
  • embedding 模型不适合业务。
  • 文本、图片、视频混在一起后没有做类型筛选。
  • metadata 没有参与过滤。
  • 缺少 rerank。
  • prompt 没有要求严格基于资料回答。

所以检索系统不能只看相似度分数,还要看召回内容、类型、来源和业务约束。

RAG 联网检索如何防虚假信息

联网搜索需要更谨慎。

可以把来源分成等级:

text 复制代码
官方渠道
  > 权威媒体 / 平台
  > 普通网页
  > 用户评论

如果不同来源冲突,应该提示"不一致",而不是让模型硬编一个统一答案。

Native RAG 是什么

Native RAG 可以理解为朴素 RAG 或原生 RAG:自己控制解析、切分、向量化、索引、检索、prompt 组装和生成,不完全依赖上层框架。

它的好处是流程透明,适合学习底层逻辑和做定制化系统。

最终链路

把前面的内容串起来,一个多模态 RAG 助手的整体链路大致如下:

text 复制代码
数据层
  -> 解析 Word / PDF / 图片 / 视频
  -> 文本切分,图片和视频保留原始文件
  -> 表格转 Markdown,图片可做 OCR 或图像描述

向量化层
  -> 使用多模态 Embedding
  -> 文本、图片、视频映射到统一向量空间
  -> 写入 FAISS
  -> metadata 单独保存

检索层
  -> 用户 query 转向量
  -> FAISS 做 L2 距离检索
  -> 文本取 Top-K
  -> 根据意图筛选图片或视频
  -> 按距离阈值过滤低相关媒体

生成层
  -> Top-K 文本构建上下文 prompt
  -> LLM 生成回答
  -> 自动附加匹配到的图片或视频链接

一句话总结:

text 复制代码
多模态 RAG = 文档解析 + 多模态 Embedding + 统一向量索引 + 元数据回溯 + LLM 生成

如果只记住几个关键点,可以记这些:

  1. 多模态 LLM 负责理解和生成,多模态 Embedding 负责检索和匹配。
  2. 文本、图片、视频可以进入同一个向量空间,前提是模型支持跨模态对齐。
  3. FAISS 只解决向量检索,原始文本、图片、视频和 metadata 要单独保存。
  4. 图片可以直接 embedding,也可以先转文本,实际项目里常常两者结合。
  5. 视频可以直接输入模型,也可以抽关键帧再聚合向量。
  6. 统一索引后要做类型筛选,否则图片、视频、文本容易互相干扰。
  7. chunk 切分没有固定最优解,要根据测试问题集调。
  8. 权威来源、版本、权限、metadata,比单纯相似度更重要。
  9. 多模态 RAG 的目标不是"让模型看起来很聪明",而是让它能在复杂资料里找到正确证据,并把答案和证据一起交付。 AI应用开发四:RAG多模态数据处理
相关推荐
碧海银沙音频科技研究院3 小时前
高通QCC3084-QCC518X蓝牙耳机项目
人工智能·深度学习·算法
@蔓蔓喜欢你3 小时前
CSS Grid布局完全指南:构建复杂布局的利器
人工智能·ai
xiami_world3 小时前
2026年团队AI工具栈架构指南:ChatGPT + Codex + AI白板智能体工程化落地方案
人工智能·ai·信息可视化·aigc·流程图
sheji1053 小时前
割草机器人行业浏览版内容汇总
人工智能·机器人·智能硬件
乐维_lwops3 小时前
【无标题】
运维·人工智能
qcx233 小时前
【AI Daily】每日Arxiv论文研读Top5-2026-05-18
人工智能·ai·llm·论文·agent·arxiv
叶子Talk3 小时前
谷歌I/O明日开幕:Gemini 3.2对标GPT-5.5,AI眼镜十年后重启
人工智能·gpt·ai·谷歌·gemini·google i/o·gpt-5.5
overwizard3 小时前
AI工程双剑:gstack与Superpowers实战指南
人工智能·claude code·vibe-coding·skills·cc switch
踏着七彩祥云的小丑3 小时前
AI——多模态 / 复杂文档 RAG
人工智能·ai