ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

RAG-Anything自定义模态处理器:20行代码跑通

RAG-Anything自定义模态处理器:20行代码跑通 RAG-Anything自定义模态处理器20行代码跑通【免费下载链接】RAG-AnythingRAG-Anything: All-in-One RAG Framework项目地址: https://gitcode.com/GitHub_Trending/ra/RAG-Anything解析出的文档里混着一种内置链路没覆盖的内容类型——音频、公式截图、自定义标注图片、表格、公式各有专用逻辑你的类型却掉进通用兜底只能拿到一段泛泛描述。给 RAG-Anything 挂一个自定义模态处理器就能接管它入口就一个文件raganything/modalprocessors.py。一条模态内容在管道里经历了什么把整条链路看成三级流水线解析器把文档拆成带type字段的内容项image、table、equation其余一律进generic分支处理器按上下文 提示词 → 模型 → 结构化描述把内容项变成实体和文本块落库分块进文本库与向量库实体节点进图谱再抽出关联实体建边。第 2 级是自定义模态处理器要接管的位置。拆开看ImageModalProcessor.generate_description_only这条参照路径先self._get_context_for_item(item_info)取当前项前后几页的文本作为上下文塞进 prompt拼 prompt 后调self.modal_caption_func(prompt, image_data..., system_prompt...)这是异步调用模型必须回结构化 JSON顶层detailed_description嵌套entity_info含entity_name、entity_type、summary三个键缺一个就走 fallbackprocess_multimodal_content接着用*_chunk模板把原始内容 描述拼成文本块_create_entity_and_chunk一次写入 text_chunks、chunks_vdb、entities_vdb 和知识图谱最后extract_entities抽出关联实体并建belongs_to边。注意返回值(description, entity_info, chunk_results)。LLM 写的detailed_description被拼进文本块存进向量库而不是原样返回——元组里第一个值是entity_info[summary]这个短摘要想看完整描述得去 chunks_vdb 里查。这个约定决定了你自定义处理器该往哪个键里写什么。动手实践写一个能接进管道的音频处理器示例文件 examples/modalprocessors_example.py 已经把三种内置处理器直接调用的姿势写全了下面按最小改动增量走。继承 BaseModalProcessor 的最小改动骨架from raganything.modalprocessors import BaseModalProcessor class AudioClipModalProcessor(BaseModalProcessor): 处理 MinerU 之外自己拆出来的音频片段 async def generate_description_only(self, modal_content, content_type, item_infoNone, entity_nameNone): # 唯一必须实现的方法返回 (描述文本, 实体信息 dict) raise NotImplementedError async def process_multimodal_content(self, modal_content, content_type, file_pathmanual_creation, entity_nameNone, item_infoNone, batch_modeFalse, doc_idNone, chunk_order_index0): raise NotImplementedError骨架里不写__init__不是偷懒基类构造时直接从 LightRAG 实例接管了文本/向量存储、embedding 函数、LLM 函数、响应缓存llm_response_cache和分词器。你自己再建一套存储只会和管道写入的两个库打架。generate_description_only 与 process_multimodal_content 核心逻辑第一块生成描述。为什么必须返回结构化 JSON——_create_entity_and_chunk直接读entity_info[entity_name]、[entity_type]、[summary]三个键去建图谱节点字段名不能改async def generate_description_only(self, modal_content, content_type, item_infoNone, entity_nameNone): audio_ref modal_content[clip_ref] transcript await self._transcribe(audio_ref) # 你的音频转写实现 context self._get_context_for_item(item_info) if item_info else prompt self._build_prompt(transcript, context) raw await self.modal_caption_func(prompt) # 基类持有的 LLM自带缓存 data self._robust_json_parse(raw) # 直接复用基类解析器 entity data.get(entity_info, {}) if not all(k in entity for k in (entity_name, entity_type, summary)): raise ValueError(missing required entity_info fields) if entity_name: entity[entity_name] entity_name return data[detailed_description], entity这里用基类的_robust_json_parse而不是裸json.loads它按代码块→花括号配平→引号修复→正则兜底四级策略解析还顺手剥掉推理模型会夹带的 thinking 标签。第二块落库。为什么必须走_create_entity_and_chunk而不是自己 upsert——它内部完成哈希 id 生成、分块入库、图谱建节点、belongs_to建边、非批量模式下的节点合并你复制其中任何一步都会漏掉和主库的合并语义async def process_multimodal_content(self, modal_content, content_type, file_pathmanual_creation, entity_nameNone, item_infoNone, batch_modeFalse, doc_idNone, chunk_order_index0): description, entity await self.generate_description_only( modal_content, content_type, item_info, entity_name) # 你的 *_chunk 模板原始转写 描述 章节路径 modal_chunk self._assemble_chunk(modal_content, description) return await self._create_entity_and_chunk( modal_chunk, entity, file_path, batch_mode, doc_id, chunk_order_index)把自定义类型接进类型路由处理器实例存在self.modal_processors字典里按content_type分发。分发逻辑在 raganything/utils.py 的get_processor_for_type里if/elif 写死了 image、table、equation 三个分支其余类型统统落generic。想让你的类型走自己的处理器给这个函数加一个分支def get_processor_for_type(modal_processors, content_type): if content_type image: return modal_processors.get(image) # ... table / equation 分支保持不变 elif content_type audio_clip: # 新增分支 return modal_processors.get(audio_clip) else: return modal_processors.get(generic)然后在处理器初始化完成后LightRAG 就绪之后把实例塞进去rag.modal_processors[audio_clip] AudioClipModalProcessor( lightragrag.lightrag, modal_caption_funcrag.llm_model_func, context_extractorrag.context_extractor, )也可以像示例那样跳过字典直接await processor.process_multimodal_content(...)单条调用。跑起来之后预期看到控制台打印处理条数rag_storage下的 chunks 里多出一条音频片段转写描述的文本块用图查询接口找entity_name能沿belongs_to边看到转写里抽出的关联实体。注册时机、缺失字段与阻塞调用的排查三个坑都是真实链路里会踩的按现象→原因→修复走 处理器提前实例化content_source恒为空。modal_processors字典要等 LightRAG 初始化完才注入context_extractor依赖lightrag.tokenizer在此之前构造的处理器拿不到内容源_get_context_for_item永远返回空串描述缺少章节锚点。修复等initialize_storages完成后再实例化或像示例文件那样在 LightRAG 就绪后直接构造。 LLM 返回的 JSON 缺entity_info.summary整条内容悄悄走了 fallback。基类校验缺键会抛ValueError被外层 except 吞掉落库的是audio_clip_xxx哈希命名的 fallback 实体图里全是脏数据且不报明显错误。修复prompt 里显式要求三个必填键解析直接复用_robust_json_parse别写裸json.loads。 同步阻塞 IO 卡死整个事件循环。generate_description_only里如果直接requests.get拉音频或open(...).read()一个慢 IO 就把同批所有处理器一起卡住——所有协程共享同一个 loop日志会整批停滞。修复阻塞调用一律await asyncio.to_thread(...)丢进线程。还有一个容易被当成坑的成本项上下文窗口。ContextConfig.max_context_tokens默认 2000调大后 prompt 明显变长llm_response_cache按 prompt 哈希命中同一内容换个窗口值就是缓存失效、token 账单翻倍。想省先把context_window从默认的 1 页压回更小的值再观察单次耗时。如果你要深入某条支线如果你要理解上下文窗口怎么影响描述质量去看 docs/context_aware_processing.md 和基类里的ContextConfig、set_content_source如果你要把三种内置处理器当参照实现对着抄去看 examples/modalprocessors_example.py 里三个process_*_example函数如果你要弄清管道何时走单条、何时走批量去看 raganything/processor.py 的_process_multimodal_content_individual与_process_multimodal_content_batch_type_aware如果你要改描述风格prompt 模板的 key 在 raganything/prompt.pyvision_prompt、image_chunk等响应字段的校验逻辑在BaseModalProcessor._parse_response。现在去 examples/modalprocessors_example.py 把--api-key填上跑一遍对照三个处理器的返回结构再回来填你的骨架。【免费下载链接】RAG-AnythingRAG-Anything: All-in-One RAG Framework项目地址: https://gitcode.com/GitHub_Trending/ra/RAG-Anything创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表