
本地智能体跑通后最大收益不是省了那点API费而是终于敢把整年的合同、几十页的会议纪要、上百条需求清单直接丢给模型处理再也不用先人工截断再分段喂了。这篇文章就从我一个实际的本地智能体项目出发完整讲清楚办公文档预处理怎么做、任务调度链路如何设计、长文本超限用什么思路去突破以及我在实测过程中踩过的坑和最终沉淀下来的方案。1. 长文本超限这个痛点让我决定自己搭一套本地链路先说背景。我日常处理最多的不是代码而是各类办公文档——合同扫描件、PDF版会议纪要、Word格式的需求说明、Excel导出的数据清单动辄几十页起步。早期用云端大模型做摘要、做问答、做信息抽取最头疼的就是输入长度限制。模型上下文窗口就那么大文档一长直接截断截断位置还是随机的经常出现前半段是正文、后半段莫名其妙断在半句话上的情况。后来试过先复制粘贴到文本编辑器里手工分段再一段一段问但文档一多就完全失控而且分段本身就会破坏上下文模型对文档整体的理解基本为零。这就是我做这个项目的直接动机——把喂给模型之前的所有脏活、累活、容易出错的活全部交给一个本地智能体自动化处理。智能体的职责很清楚接收一份或多份办公文档自动完成格式识别、内容清洗、语义分块、索引构建然后根据任务类型决定查哪一块还是全量汇总最终在模型上下文窗口允许的范围内给出可靠的回答或产出物。先放一张整体链路图文字版输入文档 → 格式解析PDF/Word/Excel → 内容清洗去页眉页脚水印 → 语义分块保留标题层级 → 分块摘要 索引 → 任务调度器 → 检索增强命中相关分块 → 组装上下文 → 本地大模型生成结果这条链路里每一环都有独立的技术决策并不是简单调一个库就能完事。下面拆开讲。2. 办公文档预处理从一堆文件到结构化片段预处理是整个链路的地基地基不牢后面模型再强也白搭。我在这部分花的时间最多也踩了最多的坑。2.1 格式解析层PDF、Word、Excel各走各的路办公文档最麻烦的一点是格式五花八门统一用一套工具解析必然出问题。我的实践是分格式走独立解析管线PDF优先用PyMuPDFfitz提取文本层PDF里如果带扫描件则叠加OCR我用的是PaddleOCR本地跑不吃云资源。这里有个容易踩的坑——很多PDF其实是图片PDF直接提取文本得到的是空字符串必须先用fitz.Page.get_text()判断提取出的文本长度是否低于阈值比如小于50字符触发OCR兜底。Word.docx不用问python-docx是标准选择。但要注意嵌套表格python-docx默认只遍历段落表格里的文本取不到需要额外写一段递归遍历document.tables的逻辑。Excel.xlsx数据处理用pandas但转成文本结构时需要保留表头和行关系我习惯把每行格式化成字段名值的KV文本这样后面模型理解起来更直接。每条管线输出统一的中间格式——带元数据的文本块text chunk元数据保留来源文件名、页码、标题路径等信息。这一步的核心原则是保留结构信息不让文档变成一锅粥。2.2 内容清洗页眉页脚、水印、乱码一次性处理掉原始文档里的噪音比想象中多。页眉页脚、公司logo注释、分页符造成的重复文本、PDF提取时出现的断裂空格和乱码字符这些如果不清理会直接影响后续分块的语义完整性。我的处理策略页脚识别很多文档页脚是第X页共Y页通过正则第\s*\d\s*页\s*共\s*\d\s*页可以直接命中删除。页眉重复文本统计全文出现次数超过阈值比如每页都出现且位置在页首的短文本自动判定为页眉。PDF断裂空格re.sub(r(?\w)\s(?\w), , text)把英文单词中间的断行空格合并。中文场景还要处理全角半角统一。内容清洗的价值要在后面分块环节才体现出来——页脚混杂在正文中间会把一个语义完整的段落拦腰截断导致分块边界全部偏移。2.3 语义分块为什么不能按固定字符数硬切很多人做长文本处理最简单的做法是text[i:i500]硬切这是最省事也最蠢的方案。硬切会把一个完整的自然段、一个表格、一条逻辑链拦腰斩断检索时命中半截内容模型看到的上下文是残缺的回答质量自然拉胯。我的分块策略是结构优先 语义兜底优先按标题层级分块。#、##、###以及Word里的Heading 1/2/3样式都映射成层级节点每个标题下的内容作为一个候选大块。候选大块超过模型上下文限制时再按段落边界二次切分优先保段落完整。段落也超长时才做语义切分——用句号、分号、换行符做软边界不让句子断裂。相邻小分块之间保留10%的字符重叠overlap保证检索时跨分块的上下文不丢。这里要给一个关键参数建议分块大小不要拍脑袋定要根据你最终使用的模型上下文长度倒推。比如本地用Qwen2.5-7B-Instruct4K上下文窗口那么每个分块在500-800字左右比较合适叠加重叠区域后检索返回3-5个分块刚好塞得进上下文。# 伪代码示例结构优先分块 def chunk_by_structure(doc): chunks [] for heading, content in doc.extract_heading_tree(): if len(content) MAX_CHUNK_SIZE: chunks.append(Chunk(headingheading, textcontent)) else: # 按段落二次切分 for para in split_by_paragraph(content): if len(para) MAX_CHUNK_SIZE: chunks.append(Chunk(headingheading, textpara)) else: # 语义软边界切分 chunks.extend(split_by_semantic_boundary(para)) return chunks2.4 元数据与索引让每一块内容找得到分块完成后不是直接一股脑丢给模型而是先建索引。我用的是本地向量库BM25混合检索向量库选用的是轻量级方案chromadb不需要单独起服务嵌入模型用的bge-small-zh-v1.5在中文场景下效果比通用嵌入模型好不少。每块内容入库时带上完整元数据字段示例用途source2024年度合同汇总.pdf溯源page12定位原文heading_path合同管理 甲方义务保留逻辑层级chunk_id012-4全局唯一标识summary本合同约定甲方应在30日内付款...分块摘要用于快速筛选分块摘要这里有个进阶操作——每块入库前用本地小模型先生成一个短摘要检索时先比对摘要再比对全文既能提高命中准确率又能在后续组装上下文时控制长度。这个技巧在长文本超限问题上帮了大忙后面细说。3. 任务调度链路从一个问题到一组任务的自动路由预处理把文档变成了结构化片段接下来要解决的是怎么调度的问题。一个智能体面对的真实办公任务往往不是一句简单的总结这份文档而是复合型的比如把上周所有会议纪要里提到的待办事项提取出来按负责人分组并标出截止时间。这类任务需要拆分步骤、按序执行、每步引用不同的文档片段。3.1 任务拆解把大问题切成子任务我在本地智能体里内置了一个任务拆解层本质上是一套Prompt模板驱动的拆解器。用户输入任务后先让模型判断任务类型再拆解出子任务链。典型任务类型和对应策略单文档摘要类直接全量处理无需拆分跨文档汇总类多文档逐篇摘要再统一汇入最终输出信息抽取类定位检索 → 分块抽取 → 结构化输出对比分析类按维度拆解 → 各维度独立检索 → 合并对比拆解示意非代码用户输入汇总上月所有项目周报中的风险事项 → 子任务1: 识别文档列表上月周报 → 子任务2: 逐文档检索风险相关关键词 → 子任务3: 汇总去重、按严重程度排序 → 子任务4: 输出结构化风险清单拆解最大的价值是把一次超长输入化解为多次小输入 汇聚。3.2 调度器设计规则路由 模型判断双轨调度器是任务链路的枢纽我的设计是规则优先、模型兜底规则路由命中关键词如摘要提取对比列出的任务直接走预设流程模板不消耗模型推理响应快、可预期。模型路由规则无法判断的模糊任务交给模型做意图识别输出JSON格式的任务计划调度器解析后按计划执行。这套双轨设计实测下来最稳。纯规则覆盖不了所有场景纯模型路由又容易在任务复杂时跑偏双轨结合在几十次真实测试中任务理解准确率从单模型的78%提升到了93%。3.3 任务队列串行执行还是并行执行涉及多文档处理时任务队列设计直接决定耗时。我的方案是同一文档内的多个子任务串行执行避免重复读盘和重复解析。不同文档间的独立任务并行执行用concurrent.futures.ThreadPoolExecutor线程数控制在CPU核心数附近。模型推理环节统一排队本地推理单线程更稳定多线程并发反而会因显存抢占导致生成速度大幅下降。实测数据处理10份平均30页的文档纯串行耗时约12分钟加上文档级并行后压缩到4分半瓶颈最终落在推理环节而不是解析环节。4. 长文本超限的破解之道不是塞进去而是按需组装回到最核心的问题——长文本怎么塞进模型我的答案非常明确能塞多少取决于组装策略而不是模型上限。不管模型上下文窗口是4K、32K还是128K盲目把全文塞进去都会导致三个问题注意力分散、关键信息被淹没、费用或耗时飙升。正确的做法是让模型只看它该看的部分。4.1 检索增强从全量文档到Top-K相关分块我的方案是混合检索BM25关键词检索 向量语义检索两者结果做融合排序。BM25擅长精确词匹配比如合同编号、人名、具体日期向量检索擅长语义相关比如项目的风险点这种开放式问题。融合策略用最简单的RRFReciprocal Rank Fusion实现成本极低效果提升却很明显。实际效果数据检索方式命中准确率人工标注平均耗时纯BM2561%20ms纯向量检索72%80msRRF混合84%95msTop-K的取值要根据分块大小和模型上下文窗口计算。我习惯给模型留出30%的生成余量剩余70%用于上下文。如果模型窗口是8K分块平均700字那么K取7左右加上重叠区域总文本约5.5K字留出2.5K给指令和输出。这个比例是我在多次实测中调出来的留太少模型输出会被截断留太多上下文又装不下。4.2 分级摘要全局浏览 局部聚焦有些任务天然要求看全文比如总结这份50页报告的核心结论。这种场景不能靠检索必须全量理解。我的破法是把长文本拆成一个树状摘要结构每个分块先生成块级摘要这一步把700字压缩到50-80字保留核心观点和关键数据。同一章节下的若干分块把块级摘要再合并生成章节级摘要。最上层把章节级摘要汇总生成文档级摘要。到输出阶段分级摘要的形态就是一颗摘要树。模型生成最终回答时先读文档级摘要500字以内再按需展开到章节级摘要最后定位到具体分块全文。这样总阅读量只有全文的15%~25%但关键信息基本不丢。4.3 上下文组装模板把喂什么变成固定流程组装上下文不是简单地把分块拼起来扔进去而是要编排成模型友好的结构。我沉淀了一套固定模板【任务指令】 明确的动作要求提取/总结/对比/列举 【背景说明】 一句话交代文档来源和范围如以下是2024年度项目周报第3-12期 【材料内容】 检索命中的分块带标题路径前缀 【输出要求】 限定格式表格/列表/JSON/纯文本限定长度这套模板的关键点在于指令放在最前面材料放在中间输出约束放在最后。模型对开头和结尾的内容关注度最高把重要的约束分别放在首尾比一股脑堆在中间效果显著更好。4.4 本地模型的选型与量化既然冠名本地智能体模型选型也是绕不开的一环。我的主力配置是Qwen2.5-7B-Instruct的GPTQ量化版4bit在16GB显存的消费级显卡上推理速度约18-22 tokens/s足够交互式使用。备选方案模型量化方式显存占用中文能力我的用途Qwen2.5-7B-InstructGPTQ 4bit6.2GB强主力问答/摘要Qwen2.5-14B-InstructAWQ 4bit11GB更强复杂推理任务glm-4-9b-chat原始FP1618GB中偏强备用切换选模型的判断标准不是跑分而是在你自己的任务集上人工对比输出。我建议每个项目都留一个包含20-30条典型任务的评测集换模型时批量跑一遍比看任何榜单都靠谱。5. 实测过程中的问题与调优心得链路搭建初期问题不断我把几个影响最大的问题单独列出来每个都值得后来者注意。5.1 分块过碎导致检索命中但信息丢失第一次跑通链路后我发现一个诡异问题——检索命中率很高但模型回答里经常出现张冠李戴。排查下来终于定位到原因分块太碎平均300字一个完整的事件描述被切成3块检索只命中了其中一块模型只看到事件起因没看到结果和责任人。打了蝴蝶结的问题。解决方案分块大小回调到600-800字并且给每一块加前后文摘要——在分块正文前插入上一块的末尾2句话作为承接。这样即使只命中一块模型也能通过承接文本感知上下文。实测回答准确率从76%提到了88%。5.2 表格转文本后变成一坨乱麻Excel或Word里的表格直接转纯文本后结构尽失模型经常把表头和表体混在一起分不清哪列是什么。后来我按行做KV格式化把每一行转成列名:值, 列名:值的结构并保留表头行作为前缀。修复前 张三 2024-01 已验收 李四 2024-02 未验收 修复后 [表格: 项目验收记录] 姓名张三, 提交时间2024-01, 状态已验收, 复核人李四, 计划验收时间2024-02, 当前状态未验收5.3 任务调度器在长任务链上迷失一次处理30个文档、拆出15个子任务、调用模型40次的场景下调度器偶尔会出现死循环——某一步模型输出了空结果调度器认为没完成重试后又失败陷入卡死。排查后发现是缺少两个重要机制超时保护每个子任务设定最大执行轮数默认3轮超轮后跳过并记录失败原因。空结果降级模型输出为空时不再直接重试先降级为继续下一步标记缺失最后汇总阶段统一提示第X步未获得有效结果。加上这两个机制后长任务链的执行稳定率从82%提升到99%再也没出现过链路卡死的情况。5.4 调用开销与耗时一个容易低估的隐性成本本地智能体虽然不花API费用但时间和电费也是成本。我在全链路中加入了一个轻量级缓存层同一个文档经过预处理后的分块结果、向量索引、摘要树全部落盘缓存二次处理同文档时直接读缓存预处理耗时从平均8秒降到0.3秒。对于每周重复处理本周新增文档这类场景缓存命中率能达到70%以上整体耗时节省非常可观。6. 项目落到实处的效果与后续扩展方向这套链路跑了两个月处理了500多份真实办公文档。最终效果比预期好但最大的收获是对长文本超限这件事有了全新认识——它不是一个模型能力问题而是一个工程架构问题。解决思路不是无限加大上下文窗口而是通过文档预处理把长文本结构化通过任务调度把复杂任务拆解通过检索和分级摘要让模型只看该看的部分。如果只让我说一个最值得复用的经验那就是先把文档变短再让模型变聪明。所有围绕文档做智能体的朋友都应该把70%的精力放在文档结构化和组装策略上模型本身反而不用纠结太多。后续我计划往两个方向扩展一是把调度器升级成可以自动编排外部工具比如调用OCR服务、发邮件通知让链路从问答走向动作执行二是尝试在分块过程中加入更多版面分析表格、图片、页眉页脚等视觉信息进一步提升文档结构还原的完整性。这两个方向后续有阶段性成果再单独写文章分享。