本节在算法章的第四站:零散且已验证的命题,怎么拼成一份人能用的报告。全册里这一步是数据流终点(2.4 的成稿),也是价值兑现处(1.4)。设想一个场景:你拿到二十条带出处的命题,其中三条互相矛盾、两条标着待验证,若只是把命题堆砌,读者反而更晕。合成不是拼接,是建立论证结构并诚实标注不确定。
合成分两步。先聚类:把讲同一件事的命题归组,矛盾的对立保留而非强行统一。再成文:每组给"结论 + 证据等级 + 来源"。证据等级由印证数决定——已印证、单一源、存疑三档,直接写在句末。下面用代码实现合成器,看它如何把命题组转成带等级的句子。

# 知识合成:聚类命题 按印证数定证据等级 生成报告句 def synthesize_report(proposition_groups: list) -> list: lines = [] for group in proposition_groups: claim = group["claim"] indep = group["independent_sources"] if indep >= 2: level = "已印证" elif indep == 1: level = "单一源 待验证" else: level = "存疑" lines.append(f"{claim}({level},来源 {indep} 个独立印证)") return lines # 运行示例 groups = [ {"claim": "产能翻倍", "independent_sources": 1}, {"claim": "获批临床", "independent_sources": 2}, {"claim": "成本下降", "independent_sources": 0}, ] for line in synthesize_report(groups): print("-", line) # - 产能翻倍(单一源 待验证,来源 1 个独立印证) # - 获批临床(已印证,来源 2 个独立印证) # - 成本下降(存疑,来源 0 个独立印证)
运行输出三行,每行带证据等级与独立印证数。"产能翻倍"被标待验证而非删掉——合成器的纪律是:不确定就明说,绝不替读者拍板。这正是 1.1 定义里"诚实边界"的落地。
我们主张:报告生成的关键不是文采,是可审计。每句话应能溯回某条命题及其来源,否则读者无法判断该信几分。因此综合层输出的是"结构化句子"(结论+等级+源),而非自由散文。文采交给下游模板,研究智能体只负责把证据讲清楚。
下面演示"矛盾不强行统一"的实现:当一组内出现对立 claim,都保留并标冲突,而非取多数。
# 矛盾处理:对立主张并存 标冲突 而非投票统一 def handle_conflict(group: dict) -> str: views = group["views"] # 如 [("利好", 2), ("利空", 2)] if len(views) > 1: desc = ";".join(f"{v}({n}源)" for v, n in views) return f"存在分歧:{desc}——均待验证" return f"{views[0][0]}({views[0][1]}源)" print(handle_conflict({"views": [("短期利好", 2), ("长期利空", 2)]})) # 存在分歧:短期利好(2源);长期利空(2源)——均待验证
输出保留两派并标分歧。强行统一会掩盖真实不确定性,是研究报告的大忌。下一节 3.5 看大模型在这些步骤里到底在哪出力。
完整案例:背景→操作→结果→解读→变式
synthesize_report 重跑,该条被标"存疑",并在脚注暴露"仅厂商单方"。3.4 说综合层输出"结构化句子"(结论+等级+源)。落地时更稳的做法是输出一份结构化报告对象而非纯文本:每个结论带 evidence_level、independent_sources、conflicting 三个字段,下游无论是渲染成文档、还是被 4.3 评估整合分、还是被 5.2 接闸门,都直接读字段,不用再让大模型从散文里硬抠。这把"可审计"从口号变成数据契约。
下面演示把命题组合成结构化报告对象:
# 合成结构化报告:每结论带等级/独立源/冲突字段 def build_report(groups: list) -> dict: items = [] for g in groups: indep = g["independent_sources"] level = "已印证" if indep >= 2 else ("单一源" if indep == 1 else "存疑") items.append({ "claim": g["claim"], "evidence_level": level, "independent_sources": indep, "conflicting": g.get("conflicting", False), }) return {"items": items, "auditable": True} # 运行示例 rep = build_report([ {"claim": "获批临床", "independent_sources": 2, "conflicting": False}, {"claim": "成本下降", "independent_sources": 0, "conflicting": True}, ]) print(rep["items"][0]) # 已印证 2源 print(rep["items"][1]) # 存疑 冲突=True
运行输出两个结论对象,第一已印证、第二存疑且标冲突。注意 auditable: True 这个顶层标志——任何消费方拿到报告先查它,没有字段就拒绝当作可信结论,这和 2.1 的契约校验、4.4 的诚实核验是一脉相承的护栏。
| 证据等级 | 独立源数 | 报告呈现 | 下游动作 |
|---|---|---|---|
| 已印证 | ≥2 | 正常陈述 | 直接采用 |
| 单一源 | 1 | 标待验证 | 提示读者 |
| 存疑 | 0 | 标存疑/列冲突 | 转人工或归因 |
💡 关键直觉:报告的价值不在"读着顺",在"查得清"。自由散文再流畅,读者也无法逐句溯源;结构化报告每句挂字段,读者、评估器、闸门都能机器核验。所以 3.4 主张文采交给下游模板,研究智能体只负责把证据讲清楚。
⚠️ 常见坑:把"存疑"和"矛盾"混为一谈。存疑是"没足够独立源印证"(证据不足),矛盾是"有对立且各自有源"(证据冲突)。两者处置不同:存疑应标待验证,矛盾应两派并存标分歧(见 3.4 handle_conflict)。混为一谈会导致真冲突被压成"不确定",掩盖了真实分歧。