资讯动态

人工智能在QARA中的落地实践:从RAG检索到版本比对与合规审核

发布时间:2026/9/18 10:52:32 来源:尧图企业网站定制
简介艾昆纬IQVIA2024年发布的《人工智能在QARA过程现实中的应用》聚焦医疗技术MedTech领域质量保证、风险评估与合规性QARA流程中AI的落地现状面向质量、监管事务及合规从业者解答如何理解并善用AI、如何应对真实世界数据RWD带来的挑战等关键问题。报告结合微软、飞利浦、毕马威、IQVIA Technologies等机构专家的讨论梳理了机器学习ML与大型语言模型LLMs的差异强调数据准确性、一致性与完整性对模型性能的决定性作用并分析了AI在优化流程、减少人为错误、支持安全性评估与市场后监测等方面的价值以及数据隐私和安全性带来的新要求。文档为单份PDF约1.41MB轻量易用适合案头查阅目前已有58人学习下载。报告中包含执行摘要、关键要点与专家观点实录可帮助读者快速掌握AI在QARA场景中的现实难点与组织教育方向为后续技术选型、数据治理和能力建设提供参考。1. 人工智能在 QARA 过程中的现实应用突破口往往不是“会写文档”而是“能说清在哪一段改的”一个常被误判的结论是人工智能在 QARA 过程里最先替代的是医学写作。但跑到真实的项目现场就会发现最先被质量与法规事务团队接受的反而是“把一段历史从海量文件里捞出来”和“告诉审阅者这次修订到底动了哪一个段落、哪一处交叉引用”。原因不复杂QARA 的文档天然带有版本、审批、签名和审计链路任何生成结果只要无法指出出处就不能进入受控流程而一旦能指出出处原来的查找、比对和复核成本会立刻降下来。这篇内容围绕“检索为入口、生成为辅助、回归比对做兜底”的落地路线展开适合正在规划 QARA 文档智能化但又不希望被大模型“创造内容”带偏的团队参考。2. 从 PDF 到向量库把 QARA 文档流拆成可检索的切片2.1 为什么现实中的 QARA 先从“检索增强”而不是“自动撰写”开始QARA 的工作对象大致可以分成四类质量标准文件、注册申报资料、质量事件记录、审评回复信函。前两类结构相对固定有稳定的目录和章节编号后两类则频繁出现跨文件引用常见句式是“请参见 3.2.1 节的稳定性数据”或“与 CTD 模块 2.6.4 保持一致”。这类引用在人工阅读时很自然但到了模型面前就成了难题模型如果没被精准投喂到对应章节就会用自己的“常识”补全而这种补全在受控环境里是不可接受的。我一般会把 QARA 场景里的 AI 落地顺序定为先做增强检索再做文档起草最后才是端到端应答。原因在于检索结果可以被人工验证错了也只是“没找到”不会像内容生成那样产生“看似正确”的噪音。另一个更实际的原因是 QARA 文档的颗粒度非常大单份临床试验方案可能上千页PDF 解析后的文本顺序和真实阅读顺序未必一致先解决“能不能找到”比先解决“能不能写”要稳妥得多。2.2 切片时保留三个字段章节锚点、版本与审评状态把文档切块存入向量库之前要先把文档结构从排板信息里还原出来。多数 QARA 文档遵循“编号-标题-正文-表格”的固定模式目录中的编号如 5.3.1 往往在正文里也保持一致。切片时我会强制保留三个字段它们直接影响后续比对和引用第一个是章节锚点即文档里真实存在的编号路径例如“模块3.2.S.4.1”。第二个是版本信息要精确到该文档被审阅时的版本号而不是文件修改日期因为同一份文件在不同阶段可能有多个正式版本。第三个是审评状态例如“已批准”“待修订”“草案”这个状态决定了该切片能不能被大模型直接引用。切片方案建议按章节而不是按固定字数切章节过长时再拆并设置一定的重叠区间通常取块大小 512 至 1024 字符、重叠 64 至 128 字符这样既能避免引用跳章又不会让向量语义被无关段落污染。2.3 用 Python 建立最小 RAG 索引库这里给出一套可以直接改用的搭建代码。常见做法是先用 PyMuPDF 或 python-docx 把文件抽成纯文本再按章节锚点切块最后通过兼容 OpenAI 接口的本地网关做向量化写入支持余弦检索的向量库。代码里不绑定具体云服务只约定环境变量方便内网部署时替换地址。import os import fitz # PyMuPDF from openai import OpenAI import re client OpenAI( base_urlos.environ.get(LLM_BASE_URL), api_keyos.environ.get(LLM_API_KEY), ) def extract_pdf_text(path: str) - str: doc fitz.open(path) return \n.join(page.get_text(text) for page in doc) def split_by_section(text: str, max_chars: int 1024): pattern re.compile(r(?m)^\s*(\d(?:\.\d){1,4})\s(.?)$) lines text.splitlines() sections [] current_no, current_title, buf None, None, [] for line in lines: m pattern.match(line) if m and len(buf) 50: if current_no: sections.append((current_no, current_title, .join(buf))) current_no, current_title m.group(1), m.group(2) buf [line] else: buf.append(line) if current_no and buf: sections.append((current_no, current_title, .join(buf))) return sections def embed_texts(texts: list[str], model: str text-embedding-3-small): resp client.embeddings.create(modelmodel, inputtexts) return [item.embedding for item in resp.data]逻辑说明extract_pdf_text负责从 PDF 中抽取文本层若文件本身是扫描件要先做 OCR 再做文本抽取否则后续切片会丢内容。split_by_section用正则识别“数字编号 标题 正文”的结构并且只在当前段落累计长度超过 50 字符时才截断防止把孤立的目录页也算成章节。embed_texts是向量化的入口模型名要与服务端能力对齐。参数方面比较关键的是max_chars建议先按照文档类型试跑一次如果发现章节被切得七零八落就调大这个值如果发现检索召回过低就适当下调并增加重叠。2.4 权限与元数据参数表切片入库后检索时还要过滤权限。以下参数是 QARA 场景里最常用的一组建议在索引初始化时就写入字段避免上线后补数据。元数据字段示例值用途doc_idPROTOCOL-2024-032定位原始文件version1.2区分同一文档的不同批准状态section_no5.5.6生成引用时给出精确锚点approval_statusapproved / draft限制模型引用未批准内容access_groupclinical / regulatory / qa控制检索范围source_langen处理需要翻译或双语对照的场景实际配置时权限过滤不要放在向量检索之后要前置到检索条件里。先缩小可检索集合再做向量相似度计算既减少计算量也从机制上避免越权引用。这个字段设计可以和现有文档管理系统的元数据保持一致不需要为 AI 场景重新建一套分类法否则后面做审计追踪时会面临两套编号对照的麻烦。3. 起草类任务的提示设计三段式结构、结构化输出与可复核参数3.1 为什么“一句话要结果”在 QARA 里行不通面向 QARA 的提示设计最忌讳的是让模型直接“写一份偏差调查报告”。这类文档包含事实描述、根因分析、影响评估和纠正预防措施任何一段缺失都会被审评打回。把一个大任务拆成三段式会稳定得多第一段让模型从检索结果中抽取事实第二段让模型只根据这些事实组织语言第三段让模型对照原文档做自检并输出缺失项。每个阶段使用独立的提示和独立的温度参数而不是在一个大提示里要求模型“一次到位”。这样做有三个直接好处事实抽取阶段可以被单独检查只要这一步结果正确后续生成即使措辞不佳也不会篡改事实语言组织阶段可以放心调整风格不影响对错判断自检阶段能够暴露检索遗漏例如“未找到剂量变更的原始批准记录”这一句提示会促使审阅者去补材料而不是默认模型已经写全了。3.2 结构化输出模板把答案钉在 JSON 框架里实际操作中我会在提示里强制模型输出 JSON 结构而不是自由文本。QARA 文档的后续处理流程是入库、比对、审阅自由文本无法做程序化校验。以下是一个偏差事件描述任务的提示骨架可以直接套用你是质量保证文档助理。以下是从检索库中召回的相关片段。 请仅根据片段内容完成以下任务 1. 抽取事件发生时间、发现部门、影响批次范围 2. 按“发生了什么 / 发现了什么 / 当前状态是什么”组织一段不超过 150 字的描述 3. 如果片段中没有某个信息在 gaps 字段写明“缺失”。 输出 JSON格式为 { facts: { event_time: , found_by: , batch_scope: }, description: , gaps: [] } 检索片段 {context}这里的{context}是上一阶段检索召回并拼接后的文本它只包含已批准或待审阅的内容。强制 JSON 输出的意义在于后续程序可以直接读取gaps字段决定是否阻断流程如果batch_scope为空且业务上批次信息是必填项就不应该继续进入批准环节。提示中的“仅根据片段内容”是防止模型调用自身记忆的关键约束在部分模型上需要重复出现才会稳定生效。3.3 可选配的生成参数参数建议值适用场景temperature0.1偏差描述、事实性摘要temperature0.3给审评回复信函起草初稿top_p0.9对措辞一致性要求较高的文本frequency_penalty0.1防止重复使用同一组术语presence_penalty0.0保持医疗术语不被随意替换max_tokens按段落长度设定限制单次输出规模便于拆段复核值得提醒的是temperature不宜在 QARA 场景里超过 0.4。质量文件的用词必须严格遵循定义温度过高会导致同一概念在不同段落被表达成不同术语给后续的术语一致性检查增加负担。如果团队里有人反馈“模型写得太死板”优先考虑调整提示里的示例而不是调高温度。3.4 生成结果必须带出处没有出处的一律算失败在 QARA 过程里没有出处的生成结果等于没有结果。我通常会在生成阶段的系统提示里明确要求每个事实性陈述后附加[ref: 文档编号/章节号]并从程序侧做校验。校验逻辑并不复杂可以用一个正则表达式截取所有ref标记再与检索召回列表做比对只要出现标记不存在的编号整段文本就标记为“待人工核实”不进入批准队列。这种做法把“模型幻觉”问题转化成了可跟踪的提示缺失问题当某类问答频繁出现无效引用时说明检索切片覆盖不足或提示里对引用格式的交代不够。与其不断修改提示词去纠正生成不如让程序把大量待复核样本聚到一起看清模式后再改索引结构。这种“先标记、后治理”的思路对 QARA 的审计文化也更友好。4. 版本比对与交叉引用回归让 AI 先做审阅者的第二个大脑4.1 同一文档的不同版本之间哪些改动需要重点盯QARA 审阅者最常做也最耗时的工作是版本差异分析。一份文件从 v1.0 改到 v1.1可能只改变了一个数字但这个数字可能同时影响三个章节、两个附录和一份报告的结论。人工做法通常是边翻边记既慢又容易漏。AI 在这里的价值不是“判断改动对不对”而是“算出这次改动的波及面”把波及清单先给到审阅者再由审阅者做专业判断。常见的处理流程是将 v1.0 和 v1.1 的文本按同一套切片规则切块对每个章节编号做逐块比对输出三类差异内容新增、内容删除、内容替换。之后对差异片段做一次“引用探测”把所有提及该章节编号的段落都捞出来形成一张影响面清单。这个阶段不涉及生成只是精确的文本运算因此稳定性和可信度都很高。4.2 用 Python 做章节级差异扫描的最小实现import re from difflib import SequenceMatcher def load_sections(path: str): sections {} pattern re.compile(r(?m)^\s*(\d(?:\.\d){1,4})\s(.?)$) current_no None with open(path, encodingutf-8) as f: for line in f: m pattern.match(line) if m: current_no m.group(1) sections[current_no] [line] elif current_no: sections[current_no].append(line) merged {k: .join(v) for k, v in sections.items()} return merged def compare_versions(old_path: str, new_path: str): old load_sections(old_path) new load_sections(new_path) report [] for sec_id in sorted(set(old) | set(new), keylambda x: [int(i) for i in x.split(.)]): a old.get(sec_id, ) b new.get(sec_id, ) if a ! b: ratio SequenceMatcher(None, a, b).ratio() report.append((sec_id, round(ratio, 3), len(a), len(b))) return report for sec_id, ratio, len_a, len_b in compare_versions(v1.0.txt, v1.1.txt): print(sec_id, ratio, len_a, len_b)逻辑说明load_sections按章节编号组织文本编号最多支持四级compare_versions用SequenceMatcher计算相似度相似度低于 1.0 的章节进入报告。这里不直接采用行级 diff是因为 QARA 文档经常发生排版重排行级差异会被空行和换行干扰章节级相似度更能反映实质变化。输出的四项分别是章节号、相似度、旧版字符数、新版字符数当相似度介于 0.7 到 0.95 时通常需要优先人工复核这类改动往往涉及“表述调整但含义可能变化”的情况。4.3 交叉引用一致性检查在版本差异之上另一个高价值检查点是交叉引用是否失效。例如某附录编号从“附录 4”调整为“附录 5”正文里所有指向“附录 4”的文字都必须同步更新。实现方式是在文档全文中提取所有符合引用格式的编号再与当前章节集合做差集。以下代码可以复用import re from pathlib import Path def find_cross_references(text: str): candidates re.findall(r(?![A-Za-z0-9])(\d(?:\.\d){1,4})(?![A-Za-z0-9]), text) return set(candidates) valid_ids set(load_sections(v1.1.txt).keys()) raw_text Path(v1.1.txt).read_text(encodingutf-8) invalid_refs find_cross_references(raw_text) - valid_ids print(失效引用:, sorted(invalid_refs))建议把这类检查固化成每次提交前的自动化门槛。QARA 项目的很多偏差都源于改了一处但没有追改另一处交叉引用检查把这个问题转化为机械规则模型的参与度反而可以降低。我在实际项目中会把这个脚本接进文档管理系统的 pre-commit 钩子里每次上传新版本时自动生成失效引用清单。4.4 人工复核时的那张对照表为了让审阅者不迷失在差异报告里我会把扫描结果整合成一张对照表包含章节号、差异类型、相似度、涉及文件、建议动作。常用字段如下章节号差异类型关键词摘要受影响文件建议动作3.2.1替换剂量从 10mg 改为 20mg方案正文、知情同意书必须同步更新5.5.6新增增加安全性随访窗口统计分析计划需统计师确认8.1删除删除重复的风险说明无低风险存档即可这张表的价值在于把审阅者的注意力引导到“受影响文件”这一列而不是从头到尾重读整份文档。审阅者只需要重点核对列出的文件是否同步更新大幅缩短了差异分析的时间也减少了重复劳动带来的认知疲劳。5. 用回归集给 QARA 生成类任务设门槛5.1 把真实驳回样本沉淀成固定的验证用例当 AI 应用开始稳定运行时要把质量部门过去半年里真实驳回的文档改写样本沉淀为一个固定回归集。这个回归集不需要很大二十到三十个典型案例就够但要有明确的正反标注哪些是可以通过的哪些必须被拦截。规则很简单每次优化提示或调整切片参数后先跑回归集再上生产任何一项反例被错误放行都视为阻断问题。回归集的构建来源可以是历史偏差记录、审评意见信函和内部质量抽查。重点收录那些模型容易“聪明反被聪明误”的样本例如把“不适用”误判为“缺少数据”、把“待补充”误判为“已完善”。这个集合更像是质量体系里的标准品价值会随着月份增长越来越大而不是一次性的验收材料。5.2 建立三个可量化的验收指标在回归集之上建议用三个数据指标做门禁检索命中率、人工采纳率、返工率。检索命中率指模型输出所引用片段实际出现在检索召回集中的比例人工采纳率指审阅者在最终批准稿中保留 AI 生成内容的占比返工率指进入批准流程后又因缺失或错误被退回的文档比例。这三个指标不用做得很复杂每月统计一次趋势即可。操作上可以把 AI 输出记录成结构化日志包含提示版本、检索召回编号、输出内容、审阅者修改标记。审阅者不需要额外填写表单只需在最终文档里留下修订痕迹程序通过对比 AI 初稿和终稿的差异就能反推出采纳率。这套机制的最终效果是把“AI 好不好用”从主观感受变成可回归的数据也让质量部门敢于逐步扩大应用范围。5.3 一个容易被忽视的制约锁定输入环境再谈输出质量在验证和扩量阶段最容易踩的坑是只盯着模型输出忽略了输入环境的漂移。QARA 的输入文档本身会随版本更新而变化同一份文件的 OCR 质量、表格抽取结果也可能不同。一次质量回退的原因也许不是模型变笨了而是某个 PDF 的字体嵌入了非标准编码。建议为每个正式用例固定输入版本就像做实验固定试剂批次一样。在系统里记录每次运行用到的文件哈希值发现指标异常时先对比输入哈希再排查提示参数。把输入版本锁定后回归集才能忠实反映模型变化团队也不会在排查问题上多耗无谓的时间。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价