Sign in
Agent EngineeringAug 04, 20265 min read

Agent Design Best Practices for Reliable and Secure Outcomes

Tamao Nakahara

Agents can help give you and your teams super powers when they can take on menial tasks, automate workflows, and give you time to focus on deep learning and meaningful execution. Having best practices in place minimizes slot machine randomness, hallucinations, or security risks. Below are some foundational best practices to improve success.

To put this guidance into practice within an agent file (like Guild’s agent.ts), AI engineers use the following coding strategies.

Write in Idempotency

Agents are designed to keep trying over and over to reach their assigned goal, especially if they encounter issues. It is critical to design agents to be idempotent: they should check state before each action and skip steps previously completed. Otherwise, as your agent tries and retries, you could have problems like posting the same Slack messages repeatedly or generating duplicate pull requests. This check for past completed steps is best hard-coded into the agent's run functions, not left to the LLM's memory.

Log the State of Completed Actions

Guild’s agent runtime automatically stores state in the background, so a best practice is to have the agent’s code take in that state data as a parameter. You might have a boolean flag to ensure that the agent won’t try to create a PR if hasCreatedPR is true:

interface AgentState {  hasCreatedPR: boolean;}

Pure Code Agent example

Depending on whether your agent is written purely in TypeScript or is an LLM-based agent with both code and text prompts,  you have different options to have your agent leverage state from the runtime.

If you have a code-only agent, you would follow a model like this:

export default async function run(inputs: any, context: any) {  const state = context.state || { hasCreatedPR: false };
  // Your code controls the logic. No prompt is needed to make this decision!  if (!state.hasCreatedPR) {
    // The LLM is only called to do a specific task, like generating text.    const draftedText = await callLLMToDraftContent(inputs.topic);        // Your code uses that text to perform the action.    await createPullRequest(draftedText);        state.hasCreatedPR = true;    await context.saveState(state);  }}

In this case, the code determines the workflow for drafting content and creating a pull request. Its functions trigger using the LLM for appropriate tasks (and in this example, the LLM owns the task of drafting content). Otherwise, creating the pull request and storing the updated state are performed with code.

LLM Agent example

If you have an LLM agent file that includes system prompts to instruct the LLM, you would include additional prompts to communicate state to the LLM:

export default async function run(inputs: any, context: any) {
  // 1. Retrieve or initialize the runner's state const state: AgentState = context.state || { hasCreatedPR: false };
  // 2. Inject the state directly into the LLM's system prompt  const systemPrompt = `    You are a GitHub coordinator.     Current PR status: ${state.hasCreatedPR ? "COMPLETED" : "PENDING"}.    CRITICAL: If the status is "COMPLETED", do NOT call the create_pull_request tool again.  `;
  // 3. Define the tool that updates state when executed  const tools = [    {      name: "create_pull_request",      description: "Creates a GitHub Pull Request.",      execute: async () => {        // [Hardcoded GitHub API logic goes here]        console.log("Executing PR creation on GitHub...");        // Update and persist state to Guild's runner        state.hasCreatedPR = true;        await context.saveState(state);        return { success: true, message: "PR created successfully." };      }    }  ];
  // 4. Run the LLM with the state-aware prompt and tools  return await context.llm.run({    systemPrompt,    prompt: inputs.prompt,    tools  });}

 

If the state is already captured in code, why do you need the additional prompt-based state capture for these types of LLM agents? If you have built an agent that also engages with the LLM through prompts, the LLM also has to be informed. It will not automatically look for state data in the runtime, nor would it have access. Because of this, you may decide to have a code-only agent or include the code.

A few other tips …

Advanced Prompting & Guardrails: The Power of Examples

When prompt engineering is necessary, it is tempting to write restrictive rules ("don't write robotic," "do not use em-dashes," or "don't post to random channels"). Although sometimes they can guide toward the goal, they should be left as a last resort. LLMs work best when you give them examples.

LLMs learn far more effectively from positive examples than negative constraints. If you want an agent to write in a specific voice, provide 3 to 5 high-quality examples of that exact writing style in its system instructions.

When building or updating agents in Guild, these types of examples would go in the context file. Over time, you can also ask the agent to add new knowledge and examples into the context file to make it more precise when meeting your needs.


Integrations Belong in Code, Not Prompts

To ensure reliable, secure, and cost-effective outcomes, integrations should be hard-coded into agent files rather than relying solely on system prompts. Defaulting to deterministic code for critical components ensures your workflows remain efficient and stable.

  • Security: Hard-coding integrations prevents prompt manipulation, ensures only authorized agents connect to external systems, and properly manages access via organization-level credentials. System prompts should never handle or store API keys.
  • Predictability: Utilizing deterministic code for critical tasks makes outcomes highly reliable and structured, avoiding the inherent randomness of LLM text generation.
  • Token Efficiency: Offloading tasks to code reduces the system prompt token count, significantly managing execution speed and overall token costs.

Guild’s design ensures that integrations are added at the Organization level and at the individual agent level. Guild’s templates and IDE provide options to add these integrations with just a few clicks, and then Guild’s credentialing process ensures that any access to those integrations has been vetted properly.


Let us know if these tips were helpful and whether Guild’s design helps bake in some of these best practices!

One control plane.
The complete agent lifecycle.
Get a working agent in under 10 minutes.
No credit card required.
Explore docs