Skip to content

Prompts ​

Aparture's LLM prompts live in two places. The ones meant for tuning sit in prompts/ as Markdown files and reload on every API call with no restart — this is where the main briefing prompt, the scoring rubrics, and the hallucination audit all live. A smaller set stays embedded in source code, because the content is either too short (repair templates) or too programmatic (the podcast focus-prompt builder).

The two categories ​

Hot-reloadable prompt files live in prompts/ as Markdown files. Each API route reads its template from disk per request, substitutes its template variables, and dispatches. Edits land on the next call with no rebuild or deploy — this is the primary tuning surface.

Embedded prompts are string literals inside lib/**/*.js and a few pages/api/*.js routes. They're short and rarely tuned — editing any of them means restarting npm run dev to see the change.

Template variables in the Markdown files use double-curly-brace syntax (the literal form in each file is { + {variable} + }). The sections below list variable names without the braces for readability.

Hot-reloadable prompts (prompts/*.md) ​

Ten files. Each renders its template variables at dispatch time from values the route passes in.

prompts/synthesis.md ​

Main briefing generation — executive summary, themes, paper cards. Fires after PDF analysis via /api/synthesize.

Variables: profile, papers

The route can append two sections after the rendered template: the reader's notes from Gate 3 (lib/synthesis/readerNotes.js), as editorial instructions for that run, and a retry hint when the audit sends the briefing back. Both go after the cached prefix, so they don't affect prompt caching.

prompts/analyze-pdf-quick.md ​

~300-word per-paper compression used for the inline expansion view. Fires in parallel during briefing prep via /api/analyze-pdf-quick, once per paper. The output is for the reader only: neither the synthesis prompt nor the audit receives it.

Variables: title, authors, arxivId, fullReport, abstract, scoringJustification

prompts/check-briefing.md ​

Hallucination audit — flags unsupported claims in a generated briefing. Fires after synthesis via /api/check-briefing.

Variables: briefing, papers

prompts/editor-questions.md ​

The editor's questions: up to two tap-to-answer questions after a briefing, asked only when the day's papers expose something the profile doesn't settle. Fires once per briefing, after the claims check, via /api/editor-questions on the briefing model, when the editorQuestions setting is on. lib/editorQuestions/questions.js keeps a question only if it ends in a question mark, offers two to four short answers, and cites an arXiv ID the model was shown; it reports what it dropped. Zero questions is a normal result.

Variables: profile, feedback, papers, nearMisses

prompts/suggest-profile.md ​

Profile refinement proposals from accumulated feedback: edits to the profile text and changes to the editorial notes (add, revise, retire). Fires when the user clicks Suggest improvements via /api/suggest-profile.

Variables: profile, feedback

prompts/notebooklm-discussion-guide.md ​

Podcast outline for Gemini Notebook (formerly NotebookLM): themes, pruning, and depth scaled to the length preset (Short, Default, Long). Fires when the user clicks Generate podcast via /api/generate-notebooklm.

Variables: themes, papers, duration, date

prompts/rubric-filter.md ​

Stage 2 quick-filter rubric — YES / MAYBE / NO verdict, one-sentence summary, one-sentence justification. Fires every filter batch via /api/quick-filter.

Variables: profile, papers

prompts/rubric-scoring.md ​

Stage 3 abstract-scoring rubric — 0.0–10.0 scale, alignment × quality split, scoring guidance. Fires every scoring batch via /api/score-abstracts.

Variables: profile, papers

prompts/rubric-rescoring.md ​

Stage 3.5 post-processing pass — comparative re-scoring across a batch for consistency. Fires every post-processing batch when the stage is enabled, via /api/rescore-abstracts.

Variables: profile, papers

prompts/rubric-pdf.md ​

Stage 4 full-PDF analysis — structured summary, key findings, methodology, limitations, updated score, and the key figure's label with a clause on what it shows. Fires for every paper that reaches Stage 4, via /api/analyze-pdf. The route crops the named figure from the PDF; the prompt only picks it.

Variables: profile, originalScore

Changes land immediately

Edits to any file in prompts/ take effect on the next API call that reads it. No rebuild, no restart — save the file, trigger the flow, look at the output.

Variable conventions ​

Variables are rendered as simple string substitution. A few are worth noting:

  • notes in suggest-profile.md lists the editorial notes by scope, each with the id a revise or retire names.
  • profile is the exact profile text stored in scoringCriteria in your config, followed by the editorial notes for the prompt's scope when there are any (lib/profile/editorialNotes.js). It's inserted verbatim, so the profile's formatting (headings, bullets, section breaks) carries through to the prompt.
  • papers in synthesis.md is a JSON-serialised list of the final-round papers: arXiv ID, title, abstract, score, the scoring justification, the full report, and an engagement object when you starred, dismissed, or commented on the paper. Quick summaries are not part of it.
  • papers in check-briefing.md is rendered text, one block per paper (lib/synthesis/checkCorpus.js), holding what the writer saw: title, relevance score, your engagement, abstract, scoring justification, and the full report, untruncated. Quick summaries are left out because the writer never sees them. briefing is a text rendering of the executive summary, themes, and paper cards.
  • feedback in suggest-profile.md is a pre-formatted block built by lib/profile/suggestPrompt.js that groups the pending events you ticked by type. It includes an ANSWERS TO THE EDITOR'S QUESTIONS section when you've answered any, and ends with a PREVIOUSLY REJECTED SUGGESTIONS section when you've turned changes down before (the 10 most recent). The rationales the model writes matter beyond the dialog: Aparture marks feedback as incorporated only when an accepted change's rationale cites it by arXiv ID or quotes it.
  • In editor-questions.md, papers lists the briefing's cards (ID, title, score, pitch) and nearMisses the highest-scored papers left out of it. feedback is your recent stars, dismissals, comments, and earlier answers, so the model doesn't repeat a question you've answered.
  • themes and papers in notebooklm-discussion-guide.md carry only briefing-level information (theme arguments, paper indices, scores) — the prompt deliberately doesn't include full reports because those get uploaded to the notebook as separate source files.

The cache-boundary marker ​

The four rubric-*.md files contain a single line that splits each file into the stable prefix (rubric scaffolding + your profile) and the variable tail (the batch of papers). lib/llm/loadRubricPrompt.js reads the file, substitutes the variables, and returns both halves so the Anthropic prompt-cache block can be placed on the stable portion only. The split point is invisible to the LLM — the two halves concatenate with no separator — but it's the reason edits to the rubric prose above the marker are cache-aware while edits below the marker are per-batch variable content.

Keep the marker as a single line with blank lines before and after. Removing it will raise a load error on the next call.

Output format lives in the route, not the prompt ​

Every LLM route except quick summaries and the podcast outline sends a JSON schema with the request, and the provider's structured output fills it. The prompts describe what each output field means; they don't restate the shape. When you edit a prompt, don't add a JSON example or a "respond only with…" instruction: if it disagrees with the route's schema (an array where the schema expects an object, a renamed field), the model gets two conflicting instructions. To change the output itself, change the schema in the route under pages/api/.

Embedded prompts (require server restart) ​

A smaller set of prompts still lives in the source code. Each is either too short or too programmatic to move cleanly to a template file.

LocationWhat it controlsWhen it runs
lib/notebooklm/buildFocusPrompt.jsFocus-prompt generator for Gemini Notebook; per-preset theme and paper budgets in podcastLength.jsDuring podcast bundle generation; output lands in focus-prompt.txt
lib/synthesis/repair.jsSynthesis repair templateOnly when /api/synthesize output fails the schema or the briefing checks
lib/synthesis/readerNotes.jsThe reader's-notes section appended to the synthesis promptWhen you left a note at Gate 3 that day
pages/api/check-briefing.jsHallucination-check schema-repair templateOnly when /api/check-briefing output fails validation
pages/api/suggest-profile.jsSuggest-profile schema-repair template and the overlap-retry hintOnly when /api/suggest-profile output fails validation
pages/api/{quick-filter,score-abstracts,rescore-abstracts,analyze-pdf}.jsThe # RETRY NOTICE hint appended to the rubric promptWhen the first response fails the route's validation
lib/analyzer/stages/core.jsThe client-side correction promptWhen a route's 200 response still fails to parse in the browser
lib/analyzer/briefingClient.jsThe retry hint for a flagged briefingWhen the audit returns a verdict that the retry settings act on
lib/profile/suggestPrompt.jsThe USER GUIDANCE FOR THIS CALL block and the rejected-suggestions sectionWhen you type guidance, or have rejected changes before

The focus-prompt builder is a JavaScript function that computes paper and theme budgets from the podcast duration (not a static template). The repair templates and retry hints are short and tightly coupled to the schema or validator they serve, so they stay next to that code.

What to tune for which quality knob ​

Match the symptom to a file.

Briefings don't feel editorial enough. Themes read as category labels. Pitches are generic. Edit prompts/synthesis.md. This is the largest lever in the system — a detailed prompt specifying what each field should sound like. Reading the whole prompt first is usually worth it; small edits (one more bullet in the pitch guidance, one extra example in the theme-title section) often have visible downstream effects.

Quick summaries come out too long, too short, or too hedged. Edit prompts/analyze-pdf-quick.md. Controls the compression ratio, the ordering (finding vs. caveats), and the voice.

The hallucination audit fires too often or misses genuine issues. Edit prompts/check-briefing.md. The prompt separates factual claims about a paper (held to its sources) from editorial synthesis across papers (allowed unless it contradicts them), then lists explicit YES / MAYBE / NO categories (YES = hallucinations detected, NO = clean); adjusting those boundaries shifts sensitivity. The "Sourcing discipline" section of prompts/synthesis.md uses the same split, so change the two together. Pair with the briefingRetryOnYes and briefingRetryOnMaybe checkboxes in Settings, which control what the pipeline does after the audit returns.

The refinement flow proposes changes that feel off-target or too aggressive. Edit prompts/suggest-profile.md. This is where the weighting across stars, dismisses, comments, and filter overrides is described, along with the "return no changes if signal is thin" guidance.

The editor asks questions that miss, or asks too often. Edit prompts/editor-questions.md. Its list of good reasons to ask is what keeps the questions rare; the validator in lib/editorQuestions/questions.js enforces the format.

Podcasts drift off-topic or cover too many papers shallowly. Edit prompts/notebooklm-discussion-guide.md for the outline itself, or lib/notebooklm/buildFocusPrompt.js for the focus prompt the notebook receives (the per-preset budgets are in lib/notebooklm/podcastLength.js) in focus-prompt.txt.

Scoring doesn't match your profile. This is usually a profile problem, not a prompt problem. The rubrics in prompts/rubric-filter.md, rubric-scoring.md, rubric-rescoring.md, and rubric-pdf.md describe the verdicts and the 0–10 scale itself; they don't describe what's relevant to you. Edit your profile, then run the refinement flow. Only edit the rubric files if you want to recalibrate the scale — which invalidates score comparisons against older briefings.

Editing scoring rubrics is a breaking change

The rubric defines what a 7 means versus a 9. Edits change the score distribution of every subsequent briefing, which invalidates cross-date comparisons. Generation metadata records the models and the profile snapshot but not the rubric text, so an old briefing with "score 8.2" and a new briefing with "score 8.2" aren't automatically comparable if the rubric changed in between. Treat rubric edits as calibration events worth noting separately.

See also ​

Released under the MIT License.