ARTICLE DETAIL

资讯详情

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

DeepSeek + RAG 构建政策文件智能解读系统:从选型到部署实践

DeepSeek + RAG 构建政策文件智能解读系统:从选型到部署实践 简介围绕DeepSeek在政务场景中的落地应用这份PDF全面呈现政策文件智能解读系统的建设方法论。文档首先从政务数字化背景与政策解读需求切入概述DeepSeek的神经网络架构、训练机制以及在自然语言处理、计算机视觉等领域的应用案例随后按系统建设流程逐一讲解需求分析、总体架构设计、数据收集与清洗标注、模型训练与优化、功能模块开发、系统集成部署、测试评估和案例实践等内容。其中对政策文件上传管理、智能解读、检索查询、可视化展示、数据安全、多语言支持等关键环节均给出设计思路与实施要点并配有地方政务案例的效果展示与经验总结帮助读者形成从需求到落地的完整认知。资源为1个PDF文件共37页约2.06MB目录清晰、内容完整已有147人学习浏览适合政务信息化从业者、AI应用开发人员及高校相关专业学生参考学习。1. 政务数字化实践为什么政策文件解读最适合先上大模型政务数字化喊了好几年真正落到办事人员手上的工具并不多。一个很具体的痛点政策文件以 PDF 形式下发量大、更新快、表述严谨新来的同事翻几十页文件找一条申报条件老同事靠经验记「大概在第三部分」。这套工作流消耗的时间成本极高而 DeepSeek 这类开源大模型恰好把「读文件、找依据、答问题」这件事的成本打了下来——不需要微调不需要从头训练把 PDF 解析、向量检索和提示词工程串起来就能搭出一个政策文件智能解读系统。这篇文章要讲的就是这套系统的完整建设路径从选型、架构到落地包含我实际部署中踩过的坑和调过的参数。适合政务信息中心的开发人员、做企业政策申报系统的团队以及任何需要在内部网络里处理大量非结构化政策文档的从业者。我不写理论框架只写能照着复现的操作。2. 选型与整体架构DeepSeek 做政策解读的合理性和系统组成2.1 为什么选 DeepSeek可私有化部署是政务场景的硬门槛政务场景对数据出境和数据安全的要求比一般企业严格得多。政策文件可能涉及尚未公开发布的征求意见稿、内部口径汇总甚至包含地方财政补贴的具体金额和分配方案这些数据不可能送到公网 API 上去跑。DeepSeek 这类开源模型的最大价值就在这里——权重完全开放可以部署在政务云或内网的 GPU 服务器上做到数据不出域。相比之下调用公网闭源 API 虽然在效果上也可能不错但合规审核这一关大概率过不了。选 DeepSeek 而不是其他开源模型的另一个理由是中文长文本理解能力。政策文件的特点是长句子、多重复、大量「原则上」「视情」「按照有关规定」这类带有弹性空间的表述对模型的语义理解要求高。DeepSeek 系列模型在中文语料上的表现属于第一梯队特别是在零样本提取和指令跟随方面做「给定一段政策原文提取申报条件」这类任务实测下来输出的结构化程度高于同参数规模的其他开源模型。还有一个现实考虑部署门槛。如果用满血版模型需要多张高性能显卡预算不够的团队可以直接用量化版本或中等尺寸版本跑 CPU 推理速度慢一些但能用。这一条对经费有限的区县级政务单位非常重要。2.2 系统四层架构从 PDF 到问答结果的完整链路政策文件智能解读系统的架构可以拆成四层数据接入层负责收集和整理政策源文件包括 PDF、Word、网页通知处理后统一格式。知识构建层把非结构化文本切成小段、清洗、向量化存入向量数据库。检索推理层接收用户问题先从知识库召回相关片段再拼进提示词模板交给大模型生成答案。应用交互层面向最终用户提供一个简单的问答界面或者通过 API 给其他业务系统调用。整个系统的核心技术栈围绕 RAG 展开。为什么不直接微调因为政策文件更新频繁一个县一年要发几百份新文件每来一批新文件就微调一次模型从数据标注到训练调参的周期太长成本也高。RAG 的更新逻辑是增量的——新文件解析入库就立刻生效模型本身完全不用动。而且 RAG 能在回答时标注「根据 XX 文号第 X 条」让用户能回到原文核对这对政务问答场景来说是刚需。核心组件选型如下表所示都是我在实际项目里验证过可用的组合组件推荐选型选择理由大模型DeepSeek-R1 系列蒸馏版 / DeepSeek-V3 量化版中文理解强可私有化部署PDF 解析PyMuPDF PaddleOCR 组合PyMuPDF 处理文本型 PDFOCR 兜底扫描件向量化bge-m3 本地部署中文向量效果好支持多种粒度向量数据库Milvus 或 Elasticsearch支持混合检索和过滤条件应用框架FastAPI 后端Python政企环境熟悉 Python 技术栈生态完整这套架构没有引入太重的东西。如果单位里已经有 Elasticsearch 在跑直接复用它的向量检索能力少维护一个组件。如果是从零开始Milvus 部署更简单官方有 Docker Compose 一键启动的配置。2.3 最小可用链路三个命令先跑通再谈优化很多项目死在「设计得太完美第一步迈不出去」。我的建议是第一天先搭一条最小链路不管效果好不好先把路走通。最小链路只需要三件事把 PDF 文本抽出来、把文本切块后向量化存起来、接上 DeepSeek 做问答。第一步启动 DeepSeek 模型服务。以 vLLM 部署为例一条命令启动 OpenAI 兼容的 API 服务vllm serve deepseek-ai/DeepSeek-R1-Distill-Qwen-14B \ --host 0.0.0.0 \ --port 8000 \ --max-model-len 8192 \ --gpu-memory-utilization 0.85逻辑说明vllm serve是 vLLM 0.6 以上版本提供的便捷命令会自动加载模型并启动一个兼容 OpenAI 协议的 HTTP 服务。政务内网里其他系统对接时可以直接用标准的 OpenAI SDK 指向这个服务地址。--max-model-len 8192表示上下文窗口长度处理政策条文时建议至少设到这个值太短会导致长段落被截断。--gpu-memory-utilization 0.85允许 vLLM 使用 85% 的显存留出余量避免 OOM。如果显存只有 16G改用 7B 或 8B 的量化版本参数相同。第二步用 FastAPI 写一个最小的问答接口from openai import OpenAI client OpenAI( base_urlhttp://127.0.0.1:8000/v1, api_keyEMPTY, ) def ask_policy(question: str, context: str) - str: response client.chat.completions.create( modeldeepseek-ai/DeepSeek-R1-Distill-Qwen-14B, messages[ {role: system, content: 你是政策解读助手严格依据提供的政策原文回答问题不得编造内容。}, {role: user, content: f政策原文\n{context}\n\n问题{question}} ], temperature0.1, max_tokens1024, ) return response.choices[0].message.content逻辑说明temperature0.1是为了让生成结果稳定政务场景不需要创造性需要的是每次回答尽量一致。max_tokens1024给足输出空间政策解读的回答通常几百字。注意api_key填什么都行vLLM 默认不校验这只是格式要求。第三步向量化并检索。用一个轻量的 python 脚本验证链路from sentence_transformers import SentenceTransformer model SentenceTransformer(BAAI/bge-m3) docs [第一章 总则 第一条 为规范专项资金管理..., 第二条 本办法适用于...] embeddings model.encode(docs, normalize_embeddingsTrue) print(embeddings.shape)逻辑说明SentenceTransformer是加载 embedding 模型最常用的方式BAAI/bge-m3是智源开源的中文向量模型支持 8192 长度输入对长政策段落友好。normalize_embeddingsTrue做归一化后续用点积算相似度时结果就是余弦相似度。输出维度为 [段落数, 嵌入维度]bge-m3 的维度是 1024 维。这三步跑通说明「PDF → 文本 → 向量 → 检索 → 生成」的技术链路是通的。后面所有工作都是在每一环上做精细化。3. 政策文件解析与知识库构建PDF 处理是整条链路最脏最累的活3.1 政策文件 PDF 的三种类型解析策略完全不同政务场景的 PDF 远没有想象中那么规整。我把实际遇到的 PDF 分成三类每一类的处理方式不一样第一类是「数字排版型」PDF公文系统直接导出的文字层完整复制粘贴不会乱码。这种最简单用 PyMuPDF 直接抽取文本就行。第二类是「扫描盖章型」PDF纸质文件扫描成图片再合成的 PDF肉眼看着清楚但没有任何文字层。必须先 OCR。第三类是「混合型」PDF主体是文字层但夹杂表格、流程图、红头文件的图片扫描页。最麻烦的是带红头的文件红头部分经常是图片嵌在上面而正文是文字——解析时容易把抬头丢了。这里有一个很多新手会踩的坑拿到 PDF 先看一下有没有文字层不要上来就 OCR。OCR 又慢又容易出错一个字错了在政策文件里可能就改变了申报条件的含义。快速判断文字层是否存在的方法很简单用 PyMuPDF 抽取前几页文本如果抽出来的字符数低于某阈值比如每页少于 50 个字符再决定走 OCR 流程。判断代码如下import fitz def has_text_layer(pdf_path: str, check_pages: int 5) - bool: doc fitz.open(pdf_path) total_chars 0 for page in doc[:check_pages]: text page.get_text() total_chars len(text.strip()) doc.close() return total_chars / check_pages 50逻辑说明fitz.open打开 PDF 文件doc[:check_pages]取前五页做抽样检查page.get_text()返回该页所有文本内容。如果平均每页字符数小于 50基本可以断定是扫描件需要走 OCR。50 这个阈值是我试出来的经验值考虑了一些封面页、空白页的干扰。3.2 文本抽取与清洗PyMuPDF 为主、PaddleOCR 兜底的完整流程文本型 PDF 的抽取比较简单但抽完要洗。政策文件里有页眉页脚、发文字号、印章说明这类与正文无关的内容还有一些「第 X 页 共 Y 页」的页码标记这些都要处理掉。我一般按行处理过滤掉纯数字、包含「第 页」字样的行以及重复出现的页眉内容。对于扫描件用 PaddleOCR 做完整的 OCR 管线。PaddleOCR 虽然后期维护节奏慢了下来但中文识别能力仍然是最强的开源方案之一模型体积和推理速度都在可接受范围内。推荐用 PP-OCRv4 的中文模型精度比 v3 有明显提升特别是对仿宋_GB2312 这类政务常用字体的识别效果改善很大——这种字体细长、笔画密集用通用 OCR 模型经常识别错字。完整的抽取流程写在下面import fitz from paddleocr import PaddleOCR ocr PaddleOCR(use_angle_clsTrue, langch, show_logFalse) def extract_pdf_text(pdf_path: str) - str: doc fitz.open(pdf_path) full_text [] for page_num, page in enumerate(doc): text page.get_text().strip() if len(text) 50: # 扫描页渲染成图片后走 OCR pix page.get_pixmap(dpi200) img_path ftemp_page_{page_num}.png pix.save(img_path) result ocr.ocr(img_path, clsTrue) lines [line[1][0] for res in result for line in res] if result else [] full_text.append(\n.join(lines)) else: full_text.append(text) doc.close() return \n.join(full_text)逻辑说明对每一页先尝试抽取文字层len(text) 50判定为扫描页或图片页。扫描页用page.get_pixmap(dpi200)渲染成 PNG 图片dpi 选择 200 是精度与速度的平衡点——150 太糊识别容易错300 效果好但慢一倍。然后交给ocr.ocr()识别并拼接文字。这段代码是完整可跑的但如果 PDF 页数很多建议加个缓存机制别重复渲染同一页。抽取后的清洗我有几条经验把全角括号统一成半角括号把「一」「1」这类序号的变体格式统一去掉行首行尾的空白字符。政策文件里「第一条」「一」「1.」这些层级标记非常重要它们是后续分块的主心骨清洗时千万别把它们当噪音删掉。3.3 分块策略政策文件必须按「条」切不能按固定字数切这是整个系统里对最终效果影响最大却最容易被低估的环节。很多通用 RAG 教程教大家按固定窗口大小切块比如每 512 个字符一块、重叠 128 个字符。这种做法对新闻文章、技术博客没问题但用在政策文件上会出大问题——政策文件的最小信息单元是「条」一条可能几十字也可能几千字一条里往往同时包含适用对象、条件、时限多个要素。按固定窗口切极有可能把同一条内容切到两个块里导致检索时只召回一半模型回答问题时看到的上下文不完整给出的解读就是错的。我实测过的正确做法是用「层级感知分块」先按章节大标题切出章再在章内部按「第 X 条」切出条最后对超长条做二次切分。实现思路用正则配合状态机import re def split_by_article(text: str): # 匹配 第X条 作为切分点 pattern re.compile(r(第[一二三四五六七八九十百零\d]条)) segments [] current [] for line in text.split(\n): line line.strip() if not line: continue if pattern.match(line): if current: segments.append(\n.join(current)) current [line] else: current.append(line) if current: segments.append(\n.join(current)) return segments逻辑说明这个脚本的逻辑是逐行扫描遇到「第 X 条」开头的行就开始一个新段落其他行追加到当前段落。比用正则一次性切更可靠因为「第 X 条」可能是行首也可能是夹在段落中间的逐行处理能处理前者。切完后给每段生成一个元数据标记如{文号: X政发〔2024〕12号, 章节: 第三章, 条款: 第八条}这个标记后面做过滤检索时非常有用。分块之后每块的文本量级差异很大。把chunk_size设定为基于 token 数而不是字符数用tokenizer统计后再决定要不要二次拆分。我在项目里预定义了max_tokens 500的阈值超过这个长度就要继续切。二次切分的时候按标点层级找切点句号 分号 逗号保证切出来的块语义完整。4. 检索与生成向量召回、重排序和提示词模板设计4.1 向量检索参数top_k、相似度阈值与 Embedding 选型政策解读的检索场景有自己的特殊要求。通用问答可能允许「相关就行」但政策场景里用户问「高新技术企业的认定条件是什么」如果检索结果里只有半句话模型就有很大概率瞎编另一半。所以检索阶段的目标是宁可少召回不可漏关键信息。Embedding 选型上bge-m3 是当前中文场景比较稳妥的选择。它在长文本上的支持比早期模型好很多向量维度 1024检索效果比 OpenAI 的 text-embedding-3-small 在中文场景里不落下风且可以完全本地部署。如果单位硬件条件有限也可以用 bge-large-zh-v1.5512 维显存占用更小效果稍逊但对政策文件的区分度仍然够用。检索参数方面我建议设置两个关键数值def retrieve(query: str, top_k: int 8, min_score: float 0.60): query_vec embedding_model.encode([query], normalize_embeddingsTrue) # 假设 doc_collection 是向量数据库的集合对象 results doc_collection.query( query_embeddingsquery_vec.tolist(), n_resultstop_k, include[documents, metadatas, distances] ) filtered [ r for r in results if (1 - r.distance) min_score ] return filtered逻辑说明min_score0.60这个阈值是经验值。1 - distance是把 Milvus 等数据库返回的距离转成相似度分数0.60 表示检索结果与问题的相关度达到六成以上才会被送入大模型。阈值调得太低比如 0.4一些弱相关的片段会混进来浪费上下文窗口不说还可能把模型带偏调得太高比如 0.8很多真实相关的政策条文因为表述方式和问题差距较大而没有被召回。0.60 是我跑了多轮测试后觉得比较稳的设定如果你的政策库非常垂直比如全是工信口的文件可以尝试调到 0.65。top_k的市场共识是 6 到 10 之间。政务场景里我习惯取 8因为政策文件有很多「但同时应当满足以下条件」这种跨条款的关联取太少容易漏取太多提示词太长模型容易分不清主次。4.2 重排序只用向量检索不够BM25 与 bge-reranker 的互补向量检索擅长找语义相似的内容但存在一个天然弱点如果政策原文用了「从业满三年」这种表述而用户问的是「需要几年工作经验」向量嵌入能把它匹配上但如果用户问「工作年限有什么要求」语义距离远一点向量召回可能排到很后面。这时候需要关键词检索来互补传统做法是 BM25 算法它在精确匹配上比向量检索可靠得多——政策文件里「资质」「备案」「专项资金」这类术语用关键词一查一个准。我推荐用混合检索然后做融合具体做法是向量检索取回 top 20BM25 也取回 top 20合并去重后再用一个交叉编码器重排序。bge-reranker-v2-m3 是目前效果和性能比较平衡的重排序模型。重排序这一步能显著提升准确率因为交叉编码器是让查询和文档一起过一遍模型能捕捉到向量内积捕捉不到的细粒度相关性代价就是推理速度慢不适合对大量文档做排序——所以一定是先粗召回再精排。融合和重排序的流程示意from rank_bm25 import BM25Okapi from cross_encoder import CrossEncoder bm25 BM25Okapi([doc.split() for doc in all_docs]) bm25_hits bm25.get_top_n(query.split(), all_docs, n20) vector_hits retrieve(query, top_k20, min_score0.0) # 先不过滤 candidates deduplicate(bm25_hits vector_hits) reranker CrossEncoder(BAAI/bge-reranker-v2-m3) pairs [(query, doc) for doc in candidates] scores reranker.predict(pairs) ranked sorted(zip(candidates, scores), keylambda x: x[1], reverseTrue) final_context [doc for doc, score in ranked[:8]]逻辑说明BM25Okapi是最常用的 BM25 实现get_top_n直接返回评分最高的文档列表。向量检索这一步不要把min_score设得太高目的是多召回候选精排交给 reranker。CrossEncoder对每个 (查询, 文档) 对独立打分输出一个相关性分数按分数倒序取前 8 个作为最终送入大模型的上下文。这套流程的效果对比单纯向量检索核心指标——回答被判定为「完全正确」的比例——能提升一到两成值得做。4.3 提示词模板让模型用引用的原文说话不会就承认不会RAG 系统做出来以后效果好不好要看两方面上下文里的材料全不全以及模型会不会「照着材料说话」。后者几乎完全取决于提示词写得怎么样。政策解读的提示词有一条铁律强制模型引用原文。模型生成的每一条结论都必须对应到政策文件的文号、条款号和原文摘录这样用户可以回到原文核对也方便审核人员判断模型是不是在胡编。我的提示词模板基本长这样你是政务政策解读助手。请严格按照提供的政策文件片段回答问题。 要求 1. 优先引用原文表述标注出处文号条款。 2. 如果问题在提供的文件中找不到依据直接回复未找到相关内容不要推测。 3. 如果问题涉及多个条款分别列出来并说明条款之间的关系。 4. 回答保持书面语风格不用口语化表达。 5. 不确定的信息不得补充说明。 政策片段如下 {context} 用户问题 {question}这段提示词有几个点值得注意。第二点「未找到相关内容」的兜底非常重要——没有这一步模型在检索不到内容时会脑补一段合理的政策条文这在政务场景里是不可接受的。第五点「不确定的信息不得补充说明」也是我踩了雷之后加的早期测试时模型回答「根据《XX办法》第十二条申报企业应当具有独立法人资格」但原文第十二条只写了「具有独立法人资格或视同法人单位」模型把「视同」两个字漏掉了这个信息就失真了。另外我在系统层面做了一个强制约束在上文提示词之外用 API 参数里的logit_bias或后处理逻辑检测回答中出现的条款编号确认是否存在于本次检索的上下文范围内。不在范围内的条款号直接标记为「疑似引用错误」返回给用户前先拦截。这个兜底逻辑虽然粗暴但在真实业务中非常管用。5. 系统落地部署与避坑记录从开发环境到政务内网的五道坎5.1 部署模式选择与硬件估算政务场景的部署方式主要取决于服务器在哪里。常见的有三种政务云、独立内网服务器、本地工作站。我做过一个区级项目用的是政务云上的 GPU 节点网络隔离做得很好数据不出域但算力共享高峰期有其他业务抢占资源推理延迟波动比较大。另一个市级项目是独立机柜部署的两张国产加速卡跑量化后的 14B 模型稳定性和速度都可控。模型尺寸和硬件的匹配关系这里有一组经验数据可以作参考。7B 到 8B 的量化模型需要约 8G 显存16G 单卡能跑14B 模型 FP16 精度大约需要 32G 显存量化到 INT4 后可以降到 12G 左右。如果只有 CPU 环境能跑 7B 量化模型生成一个 300 字回答大约要两三分钟作为内部辅助工具不是不能用但体验谈不上好。向量数据库部署相对轻量。Milvus 单机版用 Docker 启动8G 内存就够跑十万级向量的场景。政务单位手上的政策文件存量通常也就几千份分块后一两万个向量这数据量对 Milvus 来说毫无压力不需要上分布式集群。5.2 七个常见坑与对策以下是这套系统在真实部署过程中最常见的七个问题每个都按「现象→原因→解决」展开基本都是我用真金白银换来的经验。坑一PDF 解析后出现大量乱码和缺字。现象是正文里「鼓励」「补贴」这类词变成了「励」「补贴」。原因很可能是 PDF 里的字体使用了自定义编码映射PyMuPDF 拿到的字符映射表不全。解决方法是改用 OCR 管线处理这些异常页面不要试图在字符层面修补。另外在解析时强制用 UTF-8 编码输出避免后续写入数据库时丢字符。坑二向量检索总返回相似但错误的内容。现象是查「小微企业税收优惠」召回的都是「中型企业税收优惠」。原因是政策文件里「小微」「中小微」「中小企业」这些词在语义上高度接近embedding 模型分不清它们的差异。解决方法是构建一个同义词/易混淆词表在检索时做实体级别的精确匹配增强——把文档里出现「中小微企业」的片段单独建一个关键词索引用户问「小微企业」时先做一次精确匹配匹配到了就把相似度分数加上一个权重。坑三回答引用了不存在的条款。现象是生成的回答标注「根据〔2024〕12号文第X条」但打开原文件没有这一条。原因是模型在生成时把上下文里的信息记忆错乱了自己组合了一个不存在的条号。解决方法是后面加一个数字校验层用正则提取回答里的「第X条」和文号去原文索引里比对匹配不上就拦截并重新生成。我建议这个校验做成强制逻辑不要只靠提示词约束——大模型在长文本生成中「记错出处」的概率比想象中高。坑四上下文窗口溢出长文件被截断。现象是问一个涉及全文多个章节的问题时模型回答「没有找到相关内容」。原因是嵌入进提示词的 8 个检索片段拼接后超过了模型上下文长度。解决方法是控制每个片段长度在 300 字以内并动态调整top_k——用户问题越复杂每个片段就越要精炼而不是增加数量。坑五政务内网无法访问外网模型仓库模型下载困难。现象是部署服务器在隔离网络huggingface-cli download直接超时。解决方法是提前在外网下载模型文件打包用移动硬盘导入内网。注意模型下载时不要只下载权重文件配置文件和 tokenizer 文件也要一起带进去少了tokenizer_config.json启动时会报错。坑六并发高时 GPU 显存溢出导致服务崩溃。现象是几个用户同时提交长问题vLLM 服务直接 OOM。原因是不加限制地接收请求多个并发请求叠加占满显存。解决方法是启动时加--max-num-seqs参数限制并发数再在应用层要做排队vllm serve deepseek-ai/DeepSeek-R1-Distill-Qwen-14B \ --max-num-seqs 4 \ --gpu-memory-utilization 0.85 \ --max-model-len 8192逻辑说明--max-num-seqs 4把并发请求数限制在 4 个多余的请求会在 vLLM 内部排队等待。显存 24G 的卡跑 14B 模型时这个并发数可以让每个请求都稳定分配足够 KV cache吞吐量和延迟是吞吐量和延迟的最佳平衡点。如果业务要求高并发需要扩容多张卡或多节点做负载均衡单卡硬扛不现实。坑七模型回答「官腔官调」但无法直接用于业务。现象是模型用一套正确的废话回答了具体问题比如问「申报需要哪些材料」回答「申报材料应齐全、真实、有效」。原因是政策原文就是这么写的模型只是忠实复述。这是 RAG 系统在政策场景里的天然局限——原文对任何阅读者来说依然是原文。解决的思路是在提示词中追加指令要求按「申报主体—条件—材料—时限」四要素列举。这不是模型能力问题是提示词设计问题。5.3 评测与回归机制用一套固定题目盯住系统不跑偏系统上线后最怕的不是效果差而是效果越来越差——换了一版 embedding 模型、调整了分块参数、更新了向量库中的文件、甚至只是改了检索权重都可能让某个之前答对的题开始答错。而且没人能说出来是哪一步导致的。所以我在项目里固定保留了一套评测集每次变更后跑一遍再放行这套方法比任何代码评审都管用。评测集的结构每个政策文件抽取 5 到 10 个问答对问题覆盖「条件查询」「流程查询」「时效查询」「豁免查询」四类。标注答案时写清楚依据的条款号和原文摘录。评测时对系统回答的判定标准不是「写得差不多的内容」而是「依据、结论、表述三个纬度是否都正确」。打分逻辑代码示意def evaluate(question: str, llm_answer: str, true_answer: str) - bool: # 抽取 LLM 回答引用的条款号 cited re.findall(r第[一二三四五六七八九十百零\d]条, llm_answer) # 判断 LLM 引用的条款号是否在标准答案引用的条款集合中 return all(any(c in ref for ref in true_answer[citations]) for c in cited)逻辑说明这个简化版本只判断引用条款号是否命中标准答案的范围是比较严格的判定方式——只要有一条引用的条款不在集合内这题就算错。实际使用中你可以调得更细核心结论对、补充引用多余算半对具体按业务容忍度来定。每次跑评测集把没有通过的题目记录在案。这是很典型的「回归测试」思路如果上一次提交所有题都过了本次提交同一道题却答错了那一定是系统变更引入的回归必须追查定位。这套机制配合按文件增量更新的向量库可以保证系统在长期运行中日拱一卒、稳定不出大坡。6. 让系统从「能答」到「好用」四个值得投入的进阶方向基础链路跑通后系统的形态已经可以满足内部辅助查询需求了。但如果想把使用体验做到政策申报人员真正愿意天天用还有四个方向值得投入精力。第一个方向是政策关联图谱。政策文件之间常有引用关系比如某区的实施细则引用市里的管理办法市里的办法又引用省里的条例。现在 RAG 系统回答问题时只检索到了区里的细则如果没有把上级文件的上下文一并拉入解读可能不完整。我试过在文档元数据中维护「关联文件」字段检索时先把关联文件抓取进来做二次扩展。这个方案不算复杂但对解读质量提升非常明显特别是处理多层级的政策体系时基本是刚需。第二个方向是表格与数据抽取。大量政策文件的核心内容在表格里——补贴标准、申报时间节点、审核流程。当前的分块主要基于文本「表格里的内容」经常被解析得七零八落。尝试过把表格单独识别出来转成结构化 JSON 存入附件库。用户问「省级专精特新补贴多少钱」时模型不再翻文本而是直接读 JSON。这块对准确率的提升仅次于重排序。第三个方向是问题日志驱动的知识库迭代。系统使用一段时间后会积累大量「用户问了什么、模型答得如何」的日志。定期把这些日志里的失败案例整理成样本反过来优化分块策略和提示词。如果你发现很多问题集中在某类文件上大概率是这类文件的解析质量有问题——检查一下是不是扫描件没走 OCR或者表格结构被破坏了。这种利用生产数据反馈的机制比任何离线调优都有效。第四个方向在正式对外提供服务之前建议增设一个人工复核入口。政务场景的容错率极低AI 解读只能作为建议参考不能直接作为办事依据。常见的做法是给每个回答加一个「复核并发送」的按钮由业务人员确认后通过短信或邮件转发给用户。在这个人机协同的闭环里模型的职责是大幅提高业务人员的处理效率而不是替代决策。最后说一个我自己形成的工作习惯每次改完检索参数或提示词先在评测集上跑一遍再拿 3 个真实业务问题做人工复核确认没有引入新问题才发布到正式环境。这套系统的价值完全建立在「可信」二字上——让业务人员信任模型给出的每一条依据都可以追溯到原文信任系统的每一个回答逻辑都是稳定的、可验证的。做到这一点技术上的所有投入才是值得的。希望帮到你。本文还有配套的精品资源点击获取
返回列表