Citationware RAG¶
This page systematizes Eagle-RAG's "citation-first RAG" concept: every generated claim must be traceable to retrieved evidence, and citations are first-class citizens rather than optional decoration. The material basis of citations comes from the evidence set consolidated by Evidence aggregation; observability and quality validation can plug into the Langfuse score integration of Agent observability.
Current maturity
Eagle-RAG has landed answer-level [n] inline citation + hovercard, which is "prompt-guided soft citation." Claim-level span grounding, faithfulness validation, etc. are in §5 Roadmap.
1. Definition¶
Citationware RAG (citation-first RAG) denotes a class of RAG systems where every generated statement (claim) must be backed by retrieved evidence and carry verifiable provenance — citations are part of the answer structure, not an after-thought, optional source list.
Maturity splits into two tiers:
| Tier | Form | Eagle-RAG status |
|---|---|---|
| L1 answer-level | The answer text uses [n] indexes to reference the sources list; the frontend hovercard displays them |
implemented |
| L2 claim-level | Each claim → source span-level grounding mapping + faithfulness validation + abstain | not implemented (roadmap) |
2. Existing citation chain (implemented)¶
2.1 Backend: citation-coordinate schema¶
In eagle_rag/api/schemas/query.py, TextSource and ImageSource carry the full citation coordinates:
# eagle_rag/api/schemas/query.py
class TextSource(BaseModel): # L71
# citation coordinates: path / document_id / score / content / page_nums / keywords / source_chunk_id
...
class ImageSource(BaseModel): # L98
# four anchors: image_id / chunk_type / parent_section / content_summary / source_chunk_id
...
class QueryResponse(BaseModel): # L148
answer: str
sources: QuerySources # L152 = {text: list[TextSource], image: list[ImageSource]}
route: RouteInfo
steps: list[QueryStep]
The four anchors of ImageSource (image_id / chunk_type / parent_section / content_summary / source_chunk_id) let visual evidence be section-attributed and cross-collection-linked (see the anchor fields in Multimodal fusion).
2.2 Backend: source construction¶
eagle_rag/generation/multimodal_engine.py maps the reranked NodeWithScore to sources:
| Function | Role |
|---|---|
text_sources_from_nodes (L857) |
text nodes → TextSource list, with registry-backfilled file_name |
_text_source (L780) |
single node → source dict (path / level / score / content) |
_enrich_text_sources (L821) |
enriches sources with the file-name registry |
_image_source (L863) |
visual node → ImageSource (with four anchors) |
2.3 Prompt: [n] index guidance¶
The VLM prompt requires the model to reference reference text with [n] indexes and to describe images without fabricating URLs (see Generation §1.4 grounding and citation). This is "soft citation" — prompt-guided, not structurally guaranteed.
2.4 Frontend: inline citation hovercard¶
frontend/components/qa/sources-utils.ts implements inline-citation alignment and display:
| Function | Line | Role |
|---|---|---|
flattenSources |
L11 | flattens {text, image} into a 1-based indexed list, aligning with [n] in the answer |
findImageSourceIndex |
L27 | looks up the 1-based index of a visual source by image_id |
sourceCitationExcerpt |
L122 | the evidence excerpt body of the hovercard |
sourceCitationTitle |
L135 | the title line of the hovercard |
resolveAnswerImageSrc |
L199 | remaps a VLM-fabricated external image URL to the real /images/{image_id} |
3. Citation pipeline¶
flowchart LR
R["retrieve"] --> RR["rerank"]
RR --> TN["top_n evidence"]
TN --> SRC["text_sources_from_nodes / _image_source<br/>build sources list"]
TN --> PROMPT["VLM prompt<br/>guides [n] indexes"]
SRC --> RESP["QueryResponse.sources"]
PROMPT --> RESP2["QueryResponse.answer (with [n])"]
RESP & RESP2 --> FE["frontend flattenSources<br/>1-based index aligns with [n]"]
FE --> HC["hovercard<br/>sourceCitationExcerpt/Title"]
FE --> IMG["resolveAnswerImageSrc<br/>image URL remap"]
Key coupling: the order of the sources list must match the order of [n] in the answer — the frontend flattenSources produces 1-based indexes, [1] maps to sources.text[0]. The evidence ordering (from Evidence aggregation) directly determines the citation numbering.
4. Current maturity positioning¶
What is landed is L1 answer-level soft citation:
- the sources list carries full citation coordinates (path / document_id / page_nums / source_chunk_id);
- the VLM prompt guides
[n]indexes; - frontend hovercard + image URL remap;
-
[n]is prompt-guided, not structurally guaranteed — the model may omit, mis-label, or fabricate indexes; - no claim → source span-level mapping, no post-generation validation.
5. Roadmap (not implemented)¶
The following are unimplemented design gaps, not current capabilities.
- claim → source span-level grounding mapping: today
[n]is an answer-level citation. The ideal is a structuredcitations: list[ClaimCitation], each carryingclaim_text/source_index/source_span(a character range within the source). Potential extension: add acitationsfield toQueryResponse; have the VLM emit structured citation JSON or post-process[n]→ span. - faithfulness / groundedness validation: after generation, validate that each claim is truly backed by a cited source. Potential extension: add a
GROUNDING_CHECKhook afterRERANK_MERGEDor after generation; plug into Langfuse score (faithfulness / citation coverage) for evaluation. - abstain / uncitable detection: when evidence is insufficient, the system should explicitly abstain ("evidence insufficient") instead of forcing a generation. Potential extension: combine with the aggregate-confidence threshold of Evidence aggregation.
- citation coverage check: count the proportion of uncited claims in the answer and expose it as a quality metric to Agent observability.
- conflicting-evidence annotation: when multiple sources contradict on the same claim, annotate the conflict in the citation (connects to the conflict-resolution roadmap of Evidence aggregation).
6. Connection to observability¶
Citation quality can be plugged into Agent observability as an observation metric:
- Langfuse score (not implemented): faithfulness / citation coverage / abstain rate, reported as scores on the generation observation;
- structlog AI JSONL (implemented): the
generateevent already records prompt/completion (truncated); it can be extended to recordcitation_count/uncited_claim_count; - Prometheus (implemented): a new metric like
eagle_citation_coveragecould be added (roadmap).