Summary Index#
Summary Index is Dify's two-tier vector storage mechanism for RAG retrieval. Each DocumentSegment (chunk) can have an LLM-generated summary that is independently embedded and stored in the vector database alongside the chunk's own embedding. During retrieval, a user query may match the summary vector; the system then resolves the original_chunk_id link and returns the original chunk content β the summary acts as a higher-signal proxy document without replacing the source text.
The feature activates only when all three conditions hold :
document.need_summaryisTruedataset.indexing_technique == "high_quality"(economy mode excluded)dataset.summary_index_setting.enable == True
qa_model documents are always skipped .
Data Model & Configuration#
DocumentSegmentSummary (PostgreSQL) is the per-chunk summary record . Key fields:
| Field | Purpose |
|---|---|
chunk_id | FK β DocumentSegment.id |
summary_index_node_id | ID of the corresponding vector in the VDB |
summary_content | LLM-generated summary text |
status | NOT_STARTED β GENERATING β COMPLETED / ERROR |
enabled | Mirrors parent segment's enabled state |
In the vector store, summary documents carry the metadata is_summary=True and original_chunk_id (the segment UUID) . These attributes must be declared in the Vector class init; without them, backends like Weaviate silently omit the fields and summary hits degrade to ordinary results.
SummaryIndexSettingDict is stored as a JSON column on Dataset and typed in index_processor_base.py. Fields: enable, model_name, model_provider_name, summary_prompt (falls back to DEFAULT_GENERATOR_SUMMARY_PROMPT with {language} interpolation).
Generation Pipeline#
document_indexing_task
ββ (on completion) generate_summary_index_task.delay() [dataset_summary queue]
ββ SummaryIndexService.generate_summaries_for_document()
ββ batch_create_summary_records() β NOT_STARTED stubs
ββ per segment: generate_and_vectorize_summary()
ββ ParagraphIndexProcessor.generate_summary() [LLM call]
ββ vectorize_summary() [embed + write to VDB]
After normal document indexing completes, document_indexing_task enqueues generate_summary_index_task on the dataset_summary Celery queue (separate from the main dataset queue) .
generate_summaries_for_document() batch-creates NOT_STARTED stub records upfront for status tracking , then iterates over segments β errors on individual segments are caught and logged without aborting the rest .
vectorize_summary() embeds the summary text, writes it to the vector DB with duplicate_check=False (upsert semantics), retries up to 3Γ with exponential backoff on connection errors , and updates DocumentSegmentSummary.status to COMPLETED on success .
Vision support: ParagraphIndexProcessor.generate_summary() extracts images from SegmentAttachmentBinding or inline markdown ![]() links when the configured model supports vision .
Manual triggers:
POST /datasets/<dataset_id>/documents/generate-summaryβ enqueues the Celery task per document.SummaryIndexService.update_summary_for_segment()β used when a user edits a segment's summary manually; runs synchronously so the updated summary is immediately searchable.
Retrieval Mechanics#
Vector search returns a mixed result set that may include regular chunk documents and summary documents (is_summary=True). RetrievalService.format_retrieval_documents() processes them in two passes:
-
First pass β for each document with
is_summary=True, extractoriginal_chunk_idand save the relevance score tosummary_score_map(taking the max if the same segment is hit multiple times) . -
Second pass β query
DocumentSegmentbyoriginal_chunk_id, batch-fetch the correspondingDocumentSegmentSummaryrecords , and attach the summary text to the result.
The summary's score propagates to the original chunk: if a segment was only reached via summary, summary_score_map supplies the score; if it was reached both via summary and a direct vector match, the higher score wins .
The final RetrievalSegments object carries segment (original chunk), score, and summary (summary content string, if any). This means callers receive the original chunk text β not the summary text β for answer synthesis, while the summary provides both the retrieval signal and optional display context.
Cleanup Gap: Orphaned Summary Data on Re-upload#
Bug β tracked in #36937
When an existing document is re-uploaded, Dify routes it through DuplicateDocumentIndexingTask. This task:
- β
Deletes old
DocumentSegmentrecords and their vectors (viaindex_processor.clean()) - β Does not clean up
DocumentSegmentSummaryrecords or their vectors - β Does not enqueue
generate_summary_index_taskafter re-indexing
As a result, the old summary rows accumulate in PostgreSQL and the old summary vectors remain in the vector store. Using the "Regenerate Summary" button adds new summary records on top of the stale ones. An identical gap exists in document_indexing_update_task (issue #35950) .
Root cause: The summary index was added to the initial indexing flow (document_indexing_task) but the duplicate-upload and document-update paths were never updated accordingly.
Correct fix (two additions to DuplicateDocumentIndexingTask):
- Before re-indexing: Call
SummaryIndexService.delete_summaries_for_segments()to delete both PG rows and vector embeddings for the old segments. - After re-indexing succeeds: Enqueue
generate_summary_index_task.delay()with the same conditional guard used indocument_indexing_task(high-quality indexing +summary_index_setting.enable+need_summary == True).
Key Files#
| File | Role |
|---|---|
api/services/summary_index_service.py | Core orchestration: generation, vectorization, enable/disable/delete lifecycle |
api/tasks/generate_summary_index_task.py | Celery task entry point (dataset_summary queue) |
api/tasks/document_indexing_task.py | Post-indexing trigger that enqueues the summary task |
api/tasks/duplicate_document_indexing_task.py | Re-upload path β missing summary cleanup and re-trigger |
api/core/rag/datasource/retrieval_service.py | Two-tier retrieval: is_summary detection + original_chunk_id resolution |
api/core/rag/embedding/retrieval.py | RetrievalSegments model (carries summary field) |
api/core/rag/index_processor/processor/paragraph_index_processor.py | LLM call for summary generation, vision extraction |
api/core/rag/index_processor/index_processor_base.py | SummaryIndexSettingDict type definition |