让大模型在知识图谱上做推理,本质上是一场信息组织与表达方式的博弈。图谱本身是结构化数据,由实体、关系构成的三元组组成,而大模型更擅长处理自然语言。如果直接把几百条三元组一股脑塞进提示词,模型往往抓不住重点,推理结果也会飘忽不定。链接预测和路径推理是知识图谱最常见的两类推理任务,前者要求模型判断两个实体之间是否可能存在某条缺失的边,后者要求模型沿着已有的多跳路径完成推理并给出解释。这两类任务对提示词的要求截然不同,写法上也有各自的门道。

一、链接预测提示词:如何让模型判断缺失的关系边
链接预测的核心问题是:给定头实体和尾实体,模型能否判断它们之间存在什么关系,或者某个给定关系成立的可能性有多大。比如已知阿司匹林属于非甾体抗炎药,也知道非甾体抗炎药常用于缓解疼痛,那么模型能否推断出阿司匹林可以缓解疼痛?这类任务用提示词来实现时,最关键的一点是把候选关系和上下文实体描述清楚,不要让模型在模糊的语义空间里瞎猜。
一个实用的技巧是采用结构固定的模板,把任务定义、图谱片段、候选关系列表、输出格式分块呈现。任务定义放在最前面,明确告诉模型这是链接预测任务;图谱片段用编号列表给出相关三元组;候选关系用封闭集合约束,避免模型自由发挥输出图谱中根本不存在的关系;最后用JSON格式约束输出,便于程序解析。
prompt = """你是一个知识图谱推理专家。请根据以下图谱三元组,判断头实体与尾实体之间最可能的关系,并从候选关系中选择一个。
【图谱三元组】
1. (阿司匹林, 属于, 非甾体抗炎药)
2. (非甾体抗炎药, 治疗, 轻度疼痛)
3. (阿司匹林, 抑制, 环氧化酶)
4. (环氧化酶, 参与, 炎症反应)
【任务】
预测三元组: (阿司匹林, ?, 炎症反应)
【候选关系】
抑制、缓解、导致、无关
【输出格式】
{"relation": "关系名", "confidence": 0到1之间的数值, "reason": "一句话推理依据"}
"""这个模板里有几个容易被忽视的细节。第一,三元组用括号加逗号的紧凑格式,比自然语言句子更省token,也更不容易让模型混淆主客体顺序。第二,候选关系用顿号分隔放在一个段落里,比列表更省空间,但候选数量超过十个时建议改回列表,否则模型可能漏看。第三,输出格式要求模型同时给出置信度和推理依据,这一步非常重要,因为链接预测的结果往往要进入下游流程,没有置信度就无法做阈值过滤,没有推理依据就无法人工抽检。
二、路径推理提示词:让模型输出可解释的推理链
路径推理比链接预测更进一步,它不仅要求给出结论,还要求给出完整的推理路径。这类任务非常适合用思维链提示来实现,即引导模型先沿着图谱的边一步步走,再归纳出最终答案。路径推理提示词的关键在于明确告诉模型每一跳的证据来源,禁止模型引用图谱之外的世界知识。
这里有一个常见的坑:如果不加约束,大模型会习惯性地调用自己预训练时学到的知识来补全推理,而不是严格按照提示词中给出的图谱片段推理。这在开放问答里是优点,但在知识图谱场景里是致命缺陷,因为图谱推理的目标恰恰是验证图谱本身的完整性,模型自己脑补的结论会污染评估结果。所以提示词中必须写明只允许基于给定三元组推理,遇到图谱中不存在路径时明确输出无法推理。
prompt = """你是一个严格基于给定知识图谱的推理引擎。 【规则】 1. 只能使用下方三元组中明确存在的事实进行推理,禁止使用你自己的知识 2. 每一步推理必须标注引用的三元组编号 3. 如果给定三元组无法推出目标结论,输出 "无法推理" 【图谱三元组】 T1: (姚明, 出生地, 上海) T2: (上海, 位于, 长三角) T3: (长三角, 属于, 中国东部) T4: (中国东部, 气候类型, 亚热带季风气候) 【问题】 请推理姚明出生地的气候类型,并给出完整路径。 【输出格式】 推理路径: 实体1 -[关系A]-> 实体2 -[关系B]-> 实体3 ... 最终答案: xxx """
用这个提示词,模型会输出类似姚明出生地是上海,上海位于长三角,长三角属于中国东部,中国东部的气候类型是亚热带季风气候这样的链条,每一跳都能对应到具体的三元组。这种带编号引用的设计还有一个好处:程序可以自动校验模型引用的三元组是否真的存在于输入中,从而过滤掉模型幻觉产生的虚假证据,这在批量评估时能省去大量人工核对工作。
三、多跳数量控制与提示词的进阶优化
实际项目中,路径推理很少只走一两跳。当推理深度超过三跳时,模型的错误率会明显上升,主要表现为中途实体张冠李戴,或者把两条不同路径拼接在一起。应对办法有两个:一是把长路径拆分成多轮对话,每轮只处理一到两跳,用程序把上一轮的结果拼接进下一轮的提示词;二是在提示词中显式要求模型先复述当前已知的路径状态,再决定下一步往哪走,相当于强制模型维护一个工作内存。
另一个值得投入的优化方向是少样本示例。与其反复用文字描述推理规则,不如直接给模型一两个完整的输入输出示例,包括正确的推理路径和遇到死路时的处理方式。示例的三元组内容要和实际任务领域保持一致但实体不同,这样模型能学到格式和推理风格,又不会直接照抄示例答案。
few_shot = """ 【示例】 三元组: S1: (李白, 朝代, 唐朝) S2: (唐朝, 都城, 长安) 问题: 李白所在朝代的都城 推理路径: 李白 -[朝代]-> 唐朝 -[都城]-> 长安 最终答案: 长安 """ # 拼接到正式提示词的规则之后、任务之前 final_prompt = system_rules + few_shot + user_task
最后谈谈token预算的问题。大图谱不可能整体塞进提示词,常规做法是先用子图检索(比如基于向量相似度或图遍历)筛选出与问题相关的几十条三元组,再交给模型推理。这时提示词的排序也有讲究:相关度最高的三元组放在列表开头和结尾,因为模型对中间位置的注意力相对较弱,这个位置效应在三元组数量超过三十条时尤其明显。经过检索加精心组织的提示词,链接预测的准确率通常能比无组织的原始输入提升两成以上,路径推理的可解释性也会大幅改善。