Warnung
Zitate sind experimentell. Der Optionsname, die Ereignisnutzlast und die Anbieterabdeckung können sich in einer zukünftigen Version ändern.
Funktionsweise von Zitaten
Zitate werden vom Modellanbieter erstellt, nicht vom SDK. Der Fluss hat drei Teile:
- Ihre Anwendung stellt zitierbares Material bereit, z. B. einen Dokumentanhang oder das Ergebnis eines Tools, das Quellmaterial enthält.
- Die Laufzeit kennzeichnet dieses Material bei der Übertragung als zitatierbar, wenn
enableCitationsaktiviert ist. Bei Anthropic-Modellen werden Dateianhänge alsdocument-Blöcke mit aktivierten Quellenangaben gesendet. - Das Modell gibt Zitatmetadaten zurück, und die Laufzeit normalisiert es in ein anbieteragnostisches
citationsObjekt für das endgültigeassistant.messageEreignis.
Der Anbietersupport ist eingeschränkt. Das provider-Feld in jedem Quelldatensatz gibt an, woher das Zitat stammt:
| Anbieterwert | Bedeutung |
|---|---|
anthropic | Zitat, das durch eine Antwort eines Anthropic-(Claude-)Modells erzeugt wurde |
openai | Zitat aus einer Antwort eines OpenAI-Modells |
client | Zur Laufzeit aus der Toolausgabe synthetisiertes Zitat |
Hinweis
enableCitations Das Aktivieren garantiert nicht, dass eine Antwort Zitate enthält. Modelle geben sie nur dann aus, wenn die Antwort auf zitierfähigem Quellenmaterial basiert. Behandeln Sie das citations Feld immer als optional.
Aktivieren von Zitaten in einer Sitzung
Legen Sie die Option für die Sitzungserstellung fest, und legen Sie sie erneut beim Fortsetzen fest, wenn Nach einem Neustart Zitate angezeigt werden sollen.
const session = await client.createSession({
onPermissionRequest: approveAll,
enableCitations: true,
});
const resumed = await client.resumeSession(session.sessionId, {
onPermissionRequest: approveAll,
enableCitations: true,
});
session = await client.create_session(
on_permission_request=PermissionHandler.approve_all,
enable_citations=True,
)
resumed = await client.resume_session(
session.session_id,
on_permission_request=PermissionHandler.approve_all,
enable_citations=True,
)
session, err := client.CreateSession(ctx, &copilot.SessionConfig{
OnPermissionRequest: copilot.PermissionHandler.ApproveAll,
EnableCitations: copilot.Bool(true),
})
resumed, err := client.ResumeSession(ctx, session.SessionID, &copilot.ResumeSessionConfig{
OnPermissionRequest: copilot.PermissionHandler.ApproveAll,
EnableCitations: copilot.Bool(true),
})
var session = await client.CreateSessionAsync(new SessionConfig
{
OnPermissionRequest = PermissionHandler.ApproveAll,
EnableCitations = true,
});
var resumed = await client.ResumeSessionAsync(session.SessionId, new ResumeSessionConfig
{
OnPermissionRequest = PermissionHandler.ApproveAll,
EnableCitations = true,
});
CopilotSession session = client
.createSession(new SessionConfig()
.setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
.setEnableCitations(true))
.get();
CopilotSession resumed = client
.resumeSession(session.getSessionId(), new ResumeSessionConfig()
.setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
.setEnableCitations(true))
.get();
let session = client
.create_session(
SessionConfig::new()
.approve_all_permissions()
.with_enable_citations(true),
)
.await?;
let resumed = client
.resume_session(
ResumeSessionConfig::new(session.id().clone())
.approve_all_permissions()
.with_enable_citations(true),
)
.await?;
Lesen von Zitaten aus Assistentennachrichten
Zitate treffen erst beim abschließenden assistant.message-Ereignis ein, nicht bei assistant.message_delta-Ereignissen. Warten Sie auf die endgültige Nachricht, bevor Sie Quellmarkierungen rendern.
session.on((event) => {
if (event.type !== "assistant.message" || !event.data.citations) {
return;
}
const { sources, spans } = event.data.citations;
const sourceById = new Map(sources.map((source) => [source.id, source]));
for (const span of spans) {
const quoted = event.data.content.slice(span.startIndex, span.endIndex);
for (const reference of span.references) {
const source = sourceById.get(reference.sourceId);
const label = source?.title ?? source?.url ?? source?.path ?? source?.id;
console.log(`"${quoted}" — ${label}`);
}
}
});
from copilot.session_events import SessionEventType
def utf16_slice(text: str, start: int, end: int) -> str:
"""Slice by UTF-16 code units, which is how span offsets are measured."""
units = text.encode("utf-16-le")
return units[start * 2 : end * 2].decode("utf-16-le")
def handle(event):
if event.type != SessionEventType.ASSISTANT_MESSAGE or not event.data.citations:
return
sources = {source.id: source for source in event.data.citations.sources}
for span in event.data.citations.spans:
quoted = utf16_slice(event.data.content, span.start_index, span.end_index)
for reference in span.references:
source = sources[reference.source_id]
label = source.title or source.url or source.path or source.id
print(f'"{quoted}" — {label}')
session.on(handle)
// import "unicode/utf16"
session.On(func(event copilot.SessionEvent) {
d, ok := event.Data.(*copilot.AssistantMessageData)
if !ok || d.Citations == nil {
return
}
sources := map[string]copilot.CitationSource{}
for _, source := range d.Citations.Sources {
sources[source.ID] = source
}
// Span offsets are UTF-16 code units, so index the UTF-16 view of the content.
units := utf16.Encode([]rune(d.Content))
for _, span := range d.Citations.Spans {
quoted := string(utf16.Decode(units[span.StartIndex:span.EndIndex]))
for _, reference := range span.References {
source := sources[reference.SourceID]
label := source.ID
switch {
case source.Title != nil:
label = *source.Title
case source.URL != nil:
label = *source.URL
case source.Path != nil:
label = *source.Path
}
fmt.Printf("%q — %s\n", quoted, label)
}
}
})
session.On<SessionEvent>(evt =>
{
if (evt is not AssistantMessageEvent message || message.Data.Citations is null)
{
return;
}
var sources = message.Data.Citations.Sources.ToDictionary(source => source.Id);
foreach (var span in message.Data.Citations.Spans)
{
var quoted = message.Data.Content[(int)span.StartIndex..(int)span.EndIndex];
foreach (var reference in span.References)
{
var source = sources[reference.SourceId];
var label = source.Title ?? source.Url ?? source.Path ?? source.Id;
Console.WriteLine($"\"{quoted}\" — {label}");
}
}
});
session.on(AssistantMessageEvent.class, event -> {
Citations citations = event.getData().citations();
if (citations == null) {
return;
}
Map<String, CitationSource> sources = citations.sources().stream()
.collect(Collectors.toMap(CitationSource::id, source -> source));
for (CitationSpan span : citations.spans()) {
String quoted = event.getData().content()
.substring(span.startIndex().intValue(), span.endIndex().intValue());
for (CitationReference reference : span.references()) {
CitationSource source = sources.get(reference.sourceId());
String label = source.title() != null ? source.title()
: source.url() != null ? source.url()
: source.path() != null ? source.path()
: source.id();
System.out.printf("\"%s\" — %s%n", quoted, label);
}
}
});
use github_copilot_sdk::session_events::AssistantMessageData;
use std::collections::HashMap;
let mut events = session.subscribe();
while let Ok(event) = events.recv().await {
if event.event_type != "assistant.message" {
continue;
}
let Some(data) = event.typed_data::<AssistantMessageData>() else {
continue;
};
let Some(citations) = data.citations.as_ref() else {
continue;
};
let sources: HashMap<&str, _> = citations
.sources
.iter()
.map(|source| (source.id.as_str(), source))
.collect();
// Span offsets are UTF-16 code units, so index the UTF-16 view of the content.
let units: Vec<u16> = data.content.encode_utf16().collect();
for span in &citations.spans {
let quoted = String::from_utf16_lossy(
&units[span.start_index as usize..span.end_index as usize],
);
for reference in &span.references {
let Some(source) = sources.get(reference.source_id.as_str()) else {
continue;
};
let label = source
.title
.as_deref()
.or(source.url.as_deref())
.or(source.path.as_deref())
.unwrap_or(source.id.as_str());
println!("\"{quoted}\" — {label}");
}
}
}
Referenz zur Zitatnutzlast
Das citations Objekt trennt deduplizierten Quellen von den Textspannen, die auf sie verweisen, sodass eine Quelle, die fünfmal zitiert wird, in sources nur einmal erscheint.
| Typ | Feld | Description |
|---|---|---|
Citations | sources | Deduplizierte Menge von Quellen, auf die durch die Zitationsspannen verwiesen wird |
Citations | spans | Textabschnitte des generierten Textes, die mit ihren zugehörigen Quellen annotiert sind |
CitationSource | id | Stabiler, auf einen Turn begrenzter Identifier, referenziert durch Citation |
CitationSource | provider | System, das das Zitat erzeugt hat: anthropic, , openaioder client |
CitationSource | title? | Lesbarer Titel der Quelle |
CitationSource | url? | URL der Quelle, wenn es sich um eine Webressource handelt |
CitationSource | path? | Dateipfad relativ zum Stammverzeichnis des Agent-Arbeitsbereichs, wenn die Quelle eine Datei ist |
CitationSpan | startIndex | Startversatz im endgültigen Nachrichteninhalt (UTF-16-Codeeinheiten, nullbasiert, einschließlich) |
CitationSpan | endIndex | End-Offset im endgültigen Inhalt der Nachricht (UTF-16-Codeeinheiten, nullbasiert, exklusiv) |
CitationSpan | references | Die Quellen, die diese Spanne unterstützen |
CitationReference | sourceId | Bezeichner des CitationSource, auf das diese Referenz verweist |
CitationReference | citedText? | Exakter Text aus der Quelle, der die Textspanne unterstützt, wenn er vom Modell bereitgestellt wird |
CitationReference | location? | Position innerhalb der Quelle, die die Spanne unterstützt |
CitationReference | providerMetadata? | Anbietereigene Korrelationsdaten, die undurchsichtig durchgegangen werden |
Tipp
Span-Offsets werden in UTF-16-Codeeinheiten relativ zur endgültigen content-Zeichenfolge gemessen. TypeScript, Java und .NET Zeichenfolgen sind bereits UTF-16, sodass Sie sie direkt segmentieren können. Python-Zeichenfolgen werden nach Unicode-Codepunkten indiziert, und Zeichenfolgen in Go und Rust sind UTF-8-codiert. Konvertieren Sie den Inhalt daher vor dem Slicing in UTF-16-Codeeinheiten, wie es in den obigen Beispielen geschieht.
Zitatstellen
CitationReference.location ist eine diskriminierte Union mit type als Diskriminator:
| Lagerplatztyp | Felder | Verwendung |
|---|---|---|
char | ||
startIndex, endIndex | Zeichenbereich innerhalb des Quelltexts | |
page | ||
startPage, endPage | Seitenbereich innerhalb eines paginierten Dokuments | |
block | ||
startBlock, endBlock | Inhaltsblockbereich innerhalb eines strukturierten Dokuments |
Bereitstellen zitierfähiger Quellen
Zitate benötigen Quellmaterial, das das Modell attributieren kann. Es gibt zwei Möglichkeiten, sie zu liefern.
Anfügen von Dokumenten an eine Nachricht
Wenn Zitate aktiviert sind und die Sitzung einen Anbieter von Anthropic verwendet, werden Dateianhänge als document-Blöcke mit aktivierter Zitierfunktion gesendet, sodass das Modell Passagen daraus zitieren kann.
await session.sendAndWait({
prompt: "Summarize the attached PDF and cite the passages you used.",
attachments: [
{
type: "blob",
data: pdfBase64,
displayName: "quarterly-report.pdf",
mimeType: "application/pdf",
},
],
});
Siehe Bildeingabe für die Anhang-API sowie die file- und blob-Anhang-Formen.
Zurückgeben zitatbarer Quellen aus einem Tool
Toolergebnisse enthalten ein experimentelles citableSources-Array. Jeder Eintrag stellt content bereit, auf das sich das Modell beziehen kann, zusammen mit einem id sowie optionalen title, url und path. Diese Quellen werden zusammen mit dem Toolergebnis gespeichert, sodass sie auch nach dem Fortsetzen der Sitzung erhalten bleiben, und auf ihrer Grundlage erstellte Zitate werden mit dem Anbieter client gekennzeichnet.
Limitations
- Zitate sind in jedem SDK experimentell und unterliegen nicht den Kompatibilitätsgarantien.
- Die Abdeckung hängt vom Modellanbieter ab. Eine für einen Anbieter ohne Unterstützung für Zitate konfigurierte Sitzung sendet keine
citations-Nutzlast. - Zitate sind nur im letzten
assistant.message-Ereignis enthalten, sodass Streaming-Clients sie während der laufenden Antwort nicht darstellen können. - Öffentliche Code- und IP-Duplizierungszitate sind nicht Teil dieser Oberfläche.
Weiterführende Lektüre
- Ereignisse einer Streaming-Sitzung: Session-Ereignisse abonnieren und Ereignistypen eingrenzen
- Bildeingabe: Anfügen von Dateien und Speicher-BLOBs an eine Nachricht
- Wiederaufnahme und Persistenz der Sitzung: Sitzungen fortsetzen und Sitzungsoptionen erneut anwenden
- SDK- und CLI-Kompatibilität: SDK- und CLI-Featurematrix