{"id":47918031,"url":"https://github.com/snickerjp/mastra-agent-tutorial","last_synced_at":"2026-04-04T05:47:44.185Z","repository":{"id":346312453,"uuid":"1189300805","full_name":"snickerjp/mastra-agent-tutorial","owner":"snickerjp","description":null,"archived":false,"fork":false,"pushed_at":"2026-04-01T16:57:50.000Z","size":186,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-04T05:47:42.776Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Jupyter Notebook","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/snickerjp.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-23T07:18:15.000Z","updated_at":"2026-04-01T16:57:54.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/snickerjp/mastra-agent-tutorial","commit_stats":null,"previous_names":["snickerjp/mastra-agent-tutorial"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/snickerjp/mastra-agent-tutorial","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/snickerjp%2Fmastra-agent-tutorial","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/snickerjp%2Fmastra-agent-tutorial/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/snickerjp%2Fmastra-agent-tutorial/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/snickerjp%2Fmastra-agent-tutorial/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/snickerjp","download_url":"https://codeload.github.com/snickerjp/mastra-agent-tutorial/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/snickerjp%2Fmastra-agent-tutorial/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31389391,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T04:26:24.776Z","status":"ssl_error","status_checked_at":"2026-04-04T04:23:34.147Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-04-04T05:47:43.579Z","updated_at":"2026-04-04T05:47:44.169Z","avatar_url":"https://github.com/snickerjp.png","language":"Jupyter Notebook","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Mastra エージェント チュートリアル\n\n技術ブログ記事を自動生成するエージェントシステムを、段階的に作りながら学ぶハンズオンです。\n\n## このチュートリアルで学べること\n\n- エージェント開発で避けて通れない**タスク分割の難しさ**\n- プロンプトの品質がアウトプットに与える影響を**数値で比較**\n- **ツール**でエージェントに「行動」させる方法\n- **構造化出力**でプログラムから扱えるデータを得る方法\n- **MCP サーバー**で自作ツールを外部クライアントに公開する方法\n\n---\n\n## セットアップ\n\n```bash\nnpm install\n```\n\n### OpenAI版\n\n```bash\ncp .env.example .env\n# .env ファイルを開いて OPENAI_API_KEY を設定してください\n```\n\n### AWS Bedrock版（SageMaker Studio等）\n\n`.env` ファイルは不要です。IAM Roleで認証されます。\n\n**使用モデル**: Amazon Nova Lite（入力: $0.06/1M tokens, 出力: $0.24/1M tokens）\n\n---\n\n## チュートリアルの流れ\n\n```mermaid\ngraph LR\n    A[Chapter 1\u003cbr/\u003e単純な\u003cbr/\u003e1エージェント] --\u003e B[Chapter 2\u003cbr/\u003eタスクを\u003cbr/\u003e分割する]\n    B --\u003e C[Chapter 3\u003cbr/\u003eプロンプトの\u003cbr/\u003e品質を測る]\n    C --\u003e D[Chapter 4\u003cbr/\u003eメモリで\u003cbr/\u003e改善を重ねる]\n    D --\u003e E[Chapter 5\u003cbr/\u003eツールで\u003cbr/\u003e行動する]\n    E --\u003e F[Chapter 6\u003cbr/\u003e構造化\u003cbr/\u003e出力]\n    F --\u003e G[Chapter 7\u003cbr/\u003eMCP\u003cbr/\u003eサーバー]\n```\n\n---\n\n### Chapter 1: 「全部やって」の落とし穴\n\n```bash\n# OpenAI版\nnpm run ch1\n\n# Bedrock版\nnpm run ch1:bedrock\n```\n\n**何を体験するか**\n- 1つのエージェントに記事全体を任せると、どんな問題が起きるか\n- 同じ指示でも、毎回違う結果が返ってくる不安定さ\n- 「どこを直せばいいのか」が分からない状態\n\n---\n\n### Chapter 2: タスクを分割して設計する\n\n```bash\n# OpenAI版\nnpm run ch2\n\n# Bedrock版\nnpm run ch2:bedrock\n```\n\n**何を体験するか**\n- `research → outline → write → review` のワークフロー設計\n- **分割の本質的な難しさ**: 「どこで切るか」より「何を渡すか（スキーマ設計）」が重要\n- 最初のステップ（research: リサーチ）の品質が、後続すべてに影響する問題\n- 間違った分割の例: 「前半を書く → 後半を書く」（文脈が途切れてしまう）\n\n**ワークフローの流れ**\n```\nresearchStep (リサーチ)  → { keyPoints[], targetAudience, tone }\n  ↓\noutlineStep (構成設計)   → { sections[], targetAudience, tone }\n  ↓\nwriteStep (執筆)        → { draft, targetAudience }\n  ↓\nreviewStep (レビュー)    → { revisions[], article }\n```\n\n---\n\n### Chapter 3: プロンプトの品質を測る\n\n```bash\n# OpenAI版\nnpm run ch3\n\n# Bedrock版\nnpm run ch3:bedrock\n```\n\n**何を体験するか**\n- 3つのパターンを同じ評価基準で比較し、スコアの違いを確認する\n\n| パターン | instructions | リクエスト | 期待スコア |\n|---|---|---|---|\n| A（最悪）| 「記事を書いてください」 | 「TypeScriptについて」 | 低 |\n| B（中程度）| 詳しい役割設定あり | トピックのみ | 中 |\n| C（最良）| 詳しい役割設定あり | 対象読者・構成・トーン指定あり | 高 |\n\n**評価の観点**\n- `指示準拠スコア`: instructions の要件をどれだけ守っているか\n- `コンテンツ品質`: 構成・深さ・具体性・読者への適切さの総合評価\n\n---\n\n### Chapter 4: メモリで改善を重ねる\n\n```bash\n# OpenAI版\nnpm run ch4\n\n# Bedrock版\nnpm run ch4:bedrock\n```\n\n**何を体験するか**\n- メモリなし: 「2番目のタイトルに決めた」 → 何の2番目か分からず的外れな回答\n- メモリあり: 同じ `thread` で送ると前の提案を覚えていて、「2番目」が通じる\n\n```typescript\n// memory: { thread, resource } で会話を識別\nawait agent.generate(\"TypeScriptの型システムについてブログ記事を書きたい。タイトル候補を3つ提案して。\", {\n  memory: { thread: \"session-1\", resource: \"user-1\" }\n});\n\n// 同じ thread → 前の提案を覚えている\nawait agent.generate(\"2番目のタイトルに決めた。そのタイトルで記事の構成案（章立て）を5つ出して。\", {\n  memory: { thread: \"session-1\", resource: \"user-1\" }\n});\n```\n\n---\n\n### Chapter 5: ツールを使うエージェント\n\n```bash\n# OpenAI版\nnpm run ch5\n\n# Bedrock版\nnpm run ch5:bedrock\n```\n\n**何を体験するか**\n- `createTool` でエージェントが使える「アクション」を定義する\n- エージェントが `searchTopic` / `getCurrentDate` ツールを自律的に呼び出す\n- Chapter 1（ツールなし）と比べて、事実に基づいた記事が生成される\n\n**お題**: TypeScriptの最新動向\n\n```typescript\n// ツールを定義して Agent に渡すだけ\nconst agent = new Agent({\n  tools: { getCurrentDate, searchTopic },\n  // ...\n});\n// → LLM が description と inputSchema を見て、いつ呼ぶかを自律的に判断\n```\n\n---\n\n### Chapter 6: 構造化出力（Structured Output）\n\n```bash\n# OpenAI版\nnpm run ch6\n\n# Bedrock版\nnpm run ch6:bedrock\n```\n\n**何を体験するか**\n- Zod スキーマで出力の型を定義し、型付きオブジェクトとして受け取る\n- `article.title`, `article.sections[0].heading` のようにプログラムでアクセスできる\n- JSON シリアライズ可能 → DB保存、API返却等の後続処理に使える\n\n**お題**: TypeScriptの型システムを活用したバグ防止テクニック\n\n```typescript\n// ブログ記事の出力構造を定義する Zod スキーマ\nconst BlogArticleSchema = z.object({\n  title: z.string(),\n  sections: z.array(z.object({ heading: z.string(), body: z.string() })),\n  tags: z.array(z.string()),\n});\n\nconst result = await agent.generate(messages, {\n  structuredOutput: {\n    schema: BlogArticleSchema,\n  },\n});\n\n// スキーマで検証してから、型付きオブジェクトとして扱う\nconst article = BlogArticleSchema.parse(result.object);\n```\n\n---\n\n### Chapter 7: MCP サーバーを作る\n\n```bash\nnpm run ch7\n```\n\n※ MCP サーバーは LLM を使わないため、OpenAI/Bedrock の区別はありません。\n\n**何を体験するか**\n- Chapter 5 で作ったツールを MCP (Model Context Protocol) サーバーとして公開する\n- `MCPServer` + `startStdio()` で、Cursor / Windsurf / Claude Desktop 等から接続可能に\n- 「自分のツールを他のアプリに提供する」という MCP の基本を理解する\n\n```typescript\nimport { MCPServer } from \"@mastra/mcp\";\nimport { getCurrentDate, searchTopic } from \"../chapter5/tools.js\";\n\nconst server = new MCPServer({\n  id: \"blog-research-server\",\n  name: \"Blog Research MCP Server\",\n  version: \"1.0.0\",\n  tools: { getCurrentDate, searchTopic },\n});\n\nserver.startStdio();\n// → MCP クライアントから get-current-date / search-topic が呼べるようになる\n```\n\n---\n\n## このチュートリアルで伝えたいこと\n\n### タスク分割の本質的な難しさ\n\nエージェント開発で「タスクを分割する」というと、多くの人は「どこで切るか」を考えます。\n例えば「記事を書く」を「前半を書く → 後半を書く」のように分割するイメージです。\n\nしかし、本当に難しいのは**「次のステップに何を渡すか」を設計すること**です。\n\n```typescript\n// ❌ 悪い例: 何を渡すか曖昧\nresearchStep → outlineStep\n// 何が渡されるか分からない\n\n// ✅ 良い例: スキーマで明確に定義\nresearchStep → { keyPoints[], targetAudience, tone } → outlineStep\n// 次のステップが必要とする情報が明確\n```\n\nスキーマ設計を間違えると、後続のステップが「必要な情報が足りない」状態になり、\nいくらプロンプトを調整しても品質が上がりません。\n\n**Chapter 2で体験すること**: 最初のステップ（research）の出力スキーマが不十分だと、\n後続の outline → write → review すべてが影響を受けてしまう問題。\n\n---\n\n### プロンプトの品質とアウトプットの関係\n\nプログラミングでは、間違ったコードを書くと「エラー」が出ます。\nしかし、エージェントに曖昧な指示を出しても**エラーにはなりません**。\n\n代わりに「低品質なアウトプット」が返ってきます。\n\n```typescript\n// ❌ 曖昧な指示 → エラーにならないが品質が低い\n\"記事を書いてください\"\n→ 毎回違う構成、対象読者が不明確、トーンがバラバラ\n\n// ✅ 明確な指示 → 安定した高品質なアウトプット\n{\n  topic: \"TypeScript\",\n  targetAudience: \"JavaScript経験者でTypeScriptは初めての人\",\n  tone: \"丁寧で具体例が多い\",\n  structure: [\"基本概念\", \"実践例\", \"よくある間違い\"]\n}\n→ 期待通りの記事が生成される\n```\n\nこの「曖昧さがエラーにならない」特性が、エージェント開発を難しくしています。\n\n**Chapter 3で体験すること**: 同じエージェントでも、インプットの品質によって\nアウトプットのスコアが大きく変わることを数値で確認します。\n\n**ワークフローとの関係**: ワークフローの `inputSchema` を丁寧に設計することが、\n実は最良のプロンプトエンジニアリングになります。スキーマが「良いインプットを強制する装置」として機能するからです。\n\n---\n\n## 使用しているパッケージ\n\n| パッケージ | 用途 |\n|---|---|\n| `@mastra/core` | Agent, createTool, createWorkflow, createStep, createScorer |\n| `@mastra/core/tools` | createTool（Chapter 5で使用） |\n| `@mastra/memory` | Memory クラス（Chapter 4で使用） |\n| `@mastra/libsql` | インメモリ SQLite ストレージ（Chapter 4で使用） |\n| `@mastra/core/evals` | createScorer など（Chapter 3で使用） |\n| `@mastra/mcp` | MCPServer（Chapter 7で使用） |\n| `zod` | スキーマ定義（全章で使用） |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsnickerjp%2Fmastra-agent-tutorial","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsnickerjp%2Fmastra-agent-tutorial","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsnickerjp%2Fmastra-agent-tutorial/lists"}