跳到主要内容

三种查询:纯文本 / 多模态注入 / VLM 增强

本章讲读路径:QueryMixin(query.py)提供的三种问答方式,以及最有意思的「VLM 增强」是怎么把图片真的喂回给模型看的。

这一节讲什么

先分清三种查询各解决什么场景,再深入 VLM 增强的关键三步(检索→找回图片→喂 VLM),最后看其中的防 prompt 注入安全设计。

5.1 三种查询一览

方法场景图片怎么用
aquery(纯文本)问已入库内容不直接用图;但有 VLM 时默认转去 VLM 增强
aquery_with_multimodal提问时自己带一张图/表/公式把带来的内容先翻译成文字拼进 query
aquery_vlm_enhanced问的内容涉及库里的图检索出相关图,把真实图片喂给 VLM 看

关键的自动切换:aquery(query.py:102)里,只要配了 vision_model_func 且没显式关掉,vlm_enhanced 默认为真,于是普通文本查询会自动转去 aquery_vlm_enhanced。用户不用关心这个细节——「问涉及图的问题,系统自动会看图」。

5.2 aquery_with_multimodal:提问时自带内容

aquery_with_multimodal(query.py:195)用于「我有一张图/一个表,想结合知识库问问题」。它不直接检索图,而是先把带来的多模态内容翻译成文字描述,拼进 query 文本,再走普通 aquery:

# 示意,逻辑见 query.py:422-473 _process_multimodal_query_content
enhanced_parts = [f"User query: {base_query}"]
for content in multimodal_content:
processor = get_processor_for_type(self.modal_processors, content["type"])
description = await self._generate_query_content_description(processor, content, type)
enhanced_parts.append(f"Related {type} content: {description}")
enhanced_query = "\n".join(enhanced_parts) + PROMPTS["QUERY_ENHANCEMENT_SUFFIX"]

结果有缓存:基于「query + 模态内容 + mode」算 key(_generate_multimodal_cache_key,query.py:26),命中就直接返回,省掉重复的模型调用。注意算 key 时图片路径只取 basename、大表体取 hash,让缓存更可移植。

5.3 VLM 增强:把图片塞回检索结果

这是本章的精华。aquery_vlm_enhanced(query.py:349)分三步:

怎么读这张图

从上到下是先后顺序。关键洞察在第 2 步:LightRAG 检索出的 prompt 里,因为 02-modal-processors.md 提到的 chunk 模板保留了 Image Path: 行,所以图片路径还在文本里,能被正则捞回来。

① only_need_prompt=True 让 LightRAG 只检索、不作答
│ 拿回 raw_prompt(含命中 chunk 的全文,里面有 "Image Path: /xxx.jpg")

② 正则扫 raw_prompt 里的 Image Path 行
│ 对每个路径:安全校验 → 读文件转 base64 → 在原位插入 [VLM_IMAGE_N] 标记

③ 按 [VLM_IMAGE_N] 标记切分文本,把对应图片 base64 插回相应位置
│ 组装成 VLM 的 messages(图文交错)

调 vision_model_func 看图作答

第 1 步:只检索不作答

# 示意,见 query.py:391-392
query_param = QueryParam(mode=mode, only_need_prompt=True, **kwargs)
raw_prompt = await self.lightrag.aquery(query, param=query_param)

only_need_prompt 是 LightRAG 的参数,让它把「检索 + 组装好的上下文 prompt」原样吐回来,而不去调 LLM 生成最终答案。RAG-Anything 要的就是这段含上下文的 prompt。

第 2 步:正则找回图片

_process_image_paths_for_vlm(query.py:589)用这个正则匹配路径:

# 示意,见 query.py:610
image_path_pattern = r"Image Path:\s*([^\r\n]*?\.(?:jpg|jpeg|png|gif|bmp|webp|tiff|tif))"

匹配到后,不删除原路径,而是在它后面追加一个 [VLM_IMAGE_N] 标记,同时把图片读成 base64 存进 self._current_images_base64。保留路径 + 加标记,是为了第 3 步能按标记把图片插回正确位置。

第 3 步:图文交错组装

_build_vlm_messages_with_images(query.py:708)按 [VLM_IMAGE_N] 标记切分文本,在每个标记处插入对应的 base64 图片,拼成 OpenAI 风格的图文交错 messages({type:text}{type:image_url} 交替),最后调 VLM(_call_vlm_with_multimodal_content,query.py:792)。

如果没找到任何有效图片,直接退回普通文本查询(query.py:401)——VLM 增强对没图的问题不会平白多花一次视觉调用。

5.4 安全:防间接 prompt 注入

第 2 步有个不显眼但重要的安全门(query.py:632-671)。威胁模型是:知识库里的某段文本可能被恶意构造,塞进 Image Path: /etc/passwd 之类的路径,诱导系统去读任意系统文件再喂给模型。

防御:只允许加载安全目录内的图片——

# 示意,逻辑见 query.py:634-670
abs_image_path = Path(image_path).resolve()
is_in_safe_dir = (
abs_image_path.is_relative_to(Path.cwd()) # 当前工作目录
or abs_image_path.is_relative_to(config.working_dir) # RAG 存储目录
or abs_image_path.is_relative_to(config.parser_output_dir) # 解析输出目录
or any(abs_image_path.is_relative_to(d) for d in extra_safe_dirs) # 显式额外目录
)
if not is_in_safe_dir:
is_valid = False # 拒绝,保留原文不读图

配套的 validate_image_file(utils.py:248)还会拒绝符号链接(防 symlink 绕过目录限制)、校验扩展名、限制文件大小(默认 50MB)。这套检查是该项目少有的、明确针对安全的工程化设计。

关键细节 / 坑

  • VLM messages 的两种形态:vision_model_func 既可能收到 image_data(单图,旧式),也可能收到 messages(图文交错,VLM 增强用)。README 的示例函数就同时处理这两种分支——用户的 vision_model_func 必须兼容 messages 参数,否则 VLM 增强用不了。
  • 同步包装:query / query_with_multimodal(query.py:826/844)是异步版的同步封装,用 always_get_an_event_loop 跑事件循环。
  • mode 默认 mix:三种查询默认都用 LightRAG 的 mix 模式(向量 + 图谱混合检索),也可传 local/global/hybrid/naive/bypass