feat(rag): lite-model document summaries — non-markdown docs summarized at import, summary chunk retrieves and resolves to the full source doc

This commit is contained in:
2026-08-25 17:48:37 -04:00
parent 9809482a4b
commit 572a4190a6
32 changed files with 1806 additions and 26 deletions
+24 -4
View File
@@ -28,6 +28,14 @@ Steering (phase 15): the owner's stored tuning notes are loaded per turn
(oldest first) and injected into the system prompt as a ``<tuning>``
section — both the HIGH and the LOW prompt carry it. The per-turn log
line records ``tuning=N`` (the number of injected notes).
Summaries (phase 30): a lite-model summary chunk's parent *is* the
source document, so a summary hit resolves to the full source document
through the unchanged chunk→document mapping (A7 revised) — context
assembly is untouched. ``TurnPlan.summary_hits`` counts the hit chunks
with ``is_summary`` whose parent document landed in the selected
top-N context, and the per-turn log line records ``summary_hits=N``
after ``fts_hits`` (PLAN §9 line extension).
"""
from __future__ import annotations
@@ -96,6 +104,9 @@ class TurnPlan:
docs: list[Document] # cited sources (weak hits when deflected)
suggestions: list[str] # "Maybe try" chips (deflected turns only)
tuning_count: int = 0 # steering notes injected into the system prompt
#: Hit chunks with ``is_summary`` whose parent document made it into
#: *docs* (phase 30; per-turn log line ``summary_hits=N``).
summary_hits: int = 0
def plan_turn(
@@ -121,12 +132,18 @@ def plan_turn(
*notes* are the owner's steering notes (phase 15, oldest first):
when non-empty, both the HIGH and the LOW prompt carry the
``<tuning>`` section; with no notes the prompts are unchanged.
``summary_hits`` (phase 30) counts the hit chunks with
``is_summary`` whose parent document is among the selected
top-N documents — both the HIGH and the LOW branch record it.
"""
steering = list(notes or [])
best_cosine = max((c.cosine for c in chunks), default=0.0)
fts_hits = sum(1 for c in chunks if c.fts_hit)
docs = select_documents(chunks, n=settings.top_n_docs)
selected_ids = {d.id for d in docs}
summary_hits = sum(1 for c in chunks if c.is_summary and c.document.id in selected_ids)
if best_cosine >= settings.relevance_threshold or fts_hits > 0:
docs = select_documents(chunks, n=settings.top_n_docs)
return TurnPlan(
best_cosine,
fts_hits,
@@ -135,6 +152,7 @@ def plan_turn(
docs,
[],
len(steering),
summary_hits,
)
titles = weak_hit_titles(chunks)
return TurnPlan(
@@ -142,9 +160,10 @@ def plan_turn(
fts_hits,
True,
build_deflect_prompt(titles, notes=steering),
select_documents(chunks, n=settings.top_n_docs),
docs,
derive_suggestions(titles, settings.suggestions),
len(steering),
summary_hits,
)
@@ -264,12 +283,13 @@ async def chat(
logger.exception("chat: failed to write query_log question=%r", request.message)
logger.info(
"question=%r embed_ms=%d top_score=%.3f fts_hits=%d tuning=%d threshold=%.2f "
"deflected=%s sources=%r thinking_chars=%d total_ms=%d",
"question=%r embed_ms=%d top_score=%.3f fts_hits=%d summary_hits=%d tuning=%d "
"threshold=%.2f deflected=%s sources=%r thinking_chars=%d total_ms=%d",
request.message,
embed_ms,
plan.top_score,
plan.fts_hits,
plan.summary_hits,
plan.tuning_count,
settings.relevance_threshold,
plan.deflected,