OpenAI 引文格式
可靠的引用能建立信任,帮助读者验证回答。本指南说明如何准备可引用材料,并用OpenAI模型熟悉的模式指示引用格式。
<a id="overview"></a>
概览
引用系统包含多个环节:确定哪些材料可引用,清楚表示材料,告诉模型怎样引用,并在展示前验证结果。模型直接接触的五个核心要素是:可引用单元、材料表示、引用格式、提示指令、引用解析。
<a id="choose-citable-units"></a>
选择可引用单元
写提示词前,先确定模型允许引用什么。
| 单元 | 适用情况 | 局限 | 示例 |
|---|---|---|---|
| 文档 | 只需指出答案来自哪份文档 | 不够精确 | 以整份员工手册支持说法 |
| 块/分块 | 兼顾简单与精度 | 不能精确到行 | 引用包含条款的合同段落或检索块 |
| 行范围 | 需要显示确切支持文本 | 对模型更困难 | 引用L42-L47供用户核查原段落 |
单元应保持一致:同一来源跨次运行使用同一ID;易于检查:人能读懂并理解周边上下文;大小适当:足够表达意义,又足够精确。多数系统可默认采用块级引用,它通常比行级引用更易生成,也比文档级引用更有用。
<a id="represent-citable-material"></a>
表示可引用材料
材料没有清楚呈现,模型就无法正确引用。无论来自工具还是直接注入,都应有稳定来源ID(如file1、block1)、清楚排版的文本,以及可选URL、时间戳、标题等元数据。
<a id="example-citable-material"></a>
可引用材料示例
可引用材料示例(来源示例1,text)
引用标记:{CITATION_START}cite{CITATION_DELIMITER}file0{CITATION_STOP}
标题:员工手册
URL:https://company.example/handbook
更新:2026-03-01
[L1] 员工每周最多可远程工作三天。
[L2] 增加远程工作天数需经理批准。
[L3] 获批的特殊便利安排可能适用例外。
来源ID与定位符不同:来源ID是稳定标识,例如block1;定位符指向界面上精确高亮的位置,例如lines L8-L13或第21段。通常让模型输出来源ID,由系统解析或渲染定位符。过早混合两者容易增加格式错误。
<a id="define-citation-format"></a>
定义引用格式
明确规定生成格式,要求显式、一致且易于可靠复现。以下标记接近模型训练时使用的格式,因此推荐采用;如需更换标记值,也应尽量维持整体格式。
| 部分 | 作用 | 推荐值 |
|---|---|---|
CITATION_START |
开始引用标记 | \ue200 |
| 引用类别 | 区分引用类型;所有支持来源用cite |
cite |
CITATION_DELIMITER |
分隔字段 | \ue202 |
| 来源ID | turn#是工具调用轮号,item#是文件、块或URL |
turn0file1、turn0block1、turn0url1 |
| 定位符(可选) | 限定确切范围 | L8-L13 |
CITATION_STOP |
结束标记 | \ue201 |
turnN按每次工具调用递增,不按单个结果递增。同次调用的来源以file0、file1等后缀区分。一次回答前恰好只调用一次工具时,所有引用才均为turn0...;多次调用则可能有turn0fileX、turn1fileX等。
<a id="template"></a>
模板
模板(来源示例2,text)
{CITATION_START}<citation_family>{CITATION_DELIMITER}<source_id>{CITATION_DELIMITER}<locator>{CITATION_STOP}
<a id="example"></a>
示例
示例(来源示例3,text)
{CITATION_START}cite{CITATION_DELIMITER}turn0file1{CITATION_DELIMITER}L8-L13{CITATION_STOP}
不使用定位符时,省略该字段:
示例(来源示例4,text)
{CITATION_START}cite{CITATION_DELIMITER}turn0file1{CITATION_STOP}
<a id="write-effective-citation-instructions"></a>
编写有效引用指令
熟悉的模式有助于准确。自定义或陌生格式会增加认知负担,特别是在低推理力度、难以修复格式错误时,或复杂任务已把大部分推理预算用于任务本身时。
下面格式接近模型熟悉的模式,可以直接使用或适配。如果自行设计提示词,应定义确切语法、引用位置、何时引用或不引用、多来源如何处理、禁止哪些格式,以及缺少支持时如何处理。
<a id="recommended-prompt-instructions"></a>
推荐提示指令
推荐提示指令(来源示例5,md)
## 引用
结果由"tool_1"返回。其每条消息称为“来源”,引用ID为首次出现的【turn\d+\w+\d+】,如【turn2file1】,其中turn2file1就是来源引用ID。
引用指向tool_1来源,可引用一个或多个。
单来源必须写为{CITATION_START}cite{CITATION_DELIMITER}turn\d+\w+\d+{CITATION_STOP},如{CITATION_START}cite{CITATION_DELIMITER}turn2file5{CITATION_STOP}。
多来源必须写为{CITATION_START}cite{CITATION_DELIMITER}turn\d+\w+\d+{CITATION_DELIMITER}turn\d+\w+\d+{CITATION_DELIMITER}...{CITATION_STOP},如{CITATION_START}cite{CITATION_DELIMITER}turn2file5{CITATION_DELIMITER}turn2file1{CITATION_DELIMITER}...{CITATION_STOP}。
不要把引用放进Markdown加粗、斜体或代码围栏内,否则无法正确展示;放在这些格式外。围栏外的引用也不得与结束围栏处于同一行。
绝不直接在正文写turn\d+\w+\d+,必须包在{CITATION_START}...{CITATION_STOP}中。
- 默认段末引用,段落长时可行内引用,用户指定位置时按其要求。
- 引用放标点之后。
- 不把全部引用集中在回复末尾。
- 不让某行或某段只有引用。
如还需输出L1-L22这样的行定位符,进一步规定:
推荐提示指令(来源示例6,text)
仅当项目有对应引用标记时,你必须用`\ue200cite\ue202turn0file0\ue202L8-L13\ue201`格式引用本工具中使用的任何结果。
- 没有对应引用标记的项目不应引用,它们并非可引用材料。
- 引用必须包含行范围。
<a id="optional-instructions-for-higher-quality-grounding"></a>
可选:提高依据质量
需要更高质量的依据时,可结合用例加入以下规则。
可选:提高依据质量(来源示例7,xml)
<extra_considerations_for_citations>
- 相关性:只纳入支持被引文字的搜索结果与引用;无关来源会永久损害信任。
- 多样性:答案必须基于不同域名来源,并相应引用。
- 可信度:依赖高质量域名,低声誉域名的信息除非是唯一来源,否则忽略。
- 准确表达:引用必须准确反映来源,不能选择性解释。
记住,域名或来源质量依上下文而定。
- 有多种观点时,引用覆盖各方意见,保证平衡全面。
- 可靠来源有分歧时,每个主要观点至少引用一个高质量来源。
- 超过一半的引用来自该主题广泛认可的权威机构或媒体。
- 有争议主题至少为每个主要观点引用一个可靠来源。
- 不要因为相关来源质量较低,就忽略其内容。
</extra_considerations_for_citations>
<a id="parse-citations"></a>
解析引用
模型生成后,从回答文字提取标记,以解析来源ID、渲染链接,或展示前移除原始标记。下面的辅助实现可直接复制进应用,解析单来源、多来源及可选行范围,并保持原文本的字符偏移。示例只支持行定位符;采用其他定位格式时应修改。
<a id="post-processor-examples"></a>
后处理示例
后处理示例(来源示例8,javascript)
// [中文] 用私用区分隔符识别引文标记,再拆分引文家族、来源ID及可选定位信息。
// [中文] 提取范围用于替换或渲染引用;不要把展示用占位符当作实际分隔符。保持当前语言的字符串偏移单位,不混用字符数与字节数。
const CITATION_START = "\uE200";
const CITATION_DELIMITER = "\uE202";
const CITATION_STOP = "\uE201";
const SOURCE_ID_RE = /^[A-Za-z0-9_-]+$/;
const LINE_LOCATOR_RE = /^L\d+(?:-L\d+)?$/;
// [中文] 提取下列形式的引文;下面保留分隔符及定位器示例。
// Extract citations such as:
//
// {CITATION_START}cite{CITATION_DELIMITER}turn0file0{CITATION_STOP}
// {CITATION_START}cite{CITATION_DELIMITER}turn0file0{CITATION_DELIMITER}L8-L13{CITATION_STOP}
// {CITATION_START}cite{CITATION_DELIMITER}turn0search0{CITATION_DELIMITER}turn1news2{CITATION_STOP}
function extractCitations(text, { families = ["cite"] } = {}) {
if (families.length === 0) {
return [];
}
const familyPattern = families
.map((family) => family.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))
.join("|");
const tokenRe = new RegExp(
`${CITATION_START}(?<family>${familyPattern})${CITATION_DELIMITER}(?<body>[\\s\\S]*?)${CITATION_STOP}`,
"g"
);
const citations = [];
for (const match of text.matchAll(tokenRe)) {
const body = match.groups?.body ?? "";
const parts = body
.split(CITATION_DELIMITER)
.map((part) => part.trim())
.filter(Boolean);
if (parts.length === 0) {
continue;
}
let locator = null;
const lastPart = parts[parts.length - 1];
if (LINE_LOCATOR_RE.test(lastPart)) {
locator = parts.pop() ?? null;
}
if (parts.length === 0 || parts.some((part) => !SOURCE_ID_RE.test(part))) {
continue;
}
citations.push({
raw: match[0],
family: match.groups?.family ?? "",
source_ids: parts,
locator,
start: match.index ?? 0,
end: (match.index ?? 0) + match[0].length,
});
}
return citations;
}
function stripCitations(text, citations) {
let cleanText = text;
const sortedCitations = Array.from(citations).sort(
(left, right) => right.start - left.start
);
for (const citation of sortedCitations) {
cleanText =
cleanText.slice(0, citation.start) + cleanText.slice(citation.end);
}
return cleanText;
}
后处理示例(来源示例9,python)
## [中文] 用私用区分隔符识别引文标记,再拆分引文家族、来源ID及可选定位信息。
## [中文] 提取范围用于替换或渲染引用;不要把展示用占位符当作实际分隔符。保持当前语言的字符串偏移单位,不混用字符数与字节数。
import re
from collections.abc import Iterable
from typing import TypedDict
CITATION_START = "\ue200"
CITATION_DELIMITER = "\ue202"
CITATION_STOP = "\ue201"
SOURCE_ID_RE = re.compile(r"^[A-Za-z0-9_-]+$")
LINE_LOCATOR_RE = re.compile(r"^L\d+(?:-L\d+)?$")
class Citation(TypedDict):
raw: str
family: str
source_ids: list[str]
locator: str | None
start: int
end: int
def extract_citations(
text: str,
*,
families: tuple[str, ...] = ("cite",),
) -> list[Citation]:
"""
Extract citations such as:
{CITATION_START}cite{CITATION_DELIMITER}turn0file0{CITATION_STOP}
{CITATION_START}cite{CITATION_DELIMITER}turn0file0{CITATION_DELIMITER}L8-L13{CITATION_STOP}
{CITATION_START}cite{CITATION_DELIMITER}turn0search0{CITATION_DELIMITER}turn1news2{CITATION_STOP}
"""
if not families:
return []
family_pattern = "|".join(re.escape(family) for family in families)
token_re = re.compile(
rf"{re.escape(CITATION_START)}"
rf"(?P<family>{family_pattern})"
rf"{re.escape(CITATION_DELIMITER)}"
rf"(?P<body>.*?)"
rf"{re.escape(CITATION_STOP)}",
re.DOTALL,
)
citations: list[Citation] = []
for match in token_re.finditer(text):
parts = [part.strip() for part in match.group("body").split(CITATION_DELIMITER)]
parts = [part for part in parts if part]
if not parts:
continue
locator = None
if LINE_LOCATOR_RE.fullmatch(parts[-1]):
locator = parts.pop()
if not parts or any(not SOURCE_ID_RE.fullmatch(part) for part in parts):
continue
citations.append(
{
"raw": match.group(0),
"family": match.group("family"),
"source_ids": parts,
"locator": locator,
"start": match.start(),
"end": match.end(),
}
)
return citations
def strip_citations(text: str, citations: Iterable[Citation]) -> str:
"""
Remove raw citation markers from text using offsets returned by
extract_citations().
"""
clean_text = text
for citation in sorted(citations, key=lambda item: item["start"], reverse=True):
clean_text = clean_text[: citation["start"]] + clean_text[citation["end"] :]
return clean_text
后处理示例(来源示例10,go)
// [中文] 用私用区分隔符识别引文标记,再拆分引文家族、来源ID及可选定位信息。
// [中文] 提取范围用于替换或渲染引用;不要把展示用占位符当作实际分隔符。Go的Start/End按UTF-8字节偏移切片。
// [中文] Start和End是UTF-8字节偏移,可直接用于Go字符串切片。
// Start and End are UTF-8 byte offsets, suitable for Go string slicing.
type Citation struct {
Raw, Family string
SourceIDs []string
Locator string
Start, End int
}
var sourceID = regexp.MustCompile(`^[A-Za-z0-9_-]+$`)
var lineLocator = regexp.MustCompile(`^L\d+(?:-L\d+)?$`)
func extractCitations(text string, families []string) []Citation {
citations := []Citation{}
if len(families) == 0 {
return citations
}
escaped := make([]string, len(families))
for i, family := range families {
escaped[i] = regexp.QuoteMeta(family)
}
token := regexp.MustCompile("(?s)\uE200(" + strings.Join(escaped, "|") + ")\uE202(.*?)\uE201")
for _, match := range token.FindAllStringSubmatchIndex(text, -1) {
parts := []string{}
for _, part := range strings.Split(text[match[4]:match[5]], "\uE202") {
if part = strings.TrimSpace(part); part != "" {
parts = append(parts, part)
}
}
locator := ""
if len(parts) > 0 && lineLocator.MatchString(parts[len(parts)-1]) {
locator = parts[len(parts)-1]
parts = parts[:len(parts)-1]
}
valid := len(parts) > 0
for _, part := range parts {
if !sourceID.MatchString(part) {
valid = false
break
}
}
if valid {
citations = append(citations, Citation{
text[match[0]:match[1]],
text[match[2]:match[3]],
parts,
locator,
match[0],
match[1],
})
}
}
return citations
}
func stripCitations(text string, citations []Citation) string {
sorted := append([]Citation(nil), citations...)
sort.Slice(sorted, func(i, j int) bool {
return sorted[i].Start > sorted[j].Start
})
for _, citation := range sorted {
text = text[:citation.Start] + text[citation.End:]
}
return text
}
后处理示例(来源示例11,java)
// [中文] 用私用区分隔符识别引文标记,再拆分引文家族、来源ID及可选定位信息。
// [中文] 提取范围用于替换或渲染引用;不要把展示用占位符当作实际分隔符。Java的偏移以UTF-16代码单元计,与substring一致。
import java.util.*;
import java.util.regex.Pattern;
// [中文] Java字符串偏移按UTF-16代码单元计算,与substring一致。
// Java string offsets are UTF-16 code units, matching substring().
record Citation(
String raw, String family, List<String> sourceIds, String locator, int start, int end) {}
static final Pattern SOURCE_ID = Pattern.compile("^[A-Za-z0-9_-]+$");
static final Pattern LINE_LOCATOR = Pattern.compile("^L\\d+(?:-L\\d+)?$");
static List<Citation> extractCitations(String text, List<String> families) {
var citations = new ArrayList<Citation>();
if (families.isEmpty()) return citations;
var familyPattern =
families.stream().map(Pattern::quote).collect(java.util.stream.Collectors.joining("|"));
var matcher =
Pattern.compile("\uE200(" + familyPattern + ")\uE202(.*?)\uE201", Pattern.DOTALL)
.matcher(text);
while (matcher.find()) {
var parts =
new ArrayList<>(
Arrays.stream(matcher.group(2).split("\uE202"))
.map(String::strip)
.filter(s -> !s.isEmpty())
.toList());
String locator = null;
if (!parts.isEmpty() && LINE_LOCATOR.matcher(parts.get(parts.size() - 1)).matches())
locator = parts.remove(parts.size() - 1);
if (parts.isEmpty() || parts.stream().anyMatch(s -> !SOURCE_ID.matcher(s).matches()))
continue;
citations.add(
new Citation(
matcher.group(),
matcher.group(1),
List.copyOf(parts),
locator,
matcher.start(),
matcher.end()));
}
return citations;
}
static String stripCitations(String text, List<Citation> citations) {
var sorted = new ArrayList<>(citations);
sorted.sort(Comparator.comparingInt(Citation::start).reversed());
for (var citation : sorted)
text = text.substring(0, citation.start()) + text.substring(citation.end());
return text;
}
后处理示例(来源示例12,ruby)
## [中文] 用私用区分隔符识别引文标记,再拆分引文家族、来源ID及可选定位信息。
## [中文] 提取范围用于替换或渲染引用;不要把展示用占位符当作实际分隔符。保持当前语言的字符串偏移单位,不混用字符数与字节数。
CITATION_START = "\u{E200}"
CITATION_DELIMITER = "\u{E202}"
CITATION_STOP = "\u{E201}"
SOURCE_ID_RE = /\A[A-Za-z0-9_-]+\z/
LINE_LOCATOR_RE = /\AL\d+(?:-L\d+)?\z/
def extract_citations(text, families: ["cite"])
return [] if families.empty?
family_pattern = families.map { |family| Regexp.escape(family) }.join("|")
token_re = Regexp.new(
"#{Regexp.escape(CITATION_START)}" \
"(?<family>#{family_pattern})" \
"#{Regexp.escape(CITATION_DELIMITER)}" \
"(?<body>.*?)" \
"#{Regexp.escape(CITATION_STOP)}",
Regexp::MULTILINE
)
text.enum_for(:scan, token_re).map do
match = Regexp.last_match
next unless match
body = match[:body]
next unless body
parts = body.split(CITATION_DELIMITER).map(&:strip).reject(&:empty?)
locator = parts.pop if parts.last&.match?(LINE_LOCATOR_RE)
next if parts.empty? || parts.any? { |part| !part.match?(SOURCE_ID_RE) }
{
raw: match[0],
family: match[:family],
source_ids: parts,
locator: locator,
start: match.begin(0),
end: match.end(0)
}
end.compact
end
def strip_citations(text, citations)
citations.sort_by { |citation| -citation.fetch(:start) }.each_with_object(text.dup) do |citation, clean|
clean[citation.fetch(:start)...citation.fetch(:end)] = ""
end
end
来源ID形状不同,则调整SOURCE_ID_RE。
<a id="examples"></a>
示例
下面展示工具返回可引用材料与ID,以及直接将可引用块注入提示词这两种模式。
<a id="format-citations-for-retrieved-tool-context"></a>
为工具检索上下文设置引用
模型通过工具取得上下文并在回答中引用时,使用此模式。
<a id="define-citable-units"></a>
定义可引用单元
按用例所需精度选择。具体工具随应用而异,重要的是结果像下例一样清楚、结构稳定。
<a id="line-level-example"></a>
行级示例
行级示例(来源示例13,text)
引用标记:{CITATION_START}cite{CITATION_DELIMITER}turn0file0{CITATION_STOP}
[L1] 服务协议规定,除非客户专属补充条款另行取代,无理由终止需提前三十(30)天书面通知。
[L2] 实际上,截止前未收到不续约书面通知时,合同按一年一期自动续展。
[L3] 附录B进一步规定,价格例外必须由财务部门及账户负责人双方书面批准。
引用标记:{CITATION_START}cite{CITATION_DELIMITER}turn0file1{CITATION_STOP}
...
turn0file0是稳定来源ID,行号是定位符。
<a id="block-level-example"></a>
块级示例
块级示例(来源示例14,text)
引用标记:{CITATION_START}cite{CITATION_DELIMITER}turn0file0{CITATION_STOP}
[Block1]
服务协议规定,除非客户专属补充条款另行取代,无理由终止需提前三十(30)天书面通知。
实际上,截止前未收到不续约书面通知时,合同按一年一期自动续展。
附录B进一步规定,价格例外必须由财务部门及账户负责人双方书面批准。
引用标记:{CITATION_START}cite{CITATION_DELIMITER}turn0file1{CITATION_STOP}
[Block2]
...
块级引用仍建议每个检索块使用独立稳定来源ID,维持同样的两字段引用形状,例如{CITATION_START}cite{CITATION_DELIMITER}turn0file0{CITATION_STOP},不要另造完全不同的引用类别。
<a id="write-prompt-instructions"></a>
编写提示指令
编写提示指令(来源示例15,md)
## 引用
结果由tool_1返回,每条消息为一个来源,其引用ID是首次出现的`turn\\d+file\\d+`,如turn0file0或turn2file1。本例turn0file0是来源引用ID。
引用指向tool_1来源,可引用一个或多个。
单来源格式:
{CITATION_START}cite{CITATION_DELIMITER}turn\d+file\d+{CITATION_STOP}
支持行级引用时,确切行范围格式:
{CITATION_START}cite{CITATION_DELIMITER}turn\d+file\d+{CITATION_DELIMITER}L\d+-L\d+{CITATION_STOP}
多来源应分别输出多个标记,每个支持来源一个。
不可在正文直接写turn0file0等引用ID,必须包在{CITATION_START}...{CITATION_STOP}中。
- 引用置于所支持句子末尾;长句含多项受支持分句时可行内放置。
- 放在标点之后。
- 只引直接支持文字的已检索来源。
- 绝不编造工具未返回的来源ID、行范围或块定位符。
- 多个来源实质支持同一命题时,全部引用。
- 来源冲突时,引用冲突双方并准确描述分歧。
输出示例:
编写提示指令(来源示例16,text)
值班交接流程记录在每周支持同步会议笔记中。\ue200cite\ue202turn0file0\ue202L8-L13\ue201
<a id="format-citations-for-injected-context"></a>
为注入上下文设置引用
提前检索或准备上下文,再直接注入提示词时使用此模式。
<a id="define-citable-units-1"></a>
定义可引用单元
常见做法是用具有稳定引用ID的明确标签包裹来源片段。
定义可引用单元(来源示例17,xml)
<BLOCK id="block1">
服务协议规定,除非客户专属补充条款另行取代,无理由终止需提前三十(30)天书面通知。
实际上,截止前未收到不续约书面通知时,合同按一年一期自动续展。
附录B进一步规定,价格例外必须由财务部门及账户负责人双方书面批准。
</BLOCK>
<BLOCK id="block2">
课程大纲(Syllabus)
</BLOCK>
...
这样能明确可引用单元,便于模型引用。此处及下一段提示中的BLOCK标签按官方HTML代码补回;Markdown导出缺少这些标签,校勘证据保存在来源快照中。
<a id="write-prompt-instructions-1"></a>
编写提示指令
编写提示指令(来源示例18,md)
## 引用
支持上下文以可引用单元直接放在提示中。每个单元由首次出现标签的id属性值标识,例如`<BLOCK id="block5"> ... </BLOCK>`。本例block5就是来源引用ID。
本模式不调用工具,因此没有需递增的工具轮号,标记无需turn#前缀。可保持turn0block5风格以统一系统,也可用这里的block5。关键是标记必须精确、一致地匹配注入上下文ID。
引用指向这些给定单元,可引用一个或多个。
单来源格式:
{CITATION_START}cite{CITATION_DELIMITER}<block_id>{CITATION_STOP}
例如:
{CITATION_START}cite{CITATION_DELIMITER}block5{CITATION_STOP}
多来源分别输出多个标记,每个支持块一个。
不在正文直接写块ID,必须包在{CITATION_START}...{CITATION_STOP}中。
- 引用放被支持句末;长句有多项受支持分句时可行内引用。
- 放标点之后。
- 只引用给定上下文出现的块。
- 不编造块ID。
- 不引用外部知识或外部权威。
- 多块实质支持命题时全部引用。
- 给定块冲突时引用冲突块,准确描述冲突。
输出示例:
编写提示指令(来源示例19,text)
法院裁定,地区法院对申请人缺乏对人管辖权。\ue200cite\ue202block5\ue201

TRAE-AI编程


更多建议: