pdf-quiz-generator/backend/alembic/versions/s8c9d0e1f2a3_pg_trgm.py
Daniel 7cd2fa5cf4
Some checks failed
Tests / backend (push) Failing after 4s
Tests / frontend (push) Successful in 26s
Tests / e2e (push) Failing after 28s
feat: AI Mode gives the same answer twice, and a typo no longer empties the library
Measured first, by the ped-ai session, fifteen runs of five prompts with
the gateway cache bypassed. Retrieval was already deterministic:
identical shortlist and identical scores every time, and the citation
checker stripped none of the 45 markers written — invented citations are
not the problem here. Generation was the whole variance. At temperature
0.3 the same sources and the same prompt gave answers differing by
15-70% of their text; one differential swung between a 35-word uncited
paraphrase and a 180-word cited list.

So temperature 0 and a seed. Temperature 0 alone was not enough — three
runs still differed — and temperature 0 with a fixed seed came back
byte-identical. The seed is derived from the question, normalised for
case and spacing, so two people asking the same thing get the same
answer and a different question is not pinned to the same sample.

An empty reply is asked once more before it becomes a 502. One in
fifteen came back empty from a healthy model in 4.9 seconds — not a
refusal, not an error, just nothing.

A short query that finds almost nothing is retried against the nearest
article title. "kawasaki criteria" finds fourteen sources; "kawasaki
critera" found none — the lexical ranker cannot match a token that is in
no index, and the embedding of a misspelling is not near the embedding
of the word. Trigrams do not care: that typo scores 0.36 against
"Kawasaki disease" with the next article at 0.11, and the gap is what
makes it safe to act on. pg_trgm is created at startup beside vector,
with a migration for the record.

And an answer drawn from the library must cite it. Not a hallucination
guard — nothing was stripped in fifteen runs — but one answer used the
sources and cited none of them, which leaves the learner an assertion
and nowhere to check it.

Also, article drafts are weighted towards mechanism, in the wording the
ped-ai rewriter is using, so the two lanes read alike: why the body does
what it does, with features and management explained through it rather
than listed. Figure lines and cross-references survive a refine.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TqXevQJhxFrM7jJg82cgZN
2026-09-13 16:24:48 +02:00

27 lines
844 B
Python

"""pg_trgm, for the spelling fallback in AI Mode.
"kawasaki criteria" finds fourteen sources; "kawasaki critera" finds none.
Neither ranker can catch the second — lexical because the token is in no
index, semantic because the embedding of a misspelling is not near the
embedding of the word. Trigram similarity puts "Kawasaki disease" at 0.36 with
the next article at 0.11, and the gap is what makes it safe to act on.
Revision ID: s8c9d0e1f2a3
Revises: r7b8c9d0e1f2
"""
from alembic import op
revision = "s8c9d0e1f2a3"
down_revision = "r7b8c9d0e1f2"
branch_labels = None
depends_on = None
def upgrade():
op.execute("CREATE EXTENSION IF NOT EXISTS pg_trgm")
def downgrade():
# Left in place. Dropping an extension another query may since have come
# to rely on is a worse outcome than an unused one sitting there.
pass