ヒューマン・イン・ザ・ループ ワークフロー
ヒューマン・イン・ザ・ループのワークフローでは、特定のポイントで実行を一時停止し、ユーザー入力を収集したり、判断を下したり、人間の判断を必要とするアクションを実行したりすることができます。 この例では、人間の介入ポイントを含むワークフローを作成する方法を示しています。
セットアップ
npm install @ai-sdk/openai @mastra/core @inquirer/prompts
エージェントの定義
旅行エージェントを定義します。
agents/travel-agents.ts
import { Agent } from "@mastra/core/agent";
import { openai } from "@ai-sdk/openai";
const llm = openai("gpt-4o");
// Agent that generates multiple holiday options
// Returns a JSON array of locations and descriptions
export const summaryTravelAgent = new Agent({
name: "summaryTravelAgent",
model: llm,
instructions: `
You are a travel agent who is given a user prompt about what kind of holiday they want to go on.
You then generate 3 different options for the holiday. Return the suggestions as a JSON array {"location": "string", "description": "string"}[]. Don't format as markdown.
Make the options as different as possible from each other.
Also make the plan very short and summarized.
`,
});
// Agent that creates detailed travel plans
// Takes the selected option and generates a comprehensive itinerary
export const travelAgent = new Agent({
name: "travelAgent",
model: llm,
instructions: `
You are a travel agent who is given a user prompt about what kind of holiday they want to go on. A summary of the plan is provided as well as the location.
You then generate a detailed travel plan for the holiday.
`,
});
一時停止可能なワークフローの定義
一時停止ステップ(humanInputStep
)を含むワークフローを定義します。
workflows/human-in-the-loop-workflow.ts
import { createWorkflow, createStep } from "@mastra/core/workflows/vNext";
import { z } from "zod";
// Step that generates multiple holiday options based on user's vacation description
// Uses the summaryTravelAgent to create diverse travel suggestions
const generateSuggestionsStep = createStep({
id: "generate-suggestions",
inputSchema: z.object({
vacationDescription: z.string().describe("The description of the vacation"),
}),
outputSchema: z.object({
suggestions: z.array(z.string()),
vacationDescription: z.string(),
}),
execute: async ({ inputData, mastra }) => {
if (!mastra) {
throw new Error("Mastra is not initialized");
}
const { vacationDescription } = inputData;
const result = await mastra.getAgent("summaryTravelAgent").generate([
{
role: "user",
content: vacationDescription,
},
]);
console.log(result.text);
return { suggestions: JSON.parse(result.text), vacationDescription };
},
});
// Step that pauses the workflow to get user input
// Allows the user to select their preferred holiday option from the suggestions
// Uses suspend/resume mechanism to handle the interaction
const humanInputStep = createStep({
id: "human-input",
inputSchema: z.object({
suggestions: z.array(z.string()),
vacationDescription: z.string(),
}),
outputSchema: z.object({
selection: z.string().describe("The selection of the user"),
vacationDescription: z.string(),
}),
resumeSchema: z.object({
selection: z.string().describe("The selection of the user"),
}),
suspendSchema: z.object({
suggestions: z.array(z.string()),
}),
execute: async ({ inputData, resumeData, suspend, getInitData }) => {
if (!resumeData?.selection) {
await suspend({ suggestions: inputData?.suggestions });
return {
selection: "",
vacationDescription: inputData?.vacationDescription,
};
}
return {
selection: resumeData?.selection,
vacationDescription: inputData?.vacationDescription,
};
},
});
// Step that creates a detailed travel plan based on the user's selection
// Uses the travelAgent to generate comprehensive holiday details
const travelPlannerStep = createStep({
id: "travel-planner",
inputSchema: z.object({
selection: z.string().describe("The selection of the user"),
vacationDescription: z.string(),
}),
outputSchema: z.object({
travelPlan: z.string(),
}),
execute: async ({ inputData, mastra }) => {
const travelAgent = mastra?.getAgent("travelAgent");
if (!travelAgent) {
throw new Error("Travel agent is not initialized");
}
const { selection, vacationDescription } = inputData;
const result = await travelAgent.generate([
{ role: "assistant", content: vacationDescription },
{ role: "user", content: selection || "" },
]);
console.log(result.text);
return { travelPlan: result.text };
},
});
// Main workflow that orchestrates the holiday planning process:
// 1. Generates multiple options
// 2. Gets user input
// 3. Creates detailed plan
const travelAgentWorkflow = createWorkflow({
id: "travel-agent-workflow",
inputSchema: z.object({
vacationDescription: z.string().describe("The description of the vacation"),
}),
outputSchema: z.object({
travelPlan: z.string(),
}),
})
.then(generateSuggestionsStep)
.then(humanInputStep)
.then(travelPlannerStep);
travelAgentWorkflow.commit();
export { travelAgentWorkflow, humanInputStep };
Mastraクラスでエージェントとワークフローのインスタンスを登録する
エージェントと天気ワークフローをmastraインスタンスに登録します。 これはワークフロー内でエージェントにアクセスできるようにするために重要です。
index.ts
import { Mastra } from "@mastra/core/mastra";
import { PinoLogger } from "@mastra/loggers";
import { travelAgentWorkflow } from "./workflows/human-in-the-loop-workflow";
import { summaryTravelAgent, travelAgent } from "./agents/travel-agent";
// Initialize Mastra instance with:
// - The travel planning workflow
// - Both travel agents (summary and detailed planning)
// - Logging configuration
const mastra = new Mastra({
vnext_workflows: {
travelAgentWorkflow,
},
agents: {
travelAgent,
summaryTravelAgent,
},
logger: new PinoLogger({
name: "Mastra",
level: "info",
}),
});
export { mastra };
一時停止可能な天気ワークフローを実行する
ここでは、mastraインスタンスから天気ワークフローを取得し、実行を作成して、必要な入力データで作成した実行を実行します。
さらに、readlineパッケージを使用してユーザー入力を収集した後、humanInputStep
を再開します。
exec.ts
import { mastra } from "./";
import { select } from "@inquirer/prompts";
import { humanInputStep } from "./workflows/human-in-the-loop-workflow";
const workflow = mastra.vnext_getWorkflow("travelAgentWorkflow");
const run = workflow.createRun({});
// Start the workflow with initial vacation description
const result = await run.start({
inputData: { vacationDescription: "I want to go to the beach" },
});
console.log("result", result);
const suggStep = result?.steps?.["generate-suggestions"];
// If suggestions were generated successfully, proceed with user interaction
if (suggStep.status === "success") {
const suggestions = suggStep.output?.suggestions;
// Present options to user and get their selection
const userInput = await select<string>({
message: "Choose your holiday destination",
choices: suggestions.map(
({ location, description }: { location: string; description: string }) =>
`- ${location}: ${description}`,
),
});
console.log("Selected:", userInput);
// Prepare to resume the workflow with user's selection
console.log("resuming from", result, "with", {
inputData: {
selection: userInput,
vacationDescription: "I want to go to the beach",
suggestions: suggStep?.output?.suggestions,
},
step: humanInputStep,
});
const result2 = await run.resume({
resumeData: {
selection: userInput,
},
step: humanInputStep,
});
console.dir(result2, { depth: null });
}
ヒューマン・イン・ザ・ループのワークフローは、以下のような自動化と人間の判断を組み合わせたシステムを構築するのに強力です:
- コンテンツモデレーションシステム
- 承認ワークフロー
- 監視付きAIシステム
- エスカレーション機能を持つカスタマーサービスの自動化