Customizing Agent Prompts and Output

Introduction: Making Your Email Agent Smarter

Welcome back! In the last lesson, you learned how to setup your Mastra agent. Now, let’s take the next step: making your agent smarter by customizing its instructions and structuring its output. This is important because clear instructions help the agent understand exactly what you want, and structured output makes it much easier to use the agent’s results in your application.

By the end of this lesson, you will know how to:

  • Write detailed instructions for your agent.
  • Ask your agent for specific pieces of information.
  • Structure the agent’s output so you can easily extract and display what you need.

Let’s get started!

Recap: Preparing Email Data for the Agent

Before we dive in, let’s quickly remind ourselves where we are. In the previous lesson, you learned how to take a raw email thread and split it into useful parts, like headers and the body. This means you now have clean, organized email data that you can send to your Mastra agent.

For example, you might have an email like this:

From: sarah.jones@acme-corp.com  
To: engineering-team@acme-corp.com  
Subject: Project Phoenix – Action Items & Confirmation Needed

Hi Team,

Thanks for your work on Project Phoenix so far. We’ve finished the requirements and have a draft of the technical specs. The client reviewed our docs and sent feedback (see attached).

Action items for this week:
- Priya & Ahmed: Update the specs based on client feedback by Thursday.
- Jason: Set up the dev environment and confirm access for everyone by Wednesday.
- Emily: Start drafting test cases from the updated specs.

Please reply to confirm you’ve received your tasks and let me know if you have any questions or concerns.

Thanks,  
Sarah

We can load it like so:

  const emailContent = readFileSync("example-email-thread.txt", "utf-8");

This emailContent is now ready to be processed by your agent. With this in place, we can focus on customizing the agent’s behavior.

Writing Clear Instructions to Guide the Agent

The first step in making your agent smarter is to give it clear, step-by-step instructions. The instructions tell the agent exactly what you want it to do with the email thread.

Let’s look at how you can define an agent with clear instructions using the Agent class from the @mastra/core/agent package and OpenAI as the model provider:

// src/mastra/agents/summaryAgent.ts
import { Agent } from "@mastra/core/agent";
import { createOpenAI } from "@ai-sdk/openai";

const openai = createOpenAI({
  baseURL: process.env.OPENAI_BASE_URL,
  compatibility: 'strict',
});

export const SummaryAgent = new Agent({
  name: "Email Summarizer",
  instructions: `
    Given an email thread, do the following:
    1. Summarize the main points.
    2. Identify if a reply is needed.
    3. If so, suggest a draft reply.
  `,
  model: openai("gpt-4o"),
});

Here, the instructions are clear and broken down into steps, which helps the agent understand exactly what you want.

To use this agent, you can set up your main application logic as follows:

// src/mastra/index.ts
import { readFileSync } from "fs";
import { join } from "path";
import { Mastra } from "@mastra/core";
import { SummaryAgent } from "./agents/SummaryAgent.js";

const mastra = new Mastra({
  agents: { SummaryAgent },
});

async function processEmail(emailContent: string) {
  const agent = mastra.getAgent("SummaryAgent");
  const response = await agent.generate([
    { role: "user", content: emailContent }
  ]);
  
  return response.text;
}

async function main() {
  const emailContent = readFileSync("/usercode/FILESYSTEM/src/mastra/assets/example-email.txt", "utf-8");
  const response = await processEmail(emailContent);
  
  console.log("Summary:", response);
}

main();

Structuring and Extracting Agent Output

When you ask the agent for multiple pieces of information, it’s important to structure the output so you can easily extract each part. One way to do this is to ask the agent to respond in a specific format, such as a JSON object.

You can update your agent’s instructions to include formatting guidelines:

// src/mastra/agents/summaryAgent.ts
import { Agent } from "@mastra/core/agent";
import { createOpenAI } from "@ai-sdk/openai";

