Qwen-Agent 文档切块:阈值、重叠与缓存键
【免费下载链接】Qwen-AgentAgent framework and applications built upon Qwen>=3.0, featuring Function Calling, MCP, Code Interpreter, RAG, Chrome extension, etc.项目地址: https://gitcode.com/GitHub_Trending/qw/Qwen-Agent
你往上传框拖进一份 200 页 PDF,Qwen-Agent 的文件解析只做三个决定:要不要智能分块、每块多大、知识库缓存键怎么算。
第一站:这份 PDF 被拆成了几块
进入 qwen_agent/tools/doc_parser.py 的DocParser.call后,系统先把每一页每段落的 token 数累加出total_token,再拿它和max_ref_token比。默认max_ref_token是 20000(DEFAULT_MAX_REF_TOKEN),你的 200 页 PDF 远超这个数,所以走 else 分支,交给split_doc_to_chunk按parser_page_size(默认 500 token/块)去切。
为什么这样切:20000 token 大致是一次模型调用能吃下的参考资料上限,小文档塞进一个 chunk 就够了,大文档必须切成 500 token 左右的小块,每块都能独立塞进上下文。
阈值分支(tools/doc_parser.py:call())
# tools/doc_parser.py:call() L128-141 if total_token <= max_ref_token: content = [Chunk(content=get_plain_doc(doc), metadata=meta, token=total_token)] cached_name_chunking = f'{hash_sha256(url)}_without_chunking' else: content = self.split_doc_to_chunk(doc, url, title=title, parser_page_size=parser_page_size)切块是贪心装箱:available_token初始等于parser_page_size,段落逐段塞进当前块、扣减预算,扣不下就封块、开新块。每个块开头插一行[page: N]页码标记方便回原文定位,封块前若块里只剩页码标记会pop掉避免空块。遇到一段太长,先按.或。切成句子再装;单句还超长,就按available_token直接把句子截断。封块时_get_last_part从末尾取最多 150 字符作下一块开头,且只取同一页(need_page校验)的内容,让交界处的句子不被拦腰截断。200 页 PDF 大概会切成几百个 500 token 的 chunk。
第二站:拆完的块存到哪、怎么找回来
分块结果不直接返回就丢,而是写进磁盘。键只有一行:
缓存键怎么算(tools/doc_parser.py:call())
# tools/doc_parser.py:call() L106 cached_name_chunking = f'{hash_sha256(url)}_{str(parser_page_size)}'Storage(qwen_agent/tools/storage.py)把每个键当一个文件名,落在workspace/tools/doc_parser目录里,put时os.makedirs建目录、再save_text_to_file写整段 JSON。
同一份 PDF 第二次进来,call开头就self.db.get(cached_name_chunking),命中直接json.loads返回,跳过解析和切块(日志打Read chunked ... from cache.)。键里带了parser_page_size:你换个 page_size 重跑,键变了,旧缓存不命中,整份重新切一遍。整份不切块那条路会把键改写成{hash}_without_chunking,所以「切过」和「没切过」两份结果互不覆盖。
第三站:提问时,怎么从几百块里捞出答案
最直接的入口是 examples/parallel_doc_qa.py。它先doc_parse把文件变成 chunk(走上面那套缓存),再调retrieval工具(qwen_agent/tools/retrieval.py)。Retrieval.call内部就两步:先doc_parse,再search,两条路复用同一份 chunk 缓存。检索本身也只做两件事——先用GenKeyword让模型把问题抽成中英文关键词,再把所有 chunk 摊平丢进 BM25(rank_bm25的BM25Okapi)算分、按分排序,取 top-k 拼成参考资料喂给模型;若整份文档 token 没超max_ref_token,BaseSearch会直接返回全文、不进 BM25。parallel_doc_qa更狠一点:按PARALLEL_CHUNK_SIZE=1000切块,给每个 chunk 起一个并行成员 Agent 去答,答完再用retrieval在 4500 token 内召回资料做汇总。
踩坑与调参
- ⚠️
parser_page_size— 默认 500 token/块;调大到 1000(parallel_doc_qa里PARALLEL_CHUNK_SIZE的值)单块信息更完整,但 BM25 命中变粗、单块更占上下文;调小到 300(RAG_CHUNK_SIZE的值)块多、召回更细,跨块语义断裂却更多。 - ⚠️
max_ref_token— 默认 20000;调大让更多文档「不切块整份进」,超长的会被模型上下文截断;调小则分块更频繁、检索更准,每块也更碎。 - 缓存键里的
parser_page_size— 换 page_size 重跑会生成新键、旧缓存失效,整份重切一遍;想复用缓存就保持同一 page_size。 - 重叠字符数 — 硬编码在
_get_last_part的available_len=150且只取同页;调大跨块信息多、相邻块重复 token 也多;调成 0 则块边界句子可能被截断。
想自己验分块数量,改完parser_page_size跑python examples/parallel_doc_qa.py看日志里的 chunk 数,参数细节见 README.md。
【免费下载链接】Qwen-AgentAgent framework and applications built upon Qwen>=3.0, featuring Function Calling, MCP, Code Interpreter, RAG, Chrome extension, etc.项目地址: https://gitcode.com/GitHub_Trending/qw/Qwen-Agent
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考