An open API service indexing awesome lists of open source software.

https://github.com/snickerjp/mastra-agent-tutorial


https://github.com/snickerjp/mastra-agent-tutorial

Last synced: 2 months ago
JSON representation

Awesome Lists containing this project

README

          

# Mastra エージェント チュートリアル

技術ブログ記事を自動生成するエージェントシステムを、段階的に作りながら学ぶハンズオンです。

## このチュートリアルで学べること

- エージェント開発で避けて通れない**タスク分割の難しさ**
- プロンプトの品質がアウトプットに与える影響を**数値で比較**
- **ツール**でエージェントに「行動」させる方法
- **構造化出力**でプログラムから扱えるデータを得る方法
- **MCP サーバー**で自作ツールを外部クライアントに公開する方法

---

## セットアップ

```bash
npm install
```

### OpenAI版

```bash
cp .env.example .env
# .env ファイルを開いて OPENAI_API_KEY を設定してください
```

### AWS Bedrock版(SageMaker Studio等)

`.env` ファイルは不要です。IAM Roleで認証されます。

**使用モデル**: Amazon Nova Lite(入力: $0.06/1M tokens, 出力: $0.24/1M tokens)

---

## チュートリアルの流れ

```mermaid
graph LR
A[Chapter 1
単純な
1エージェント] --> B[Chapter 2
タスクを
分割する]
B --> C[Chapter 3
プロンプトの
品質を測る]
C --> D[Chapter 4
メモリで
改善を重ねる]
D --> E[Chapter 5
ツールで
行動する]
E --> F[Chapter 6
構造化
出力]
F --> G[Chapter 7
MCP
サーバー]
```

---

### Chapter 1: 「全部やって」の落とし穴

```bash
# OpenAI版
npm run ch1

# Bedrock版
npm run ch1:bedrock
```

**何を体験するか**
- 1つのエージェントに記事全体を任せると、どんな問題が起きるか
- 同じ指示でも、毎回違う結果が返ってくる不安定さ
- 「どこを直せばいいのか」が分からない状態

---

### Chapter 2: タスクを分割して設計する

```bash
# OpenAI版
npm run ch2

# Bedrock版
npm run ch2:bedrock
```

**何を体験するか**
- `research → outline → write → review` のワークフロー設計
- **分割の本質的な難しさ**: 「どこで切るか」より「何を渡すか(スキーマ設計)」が重要
- 最初のステップ(research: リサーチ)の品質が、後続すべてに影響する問題
- 間違った分割の例: 「前半を書く → 後半を書く」(文脈が途切れてしまう)

**ワークフローの流れ**
```
researchStep (リサーチ) → { keyPoints[], targetAudience, tone }

outlineStep (構成設計) → { sections[], targetAudience, tone }

writeStep (執筆) → { draft, targetAudience }

reviewStep (レビュー) → { revisions[], article }
```

---

### Chapter 3: プロンプトの品質を測る

```bash
# OpenAI版
npm run ch3

# Bedrock版
npm run ch3:bedrock
```

**何を体験するか**
- 3つのパターンを同じ評価基準で比較し、スコアの違いを確認する

| パターン | instructions | リクエスト | 期待スコア |
|---|---|---|---|
| A(最悪)| 「記事を書いてください」 | 「TypeScriptについて」 | 低 |
| B(中程度)| 詳しい役割設定あり | トピックのみ | 中 |
| C(最良)| 詳しい役割設定あり | 対象読者・構成・トーン指定あり | 高 |

**評価の観点**
- `指示準拠スコア`: instructions の要件をどれだけ守っているか
- `コンテンツ品質`: 構成・深さ・具体性・読者への適切さの総合評価

---

### Chapter 4: メモリで改善を重ねる

```bash
# OpenAI版
npm run ch4

# Bedrock版
npm run ch4:bedrock
```

**何を体験するか**
- メモリなし: 「2番目のタイトルに決めた」 → 何の2番目か分からず的外れな回答
- メモリあり: 同じ `thread` で送ると前の提案を覚えていて、「2番目」が通じる

```typescript
// memory: { thread, resource } で会話を識別
await agent.generate("TypeScriptの型システムについてブログ記事を書きたい。タイトル候補を3つ提案して。", {
memory: { thread: "session-1", resource: "user-1" }
});

// 同じ thread → 前の提案を覚えている
await agent.generate("2番目のタイトルに決めた。そのタイトルで記事の構成案(章立て)を5つ出して。", {
memory: { thread: "session-1", resource: "user-1" }
});
```

---

### Chapter 5: ツールを使うエージェント

```bash
# OpenAI版
npm run ch5

# Bedrock版
npm run ch5:bedrock
```

**何を体験するか**
- `createTool` でエージェントが使える「アクション」を定義する
- エージェントが `searchTopic` / `getCurrentDate` ツールを自律的に呼び出す
- Chapter 1(ツールなし)と比べて、事実に基づいた記事が生成される

**お題**: TypeScriptの最新動向

