The Five Things a Good AI Agent System Prompt Needs (With a Checker)
A perfect memory system can't save an agent that doesn't know when to reach for the tool sitting right in front of it. Prompt engineering for an agent differs from prompting a chatbot in one key way: the system prompt is re-read fresh on every single step of a loop, not judged once. This post conti

Posted on Oct 14 • Originally published at zahidhasantonmoy.vercel.app
A perfect memory system can't save an agent that doesn't know when to reach for the tool sitting right in front of it. Prompt engineering for an agent differs from prompting a chatbot in one key way: the system prompt is re-read fresh on every single step of a loop, not judged once.
This post continues my series on building agents. The previous one covered short-term vs long-term memory.
A vague instruction in a one-shot prompt costs one bad answer. The same vagueness in an agent's system prompt costs one bad decision per step, and across a long conversation that adds up instead of averaging out — the same ambiguous text, reread with the same risk, every single time.
Going back through this series' actual code: add_expense has "Use it whenever the user says they spent money" — real trigger phrase, doing real work. get_total, from the same file, has no trigger phrase at all, and gets away with it only because nothing else in that toolset sounds like "total." The rule isn't "always add a trigger" as a blanket mandate — it's that the less distinguishable a tool's name is from its neighbors, the more the description has to do.
// agent/prompt-template.ts const SYSTEM_PROMPT = ` You are an expense-tracking assistant for a single user. ## Role Record expenses the user mentions and answer questions about what they've spent. You do not give financial advice. ## Tools and when to use them - add_expense: use this whenever the user states they spent money on something. - get_total: use this whenever the user asks for a total, a sum, or how much they've spent overall — not for a single expense or an average. - save_note: use this whenever the user states a lasting preference or constraint, not a one-off detail about today. ## Boundaries - Never invent an expense, a total, or a saved note the tools did not actually return. - Never delete or change a past expense unless the user explicitly asks to. ## When you're not sure If a message could mean more than one thing (for example, an amount with no stated purpose), ask one short clarifying question instead of guessing which tool to call. ## When you're done Once every expense or note the user mentioned has been recorded and any question has been answered, reply in plain text with no further tool calls. `. trim (); function hasTriggerPhrase ( description: string ): boolean { return / \b use ( this|it ) whenever \b /i. test ( description ); } const toolDescriptions: Record < string, string > = { add_expense: ' Record one expense. Use it whenever the user says they spent money. ', get_total_before: ' Return the exact total of all recorded expenses in BDT. ', get_total_after: " Return the exact sum of every expense recorded so far, in BDT.
Use this whenever the user asks for a total, a sum, or how much they've spent overall — not for a single expense or an average. ", }; for ( const [ name, description ] of Object. entries ( toolDescriptions )) { console. log ( name, ' -> ', hasTriggerPhrase ( description )? ' has trigger phrase ': ' MISSING trigger phrase ' ); } console. log ( ' \n --- system prompt preview --- ' ); console. log ( SYSTEM_PROMPT ); Enter fullscreen mode Exit fullscreen mode Running the checker against the real get_total description and its rewrite shows the gap directly: the original fails, the rewrite passes, with no need to guess how a real model would respond to either one.
I write each post in English and Bangla. The full version is on my blog: How to Write a Good Prompt for an AI Agent
Has a vague tool description ever made your agent guess wrong?