尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

CTRAG框架实战:基于检索增强生成的自动化合规检查系统搭建

CTRAG框架实战:基于检索增强生成的自动化合规检查系统搭建 在工程项目、合同审查、行政审批这类强合规场景中“按规则检查”往往是耗时最多、最容易出错的环节。传统做法依赖人工翻阅法规条文再对照项目资料逐条判断效率低且标准很难统一。随着 LLM大语言模型能力增强很多团队开始尝试让模型自动完成合规判断但直接“把全量文档丢给大模型”显然不现实——上下文窗口有限、关键条文容易被淹没、回答还容易“张冠李戴”。CTRAG 正是在这个背景下被提出的一种解决思路。CTRAG 的全称是Compliance Testing with Retrieval-Augmented Generation或Context-based Text Retrieval-Augmented Generation不同论文和工程落地中对单词的展开略有差异但核心都是同一个组合In-Context Learning Retrieval-based LLM。简单理解它就是一个“先检索、再判断”的自动化合规检查框架。本文将围绕 CTRAG 的概念、架构、核心模块和实战代码展开帮助你从零搭建一套可运行的合规检查原型系统。1. CTRAG 是什么从合规检查的痛点说起1.1 传统合规检查为什么低效合规检查通常指把一份“待检查对象”和若干条“规则/条文”进行比对判断是否满足要求。典型场景包括建筑设计是否符合消防规范。项目投标文件是否满足招标条款。合同条款是否违反法律法规。企业内部流程是否符合 ISO 标准。传统做法高度依赖人工。一位合规工程师需要记住大量条文编号和适用条件再在几十页甚至几百页的资料里定位关键内容。这个过程的痛点非常明显人力成本高、检查标准不统一、跨领域知识难以复用。不同检查人员对同一条文的理解可能不同同样的违规项在不同报告里描述也不一致。1.2 LLM 来了但直接使用有局限LLM 出现后一个自然的想法是把条文和待检查内容都丢给模型让它输出“符合/不符合/存在风险”。实际试过你就会发现直接使用大模型做合规判断会遇到三个核心问题上下文窗口有限一份消防规范可能是几百页根本放不进模型上下文。相关条文容易被淹没即使勉强放入模型也无法准确判断该引用哪一条。幻觉问题严重模型可能编造不存在的条文编号或引用错误条款。这就需要对输入进行“有选择的加工”——只把最相关的条文和片段传给模型让它在精确的上下文范围内做判断。1.3 CTRAG 的核心思想CTRAG 把“检索”和“生成”两个环节串成一个闭环输入待检查文档 ↓ 拆解为检查项 ↓ 对每个检查项进行条文检索Retrieval ↓ 组装上下文In-Context ↓ LLM 生成合规判断Generation ↓ 输出检查报告它解决的问题不是“让 LLM 更聪明”而是“让 LLM 在正确的上下文里回答问题”。这也是 CTRAG 最值得借鉴的地方它不是依赖模型记住所有法规而是通过外部知识库动态补充证据让模型基于证据做判断从而大幅降低幻觉率。2. 框架架构拆解关键词背后的模块“CTRAG”这个名称里包含了四个关键词理解它们就理解了整个框架关键词含义对应模块In-Context基于上下文的推理上下文组装器Retrieval-based基于检索增强检索器Framework整体编排框架流程控制器LLMs大语言模型推理引擎2.1 Retrieval-based检索器模块检索器负责从法规知识库中找到与当前检查项最相关的条文片段。常见的实现方案有BM25 稀疏检索适合精确匹配关键字速度快但无法理解同义表达。向量稠密检索把文本转为向量适合语义匹配能处理“消防通道宽度不足”和“疏散走道净宽不满足要求”这类语义关联。混合检索两种方式结合先召回再重排序是工程中最稳妥的方案。CTRAG 框架并不强制使用某一种检索算法重点在于“检索结果要能够支撑后续判断”。2.2 In-Context上下文组装器模块检索到若干条条文后不能直接把所有内容都塞给 LLM。上下文组装器需要做几件事去重多条相似条文只保留最相关的一条。排序按相关度从高到低排列。裁剪每条条文保留核心片段避免冗余信息干扰判断。加上任务指令明确告诉 LLM 你的角色和判断标准。这个模块决定了 LLM 看到的证据质量是整个框架的“胜负手”。很多团队实现了检索但效果不好原因往往就是上下文组装没做好。2.3 LLMs推理引擎模块推理引擎接收组装好的上下文输出结构化判断结果。输出格式可以设计为 JSON便于下游系统直接解析。例如{ check_item: 疏散走道净宽度, status: 不符合, evidence: [GB50016-2014 第5.5.18条], reason: 设计图中疏散走道净宽度为1.0米规范要求不小于1.4米 }对于复杂的合规场景还可以让 LLM 先输出“分析过程”再输出“最终结论”能让判断结果的可解释性更强。2.4 Framework流程编排模块流程编排模块把上面三个模块连接起来提供任务拆解、批量检查、结果汇总、异常重试等能力。一个完整的 CTRAG 框架往往包含以下处理链路解析检查需求 → 拆分检查项 → 逐项检索 → 组装上下文 → LLM判断 ↓ 生成结构化报告 ← 汇总所有检查项结果3. 环境准备与技术选型下面进入实战环节。我们构建一个简化版 CTRAG 原型系统场景是“施工方案合规检查”——检查一份施工方案中的安全措施描述是否满足国家规范条文。3.1 环境清单建议环境如下版本可根据实际情况调整操作系统Windows 10/11、macOS 或 Linux 均可。Python3.9 或以上版本。LLM 调用支持 OpenAI 接口格式的大模型服务。向量数据库Chroma本地运行适合原型验证。依赖库langchain、chromadb、openai、pandas。3.2 安装依赖pip install langchain chromadb openai pandas3.3 项目结构compliance_checker/ ├── data/ │ ├── regulations/ # 法规条文库纯文本 │ └── documents/ # 待检查文档 ├── src/ │ ├── knowledge_base.py # 构建知识库 │ ├── retriever.py # 检索器 │ ├── context_builder.py # 上下文组装器 │ ├── llm_engine.py # LLM推理引擎 │ └── pipeline.py # 主流程编排 └── config.py # 配置文件4. 核心模块实现4.1 构建法规知识库第一步把法规条文按章节拆分成小块存入向量数据库。这一步骤关系到检索质量建议切块时尽量保留条文的完整语义不要把同一条规定拆到两个块里。# src/knowledge_base.py import os from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain.embeddings import OpenAIEmbeddings from langchain.vectorstores import Chroma def build_knowledge_base( regulations_dir: str, persist_directory: str ./chroma_db ): 把法规目录下的文本文件切块并写入 Chroma 向量库。 # 1. 读取所有法规文本 all_texts [] source_files [] for file_name in os.listdir(regulations_dir): if not file_name.endswith(.txt): continue file_path os.path.join(regulations_dir, file_name) with open(file_path, r, encodingutf-8) as f: content f.read() all_texts.append(content) source_files.extend([file_name] * 1) # 2. 切块按章节切分块大小 500 字符重叠 50 字符 text_splitter RecursiveCharacterTextSplitter( chunk_size500, chunk_overlap50, separators[\n\n, \n, 。, , ,] ) chunks [] chunk_sources [] for content, source in zip(all_texts, source_files): docs text_splitter.split_text(content) chunks.extend(docs) chunk_sources.extend([source] * len(docs)) # 3. 向量化并存储 embeddings OpenAIEmbeddings() vector_store Chroma.from_texts( textschunks, metadatas[{source: s} for s in chunk_sources], embeddingembeddings, persist_directorypersist_directory ) vector_store.persist() print(f知识库构建完成共 {len(chunks)} 个文本块) return vector_store代码里有一个细节值得注意切块分隔符。法规条文通常以“第X条”为最小单元分隔符里加入了。和可以让分块结果尽量落在完整句子边界上减少语义割裂。4.2 检索器实现检索器负责从向量库中召回最相关的条文片段。为了兼顾精确匹配和语义理解这里实现了混合检索先做向量检索再做 BM25 关键字检索最后合并去重排序。# src/retriever.py from langchain.vectorstores import Chroma from langchain.embeddings import OpenAIEmbeddings from langchain.retrievers import BM25Retriever, EnsembleRetriever def create_retriever( persist_directory: str ./chroma_db, k: int 4 ): 创建混合检索器向量检索 BM25 关键字检索。 # 向量检索器 embeddings OpenAIEmbeddings() vector_store Chroma( persist_directorypersist_directory, embedding_functionembeddings ) vector_retriever vector_store.as_retriever(search_kwargs{k: k}) # BM25 检索器使用同一批文本块 all_texts vector_store.get()[documents] bm25_retriever BM25Retriever.from_texts(all_texts, kk) # 混合检索权重分配为 0.5 / 0.5 ensemble_retriever EnsembleRetriever( retrievers[bm25_retriever, vector_retriever], weights[0.5, 0.5] ) return ensemble_retriever def retrieve_documents(question: str, retriever, top_k: int 4): 对单个检查项执行检索返回相关条文片段。 docs retriever.invoke(question) return docs[:top_k]这里要提醒一点BM25Retriever在做中文检索时默认分词效果可能一般。生产环境中建议接入 jieba 分词再做关键字检索能明显提升短文本命中率。4.3 上下文组装器实现得到检索片段后上下文组装器需要把它们整理成 LLM 友好的提示词格式。步骤如下给每段条文编号。标注来源文件。添加判断任务指令。限制输入长度防止关键信息丢失。# src/context_builder.py def build_context(check_item: str, evidence_docs: list) - str: 组装 LLM 上下文。 context_lines [] context_lines.append(以下是与本检查项相关的法规条文\n) for idx, doc in enumerate(evidence_docs, 1): source doc.metadata.get(source, 未知来源) content doc.page_content.strip() context_lines.append(f[条文{idx}]来源{source}) context_lines.append(content) context_lines.append() # 将检查项和条文一起封装 context \n.join(context_lines) prompt ( 你是一名专业的施工安全合规审查专家。\n f待检查项{check_item}\n\n f{context}\n 请根据上述法规条文判断待检查项是否符合要求。\n 只输出 JSON 格式\n {status: 符合/不符合/信息不足, reason: 判断理由, evidence: [引用的条文内容]}\n ) return prompt上下文组装的关键原则让模型容易找到证据。不要一次性塞入 10 条无关条文宁可只给 3~4 条高相关条文模型的表现也会更好。另外提示词中增加“输出 JSON”约束可以省去后续解析文本的麻烦。4.4 LLM 推理引擎实现推理引擎负责调用大模型接口并做输出解析。如果模型返回结果格式不规范还需要进行纠错处理。# src/llm_engine.py import json from langchain.chat_models import ChatOpenAI from langchain.schema import HumanMessage class LLMEngine: def __init__(self, model_name: str gpt-4o-mini, temperature: float 0.0): # temperature 设置为 0确保合规判断结果稳定 self.llm ChatOpenAI(model_namemodel_name, temperaturetemperature) def run(self, prompt: str) - dict: 输入完整提示词输出结构化 JSON 结果。 message HumanMessage(contentprompt) response self.llm.invoke([message]) content response.content.strip() # 尝试解析 JSON如果失败则做一次清洗 try: return json.loads(content) except json.JSONDecodeError: # 简单清洗提取第一个 { 到最后一个 } 之间的内容 start content.find({) end content.rfind(}) if start ! -1 and end ! -1: return json.loads(content[start:end 1]) raise ValueError(f无法解析模型输出: {content})temperature0.0是一个容易被忽略但很重要的细节。合规检查要求结果可复现、可审计如果每次运行结果都不同会直接影响后续的追踪与复检。因此这类判断型任务应尽量使用低温度参数。4.5 主流程编排主流程把“拆解检查项 → 检索 → 组装 → 推理 → 汇总”串联起来。# src/pipeline.py import json from src.knowledge_base import build_knowledge_base from src.retriever import create_retriever, retrieve_documents from src.context_builder import build_context from src.llm_engine import LLMEngine def split_check_items(document: str) - list[str]: 将待检查文档拆解为多个检查项。 示例按句号切分后再过滤较短片段。 sentences [s.strip() for s in document.replace(\n, ).split(。) if s.strip()] return [s 。 for s in sentences if len(s) 10] def run_pipeline( document: str, regulations_dir: str, model_name: str gpt-4o-mini ): 完整合规检查流程。 # 1. 构建知识库如果已存在可复用 build_knowledge_base(regulations_dir) # 2. 创建检索器和推理引擎 retriever create_retriever() llm_engine LLMEngine(model_namemodel_name) # 3. 拆解检查项 check_items split_check_items(document) results [] # 4. 逐项检查 for item in check_items: print(f正在检查{item}) # 检索相关条文 docs retrieve_documents(item, retriever) # 组装上下文 prompt build_context(item, docs) # LLM 判断 result llm_engine.run(prompt) result[check_item] item # 收集结果 results.append(result) return results def save_report(results: list[dict], report_path: str): 保存检查报告为 JSON 文件。 with open(report_path, w, encodingutf-8) as f: json.dump(results, f, ensure_asciiFalse, indent2) print(f检查报告已保存至{report_path})5. 完整实战施工方案合规检查5.1 准备法规文本新建data/regulations/safety_regulations.txt内容示例第5.1条 施工现场应设置不小于3.5米宽的消防车道。 第5.2条 建筑高度大于50米时消防车道与建筑外墙的距离不应小于5米。 第5.3条 施工现场应配置灭火器每处不少于2具。 第6.1条 疏散走道的净宽度不应小于1.4米。 第6.2条 疏散门应向疏散方向开启。 第7.1条 临时用电线路应采用绝缘导线严禁私拉乱接。再准备待检查文档data/documents/plan.txt本工程为某高层住宅项目建筑高度68米。施工总平面布置中消防车道宽度设计为3米消防车道距离建筑外墙5米。每层配置灭火器2具。疏散走道净宽度设计为1.2米疏散门采用推拉门。5.2 运行完整流程新建main.pyfrom src.pipeline import run_pipeline, save_report if __name__ __main__: with open(data/documents/plan.txt, r, encodingutf-8) as f: document f.read() results run_pipeline( documentdocument, regulations_dirdata/regulations, model_namegpt-4o-mini ) save_report(results, report.json) # 打印摘要 for r in results: print(f检查项{r[check_item]}) print(f状态{r[status]}) print(f理由{r[reason]}) print(- * 50)5.3 预期输出运行后report.json中应包含多条检查结果。以“消防车道宽度设计为3米”为例模型应检索到“第5.1条”相关规定并给出类似结果{ check_item: 消防车道宽度设计为3米, status: 不符合, evidence: [施工现场应设置不小于3.5米宽的消防车道。], reason: 规范要求消防车道宽度不小于3.5米设计方案为3米不满足要求 }而“消防车道距离建筑外墙5米”这一条模型应检索到“第5.2条”并判断为符合要求。这就完成了从“读条文”到“出结论”的自动化闭环。6. 常见问题与排查思路在实际复现和使用 CTRAG 过程中以下几类问题比较常见问题现象常见原因解决思路检索结果不相关切块过大或过小语义被截断调整切块大小按“第X条”进行结构化解构模型输出格式不稳定提示词约束不够明确在提示词中加入 JSON 示例或使用 Pydantic 输出解析器判断结果前后不一致temperature 过高将 temperature 设置为 0并固定模型版本漏掉关键条文单一检索方式召回率不足使用混合检索并在检索后增加重排序环节法规文本太长知识库构建缓慢嵌入计算耗时过长使用批量向量化或选择更轻量的 embedding 模型模型引用编造条文上下文里没有对应证据增加“仅依据给定条文回答”的约束并对无证据情况输出“信息不足”6.1 检索结果不相关时的排查步骤先检查切块结果打开向量库中的文本块看是否保持了完整语义。再检查检索 Top 结果打印召回片段判断是“没召回到”还是“召回了但排序不对”。如果是召回问题尝试增大top_k或调整混合检索权重。如果是排序问题引入 reranker重排序模型做二次排序。6.2 模型判断错误的排查思路如果检索到的条文明明正确但模型仍然判断错误通常是因为提示词中没有强调“忽略与条文无关的信息”。检查项本身包含多个子问题模型只回答了其中一个。条文之间存在冲突模型没有遵循“特别规定优先于一般规定”的原则。解决方案是把检查项拆得更小、更原子化。例如“消防车道宽度设计为3米距离外墙5米”应拆成两个独立检查项而不是让模型同时判断两个维度。7. 最佳实践与工程建议7.1 知识库建设阶段法规条文要以“条”为最小单元。不要使用固定字符数切块而要根据法规文档自身的结构切分。每一“条”可能有长有短但保持语义完整最重要。可以自定义解析器按“第X条”正则表达式切分import re def split_by_article(text: str): pattern r(?第[一二三四五六七八九十百\d]条) articles re.split(pattern, text) return [a.strip() for a in articles if a.strip()]7.2 检索阶段优先用混合检索配合重排序。单独使用向量检索容易出现“语义相近但条文不对”的问题单独使用 BM25 无法处理同义改写。两者可以先用 0.5/0.5 权重融合后续根据业务数据调优。如果有预算可以加一个 cross-encoder reranker效果提升非常明显。7.3 LLM 推理阶段固定模型版本不要使用latest这种自动更新的别名否则结果无法审计。每次推理保留完整 prompt 和发布时间便于问题回溯。对于高风险合规场景建议设计“人工复核”通道只对低风险项自动放行。在提示词中加入“无证据时输出信息不足”的选项避免模型编造答案。7.4 工程落地建议从原型到生产还需要补齐以下能力权限管理与操作审计合规检查结果可能涉及敏感信息必须记录谁在什么时间发起了什么检查。配置隔离不同项目、不同地区的法规体系不同应通过命名空间隔离知识库和配置。结果可追溯保存完整的检索上下文和模型输出不能只保存一个最终结论。性能优化批量检查时可以对多个检查项并发调用 LLM但要注意接口限流和错误重试。定期更新法规库法规会更新需要建立知识库版本管理机制避免用旧法规做新检查。7.5 安全与合规边界使用 CTRAG 处理工程合规审查时必须明确一个边界LLM 的判断只能作为辅助参考不能完全替代具有法律效力的专业审查。在提示词和产品设计中都应该提示用户“结果需要专业人员进行复核”。尤其是涉及消防、建筑安全、环境评估等强监管领域自动化检查可以提效但不能降低审核责任。另外如果使用第三方 LLM 服务处理敏感数据要关注数据出境合规问题。更稳妥的做法是在私有化环境部署开源模型把数据留在内部。8. 总结与下一步学习方向CTRAG 本质上是一套“检索 上下文组装 LLM 判断”的方法论它并不依赖某一个特定模型或框架。你可以用 LangChain 实现也可以直接用 OpenAI SDK 配合 pandas 手工实现向量库可以用 Chroma也可以用 Milvus 或 Elasticsearch。核心在于理解三个关键点检索质量决定了判断上限。如果检索不到正确条文再强的大模型也判断不出正确结果。上下文组装决定了判断下限。检索到了正确条文但提示词组织混乱模型依然可能出错。结构化输出是工程落地的前提。只有输出稳定的 JSON 结果才能对接审批流、告警和报告系统。下一步建议你从以下方向继续深入学习Text Splitter的不同策略理解父子分块、滑动窗口对检索效果的差异。掌握Reranker 的使用对比引入重排序前后准确率变化。研究Agent 化合规检查让模型在最开始不确定时主动追问或补充检索而不是一次性输出结论。尝试用本地部署的Qwen、ChatGLM、DeepSeek等开源模型替换 OpenAI 接口进行隐私环境下的合规检查验证。如果你准备在实际项目中落地 CTRAG优先关注知识库构建和结果可审计性这两块做得越扎实后续的模型和流程升级都会顺利得多。希望这篇文章能帮你把概念落地成可运行的代码也欢迎在实践过程中继续验证和调整这套框架。
返回列表