企业用大模型辅助编写投标书时,最危险的结果不是内容平庸,而是模型把不存在的资质、案例和人员写得像真的一样。市场上这类工具常被称为 AI 投标书撰写助手,它要面对的首要工程质量问题,就是如何让 AI 拒绝说谎:只输出能被企业素材证明的内容,而不是凭语言惯性脑补。大模型本质上是按概率补全文本的系统,它擅长生成听起来合理的句子,却并不天然具备“我只说我能证明的事”这种约束。若直接把模型输出当作投标文件提交,轻则被评委质疑,重则引发资格审查不通过、合同无效甚至商业信誉受损。要让助手真正“拒谎”,不能只靠一段提示词,需要把知识来源、生成策略、事实校验、人工审阅和持续评测串成完整的工程链路。下面按提示词约束、RAG 检索增强、独立事实校验、人工审阅闭环、评测与监控五层架构,给出可落地的方案和代码示例。
1. 先拆解“说谎”:投标场景中的幻觉到底是什么
1.1 幻觉的本质:模型在预测下一个词,不是在查档案
大模型本质上是根据上下文预测下一个 token 的语言模型。训练阶段,它从海量网页、论文、代码、书籍中学习文本的统计规律;生成阶段,它按照概率分布逐词补全。这意味着模型的首要目标是“输出看起来通顺合理的文字”,而不是“输出可被现实验证为真的句子”。当问题涉及企业私有数据,或者训练数据里根本没有对应材料时,模型仍会基于相似文本的模式“脑补”出答案。这个脑补过程,就是幻觉。
在投标书场景里,这种脑补极其危险。标书的每个断言都要经得起评标专家和甲方的核查。模型不知道公司是否真的持有某张证书,不知道某个项目是否真的签过合同,也不会有意识地区分“企业提供的素材”和“自己在训练数据里见过的相似描述”。如果不加约束,它会把两者混在一起输出。
1.2 投标书最容易被编造的四类内容
按照失真后果的严重程度,可以把幻觉分成四类:
| 幻觉类型 | 典型表现 | 翻车后果 |
|---|---|---|
| 资质证书编造 | 杜撰 ISO 认证、软件著作权、信用等级证书编号 | 评标现场无法验证,直接废标 |
| 业绩案例编造 | 虚构项目合同、甲方名称、项目金额与验收时间 | 背调环节穿帮,影响后续投标资格 |
| 人员简历编造 | 虚构项目负责人职称、PMP 证书、参与年限 | 中标后资格审查不通过,丢失项目 |
| 财务数据编造 | 虚报营收、纳税额、现金流、资产负债率 | 审计、法务和合同环节出现风险 |
这四类内容有一个共同点:它们都藏在企业素材库里,而不是公开互联网上。模型在训练阶段很可能根本没见过这些数据,所以回答“我们公司是否有 XX 资质”时,只能依靠语言模式去猜测。治理幻觉的第一步,就是明确哪些内容必须来自素材库,哪些内容模型一定不能自己生成。
1.3 治理思路:四道防线加一条监控线
明确幻觉的形态之后,可以设计一套四层防线:
- 提示词约束层:让模型面对无依据问题时,优先选择拒绝而不是猜测。
- 检索增强层:把企业经营资质、业绩合同、人员简历等材料变成可检索引擎,让模型只依据检索结果生成。
- 独立事实校验层:生成完成后,逐条提取声明,回到素材库核对证据,把不支持的内容拦下来。
- 人工审阅层:由业务人员对校验结果做最终确认,完成签字和归档。
此外还需要一条贯穿始终的评测与监控线:用固定评测集量化模型“拒谎”能力,用线上日志观察人工修改率,持续发现新问题。每层防线都不能单独完成任务,但合在一起,可以让 AI 从“大胆编造”变成“谨慎拒绝”。
2. 第一道防线:用提示词教会模型“拒绝”
2.1 提示词要同时约束角色、信息边界和拒绝行为
提示词是成本最低、见效最快的一层防线。它不能给模型增加知识,但能改变模型的行为边界。一份给投标书撰写助手的提示词,至少需要明确三件事:模型扮演什么角色、可以依据什么信息、遇到信息缺失时怎么处理。
下面是一份可作起点的基础提示词:
你是企业投标书撰写助手。你唯一的写作依据是“投标素材库”和检索系统返回的材料片段。 写作规则: 1. 涉及企业资质、业绩案例、人员简历、财务数据的断言,必须能在素材库中找到对应原文。 2. 找不到原文时,不要猜测。先输出占位标记 [待补充],并说明需要哪份材料。 3. 禁止编造奖项、排名、认证、合同金额、项目时间等任何无法验证的信息。 4. 如果检索结果为空,或检索结果与问题无关,直接回答“素材库中没有可用信息”,不要强行成文。 5. 输出中的每个关键断言都要标注引用编号,引用编号必须来自检索结果中的材料编号。这份提示词的关键不是“禁止编造”这四个字,而是第 2、4、5 条给出的具体行为路径。只写“不要编造”,模型仍然不知道无依据时该怎么办;给出“输出 [待补充] 并说明缺什么材料”的替代行为,模型才有可执行的拒绝方式。换句话说,要让拒绝成为模型的一等动作,而不是违反规则的异常动作。
2.2 把“拒绝”变成结构化输出,方便下游自动处理
提示词中的行为约束,最好配合结构化输出使用。如果模型直接输出一段自然语言,下游很难自动判断哪些句子被标记为待补充、哪些断言带引用。可以让模型按 JSON 结构返回段落和声明列表:
{ "paragraph": "公司已通过 ISO9001 质量管理体系认证[待补充:ISO9001证书扫描件],具备完善的售后服务能力。", "claims": [ { "claim": "公司已通过 ISO9001 质量管理体系认证", "status": "unverified", "evidence_id": null, "required_material": "ISO9001 证书扫描件" }, { "claim": "公司具备完善的售后服务能力", "status": "verified", "evidence_id": "MAT-00023", "evidence_snippet": "售后服务方案(第 3 节):承诺 7×24 小时响应" } ] }结构化输出的价值在于,它把“模型说了什么”和“模型凭什么这样说”变成了程序可以检查的字段。后续的事实校验层、人工审阅界面,都直接消费这份 JSON。实际开发时需要注意,直接让模型输出 JSON 并不可靠,生产环境更建议使用 function calling 或受约束解码来保证字段合法。
2.3 提示词能改变态度,不能增加知识
提示词看似解决了“说谎”问题,但它有一个根本上限:模型没有企业素材库里的私有信息,提示词不管写得多严格,都只是降低了编造的概率,没有把事实装进模型。具体来说,两个问题仍然存在:
- 如果模型在训练数据里见过“ISO9001”和“某行业资质”的常见搭配,它可能依然输出一个看起来合理的证书名称。
- 如果企业确实有某份证书,但检索环节没有把它找出来,提示词也无法让模型凭空知道这份证书的内容。
所以提示词是必要的第一道防线,但它只负责“态度”,不负责“知识”。知识必须由下一层防线提供。
3. 第二道防线:RAG 把每个结论钉回素材库
3.1 RAG 解决的核心矛盾
RAG(检索增强生成)的全称是 Retrieval-Augmented Generation,思路很简单:在模型生成之前,先从可信素材库中检索与问题相关的片段,把片段拼进上下文,再让模型基于这些片段生成答案。对投标书场景来说,这正好解决“模型没有企业私有知识”的问题。
素材库的范围要按投标内容来划定,通常是四类文件:
- 企业资质证书:营业执照、体系认证、软件著作权、高新技术企业证书等。
- 业绩合同:已验收项目的合同摘要、中标通知书、用户证明。
- 人员档案:项目负责人简历、职称证书、资格证书、社保记录摘要。
- 财务数据:审计报告、纳税申报表、银行资信证明中的关键指标。
这些文件是唯一的事实来源。RAG 之后的所有生成内容,都应该能追溯到这些文件。
3.2 最小 RAG 流水线:解析、分块、向量化、检索、生成
一个最小可用的 RAG 流水线包含五个环节。第一步是文档解析,把 PDF、Word 转成文本并保留页码和标题;第二步是分块,按结构切出适合检索的片段;第三步是向量化,把文本片段转成嵌入向量;第四步是检索,用问题向量做相似度搜索;第五步才是生成。
用 Python 伪代码可以这样表达(接口以实际部署的向量库和模型为准):
def build_context(query, top_k=5, allow_empty=False): """按 query 从素材库检索最相关的文本片段,并带上可追溯编号""" results = vector_store.search(query, top_k=top_k) if not results and not allow_empty: return None # 检索为空,后续生成必须拒绝 blocks = [] for r in results: blocks.append( f"[材料编号:{r['doc_id']} | 页码:{r.get('page', '?')}]\n{r['text']}" ) return "\n\n".join(blocks) def generate_bid_section(user_instruction, query): context = build_context(query) if context is None: return { "paragraph": "[待补充:素材库中没有与任务相关的材料]", "claims": [], "retrieval_status": "empty", } prompt = BASE_PROMPT + f"\n\n## 素材库检索结果\n{context}\n\n## 写作任务\n{user_instruction}" response = llm_client.chat.completions.create( model="your-model-name", messages=[{"role": "user", "content": prompt}], temperature=0.1, ) return parse_structured_response(response)这段代码里有几个细节值得注意。
- 检索为空时必须返回拒绝结果,而不是继续调用模型。一旦继续调用,模型仍有可能用自己的训练知识强行编造。
- 上下文块必须带
材料编号和页码,这是后续引用校验和人工核实的基础。 temperature=0.1表示生成过程更偏向确定性。投标书属于事实性文本,不需要高随机性;生产环境建议在 0.1 到 0.2 之间调试。parse_structured_response负责把模型返回文本解析成带 claims 的 JSON,在真实项目中需要结合 function calling 或格式校验来做。BASE_PROMPT即第 2 节中的基础提示词,实际项目中可以抽成配置文件。
上面是原型阶段的调用方式。进入生产环境后还需要关注素材库文件的权限控制,保证只有获得授权的角色才能导入和查看材料;同时要记录每次生成的完整输入与输出,便于审计和回滚。
3.3 分块策略与混合检索
分块质量直接影响检索质量。标书素材大多是结构化的,推荐按章节而不是固定字符切块:
| 分块方式 | 适用材料 | 优点 | 需要留意的问题 |
|---|---|---|---|
| 固定字符切块 | 证书文本、说明书 | 实现简单,长度可控 | 容易切断语义,影响检索 |
| 按标题或章节切块 | 合同、方案、简历 | 保留文档结构,召回更准 | 需要先解析标题层级 |
| 按语义切块 | 长段落和表格 | 块内主题集中 | 计算成本较高,需要调参 |
检索环节也不能只依赖向量相似度。投标材料里存在大量专有名词和编号,比如“ISO9001”“软件著作权登记号”“合同编号”,向量检索对这类精确短语的匹配不一定好。生产环境通常采用混合检索:BM25 负责精确关键词匹配,向量检索负责语义召回,再用粗排和精排把两者合并。这一步不会额外引入幻觉,只会提高“素材到底有没有被找到”的准确性。
4. 第三道防线:生成之后再做一次独立事实校验
4.1 为什么检索增强之后还需要校验
RAG 显著降低了幻觉,但没有消灭幻觉。模型完全可能在上下文里忽略某个材料片段,或者把两段不相关材料拼接成新结论。要拦截这类问题,必须在生成后增加一道独立的校验层。校验层的核心动作是:把模型生成的声明逐条提取出来,回到素材库寻找证据,判断证据是否能完整支持声明。
4.2 三种校验方式选型
| 校验方式 | 原理 | 适用场景 | 局限 |
|---|---|---|---|
| 规则校验 | 用正则、日期格式、编号规则核对 | 合同编号、证书编号、日期、金额字段 | 无法判断语义是否成立 |
| 语义证据校验 | 用另一个模型判断声明与证据是否一致 | 长句、复杂断言 | 第二个模型自身也可能误判,需要抽样复核 |
| 向量相似度阈值 | 比较声明与证据片段的向量相似度 | 快速初筛海量声明 | 阈值需要随语料调优,误报较多 |
实际项目里推荐组合使用:先用规则校验处理编号、日期、金额等硬字段,再用语义证据校验处理自然语言断言,最后用向量相似度做一轮兜底排序。规则校验是确定性的,不会出现“模棱两可”;语义证据校验灵活但可能误判,所以它的结论不能直接作为最终结果,而要交给人工审阅层做终审。
4.3 语义证据校验的最小实现
语义证据校验可以用“LLM as judge”方式实现:让一个只做判断、不负责生成的模型,判断“声明”是否被“证据”支持。
def verify_claim(claim, evidence_text, judge_model="your-judge-model"): prompt = ( "你是事实核查器。只依据给定证据判断断言是否成立。\n" f"断言:{claim}\n" f"证据:{evidence_text}\n" '只输出 JSON:{"supported": true 或 false, "reason": "不超过30字的原因"}' ) result = call_llm(prompt, model=judge_model, temperature=0.0) return result def verify_section(claims_with_evidence): results = [] for item in claims_with_evidence: verdict = verify_claim(item["claim"], item["evidence"]) item["verdict"] = verdict results.append(item) return results关键约束是:supported为 false 的声明,只能被降级为“待补充”或交给人工补材料,不能让另一个生成模型自动重写。如果让模型自我修复,等于重新打开幻觉入口,只是换了一种编造形式。
需要注意的是,证据本身也要有追溯能力。校验时应该把证据片段和它来源的材料编号 + 页码一起记录下来。这样当人工审阅