Narratives prompts - #449
Open
ddebasmita-lab wants to merge 3 commits into
Open
Conversation
Two halves of the same gap: the agent never loaded its prompts, and the prompts
that existed were written for one specific instance.
**The prompts were never loaded.** The workflows read config["prompts"][slot],
but nothing populated it. _bootstrap_config_from_url() fetched only
agent-config.json, whose prompts object is empty, and no code path read the .md
files. The MCP tool loop, the KB phase and synthesis therefore ran with no
system instruction at all. follow_up was the only governed phase, because its
default lives in code.
_bootstrap_config_from_url() now fetches prompts/<slot>.md from the same bucket
path as agent-config.json and merges the bodies in. An inline prompts slot still
wins, as the schema documents. A slot that 404s is skipped with a warning rather
than failing startup, so a partial fetch degrades to the previous behaviour
instead of taking the agent down. HTML comments are stripped so the .md files
can carry authoring notes without those notes reaching the model.
The config-bucket fetch also pins UTF-8. Buckets serve .md as text/markdown with
no charset, so requests guessed the encoding from the bytes and corrupted
currency symbols, arrows and em-dashes. Every prompt was affected, silently.
This covers agent-config.json and branding.json too.
**The prompts are now instance-agnostic.** They carry no country, agency,
dataset or currency of their own — they describe how to search, cite and format,
and nothing else. The agent loads prompts/<slot>.md from the instance's own
config bucket, so customising them is a per-instance edit in that bucket, the
same as branding.json. Only {{CURRENT_DATETIME}} is substituted; everything else
is literal text.
That is deliberate rather than a limitation. A fixed set of substitution
variables would have to assume a shape — a country with states, a fiscal year —
and instances are not all shaped that way. One may be a single state whose
sub-units are districts, another may be thematic with no default place at all.
Prose an operator writes for their own instance fits every shape; a schema of
placeholders fits one.
Dropping the instance-specific catalogue also cut the tool-loop prompt by 81%
(33KB to 6.5KB), since the bulk of it was one instance's variable reference.
Across all four prompts, 46KB became 16KB. config/README.md explains what is
worth adding back locally — above all, the custom variable DCIDs, because the
ingest indexes descriptions rather than names.
follow_up.md is byte-identical to DEFAULT_FOLLOW_UP_PROMPT, so declaring that
slot changes nothing today but stops the two copies drifting. It needed the
schema's prompts object to admit it, which additionalProperties: false blocked.
Enabling these prompts is a behaviour change, not only a fix: instructions that
have never executed now go live on every request.
`agent/config.json` was not ignored. A deployed instance never uses it — it fetches config from the instance bucket via CONFIG_URL and reads API keys from Secret Manager — but the local development flow writes real Gemini keys into it, so `git add -A` would have staged them into a public repo. Also ignores `deploy.env`, which holds the API keys used to drive a deployment, in both git and the Docker build context.
ddebasmita-lab
requested review from
beets,
juliawu and
nick-nlb
as code owners
August 25, 2026 12:43
Contributor
There was a problem hiding this comment.
Code Review
This pull request introduces a mechanism to fetch and merge markdown system prompts (mcp, kb, synthesis, and follow_up) from a GCS config bucket into the agent's configuration at startup, while removing unused template variables. The review feedback recommends using urllib.parse to robustly handle URLs with query parameters when deriving prompt URLs, and explicitly specifying encoding="utf-8" when writing configuration files to prevent platform-dependent encoding issues.
…TF-8 Two review fixes in the config bootstrap. Prompt URLs were derived by splitting the raw CONFIG_URL string, so a query containing a slash (?prefix=a/b) split mid-query and produced a nonsense base. Parse the URL and rewrite only its path. Query and fragment are dropped: they address the config object, not the prompt objects, so carrying a generation or a signed-URL signature across would 403. Private buckets are read with the metadata token, which is unaffected. Config reads and writes now name utf-8 explicitly instead of relying on the platform default, which is not UTF-8 on Windows. Both sides are pinned, so the currency symbols and em-dashes the prompts carry survive the round trip.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Prompt loading and a reusable, instance-agnostic prompt set