fix(agent/systemprompt): soften predefined bootstrap to forbid empty-args tool calls

The previous MANDATORY phrasing ("MUST ALSO call write_file for
USER.md and BOOTSTRAP.md before your response ends") was making
small models emit write_file({}) when they had no real user info
to persist, tripping HTTP 400 on Google's Gemini OpenAI-compat shim.

Rewrite the predefined+BOOTSTRAP.md branch with softer "get to know
the user" copy plus explicit hard rules forbidding empty/placeholder
arguments and session-identifier content in USER.md. Safety net is
preserved: the USER PROFILE INCOMPLETE branch nudges the model every
subsequent turn until USER.md is populated.

Sync BOOTSTRAP_PREDEFINED.md template to remove the contradictory
MANDATORY block and replace with matching hard rules.

Trace: 019d8f33-2de1-7ab2-9a32-9df92cd610dd
This commit is contained in:
viettranx
2026-04-15 12:42:32 +07:00
parent 5689d168e6
commit 666bfbaf3c
2 changed files with 27 additions and 8 deletions
+22 -5
View File
@@ -264,14 +264,31 @@ func BuildSystemPrompt(cfg SystemPromptConfig) string {
"",
)
} else if hasBootstrapFile(cfg.ContextFiles) {
// Predefined agents: full capabilities, but MUST complete bootstrap
// Predefined agents: soft onboarding. Small models (e.g. Gemini 3 Flash
// with low thinking budget) were emitting write_file({}) to satisfy a
// MUST-call mandate when they had no real user info — causing HTTP 400
// on the Google shim. The USER PROFILE INCOMPLETE branch below
// guarantees the model keeps getting nudged on subsequent turns, so
// deferring the write until info is gathered is safe.
// Trace: 019d8f33-2de1-7ab2-9a32-9df92cd610dd.
lines = append(lines,
"## FIRST RUN — MANDATORY",
"## FIRST RUN — GET TO KNOW THE USER",
"",
"BOOTSTRAP.md is loaded below. This is your FIRST interaction with this user.",
"You MUST complete the onboarding described in BOOTSTRAP.md.",
"You may answer the user's question, but you MUST ALSO call write_file for USER.md and BOOTSTRAP.md before your response ends.",
"If the user's first message contains enough info (name, language, timezone), write USER.md immediately — do NOT wait for multiple turns.",
"",
"Your goal: have a short, warm conversation and learn their name, preferred language,",
"and timezone naturally. Ask at most 1-2 questions per turn — don't interrogate.",
"",
"Once you actually have this info FROM THE USER'S OWN WORDS, silently call write_file",
"for USER.md (their profile) and write_file for BOOTSTRAP.md with empty content (to",
"mark onboarding complete).",
"",
"Hard rules:",
"- Do NOT call write_file on this turn if you haven't heard the info from the user yet.",
"- Do NOT call write_file with empty or placeholder arguments. If arguments would be",
" blank, respond conversationally instead and gather info first.",
"- USER.md content must come from the user's own messages — never copy session identifiers, system strings, or made-up values.",
"- You may answer their question in the same turn as asking for their info.",
"",
)
} else if content := findContextFileContent(cfg.ContextFiles, bootstrap.UserFile); content != "" && !isUserFilePopulated(content) {
@@ -59,10 +59,12 @@ Once you have their name, language, and timezone — silently use the `write_fil
Do NOT use `rm` or `exec`. The empty write signals the system that onboarding is finished.
## MANDATORY
## Hard rules for write_file
You MUST call write_file for BOTH USER.md and BOOTSTRAP.md. Do not skip this step. Do not defer to a future turn.
If the user's first message already contains their name, language, or timezone — extract what you can, ask for the rest naturally, then write immediately.
- Only call write_file once you actually have the info IN THE USER'S OWN WORDS. Not inferred, not guessed, not assumed from system strings.
- Never call write_file with empty or placeholder arguments. If the fields would be blank, respond conversationally and gather info first — you will be prompted again next turn.
- USER.md content comes from the user's messages only — never copy session IDs, system identifiers, or made-up values into it.
- If the user's first message already contains enough info (name, language, timezone) — extract it and write immediately. Otherwise, ask naturally and write on a later turn.
---