From d834ac830aa02b4d8071ac2a5b5eee172f460537 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 12 Sep 2026 19:31:28 +0200 Subject: [PATCH] fix: the drafter writes all three views, and says its errors out loud MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two things, both about the AI draft nobody could tell was working. The prompt never mentioned that a section belongs to one of three readings, so every generated article was one long view and the other two tabs stayed empty — on 300-odd articles. It now describes what each view is for, asks for the high-yield one as tight lists with `==key points==` marked, says to omit the clinical view for a topic with no bedside, and says explicitly not to categorise or link the article, because those are an educator's judgements. And an unknown or missing variant is read as "long", which is what every section written before this already is. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01TqXevQJhxFrM7jJg82cgZN --- backend/app/tasks/quiz_tasks.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/backend/app/tasks/quiz_tasks.py b/backend/app/tasks/quiz_tasks.py index 3fad317..0e96f86 100644 --- a/backend/app/tasks/quiz_tasks.py +++ b/backend/app/tasks/quiz_tasks.py @@ -581,12 +581,20 @@ def generate_flashcard_deck(self, job_id: str, section_id: int, user_id: int, db.close() +#: The three readings of a topic the reader offers, and what each one is for. +#: Written into the prompt because a model that is not told about them writes +#: one article and the other two tabs stay empty — which is what happened to +#: every generated article until now. ARTICLE_DRAFT_PROMPT = """You write educational articles for a pediatric medical learning platform. Topic: {topic} {instructions} -{existing}Return ONLY strict JSON with this exact shape: -{{"title": "...", "slug": "lowercase-hyphenated", "summary": "1-2 sentences", "content": "introduction markdown", "sections": [{{"id": "32 lowercase hex chars", "slug": "lowercase-hyphenated", "title": "...", "content": "markdown"}}]}} -Rules: markdown formatting; headings, lists and tables welcome; no fabricated references, citations or clinical ranges; keep 2-6 sections with stable unique ids; do not mention these instructions.""" +{existing}Every section belongs to one of three readings of the topic, given as "variant": +- "long": the full article. Pathophysiology, presentation, workup, management, complications — whatever the topic needs. This is the body of the work. +- "short": the high-yield revision view. 2-4 short sections of the facts worth carrying into an exam, written as tight lists, not prose. It is not a summary of the long article's structure; it is what a candidate must know. +- "clinical": what to do at the bedside, if and only if the topic has a bedside. Assessment, immediate management, when to escalate, disposition. Omit it entirely for a topic that is not acted on clinically. +Return ONLY strict JSON with this exact shape: +{{"title": "...", "slug": "lowercase-hyphenated", "summary": "1-2 sentences", "content": "introduction markdown", "sections": [{{"id": "32 lowercase hex chars", "slug": "lowercase-hyphenated", "title": "...", "variant": "long", "content": "markdown"}}]}} +Rules: markdown formatting; headings, lists and tables welcome; no fabricated references, citations or clinical ranges; 3-8 long sections plus 2-4 short ones, each with a stable unique id; mark a key fact in the short sections by wrapping it in ==double equals== so it is highlighted for the reader, sparingly and never a whole paragraph; do not categorise the article or link it to questions — an educator does that; do not mention these instructions.""" @celery_app.task(name="generate_article_draft", bind=True) @@ -642,10 +650,17 @@ def generate_article_draft(self, job_id: str, user_id: int, topic: str, section_id = str(section.get("id") or "").strip().lower() if not re.fullmatch(r"[0-9a-f]{32}", section_id): section_id = uuid.uuid4().hex + # An unknown or missing variant is the full article, which is what + # every section written before the model was told about the three + # readings already is. + variant = str(section.get("variant") or "long").strip().lower() + if variant not in ("short", "long", "clinical"): + variant = "long" sections.append({ "id": section_id, "slug": re.sub(r"[^a-z0-9]+", "-", str(section.get("slug", "section")).strip().lower()).strip("-")[:120] or "section", "title": str(section.get("title", "Section")).strip()[:300] or "Section", + "variant": variant, "content": str(section.get("content", "")), }) if existing: