curated_feed.policy¶
Assembling the prompt from the policy documents and the day’s candidates.
No judgement of its own: this module concatenates files and numbers a list. What the model is told lives in two documents, and the split between them is the split between the engine and a runner.
The scoring prompt ships with the package. It is not taste but contract: its response section is the schema models.Response parses, and its consequence scale is the range models.Graded validates, so a prompt and a parser that disagree fail at six in the morning with a malformed day. The two version together or not at all.
The editorial policy is named by the runner’s configuration and is loaded at runtime, so that a change of taste is a commit against a change in output quality rather than a code change.
docs/scoring-spec.md is deliberately absent. It names the score floor and the item cap, and a model that knows them pre-filters or pads.
The assembled prompt is kept in two parts. The documents are identical on every run and are most of the tokens, so they are the half worth caching; the candidate list is the half that changes.
Attributes¶
A neutral candidate, so that the doctests below have something to render. |
Classes¶
An assembled prompt, split at the boundary between stable and variable. |
Functions¶
|
The scoring prompt that ships with the package. |
|
Concatenate the policy documents, in the order given. |
|
One candidate as two lines: the reference line, then the summary. |
|
The day's candidates as a numbered list, one item per two lines. |
|
Assemble the prompt for one day. |
Module Contents¶
- curated_feed.policy.DOCUMENT_SEPARATOR = Multiline-String¶
Show Value
""" --- """
- curated_feed.policy.CANDIDATES_HEADING = '# Candidates'¶
- curated_feed.policy.PACKAGE = 'curated_feed'¶
- curated_feed.policy.PROMPT_FILENAME = 'prompt.md'¶
- curated_feed.policy.NO_SUMMARY = '(no summary)'¶
- curated_feed.policy.UNKNOWN_DATE = 'date unknown'¶
- curated_feed.policy.EXAMPLE_CANDIDATE¶
A neutral candidate, so that the doctests below have something to render.
- class curated_feed.policy.Prompt(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelAn assembled prompt, split at the boundary between stable and variable.
- curated_feed.policy.default_prompt_path() pathlib.Path[source]¶
The scoring prompt that ships with the package.
Resolved through the package rather than the working directory, because the engine is installed rather than checked out wherever it runs: an image, a virtual environment, an editable tree. A runner’s configuration cannot name it, which is the point — see this module’s docstring.
>>> default_prompt_path().name 'prompt.md' >>> default_prompt_path().is_file() True
- curated_feed.policy.read_documents(*paths: pathlib.Path) str[source]¶
Concatenate the policy documents, in the order given.
- curated_feed.policy.format_candidate(candidate: curated_feed.models.Candidate, number: int, *, with_id: bool) str[source]¶
One candidate as two lines: the reference line, then the summary.
The model refers to articles by number, so the numbering is the contract. Ids are for a human tracing a pick back to the corpus and are left out of the prompt, being long opaque hashes that models mangle.
>>> print(format_candidate(EXAMPLE_CANDIDATE, 1, with_id=False)) [1] A title — A source — 2026-08-08T09:14:00Z A summary.
- curated_feed.policy.render_candidates(candidates: collections.abc.Sequence[curated_feed.models.Candidate], *, with_ids: bool = False) str[source]¶
The day’s candidates as a numbered list, one item per two lines.
>>> print(render_candidates([EXAMPLE_CANDIDATE])) [1] A title — A source — 2026-08-08T09:14:00Z A summary.
- curated_feed.policy.build_prompt(candidates: collections.abc.Sequence[curated_feed.models.Candidate], *, policy_path: pathlib.Path, prompt_path: pathlib.Path | None = None) Prompt[source]¶
Assemble the prompt for one day.
prompt_path overrides the packaged scoring prompt. Nothing in the pipeline passes it: it exists so that a candidate prompt can be tried against a saved day without reinstalling, which is the only way to evaluate a prompt change once the corpus and the engine live in different repositories.