先日までAIエージェント(Microsoft Copilot)でバイブコーディングを楽しみましたが、ではAIエージェントってどう作るんだろうと疑問になり、今回はTypeScriptで簡単にAIエージェントを作ってみました。
環境
- VoltAgent(^2.0.0)
- NodoJS(>=20.19.0)
ツールの選択
AIといえばPythonですので、AIエージェント開発もPython必須と思っていた時期がありました。しかし私は、Pythonのような型安全性のない言語での開発があまり好きではありません。
数は少ないものの、TypeScriptでAIエージェントを開発できるフレームワークも存在します。
Geminiによると、以下のようにフレームワークを選択すると良さそうです。
| フレームワーク | 特徴・強み | 向いている用途 |
|---|---|---|
| Mastra | TypeScriptファースト、高いパフォーマンス、モダンな開発体験 | 高速なプロトタイピング、マルチエージェント開発 |
| LangChain.js / LangGraph.js | 柔軟なグラフ構造、LangSmithによる強力なデバッグ・観測機能 | 本番環境での運用、複雑な条件分岐が必要なタスク |
| VoltAgent | シンプルで軽量、実行プロセスの透明性が高い | 学習、特定のタスクに特化した軽量なエージェント |
| OpenAI Agents SDK | 公式SDK、軽量でOpenAIモデルとの親和性が極めて高い | OpenAIの最新機能を即座に活用したい場合 |
もちろん私は、VoltAgentを選びます。シンプルに作れるのが一番だよね。
今回つくるAIエージェント
Githubのユーザアカウントから様々な数値をとり、ポケモンのステータスに変換し、似ているポケモンを提案してくれるAIエージェントを作ります。もちろんポケモンや任天堂とは一切関係のないプロジェクトです。
なお、ポケモンはやったことがないので、仕様はぜんぶGeminiに考えてもらいました。
まず、Github APIを利用して以下の情報を収集します。
- ユーザー情報
- 公開リポジトリ情報
- コミット履歴
- プルリクエスト履歴
- レビューしたプルリクエスト
- 関与したクローズドイシュー
- 最近のイベント履歴
次に、収集した情報をポケモンのステータスに変換します。
ポケモンのタイプは主なプログラミング言語に基づいて決定します。
| ステータス | GitHubの指標 | 理由・解釈 |
|---|---|---|
| H (HP) | 総リポジトリ数 × 連続活動日数 | 生存戦略の広さと、開発を継続できる「タフさ」。 |
| A (攻撃) | 総コミット数 (Total Commits) | コードを積み上げ、プロダクトを前進させる直接的な「推進力」。 |
| B (防御) | プルリクエスト(PR)の作成・レビュー数 | 慎重にコードを検証し、バグの混入を防いで質を維持する「守備力」。 |
| C (特攻) | 獲得スター数 (Total Stars) | 周囲を惹きつけるコードの魅力や、広範囲に影響を及ぼす「拡散力」。 |
| D (特防) | クローズしたIssue数 / フォロワー数 | 批判やバグ報告に対処し、信頼を勝ち取ってコミュニティを維持する「精神力」。 |
| S (素早さ) | 直近の活動頻度 | Issueへの返信の速さや、最新技術へのキャッチアップの「スピード」。 |
今回は、エージェントとサブエージェントを利用して、以下の構成で作ります。
[main-agent]-[github-agent]
-[github-status-agent]
| エージェント名 | 説明 |
|---|---|
| main-agent | 全体の進行を管理し、ユーザーとの最終的なやり取りを担当する「司令塔」です。 |
| github-agent | GitHub APIを利用して、生のデータを取ってくる「調査員」です。 |
| github-status-agent | 数値データをポケモンの世界観に落とし込む「アナリスト兼クリエイター」です。 |
セットアップ
NodeJSはNode JS公式からインストールします。
まずは、VoltAgentのプロジェクトを作ります。
$ mkdir work-directory
$ npm create voltagent-app@latest agent
voltagent-appがVoltAgentのプロジェクトを作ってくれます。
-
agentがVoltAgentプロジェクトの名前です - 途中でAIの種類を聞かれますが、
gpt-4o-miniのままにしておきます - Open AIのAPIキーを求められますが、後で入力することにしてスルーします
次に、一度ビルドして実行します。
$ cd agent
$ npm i
$ npm run dev
http://localhost:3141/ にアクセスすると、VoltAgent API Runningと表示された画面が出てきます。以下のリンクがあります。
- Go to VoltOps Platform:VoltAgentが用意するリモートデバッグプラットフォームに移動できます
- Swagger UI:VoltAgentで開発したAIエージェントは自動的にREST APIとして公開されます
各種APIキーの入手・登録
ここでは、Open APIとGithubのAPIキーを入手して、agentプロジェクトに登録します。
Open APIのキー入手
https://ke1gtech.com/openai-api-free-low-cost/ を参考に、APIを発行します。
現在、Open APIではAPIを完全無料で利用することはできません。5.5$をクレジットした後、設定をいじれば幾分か無料で使わせてもらえます。
Github APIのキー入手
自分のGithubアカウントを持っていれば、Settings > Developer Settings > Personal access tokens > Fine-grained tokensでPATを発行できます。
PATを発行する際に失効期間や必要なアクセス権を聞かれるので、必要な分だけ設定してください。分からなければGeminiが教えてくれます。
APIキーの登録
キーを登録します。
# AI Provider Configuration
# Copy this file to .env and add your API keys
# OpenAI API Key
# Get your key at: https://platform.openai.com/api-keys
OPENAI_API_KEY=xxx // 修正
GITHUB_API_KEY=xxx // 追加
# VoltOps Observability
# For production monitoring and observability
# Get your keys at https://console.voltagent.dev/tracing-setup
# VOLTAGENT_PUBLIC_KEY=
# VOLTAGENT_SECRET_KEY=
# Node Environment
NODE_ENV=development
これで開発の準備は完了です。
プロジェクトの説明
どうやらエージェント開発の3要素は以下のようです。
| 要素 | 説明 |
|---|---|
| エージェント | AIエージェントの「人格」や「脳」にあたる中心的な要素 |
| ワークフロー | 複雑なタスクを完遂するための「実行手順」や「交通整理」にあたる要素 |
| ツール | エージェントが外部環境に働きかけるための「手足」にあたる要素 |
agentのsrcディレクトリを見ると、以下のような構成になっています。
エージェント開発の3要素がそのまま見て取れますね。
agent
- src
- index.ts
- workflows
- index.ts
- tools
- index.ts
- weather.ts
このうち、今回はワークフローを使いません。複雑なタスクは実行しないので、エージェントにお任せします。
Githubエージェントの開発
開発の初期段階として、Githubから情報を収集するエージェントを作ります。
ツールの開発
まずは、Github APIを利用して情報を取得するツールを作ります。
最初に、Githubのユーザー情報を取得するツールを定義します。
import { createTool } from "@voltagent/core";
import { z } from "zod";
export const getGithubUserInfo = createTool({
name: "getGithubUserInfo",
description: "GitHubユーザー名を指定して、そのユーザーの基本情報(名前、プロフィール、公開リポジトリ数、フォロワー数、Web/SNSリンクなど)を取得します。",
parameters: z.object({
githubId: z.string().describe("GitHubのユーザー名"),
}),
execute: async ({ githubId }) => {
const token = process.env.GITHUB_API_KEY;
const res = await fetch(`https://api.github.com/users/${githubId}`, {
headers: {
Authorization: `Bearer ${token}`,
},
});
const data = await res.json();
return {
login: data.login,
name: data.name,
bio: data.bio,
public_repos: data.public_repos,
followers: data.followers,
following: data.following,
blog: data.blog,
twitter: data.twitter_username,
html_url: data.html_url,
avatar_url: data.avatar_url,
location: data.location,
company: data.company,
};
},
});
VoltAgentのcreateToolメソッドを利用して、ツールを定義します。
| プロパティ | 説明 |
|---|---|
| name | ツールの名前です。 |
| description | ツールの内容を説明します。エージェントが読み取って利用を判断するので、大事な部分です。 |
| parameters | ツールの入力値です。 |
| execute | ツールの本体、実行する内容です。 |
executeの中でGithub APIを叩いていますが、詳細はGitHub REST API に関するドキュメントを参照してください。
合計7つのツールを作りますが、長くなるので完成品はリポジトリのソースコードをご覧ください。
- ユーザー情報 → 作った
- 公開リポジトリ情報
- コミット履歴
- プルリクエスト履歴
- レビューしたプルリクエスト
- 関与したクローズドイシュー
- 最近のイベント履歴
最後に、作ったツールをエージェントから参照するために、ツールをエクスポートします。
// Export all tools from this directory
export { weatherTool } from "./weather";
// 追加
export { getGithubUserInfo, getGithubRepos, getGithubUserCommits, getGithubUserPRs, getGithubUserReviewedPRs, getGithubUserInvolvesClosedIssues, getGithubUserEvents } from "./github";
エージェントの開発
次に、ツールを利用するエージェントを作ります。
Githubエージェントは本来サブエージェントですが、まずは実装して動作確認するために、メインのエージェントとして作ります。
import "dotenv/config";
import { VoltAgent, VoltOpsClient, Agent, Memory, VoltAgentObservability } from "@voltagent/core";
import { LibSQLMemoryAdapter, LibSQLObservabilityAdapter } from "@voltagent/libsql";
import { createPinoLogger } from "@voltagent/logger";
import { honoServer } from "@voltagent/server-hono";
import { z } from "zod";
import { expenseApprovalWorkflow } from "./workflows";
// 修正
import { weatherTool, getGithubUserInfo, getGithubRepos, getGithubUserCommits, getGithubUserPRs, getGithubUserReviewedPRs, getGithubUserInvolvesClosedIssues, getGithubUserEvents } from "./tools";
// 省略
// 追加
const githubAgent = new Agent({
name: "github-agent",
instructions: `
あなたはGithubのエキスパートアシスタントです。
ユーザーからGithub IDを受け取ったら、GitHubの以下の情報を取得してください。
・ユーザー情報
・公開リポジトリ情報
・コミット履歴
・プルリクエスト履歴
・レビューしたプルリクエスト
・関与したクローズドイシュー
・最近のイベント履歴
`,
parameters: z.object({
githubId: z.string().describe("GitHub ID"),
}),
model: "openai/gpt-4o-mini",
tools: [getGithubUserInfo, getGithubRepos, getGithubUserCommits, getGithubUserPRs, getGithubUserReviewedPRs, getGithubUserInvolvesClosedIssues, getGithubUserEvents],
});
// 省略
new VoltAgent({
agents: {
githubAgent, // 修正
},
// 修正
// workflows: {
// expenseApprovalWorkflow,
// },
server: honoServer(),
logger,
observability,
voltOpsClient: new VoltOpsClient({
publicKey: process.env.VOLTAGENT_PUBLIC_KEY || "",
secretKey: process.env.VOLTAGENT_SECRET_KEY || "",
}),
});
VoltAgentのAgentクラスを利用して、エージェントを定義します。
| プロパティ | 説明 |
|---|---|
| name | エージェントの名前です。 |
| instructions | エージェントに与える命令を記述します。AIチャットするときのプロンプトと同じノリで書けばOKです。大事な部分です。 |
| parameters | エージェントの入力値です。 |
| model | エージェントが利用するAIのモデル名です。 |
| tools | エージェントが利用するツールです。 |
ツールとエージェントのテスト
ここまでのコードをビルドすると、作ったツールとエージェントは個別にテストできます。
$ npm run dev
http://localhost:3141/ にアクセスし「Go to VoltOps Platform」リンクをクリックすると、リモートデバッグプラットフォームに遷移します。Githubのアカウントでログインすると、簡単な質問をされた後プラットフォームにたどり着きます。
サイドバーメニューから「Agents & Workflows」を選択すると、エージェント・ワークフロー・ツールの一覧が表示されます。ツールはAPIのテストをするノリで、エージェントはチャットをするノリでテストできます。
- ツールを選んだら入力値に
yoshikawaaを入力して、結果を見てみましょう - エージェントを選んだら入力値に
Github ID: yoshikawaaを入力して、結果を見てみましょう
ポケモンのステータスへの変換エージェントの開発
次の段階として、Githubから取得したデータをポケモンのステータスに変換するエージェントを作ります。
このエージェントでは外部APIを利用しないので、ツールは作らず、エージェントのみ作ります。
// 省略
// 追加
const githubStatusAgent = new Agent({
name: "github-status-agent",
instructions: `
あなたはGithubとポケモンのエキスパートアシスタントです。
ユーザーからGithub情報を受け取ったら、ポケモンのステータスに変換してHABCDS形式で出力してください。
HABCDSの各指標は以下のGitHubの指標に基づいて、値を必ず0から255の範囲で算出してください。
ステータスから類似したポケモンを3体ピックアップして、その画像を表示して類似する理由も説明してください。
また、取得した情報をもとに、ユーザーのGitHubでの活動内容を詳しく説明してください。
活動内容には、主なプロジェクト、得意分野、コミュニティでの役割などを含めてください。
ステータス GitHubの指標 理由・解釈
・H (HP) 総リポジトリ数 × 連続活動日数 生存戦略の広さと、開発を継続できる「タフさ」。
・A (攻撃) 総コミット数 (Total Commits) コードを積み上げ、プロダクトを前進させる直接的な「推進力」。
・B (防御) プルリクエスト(PR)の作成・レビュー数 慎重にコードを検証し、バグの混入を防いで質を維持する「守備力」。
・C (特攻) 獲得スター数 (Total Stars) 周囲を惹きつけるコードの魅力や、広範囲に影響を及ぼす「拡散力」。
・D (特防) クローズしたIssue数 / フォロワー数 批判やバグ報告に対処し、信頼を勝ち取ってコミュニティを維持する「精神力」。
・S (素早さ) 直近の活動頻度 Issueへの返信の速さや、最新技術へのキャッチアップの「スピード」。
・タイプ: 以下の主要言語とポケモンのタイプから、主なプログラミング言語に基づいて決定してください。
主要言語とポケモンのタイプ
言語 ポケモンタイプ 理由・イメージ
・Python くさ / どく データサイエンスやAIの分野で根を張る圧倒的な汎用性。スクリプトとしての即効性も魅力。
・JavaScript でんき ブラウザ上でビビッと動く動的な性質。Webの世界を駆け巡るスピード感。
・TypeScript はがね / でんき JSに堅牢な型(鎧)を装備。バグを弾く防御力の高さ。
・C / C++ はがね / いわ 圧倒的なハードウェアへの近さと、処理速度の硬さ。重厚で基盤を支える存在。
・Go ひこう / はがね 爆速のコンパイルと軽量な並行処理。シンプルで無駄のない洗練されたフォルム。
・Rust はがね / ほのお 安全性(はがね)を極めつつ、爆発的なパフォーマンスを引き出す情熱。
・Java ノーマル / エスパー どんな環境でも動く汎用性と、仮想マシン(JVM)という高度な仕組み。
・Ruby フェアリー 「楽しさ」を重視した設計思想。書き心地の優雅さとマジカルなメタプログラミング。
・PHP みず Webの歴史とともに広く浸透し、どこにでも馴染む柔軟性と生命力。
・Swift / Kotlin エスパー / ひこう モバイルという空中戦の主役。洗練されたモダンな機能。
絶対に以下のMarkdownフォーマットで出力してください。
## {Githubのユーザ名}さんのステータス
| ステータス | GitHubの指標 | 理由・解釈 |
| --- | --- | --- |
| **H (HP)** 活動の広さと持続力 | {値} | {理由・解釈} |
| **A (攻撃)** コードの推進力 | {値} | {理由・解釈} |
| **B (防御)** コードの守備力 | {値} | {理由・解釈} |
| **C (特攻)** コードの拡散力 | {値} | {理由・解釈} |
| **D (特防)** コミュニティ精神力 | {値} | {理由・解釈} |
| **S (素早さ)** 技術キャッチアップの速さ | {値} | {理由・解釈} |
| **タイプ** 得意な技術分野 | {タイプ} | {理由・イメージ} |
## {Githubのユーザ名}さんの類似したポケモン
* {ポケモン1}{画像}
* 理由: {理由}
* {ポケモン2}{画像}
* 理由: {理由}
* {ポケモン3}{画像}
* 理由: {理由}
## {Githubのユーザ名}さんのGitHubでの活動内容
{活動内容}
### 主なプロジェクト
{主なプロジェクト}
### 得意分野
{得意分野}
### コミュニティでの役割
{コミュニティでの役割}
`,
parameters: z.object({
githubData: z.string().describe("GitHubの情報"),
}),
model: "openai/gpt-4o",
});
// 省略
- ここでは、
parametersでGithubエージェントが取得したデータをgithubDataとして受け取ります -
instructionsに指定するプロンプトが非常に長くなりました。「必ず」や「絶対に」という表現が出てくるのは、これがないとエージェントが指示に従わないことが多かったからです - 指示が複雑なので
modelをgpt-4oにランクアップさせています
エージェントのテスト
Githubエージェントと同様に動作確認したいところですが、「Githubエージェントが取得したデータ」を入力値としてテストが大変なため、スキップします。
メインエージェントの開発
最後の段階として、今までのサブエージェントをオーケストレーションするメインエージェントを作ります。
このエージェントでも外部APIを利用しないので、ツールは作らず、エージェントのみ作ります。
// 省略
// 追加
const mainAgent = new Agent({
name: "main-agent",
instructions: `
あなたはGitHubの情報をまとめてポケモンのステータスを生成するエージェントです。
以下の手順に従って、GitHubの情報をまとめてポケモンのステータスを生成してください。
# 手順
0. 「GitHub ID: (GitHubのユーザーID)」と入力されたらGitHub IDとして取得する。
1. GitHub IDをgithub-agentに渡して、GitHubの情報を取得してください。それを変数githubDataに格納してください。
2. githubDataをgithub-status-agentに渡して、ポケモンのステータスを生成してください。
# 厳守事項
- 入力文からGitHub IDを必ず抽出して処理してください。
- サブエージェントやツールを呼び出す際にも、確認や同意のプロンプトは一切表示せず、ユーザーの追加アクションを求めないでください。
- GitHub IDが見つからない場合は「情報がありません」として進めてください。
`,
parameters: z.object({
prompt: z.string().describe("GitHub IDを含む自然文入力"),
}),
model: "openai/gpt-4o",
subAgents: [githubAgent, githubStatusAgent],
});
new VoltAgent({
agents: {
mainAgent, // 修正
},
// workflows: {
// expenseApprovalWorkflow,
// },
server: honoServer(),
logger,
observability,
voltOpsClient: new VoltOpsClient({
publicKey: process.env.VOLTAGENT_PUBLIC_KEY || "",
secretKey: process.env.VOLTAGENT_SECRET_KEY || "",
}),
});
VoltAgentのAgentクラスを利用して、エージェントを定義します。
| プロパティ | 説明 |
|---|---|
| name | エージェントの名前です。 |
| instructions | エージェントに与える命令を記述します。ここでサブエージェントを呼び出す順番ややり取りするデータについて詳細に取り決めています。 |
| parameters | エージェントの入力値です。promptを指定して、自然言語を受け取って解析できるようにしています。 |
| model | エージェントが利用するAIのモデル名です。オーケストレーションも難しいらしく、モデルはgpt-4oにランクアップしています。 |
| subAgents | エージェントが利用するサブエージェントです。 |
エージェントのテスト
ここまでのコードをビルドすると、メインエージェントをテストできます。ただし、サブエージェントとサブエージェントが利用するツールは逆にテストできなくなってしまいます。
$ npm run dev
http://localhost:3141/ にアクセスし「Go to VoltOps Platform」リンクをクリックし、サイドバーメニューから「Agents & Workflows」を選択すると、エージェント・ワークフロー・ツールの一覧が表示されます。メインエージェントに統合される形でサブエージェントが表示されるのを確認できます。
- エージェントを選んだら入力値に
Github ID: yoshikawaaを入力して、結果を見てみましょう
Github APIから情報を取得する以外の部分はAIが考えているので、同じデータでも毎回微妙に異なる結果が出力されます。
Swagger UIを利用したエージェントのテスト
http://localhost:3141/ にアクセスし「Swagger UI」リンクをクリックすると、Swagger UIを利用してREST APIとしてエージェントを実行できます。
- POST
/agents/{id}/textを選択して、idにmain-agentを入力し、ボディのinputにGithub ID: yoshikawaaを入力して、結果を見てみましょう
VoltOpsプラットフォームで実行したときと同様のMarkdownが出力されます。これでクライアントアプリケーションとの連携も簡単ですね。
まとめ
今回はVoltAgentを利用して、TypeScriptで簡単にAIエージェントを作ってみました。
内部的にはMCP(Anthropic社が2024年に公開した、AIアプリと外部データ・ツールを標準化された手法で接続するオープンプロトコル)とかいろいろ面倒なことがあるみたいですが、そこらへんは気にせずほぼやりたいことを書くだけでAIエージェントを実装できることが分かりました。しかし、AIチャットと同様にプロンプトとAIの機嫌により結果が変わってくるので、実務で使うのにはまた一苦労ありそうだな、という感想です。
今回は実験的にOpen AIを利用してみただけですが、今後はアプリケーションに組み込んで実践的にAIを使ってみたいですね。
なお、初学には https://qiita.com/Sicut_study/items/f0e7503e18c76e2441d9 が大変参考になりました。ありがとうございます。