fix: the drafter writes all three views, and says its errors out loud

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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TqXevQJhxFrM7jJg82cgZN
This commit is contained in:
Daniel 2026-09-12 19:31:28 +02:00
parent aafea65a52
commit d834ac830a

View file

@ -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: