-
Notifications
You must be signed in to change notification settings - Fork 46
AIT-133 - citations feature documentation #3077
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
mittulmadaan
merged 8 commits into
AIT-129-AIT-Docs-release-branch
from
AIT-133-feature-documention-citations
Jan 14, 2026
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
c58d8c1
chore: update message annotations terminology to include appends
matt423 6d6dd45
Add citations feature documentation for AI Transport
mittulmadaan 64bfda3
AIT-133 - Fixed review comments
mittulmadaan f769b0a
AIT-133 - fix lint issue + resolve conflicts
mittulmadaan 5b33416
AIT-133 - nit fix
mittulmadaan e4aca26
Fixed review comments.
mittulmadaan 3583f6b
Update src/pages/docs/ai-transport/features/advanced/citations.mdx
mittulmadaan bfc21a5
AIT-133 - Moved Citations under Messaging
mittulmadaan File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
339 changes: 339 additions & 0 deletions
339
src/pages/docs/ai-transport/features/advanced/citations.mdx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,339 @@ | ||
| --- | ||
| title: "Citations" | ||
| meta_description: "Attach source citations to AI responses using message annotations" | ||
| meta_keywords: "citations, references, RAG, retrieval augmented generation, source attribution, message annotations, AI transparency, source tracking, annotation summaries, model-agnostic, LLM-neutral" | ||
| --- | ||
|
|
||
| AI agents often draw information from external sources such as documents, web pages, or databases. Citations to those sources enable users to verify information, explore sources in detail, and understand where responses came from. Ably's [message annotations](/docs/messages/annotations) provide a model-agnostic, structured way to attach source citations to AI responses without modifying the response content. It enable clients to append information to existing messages on a channel. | ||
|
|
||
| This pattern works with both single message publishing and the [message-per-response](/docs/ai-transport/message-per-response) approach using message appends. | ||
|
|
||
| ## Why citations matter <a id="why"/> | ||
|
|
||
| Citations serve several critical purposes in AI applications: | ||
|
|
||
| **Transparency**: Users can verify claims and understand the basis for AI responses. This builds trust and allows users to fact-check information independently. | ||
|
|
||
| **Source exploration**: Citations enable users to dive deeper into topics by accessing original sources. This is particularly valuable for research, learning, and decision-making workflows. | ||
|
|
||
| **Attribution**: Proper attribution respects content creators and helps users understand which sources informed the AI's response. | ||
|
|
||
| **Audit trails**: For enterprise applications, citations provide an audit trail showing which information sources were consulted during AI interactions. | ||
|
|
||
| ## How it works <a id="how-it-works"/> | ||
|
|
||
| Citations use Ably's [message annotations](/docs/messages/annotations) feature to attach source metadata to AI response messages without modifying the response content. | ||
|
|
||
| The annotation publishing workflow: | ||
|
|
||
| 1. **Publish response**: Agent publishes an AI response as a single message or builds it incrementally using [message appends](/docs/ai-transport/message-per-response) | ||
| 2. **Publish citation annotations**: Agent publishes one or more citation annotations, each referencing the response message serial | ||
| 3. **Aggregate summaries**: Ably automatically aggregates annotations and generates summaries showing total counts and groupings (e.g., by domain) | ||
| 4. **Subscribe citations**: Clients receive citation summaries automatically and can optionally subscribe to individual annotation events for detailed citation data as part of the realtime stream. Alternatively, clients can obtain annotations for a given message via the REST API. | ||
|
|
||
| Annotations are associated with a message's `serial` identifier. This works with: | ||
|
|
||
| - **Single message publish**: Complete response published as one message | ||
| - **Message appends**: Response built incrementally by appending tokens to a single message (see [message-per-response](/docs/ai-transport/message-per-response)) | ||
|
|
||
| ## Setup <a id="setup"/> | ||
|
|
||
| Message annotations require the "Message annotations, updates, deletes, and appends" [channel rule](/docs/channels#rules) enabled for your channel or [namespace](/docs/channels#namespaces). This rule automatically enables message persistence. | ||
|
|
||
| To enable the channel rule: | ||
|
|
||
| 1. Go to the [Ably dashboard](https://www.ably.com/dashboard) and select your app. | ||
| 2. Navigate to the "Configuration" > "Rules" section from the left-hand navigation bar. | ||
| 3. Choose "Add new rule". | ||
| 4. Enter a channel name or namespace pattern (e.g. `ai:*` for all channels starting with `ai:`). | ||
| 5. Select the "Message annotations, updates, and deletes" rule from the list. | ||
| 6. Click "Create channel rule". | ||
|
|
||
| The examples in this guide use the `ai:` namespace prefix, which assumes you have configured the rule for `ai:*`. | ||
|
|
||
| ## Citation data model <a id="data-model"/> | ||
|
|
||
| ### Annotation type <a id="annotation-type"/> | ||
|
|
||
| Use the `citations:multiple.v1` annotation type for citation features. It provides: | ||
|
|
||
| - **Automatic grouping**: Citations are grouped by the `name` field (for example, grouping by domain) | ||
| - **Count aggregation**: Ably counts how many citations come from each source | ||
| - **Efficient summaries**: Clients receive grouped summaries without processing individual events | ||
|
|
||
| ### Citation payload <a id="citation-payload"/> | ||
|
|
||
| Each citation is proposed to be carried in the Ably annotation `data` field and should include, for example: | ||
|
|
||
| <Code> | ||
| ```json | ||
| { | ||
| "url": "https://example.com/article", | ||
| "title": "Example Article Title", | ||
| "startOffset": 120, | ||
| "endOffset": 180, | ||
| "snippet": "Optional short excerpt from source" | ||
| } | ||
| ``` | ||
| </Code> | ||
|
|
||
| **Field descriptions**: | ||
|
|
||
| - `url`: The source URL (required) | ||
| - `title`: Human-readable source title (required) | ||
| - `startOffset`: Character position in the LLM generated response where this citation begins to apply, enabling clients to associate citations with specific portions of the response text (optional) | ||
| - `endOffset`: Character position where the citation’s applicability ends, used together with `startOffset` to define a citation range (optional) | ||
| - `snippet`: Short excerpt from the source content, intended for preview, tooltip, or summary displays without requiring a full page fetch (optional) | ||
|
|
||
| Character offsets allow UIs to attach inline citation markers to specific portions of the response text. | ||
|
|
||
| ## Publishing citations from agents <a id="publishing"/> | ||
|
|
||
| Agents publish citations as annotation messages that reference the `serial` of the response message they relate to. This allows clients to associate citations with the correct response message. | ||
|
|
||
| Citations can be published once the response has been sent, or progressively during streaming if citation data becomes available earlier. For incremental streaming using message appends, see [message-per-response](/docs/ai-transport/message-per-response). | ||
|
|
||
| ### Publishing a single citation <a id="single-citation-example"/> | ||
|
|
||
| <Code> | ||
| ```javascript | ||
| const channel = ably.channels.get('ai:{{RANDOM_CHANNEL_NAME}}'); | ||
|
|
||
| // Publish the AI response | ||
| const responseText = "The James Webb Space Telescope launched in December 2021."; | ||
| const { serials: [messageSerial] } = await channel.publish('ai-response', { | ||
| data: responseText | ||
| }); | ||
|
|
||
| // Publish a citation annotation | ||
| await channel.annotations.publish(messageSerial, { | ||
| type: 'citations:multiple.v1', | ||
| name: 'science.nasa.gov', | ||
| data: { | ||
| url: 'https://science.nasa.gov/mission/webb/', | ||
| title: 'Webb Mission Overview - NASA Science', | ||
| startOffset: 0, | ||
| endOffset: 56, | ||
| snippet: 'The James Webb Space Telescope launched on December 25, 2021 from Europe\'s Spaceport in French Guiana.' | ||
| } | ||
| }); | ||
| ``` | ||
| </Code> | ||
|
|
||
| ### Publishing multiple citations <a id="multiple-citations-example"/> | ||
|
|
||
| <Code> | ||
| ```javascript | ||
| const channel = ably.channels.get('ai:{{RANDOM_CHANNEL_NAME}}'); | ||
|
|
||
| // Publish multiple citations | ||
| async function publishCitations(messageSerial, sources) { | ||
| for (const source of sources) { | ||
| await channel.annotations.publish(messageSerial, { | ||
| type: 'citations:multiple.v1', | ||
| name: new URL(source.url).hostname, | ||
| data: { | ||
| url: source.url, | ||
| title: source.title, | ||
| startOffset: source.startOffset, | ||
| endOffset: source.endOffset, | ||
| snippet: source.snippet | ||
| } | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| // Publish response with citations | ||
| async function publishResponseWithCitations() { | ||
| // Publish the AI response | ||
| const responseText = "The James Webb Space Telescope launched in December 2021 and captured its first images in July 2022."; | ||
| const { serials: [messageSerial] } = await channel.publish('ai-response', { | ||
| data: responseText | ||
| }); | ||
|
|
||
| // Define citation sources | ||
| const sources = [ | ||
| { | ||
| url: 'https://science.nasa.gov/mission/webb/', | ||
| title: 'Webb Mission Overview - NASA Science', | ||
| startOffset: 0, | ||
| endOffset: 56, | ||
| snippet: 'The James Webb Space Telescope launched on December 25, 2021 from Europe\'s Spaceport in French Guiana.' | ||
| }, | ||
| { | ||
| url: 'https://en.wikipedia.org/wiki/James_Webb_Space_Telescope', | ||
| title: 'James Webb Space Telescope - Wikipedia', | ||
| startOffset: 61, | ||
| endOffset: 107, | ||
| snippet: 'The telescope captured its first images in July 2022, revealing unprecedented detail...' | ||
| } | ||
| ]; | ||
|
|
||
| // Publish citations | ||
| await publishCitations(messageSerial, sources); | ||
|
|
||
| return messageSerial; | ||
| } | ||
| ``` | ||
| </Code> | ||
|
|
||
| ## Subscribing citations for clients <a id="subscribing"/> | ||
|
|
||
| Clients subscribe citations from Ably in two ways: | ||
|
|
||
| 1. **Summary view** (default): Aggregate counts from Ably `message.summary` events | ||
| 2. **Raw view** (on demand): Individual citation details from Ably annotation events | ||
|
|
||
| ### Summary view <a id="summary-view"/> | ||
|
|
||
| Subscribe to Ably channels normally to receive automatic annotation summaries: | ||
|
|
||
| <Code> | ||
| ```javascript | ||
| const channel = ably.channels.get('ai:{{RANDOM_CHANNEL_NAME}}'); | ||
|
|
||
| // Track responses | ||
| const responses = new Map(); | ||
|
|
||
| // Subscribe to receive messages and summaries | ||
| await channel.subscribe((message) => { | ||
| switch (message.action) { | ||
| case 'message.create': | ||
| // New response started | ||
| responses.set(message.serial, message.data); | ||
| break; | ||
|
|
||
| case 'message.summary': | ||
| // Citation summary | ||
| const citations = message.annotations?.summary?.['citations:multiple.v1']; | ||
| if (citations) { | ||
| console.log('Citation summary:', citations); | ||
| } | ||
| break; | ||
| } | ||
| }); | ||
| ``` | ||
| </Code> | ||
|
|
||
| **Citation summary structure:** | ||
|
|
||
| The summary is included in an `annotations.summary` field within the message and is an object whose keys are the annotation types and whose values describe the annotation summary for that type. | ||
|
|
||
| <Code> | ||
| ```json | ||
| { | ||
| "citations:multiple.v1": { | ||
| "science.nasa.gov": { | ||
| "total": 1, | ||
| "clientIds": { | ||
| "test-publisher": 1 | ||
| }, | ||
| "totalUnidentified": 0, | ||
| "totalClientIds": 1, | ||
| "clipped": false | ||
| }, | ||
| "en.wikipedia.org": { | ||
| "total": 1, | ||
| "clientIds": { | ||
| "test-publisher": 1 | ||
| }, | ||
| "totalUnidentified": 0, | ||
| "totalClientIds": 1, | ||
| "clipped": false | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| </Code> | ||
|
|
||
| **Key fields:** | ||
| - `total`: Total count of annotations for this group | ||
| - `clientIds`: Breakdown showing which clients published annotations | ||
| - `clipped`: Whether the summary was truncated due to size limits | ||
|
|
||
| Ably summary view provides: | ||
|
|
||
| - **Total citation count**: Sum of all citation counts across groups | ||
| - **Group breakdown**: Count of citations per group (e.g., per domain) | ||
| - **Efficient updates**: Ably summaries update automatically as citations are added | ||
|
|
||
| ### Raw view <a id="raw-view"/> | ||
|
|
||
| To access individual citation details from Ably, subscribe to annotation events: | ||
|
|
||
| <Code> | ||
| ```javascript | ||
| // Enable ANNOTATION_SUBSCRIBE mode | ||
| const channel = ably.channels.get('ai:{{RANDOM_CHANNEL_NAME}}', { | ||
| modes: ['ANNOTATION_SUBSCRIBE'] | ||
| }); | ||
|
|
||
| // Subscribe to annotation events | ||
| await channel.annotations.subscribe((annotation) => { | ||
| if (annotation.action === 'annotation.create' && | ||
| annotation.type === 'citations:multiple.v1') { | ||
| const citation = annotation.data; | ||
| if (citation) { | ||
| console.log('Citation data:', citation); | ||
| } | ||
| } | ||
| }); | ||
| ``` | ||
| </Code> | ||
|
|
||
| **Example raw annotation structure:** | ||
|
|
||
| When you subscribe to raw annotations, each annotation event has the following structure: | ||
|
|
||
| <Code> | ||
| ```json | ||
| { | ||
| "action": "annotation.create", | ||
| "clientId": "test-publisher", | ||
| "type": "citations:multiple.v1", | ||
| "serial": "01767705527528-000@108rFDTSQBxhtu98297114:000", | ||
| "messageSerial": "01767638186693-000@108SP4XcgBxfMO07491612:000", | ||
| "connectionId": "Y8CqupU0-E", | ||
| "name": "en.wikipedia.org", | ||
| "count": 1, | ||
| "encoding": null, | ||
| "data": { | ||
| "url": "https://en.wikipedia.org/wiki/James_Webb_Space_Telescope", | ||
| "title": "James Webb Space Telescope - Wikipedia", | ||
| "startOffset": 61, | ||
| "endOffset": 107, | ||
| "snippet": "The telescope captured its first images in July 2022, revealing unprecedented detail of the early universe." | ||
| }, | ||
| "timestamp": 1767705527528, | ||
| "id": "Y8CqupU0-E:1:0" | ||
| } | ||
| ``` | ||
| </Code> | ||
|
|
||
| **Key fields in raw annotations:** | ||
|
|
||
| - `action`: Always `"annotation.create"` for new annotations | ||
| - `type`: The annotation `type` (`citations:multiple.v1`) | ||
| - `messageSerial`: The `serial` of the message this citation is attached to | ||
| - `name`: The grouping key (e.g., domain name) | ||
| - `data`: Your citation payload with URL, title, offsets, snippet | ||
| - `clientId`: The client that published the annotation | ||
|
|
||
| Ably raw citations provide: | ||
|
|
||
| - **Full citation metadata**: All fields from the citation data payload | ||
| - **Character offsets**: For placing inline citation markers | ||
| - **Group name**: The `name` field used for grouping (e.g., domain) | ||
| - **Individual events**: Each citation arrives as a separate Ably event | ||
|
|
||
| <Aside data-type="note"> | ||
| Use summary view by default for efficient rendering and subscribe to raw annotations only when users need detailed citation information. This minimizes overhead while providing full transparency when required. | ||
|
|
||
| Annotation details can also be retrieved via the REST API without maintaining a realtime subscription. The [Annotations](/docs/api/rest-api#annotations-list) endpoint allows clients to retrieve all annotations published for a specific message, identified by its `serial`, starting from the earliest available annotation. | ||
| </Aside> | ||
|
|
||
| ## Related topics <a id="related"/> | ||
|
|
||
| - [Message annotations](/docs/messages/annotations) - Core Ably feature for attaching metadata to messages | ||
| - [Message per response](/docs/ai-transport/message-per-response) - Streaming pattern using Ably message appends | ||
| - [Token streaming](/docs/ai-transport/token-streaming) - Alternative approach with granular Ably history | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should there be a mention of obtaining annotation details via the REST API instead of realtime subscription?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, thanks for spotting this. I've added the details with a link to the REST API