```typescript
// ツールを定義して Agent に渡すだけ
const agent = new Agent({
tools: { getCurrentDate, searchTopic },
// ...
});
// → LLM が description と inputSchema を見て、いつ呼ぶかを自律的に判断
```

---

### Chapter 6: 構造化出力(Structured Output)

```bash
# OpenAI版
npm run ch6

# Bedrock版
npm run ch6:bedrock
```

**何を体験するか**
- Zod スキーマで出力の型を定義し、型付きオブジェクトとして受け取る
- `article.title`, `article.sections[0].heading` のようにプログラムでアクセスできる
- JSON シリアライズ可能 → DB保存、API返却等の後続処理に使える

**お題**: TypeScriptの型システムを活用したバグ防止テクニック

```typescript
// ブログ記事の出力構造を定義する Zod スキーマ
const BlogArticleSchema = z.object({
title: z.string(),
sections: z.array(z.object({ heading: z.string(), body: z.string() })),
tags: z.array(z.string()),
});

const result = await agent.generate(messages, {
structuredOutput: {
schema: BlogArticleSchema,
},
});

// スキーマで検証してから、型付きオブジェクトとして扱う
const article = BlogArticleSchema.parse(result.object);
```

---

### Chapter 7: MCP サーバーを作る

```bash
npm run ch7
```

※ MCP サーバーは LLM を使わないため、OpenAI/Bedrock の区別はありません。

**何を体験するか**
- Chapter 5 で作ったツールを MCP (Model Context Protocol) サーバーとして公開する
- `MCPServer` + `startStdio()` で、Cursor / Windsurf / Claude Desktop 等から接続可能に
- 「自分のツールを他のアプリに提供する」という MCP の基本を理解する

```typescript
import { MCPServer } from "@mastra/mcp";
import { getCurrentDate, searchTopic } from "../chapter5/tools.js";

const server = new MCPServer({
id: "blog-research-server",
name: "Blog Research MCP Server",
version: "1.0.0",
tools: { getCurrentDate, searchTopic },
});

server.startStdio();
// → MCP クライアントから get-current-date / search-topic が呼べるようになる
```

---

## このチュートリアルで伝えたいこと

### タスク分割の本質的な難しさ

エージェント開発で「タスクを分割する」というと、多くの人は「どこで切るか」を考えます。
例えば「記事を書く」を「前半を書く → 後半を書く」のように分割するイメージです。

しかし、本当に難しいのは**「次のステップに何を渡すか」を設計すること**です。

```typescript
// ❌ 悪い例: 何を渡すか曖昧
researchStep → outlineStep
// 何が渡されるか分からない

// ✅ 良い例: スキーマで明確に定義
researchStep → { keyPoints[], targetAudience, tone } → outlineStep
// 次のステップが必要とする情報が明確
```

スキーマ設計を間違えると、後続のステップが「必要な情報が足りない」状態になり、
いくらプロンプトを調整しても品質が上がりません。

**Chapter 2で体験すること**: 最初のステップ(research)の出力スキーマが不十分だと、
後続の outline → write → review すべてが影響を受けてしまう問題。

---

### プロンプトの品質とアウトプットの関係

プログラミングでは、間違ったコードを書くと「エラー」が出ます。
しかし、エージェントに曖昧な指示を出しても**エラーにはなりません**。

代わりに「低品質なアウトプット」が返ってきます。

```typescript
// ❌ 曖昧な指示 → エラーにならないが品質が低い
"記事を書いてください"
→ 毎回違う構成、対象読者が不明確、トーンがバラバラ

// ✅ 明確な指示 → 安定した高品質なアウトプット
{
topic: "TypeScript",
targetAudience: "JavaScript経験者でTypeScriptは初めての人",
tone: "丁寧で具体例が多い",
structure: ["基本概念", "実践例", "よくある間違い"]
}
→ 期待通りの記事が生成される
```

この「曖昧さがエラーにならない」特性が、エージェント開発を難しくしています。

**Chapter 3で体験すること**: 同じエージェントでも、インプットの品質によって
アウトプットのスコアが大きく変わることを数値で確認します。

**ワークフローとの関係**: ワークフローの `inputSchema` を丁寧に設計することが、
実は最良のプロンプトエンジニアリングになります。スキーマが「良いインプットを強制する装置」として機能するからです。

---

## 使用しているパッケージ

| パッケージ | 用途 |
|---|---|
| `@mastra/core` | Agent, createTool, createWorkflow, createStep, createScorer |
| `@mastra/core/tools` | createTool(Chapter 5で使用) |
| `@mastra/memory` | Memory クラス(Chapter 4で使用) |
| `@mastra/libsql` | インメモリ SQLite ストレージ(Chapter 4で使用) |
| `@mastra/core/evals` | createScorer など(Chapter 3で使用) |
| `@mastra/mcp` | MCPServer(Chapter 7で使用) |
| `zod` | スキーマ定義(全章で使用) |