ARTICLE DETAIL

资讯详情

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

AI标书写作如何拒绝说谎:RAG与可控生成防幻觉实践

AI标书写作如何拒绝说谎:RAG与可控生成防幻觉实践 这次我们来看一个很有意思的 AI 工程实践项目让 AI 投标书撰写者拒绝说谎。标题直译过来就是“让 AI 标书写作工具拒绝撒谎”。在真实业务场景里投标书涉及企业资质、业绩案例、技术参数、服务承诺每一行都可能直接影响是否中标更严重一点说虚假信息一旦被核实轻则废标重则进黑名单。很多 AI 写作文案工具存在一个通病为了生成流畅内容模型会“脑补”案例、编造资质、填充不存在的项目业绩。这个项目要解决的就是这个问题——让 AI 在信息不确定时主动拒绝生成而不是硬编。从架构上看这可以拆解为“指令约束 事实边界 拒绝机制”三部分通过提示词设计给模型设定行为边界通过结构化知识库让模型只能引用已验证的材料通过输出校验和拒绝策略兜底。换句话说这本质上是一个LLM 应用的可控生成工程问题涉及 API 调用、RAG 检索、提示词优化、输出校验和合规约束。这篇文章会围绕这个主题从核心能力、环境准备、部署启动、功能测试、API 批量任务、性能观察、排查手段到最佳实践完整带大家走一遍。如果你正在做 AI Agent、AI 文档生成、GPT 应用开发、AI 工程实践相关项目这篇可以直接收藏。1. 核心能力速览能力项说明项目类型AI 文档生成 / 可控文本生成 / RAG 检索增强核心目标让 AI 标书撰写工具拒绝生成不实信息主要功能真实材料召回、事实核验、拒绝生成机制、输出标注来源关键技术LLM API、RAG、提示词工程、结构化知识库、输出校验推荐硬件使用云端 API 时无需本地 GPU本地部署开源模型时建议 16G 以上显存显存占用取决于模型选择使用 API 模式几乎不占本地显存支持平台Windows / Linux / macOSPython 环境启动方式命令行启动 / API 服务模式是否支持 API支持可集成到业务系统是否支持批量任务支持可设计批量标书章节生成和校验队列适合场景投标书初稿、审计报告初稿、合规文档、需要事实约束的文本生成从材料看该项目的重点不是模型本身而是工程层的“诚实性控制”。它不是一个 UI 一键工具更像一套可以接到任意 LLM 的生成控制中间层。理解了这一点后续环境准备和部署思路就清楚了。2. 适用场景与使用边界2.1 适合谁用做标书代写工具、文档自动化 SaaS、企业知识库问答的开发者。给银行、政企、工程类项目做合规文本生成的团队。做 AI 应用开发、AI Agent 工程实践的技术人员。需要限制 LLM “胡说八道”的文档场景。2.2 能解决的问题模型编造公司业绩、项目案例、人员资质。模型在信息缺失时强行生成“看似合理”的表述。引用过期的政策法规、错误的技术参数。多段文本之间事实矛盾例如前一章写 A 公司有 100 人后一章写成 200 人。2.3 不适合什么场景纯创意写作、营销文案润色这类场景不需要严格事实约束。需要模型基于“猜”进行头脑风暴的场景。完全没有可靠材料的场景——如果库里没有内容AI 只能拒绝生成产出会非常保守。2.4 合规与隐私边界这个项目直接涉及企业资质、商业业绩、个人证书等敏感信息。使用过程中必须注意企业资质、业绩、证书等信息需获得授权后才可录入知识库。生成的标书内容属于商务文档涉及商业机密时不应上传到公共模型 API建议私有化部署或使用合规 API。建立“可追溯”机制任何由 AI 生成的事实性陈述都必须能回溯到原始材料。不要用该项目生成涉及虚假授权、仿冒签名、伪造证件的文本。3. 环境准备与前置条件从实践角度看这个项目建议按两条路准备环境API 模式本地无需 GPU调用 GPT、Claude、通义千问、文心一言等大模型接口。本地模型模式需要一张至少 16G 显存的显卡部署 Qwen、Llama、ChatGLM 等开源模型。3.1 操作系统与 Python 环境建议使用 Linux 服务器或 Windows 10/11 搭配 WSL2。Python 版本建议 3.10 到 3.12低于 3.9 的话很多依赖包可能不兼容高于 3.12 时部分本地框架可能还没有适配。# 创建虚拟环境 python3.11 -m venv venv source venv/bin/activate # 或者 Windows # venv\Scripts\activate3.2 依赖库项目需要以下核心依赖实际版本号以项目 requirements 为准安装时不要盲目用最新版本pip install openai langchain chromadb pydantic python-dotenv rapidfuzz如果走本地模型路线还需要追加pip install transformers torch sentencepiece accelerate这里多说一句openai库现在不只服务 OpenAI 官方接口国内的大模型服务商基本都兼容 OpenAI 协议只需要改 base_url 和 api_key这大大降低了后续对接成本。3.3 模型 API Key以 API 模式为例需要在环境变量里配置模型服务密钥export LLM_API_KEYyour-api-key export LLM_BASE_URLhttps://api.your-model-service.com/v1 export LLM_MODELyour-model-name为了安全不要把密钥写到代码里更不要提交到 Git 仓库。3.4 磁盘与数据准备标书知识库需要保存企业资质、过往业绩、技术方案、人员证书等文件建议文本格式PDF、Word、Markdown、TXT。每个文档建议控制在 30 页以内超长文档先拆成章节。预留至少 5G 空间用于向量数据库和临时缓存。3.5 端口检查如果要以 API 服务方式启动建议使用 8000 或 8080 端口启动前先检查lsof -i :8000 # 如果有输出说明端口被占用需要换端口或者杀掉旧进程4. 安装部署与启动方式4.1 代码结构规划建议按以下目录组织项目这样后续做批量任务时不会乱bid-writer/ ├── docs/ # 原始标书材料 │ ├── company_profile.md │ ├── performance_cases.md │ └── qualifications.md ├── storage/ # 向量数据库和索引 ├── app/ │ ├── ingest.py # 材料入库 │ ├── generate.py # 生成逻辑 │ ├── verify.py # 事实校验 │ └── server.py # API 服务 ├── config/ │ └── settings.yaml └── requirements.txt4.2 材料入库把原始标书材料拆成适合检索的块写入向量数据库。这里使用 LangChain 加 Chroma 做演示from langchain.document_loaders import TextLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain.embeddings import OpenAIEmbeddings from langchain.vectorstores import Chroma # 加载文档 loader TextLoader(docs/company_profile.md) documents loader.load() # 拆分文本块大小 500重叠 50 text_splitter RecursiveCharacterTextSplitter( chunk_size500, chunk_overlap50 ) texts text_splitter.split_documents(documents) # 构建向量库 embeddings OpenAIEmbeddings() vectordb Chroma.from_documents( documentstexts, embeddingembeddings, persist_directory./storage/chroma_db ) vectordb.persist()注意实际项目中公司资质和业绩案例通常来自 PDF 或 Word需要先用 pdfplumber、unstructured 或 docx 库做格式解析再进入上面流程。4.3 核心生成逻辑约束 检索 拒绝这一步是项目的灵魂。生成标书内容之前先执行检索从知识库中找出与问题相关的材料然后基于这些材料生成内容最后再做一个“事实核验”。如果核验不过就拒绝输出并返回原因。下面是一个简化示例from langchain.vectorstores import Chroma from langchain.embeddings import OpenAIEmbeddings from langchain.llms import OpenAI SYSTEM_PROMPT 你是一个投标书撰写助手。你的任务是基于给定的企业真实材料撰写标书内容。 你必须遵守以下规则 1. 只能使用提供的材料中明确存在的信息。 2. 如果材料中没有相关信息必须明确回答“没有找到相关材料无法生成该部分内容”。 3. 禁止编造公司资质、业绩案例、人员证书、技术参数。 4. 涉及数字、日期、金额时必须与材料完全一致。 5. 每段内容的末尾列出该段引用来自哪些材料文件。 def retrieve_similar(query: str, k: int 5): embeddings OpenAIEmbeddings() vectordb Chroma( embedding_functionembeddings, persist_directory./storage/chroma_db ) docs vectordb.similarity_search(query, kk) return \n\n.join([d.page_content for d in docs]) def generate_bid_content(query: str): context retrieve_similar(query) messages [ {role: system, content: SYSTEM_PROMPT}, {role: user, content: f材料\n{context}\n\n写作要求{query}} ] response openai.ChatCompletion.create( modelgpt-4, messagesmessages, temperature0.2 ) return response[choices][0][message][content]这里有几个关键点temperature0.2降低随机性减少模型自由发挥的空间。系统提示词里写清楚了“禁止编造”和“拒绝生成”的行为策略。把所有检索到的材料放进上下文让模型只能在这个范围内作答。4.4 事实校验与拒绝机制生成之后不能直接信任输出。需要再做一道“回查”把输出中的每个关键事实性断言映射回原始材料。如果找不到对应文本就认为该断言没有依据。如果一段输出里存在超过 1 条无依据断言整段拒绝。import re from rapidfuzz import fuzz def verify_claims(generated_text: str, source_materials: list) - dict: # 提取输出中的数字、公司名、日期作为关键断言 numbers re.findall(r\d(?:\.\d)?%?|[\d,]人|[\d,]万元, generated_text) unverified [] for num in numbers: matched False for material in source_materials: if num in material or fuzz.partial_ratio(num, material) 85: matched True break if not matched: unverified.append(num) return { unverified: unverified, safe: len(unverified) 0 } # 返回值中的 safe 如果为 False就拒绝生成该段在实际项目中校验策略需要更强可以把“断言抽取”从正则升级为基于 NER 的实体识别把“匹配”升级为向量召回比对。这里只展示简化逻辑原理是一致的。4.5 启动 API 服务为了方便接入业务系统可以包装成一个 FastAPI 服务from fastapi import FastAPI from pydantic import BaseModel app FastAPI() class BidRequest(BaseModel): section: str query: str class BidResponse(BaseModel): content: str refused: bool reason: str app.post(/api/bid/generate, response_modelBidResponse) def generate_bid(req: BidRequest): content generate_bid_content(req.query) # 这里插入事实校验逻辑 return BidResponse( contentcontent if not refused else , refusedrefused, reason没有找到相关材料无法生成该部分内容 if refused else )启动命令uvicorn app.server:app --host 0.0.0.0 --port 80004.6 启动后验证启动服务后用 curl 做一次最简单的连通性测试curl http://localhost:8000/docs正常情况下浏览器访问/docs可以看到 FastAPI 自动生成的 Swagger 调试页面。这是排查启动问题最快的手段——如果/docs能打开说明服务整体起来了。5. 功能测试与效果验证5.1 测试一基于真实材料的生成先在知识库存入一段真实的公司业绩材料XX科技有限公司成立于2015年注册资金5000万元拥有系统集成一级资质。 2021年中标XX省政务云平台建设项目合同金额8200万元。 2022年中标XX市智慧交通项目合同金额4600万元。然后发起生成请求import requests payload { section: 业绩案例, query: 请描述一下公司在政务领域的成功案例。 } resp requests.post(http://localhost:8000/api/bid/generate, jsonpayload) print(resp.json())预期结果输出包含“XX省政务云平台建设项目”和“8200万元”。输出末尾标注了来源文件。没有额外编造“2023 年中标 XX 项目”。判断成功的标准输出里的数字和项目名称都能在原材料中找到。5.2 测试二缺失信息时的拒绝行为现在询问一个知识库中没有的信息payload { section: 人员资质, query: 公司是否有 CMMI5 认证 } resp requests.post(http://localhost:8000/api/bid/generate, jsonpayload) print(resp.json())预期结果返回内容为“没有找到相关材料无法生成该部分内容”。refused字段为true。不会生成“公司拥有 CMMI5 认证”这类猜测性文本。这一步是整个项目最关键的验收项。如果模型在这类输入下仍然强行输出说明拒绝机制失效需要检查系统提示词是否被忽略或者上下文是否为空导致模型自行发挥。5.3 测试三数字准确性压力测试把多个相似数字混在上下文中测试模型是否忠实引用材料 A2021 年合同金额 8200 万元。 材料 B2022 年合同金额 4600 万元。 材料 C2023 年合同金额 9800 万元。请求“列出公司近三年每年度合同金额。”预期输出必须严格按年份对应金额不能把 8200 写成 2022 年也不能把 4600 写成 2021 年。5.4 测试四批量生成与一致性批量生成多个章节检查各章节之间是否有冲突。例如第一章写“公司成立于 2015 年”。第五章不能变成“公司成立于 2016 年”。批量任务通常需要按顺序执行并把前文的结果作为后文的事实约束。6. 接口 API 与批量任务这个项目最终的价值是接入业务系统所以 API 设计和批量任务能力必须单独讨论。6.1 API 参数设计建议字段类型说明sectionstring标书章节名称querystring写作要求max_tokensint最大生成长度temperaturefloat采样温度建议 0.1-0.3source_fileslist限定使用哪几个材料文件strict_modebool严格模式开启后拒绝一切无依据内容6.2 批量任务设计批量生成标书章节时建议使用异步任务队列不要直接同步调用 LLM。原因很简单标书通常有几十个章节每个章节要经过“检索 生成 校验”三个环节同步调用总耗时可能达到 5-10 分钟很容易触发 HTTP 超时。推荐使用 Redis Celery 或简单的 Asyncio 队列from fastapi import BackgroundTasks from celery import Celery celery_app Celery(bid_tasks, brokerredis://localhost:6379/0) celery_app.task def generate_section(section: str, query: str): content generate_bid_content(query) return {section: section, content: content} app.post(/api/bid/batch) async def create_batch(requests: list[BidRequest]): task_ids [] for req in requests: result generate_section.delay(req.section, req.query) task_ids.append(result.id) return {task_ids: task_ids}业务侧轮询任务状态时建议实现失败重试。LLM 调用经常因为限流、网络超时等原因失败重试逻辑要加上指数退避。6.3 curl 调用示例curl -X POST http://localhost:8000/api/bid/generate \ -H Content-Type: application/json \ -d {section: 技术方案, query: 描述系统架构设计}6.4 返回结果示例{ content: 系统采用微服务架构包含 API 网关、业务服务层、数据存储层。数据存储层采用分布式数据库支撑高并发访问场景。\n\n引用来源docs/technical_plan.md, refused: false, reason: }7. 资源占用与性能观察7.1 显存占用观察方法如果使用本地模型显存占用取决于模型参数量7B 模型量化后大约需要 6-8G 显存。14B 模型量化后大约需要 12-16G 显存。直接用 ChatGPT、通义等 API 则不占用本地显存。显存观测使用 nvidia-smiwatch -n 1 nvidia-smi7.2 性能瓶颈分析这个项目的性能瓶颈通常不在模型本身而在三个环节材料解析PDF 解析最耗时建议预解析为 Markdown 或纯文本。向量检索数据库量大时检索时间上升需要给向量库加索引。输出校验正则和模糊匹配速度快但如果引入大模型二轮校验耗时可能翻倍。从工程经验看API 模式的生成速度瓶颈主要受限于模型响应速度而本地模式受限于显存带宽和批量大小。7.3 降低延迟的建议把材料入库和生成分离不要每次请求都重新构建向量库。对常用标书章节做缓存相同 query 直接命中。批量任务需要控制并发数一般 3-5 个并发比较稳妥避免触发限流。严格模式会显著降低输出率但如果标书要求“每个数字都溯源”这一步不能省。8. 常见问题与排查方法问题现象可能原因排查方式解决方案启动后/docs打不开端口被占用或服务未启动检查日志和端口占用更换端口或杀掉旧进程模型总是编造业绩上下文为空或系统提示词被忽略打印最终发给模型的完整 prompt检查检索结果是否为空强化系统提示词检索不到相关内容文档入库未完成或向量库路径错误查看入库日志检查向量库目录重新执行入库命令生成结果数字错误上下文过长导致模型注意力分散检查检索片段是否混入相似数字降低 chunk_size或增加数字校验批量任务全部超时并发过大触发限流查看模型服务日志降低并发数增加重试安装依赖失败Python 版本不兼容查看报错信息中的版本要求切换到 3.10-3.12 环境PDF 材料解析乱码PDF 是扫描件检查文件是否为图片型 PDF先接入 OCR 识别再入库拒绝生成比例过高知识库内容不足检查库内材料覆盖范围补充高质量材料或调整检索阈值中文匹配准确率低使用英文分词器检查向量检索的分词方式切换为中文分词器或向量模型API Key 报 401密钥无效或服务商不匹配检查 base_url 和 api_key 是否对应重新配置环境变量8.1 排查流程建议最实用的排查顺序是“从输入到输出”先打印检索结果确认模型能看到什么。再打印完整 prompt确认系统约束是否生效。最后检查校验模块的日志确认哪些断言被判定为无依据。90% 的“AI 胡说八道”问题在第一步和第二步就能定位。9. 最佳实践与使用建议9.1 关于系统提示词系统提示词是这个项目最重要的“代码”。建议采用“能力定义 禁令列表 行为示例”三段式写法能力定义你是投标书撰写助手只能基于材料撰写。 禁令列表 - 禁止编造公司资质。 - 禁止编造过往业绩。 - 禁止修改合同金额、日期、项目名称。 - 禁止在无材料时生成推测性内容。 行为示例 用户问公司是否有 CMMI5 认证 材料中没有相关信息时回答“没有找到相关材料无法生成该部分内容。”9.2 关于知识库建设材料一定要做去重和版本管理同一份文件有多个版本时旧版本存在校验 bug 隐患。不要一次性把所有材料塞进上下文优先检索再拼装保证信息相关性。定期更新政策法规类材料过期法规会在标书中造成硬伤。9.3 关于商用合规标书中的企业资质、业绩案例属于商业事实不提供来源的 AI 生成文本不能直接使用。建议给每个生成章节附带“来源引用”便于人工复核。对外发布或投标前必须由熟悉业务的专人做最终审核AI 只负责初稿。9.4 关于批量生成批量任务使用异步队列每个任务都要有独立日志。任务失败要有重试机制但重试不能无限循环一般 3 次以内。严格模式适合批量任务默认开启宁可拒绝也不能硬编。9.5 关于模型选择追求稳定性和事实一致性优先选择指令跟随能力强的模型。项目早期可以直接用 Python 写“规则 向量检索 LLM 调用”的轻量流程不要一开始就上复杂框架。后期如果需要在本地私有化部署再考虑 Qwen、Llama 等开源模型并用 vLLM 或 Ollama 托管。10. 总结与下一步这个项目最值得尝试的点是把“AI 可控生成”从概念落到了可运行的工程流程检索真实材料、约束生成范围、校验输出事实、拒绝无依据内容。它本质上是一套“防幻觉”中间层不依赖单一模型可以接入任意主流 LLM API也可以替换为本地开源模型。最先应该验证的功能是“缺失信息时的拒绝行为”当你问一个知识库里不存在的问题时系统是否真的会拒绝而不是开始编造。这个功能跑通了整个项目的核心价值就成立了。最容易踩的坑有两个。第一个是知识库材料没有充分解析就入库导致检索阶段找不到有效内容第二个是系统提示词写得不够硬模型在用户压力下仍然尝试编造。这两个问题都要通过打印完整 prompt 和检索结果来排查。后续可以继续扩展的方向包括把“事实校验”升级为“多模型交叉验证”用另一个模型对生成内容做二次核验。增加文章级别的逻辑一致性检查防止多个章节之间数据矛盾。对接 Office 文档格式直接生成符合招标文件格式的 Word 标书。增加人工审核工作流AI 生成初稿标记不确定项人工确认后定稿。建议收藏备用尤其是在做 AI 工程实践、AI 文档生成、企业知识库相关项目的同学这套“检索 约束 校验 拒绝”的闭环可以直接复用到其他场景。
返回列表