Files
2026-04-07 22:39:28 +02:00

6.0 KiB
Raw Permalink Blame History

Implementation Plan: RAG Retrieval Quality + Topic Summary Persistence

Branch: 004-rag-retrieval-quality | Date: 2026-04-07 | Spec: spec.md
Input: Feature specification from /specs/004-rag-retrieval-quality/spec.md + user request: "Save summaries by topic; list previously saved summaries; button to generate new."

Summary

Improve RAG retrieval quality by (1) expanding user queries via LLM rewrite to bridge vocabulary gaps and (2) validating generated citations against the retrieved context to eliminate hallucinated references. Additionally, persist generated topic summaries to the database so students can revisit past summaries and generate new ones on demand.

Technical Context

Language/Version: Java 21 (backend), TypeScript / Node 20 (frontend)
Primary Dependencies: Spring Boot 4.0.5, Spring AI 2.0.0-M4, OpenAI API (chat + embeddings), Vue 3.4, Pinia 2.1, Axios 1.7
Storage: PostgreSQL (JPA + Flyway), pgvector (VectorStore)
Testing: JUnit 5, Spring Boot Test, Vitest
Target Platform: Linux server
Project Type: Web application (backend API + Vue frontend)
Performance Goals: RAG query latency remains acceptable (one additional LLM call for query expansion, ~12s overhead)
Constraints: KISS — no new architectural layers; no new external services; single deployable backend + frontend

Constitution Check

Principle Status Notes
I — KISS Pass Query expansion = one LLM call. Citation validation = string scan. Summary persistence = one new table + minimal service change. No new layers.
II — Easy to Change Pass QueryExpansionService, CitationValidatorService, TopicSummaryRepository are small, focused interfaces. Swappable without touching callers.
III — Web-First Pass All new functionality exposed via REST at /api/v1/.... Frontend-only UI changes.
IV — Documentation as Architecture ⚠️ Action needed README must be updated to reflect new topic_summary table and the summary history flow. Update in the same PR.

Project Structure

Documentation (this feature)

specs/004-rag-retrieval-quality/
├── plan.md              # This file
├── research.md          # Phase 0 — decisions on query expansion, citation grounding, summary persistence
├── data-model.md        # Phase 1 — topic_summary table, ExpandedQuery, LabelledContext DTOs
├── contracts/
│   ├── chat-api.md      # Existing chat API (updated for labelled sources)
│   └── topics-api.md    # New/updated topics API (summary list + detail endpoints)
└── tasks.md             # Phase 2 output (/speckit.tasks command)

Source Code

backend/
├── src/main/java/com/aiteacher/
│   ├── retrieval/
│   │   ├── QueryExpansionService.java        (NEW) — LLM rewrite of user query
│   │   ├── ExpandedQuery.java                (NEW) — value object {original, rewritten}
│   │   ├── LabelledContext.java              (NEW) — value object {sectionLabels, figureLabels, promptText}
│   │   └── CitationValidatorService.java     (NEW) — strip unknown [Sx]/[Fx] refs from answer text
│   └── topic/
│       ├── TopicSummaryEntity.java           (NEW) — JPA entity for topic_summary table
│       ├── TopicSummaryRepository.java       (NEW) — findByTopicIdOrderBySummaryNumberAsc
│       ├── SavedSummaryItem.java             (NEW) — DTO for list view (id, summaryNumber, generatedAt)
│       ├── TopicSummaryResponse.java         (MODIFY) — add id: UUID, summaryNumber: int fields
│       ├── TopicSummaryService.java          (MODIFY) — persist after generation; add list/get methods
│       └── TopicController.java             (MODIFY) — add GET /summaries and GET /summaries/{id}
├── src/main/resources/db/migration/
│   └── V6__topic_summary.sql               (NEW) — create topic_summary table
└── chat/
    └── ChatService.java                    (MODIFY) — use QueryExpansionService + CitationValidatorService

frontend/
└── src/
    ├── stores/
    │   └── topicStore.ts                   (MODIFY) — add fetchSummaries(), fetchSummaryDetail(), summaryList state
    └── views/
        └── TopicsView.vue                  (MODIFY) — show summary list when topic selected, "Generate New" button

Structure Decision: Option 2 (web application). Existing backend/ + frontend/ directories. No new top-level directories.

Complexity Tracking

No constitution violations introduced.


Phase 0: Research (complete)

See research.md. All decisions resolved:

  • Query expansion: single LLM rewrite call
  • Citation grounding: ref-label tagging + post-generation string scan
  • Summary persistence: new topic_summary table, sequential numbering at insert time

Phase 1: Design (complete)

Data model

See data-model.md. New topic_summary table; two new in-memory value objects; TopicSummaryResponse extended with id + summaryNumber.

API contracts

See contracts/topics-api.md:

  • POST /api/v1/topics/{id}/summary — now persists and returns id + summaryNumber
  • GET /api/v1/topics/{id}/summaries — list metadata (no full text)
  • GET /api/v1/topics/{id}/summaries/{summaryId} — full detail

Frontend behaviour

When a topic card is clicked (before generating):

  1. Call GET /api/v1/topics/{id}/summaries.
  2. If summaries exist → show list panel with chips: "Summary #1 · Apr 6", "Summary #2 · Apr 7", … + a "Generate New" button.
  3. If no summaries → show "Generate Summary" button (current behaviour).
  4. Clicking a chip → call GET /api/v1/topics/{id}/summaries/{summaryId} → display the saved summary.
  5. Clicking "Generate New" → call POST /api/v1/topics/{id}/summary → display new summary, refresh list.