const openai = createOpenAI({
  baseURL: process.env.OPENAI_BASE_URL,
  compatibility: 'strict',
});

export const SummaryAgent = new Agent({
  name: "Email Summarizer",
  instructions: `
    Given an email thread, do the following:
    1. Summarize the main points.
    2. Identify if a reply is needed.
    3. If so, suggest a draft reply.

    Respond ONLY with a valid JSON object in the following format. Do NOT add a markdown block around it:
    {
      "summary": "<your summary>",
      "replyNeeded": "<yes or no>",
      "draftReply": "<your draft reply, or 'N/A' if not needed>"
    }
  `,
  model: openai("gpt-4o"),
});

Now, when you run the agent, you can parse the JSON response in your application:

// src/mastra/index.ts
import { readFileSync } from "fs";
import { join } from "path";
import { Mastra } from "@mastra/core";
import { SummaryAgent } from "./agents/SummaryAgent.js";

const mastra = new Mastra({
  agents: { SummaryAgent },
});

async function processEmail(emailContent: string) {
  const agent = mastra.getAgent("SummaryAgent");
  const response = await agent.generate([
    { role: "user", content: emailContent }
  ]);
  const text = response.text;

  // Parse the JSON response
  let result = { summary: "", replyNeeded: "", draftReply: "" };
  try {
    result = JSON.parse(text);
  } catch (e) {
    console.error("Failed to parse agent response as JSON:", e);
  }
  return result;
}

async function main() {
  const emailContent = readFileSync("/usercode/FILESYSTEM/src/mastra/assets/example-email.txt", "utf-8");
  const { summary, replyNeeded, draftReply } = await processEmail(emailContent);

  console.log("Summary:", summary);
  console.log("Reply Needed:", replyNeeded);
  console.log("Draft Reply:", draftReply);
}

main();

By structuring the agent’s output as JSON and parsing it in your code, you make it much easier to use the agent’s results in your application.

Using a Schema for Output Validation

For even more robust output handling, you can define a schema using a library like zod to validate the agent's response. This ensures the output matches the expected structure and types.

By passing a Zod schema as the output option to the agent's generate method, the agent will automatically parse and validate its response, returning the structured result as the object property of the response. Here's an example of how you can add schema validation:

// src/mastra/index.ts
import { readFileSync } from "fs";
import { join } from "path";
import { Mastra } from "@mastra/core";
import { SummaryAgent } from "./agents/SummaryAgent.js";
import { z } from "zod";

const mastra = new Mastra({
  agents: { SummaryAgent },
});

// Define a schema to validate the agent's output structure
// This ensures we always get the expected fields with the correct types
const schema = z.object({
  summary: z.string(),
  replyNeeded: z.boolean(),
  draftReply: z.string(),
});

async function processEmail(emailContent: string) {
  const agent = mastra.getAgent("SummaryAgent");
  // Pass the schema as the 'output' option to validate the response
  const response = await agent.generate(
    [{ role: "user", content: emailContent }],
    { output: schema }
  );
  // The validated and parsed result is available in response.object
  return response.object;
}

async function main() {
  const emailContent = readFileSync("/usercode/FILESYSTEM/src/mastra/assets/example-email.txt", "utf-8");
  const { summary, replyNeeded, draftReply } = await processEmail(emailContent);

  console.log("Summary:", summary);
  console.log("Reply Needed:", replyNeeded);
  console.log("Draft Reply:", draftReply);
}

main();

By using a schema, you ensure that the agent's output is always in the format you expect, making your application more reliable and easier to maintain.

Summary and What’s Next

In this lesson, you learned how to make your Mastra agent smarter by:

  • Writing clear, step-by-step instructions.
  • Asking for structured output (such as JSON).
  • Using a schema to validate and extract the information you need.

These skills will help you build more useful and reliable agents. Up next, you’ll get to practice customizing prompts and handling structured output yourself. Great job getting this far — let’s keep building your smart email assistant!

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal