Advertencia
Las citas son experimentales. El nombre de la opción, la carga de eventos y la cobertura del proveedor pueden cambiar en una versión futura.
Cómo funcionan las citas
Las citas las proporciona el proveedor del modelo, no el SDK. El flujo tiene tres partes:
- La aplicación proporciona material que se puede citar, como un documento adjunto o el resultado de una herramienta que contiene contenido de origen.
- El tiempo de ejecución marca ese material como citable en la red cuando
enableCitationsestá activado. Para los modelos de Anthropic, los archivos adjuntos se envían en bloquesdocumentcon las citas habilitadas. - El modelo devuelve metadatos de citas y el entorno de ejecución los normaliza en un objeto
citationsindependiente del proveedor en el evento finalassistant.message.
La compatibilidad con el proveedor es limitada. El campo provider de cada registro de origen indica de dónde procede la cita:
| Valor del proveedor | Meaning |
|---|---|
anthropic | Cita generada por una respuesta de un modelo de Anthropic (Claude) |
openai | Cita producida por la respuesta de un modelo de OpenAI |
client | Cita sintetizada por el entorno de ejecución a partir de la salida de la herramienta |
Nota:
enableCitations Activar no garantiza que una respuesta contenga citas. Los modelos solo los emiten cuando la respuesta se basa en material de origen citable. Trate siempre el citations campo como opcional.
Habilitación de citas en una sesión
Configure la opción al crear la sesión y vuelva a configurarla al reanudarla si desea referencias después de reiniciar.
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?;
Leer citas de mensajes del asistente
Las citaciones se reciben con el evento final assistant.message, no con los eventos assistant.message_delta. Espere al mensaje final antes de representar los marcadores de origen.
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}");
}
}
}
Referencia del contenido de la cita
El citations objeto separa los orígenes desduplicados de los intervalos que hacen referencia a ellos, por lo que un origen citado cinco veces aparece una vez en sources.
| Tipo | Campo | Description |
|---|---|---|
Citations | sources | Conjunto de orígenes desduplicado al que hacen referencia los intervalos de citas |
Citations | spans | Intervalos de texto generado anotados con sus orígenes auxiliares |
CitationSource | id | Identificador estable con alcance de turno al que hace referencia Citation |
CitationSource | provider | Sistema que produjo la cita: anthropic, openaio client |
CitationSource | title? | Título legible de la fuente |
CitationSource | url? | Dirección URL del origen, cuando es un recurso web |
CitationSource | path? | Ruta de acceso del archivo relativa a la raíz del área de trabajo del agente, cuando el origen es un archivo |
CitationSpan | startIndex | Desplazamiento inicial en el contenido final del mensaje (unidades de código UTF-16, basadas en cero, inclusivas) |
CitationSpan | endIndex | Desplazamiento final en el contenido final del mensaje (unidades de código UTF-16, basadas en cero y exclusivas) |
CitationSpan | references | Orígenes que admiten este intervalo |
CitationReference | sourceId | Identificador de CitationSource al que apunta esta referencia |
CitationReference | citedText? | Texto exacto del origen que admite el intervalo, cuando el modelo lo proporciona |
CitationReference | location? | Ubicación dentro de la fuente que respalda el fragmento |
CitationReference | providerMetadata? | Datos de correlación nativos del proveedor, transmitidos de forma opaca |
Sugerencia
Los desplazamientos de segmento se miden en unidades de código UTF-16 con respecto a la cadena final content. TypeScript, Java y .NET cadenas ya son UTF-16, por lo que puede segmentarlos directamente. Las cadenas de Python se indexan por punto de código Unicode y las cadenas de Go y Rust son UTF-8, por lo que debes convertir el contenido a unidades de código UTF-16 antes de segmentarlo, como hacen los ejemplos anteriores.
Ubicaciones de las citas
CitationReference.location es una unión discriminada cuya clave discriminante es type:
| Tipo de ubicación | Campos | Use |
|---|---|---|
char | ||
startIndex, endIndex | Intervalo de caracteres dentro del texto de origen | |
page | ||
startPage, endPage | Intervalo de páginas dentro de un documento paginado | |
block | ||
startBlock, endBlock | Intervalo de bloques de contenido dentro de un documento estructurado |
Proporcionar orígenes citables
Las citas necesitan material de origen que el modelo pueda atribuir. Hay dos maneras de suministrarlo.
Adjuntar documentos a un mensaje
Cuando se habilitan las citas y la sesión usa un proveedor de Anthropic, los archivos adjuntos se envían como bloques document con las citas activadas, para que el modelo pueda citar pasajes de ellos.
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",
},
],
});
Consulte Entrada de imagen para ver la API de adjuntos y los esquemas de adjuntos file y blob.
Devolver orígenes citables desde una herramienta
Los resultados de la herramienta incluyen una matriz experimental citableSources. Cada entrada proporciona content que el modelo puede citar, junto con un id y opcional title, urly path. Estas fuentes se almacenan con el resultado de la herramienta, por lo que sobreviven a la reanudación de la sesión y las citas generadas a partir de ellas se etiquetan con el proveedor client.
Limitations
- Las citas son experimentales en cada SDK y no están cubiertas por garantías de compatibilidad.
- La cobertura depende del proveedor de modelos. Una sesión configurada para un proveedor que no admite citas no emite ninguna carga útil
citations. - Las referencias solo están presentes en el evento final
assistant.message, por lo que los clientes que consumen la respuesta en streaming no pueden mostrarlas durante la respuesta. - El código público y las citaciones por duplicación de IP no forman parte de este ámbito.
Lectura adicional
- Eventos de la sesión de transmisión: suscribirse a los eventos de sesión y acotar los tipos de eventos
- Entrada de imagen: adjuntar archivos y blobs en memoria a un mensaje
- Reanudación y persistencia de sesión: reanudar sesiones y volver a aplicar opciones de sesión
- Compatibilidad del SDK y la CLI: matriz de características del SDK y la CLI