20260311_[toranoana.deno#24]DenoでAIエージェント作ってみた

215 Views

March 11, 26

スライド概要

toranoana.deno#24の発表資料です
https://yumenosora.connpass.com/event/383916/

profile-image

虎の穴ラボ株式会社は、主にとらのあな関連サービスのシステム開発を専門に担う、エンジニアの会社です。

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

ダウンロード

関連スライド

各ページのテキスト
1.

DenoでAIエージェント作ってみた 虎の穴ラボ株式会社 藤原佳顕 Copyright (C) 2026 Toranoana Lab Inc. All Rights Reserved. 1

2.

目次 1. 自己紹介 2. 概要 3. AIエージェントについて 4. 12-Factor Agents/Building effective agents 5. Denoでの実装 6. まとめと今後 Copyright (C) 2026 Toranoana Lab Inc. All Rights Reserved. 2

3.

自己紹介ページ (藤原) 藤原 佳顕(ふじわら よしあき) : yoshiaki fujiwara、8年目 ‣ ‣ 新規事業担当( Fantia、Creatia)、アーキテクトチーム (CSIRTも)、AI推進チーム 出身 ‣ 大学:情報系(数学より) ‣ 前職:独立系ソフトウェア会社、主に GISとWeb、ライブラリ開発 ‣ TypeScript、Ruby on Rails、C#、C++、React、Vue、Angular ‣ 入社理由 ‣ ‣ 自分がスキルアップできそうな場所に行きたい オタク系の話ができるところに行きたい 好きなモノ ‣ ‣ ‣ シューティングゲーム、格闘ゲーム、アトラスのゲーム SF小説 プログラミング Copyright (C) 2026 Toranoana Lab Inc. All Rights Reserved. 3

4.

概要 ● DenoでAIエージェントを作ってみようとして色々試行錯誤した結果 の共有になります ● 簡単に、AIで呼び出せる形で以下を実装した話をします ○ 外部通信(今回はOpen Weather Map API利用) ○ ファイル読み書き ○ 任意コード実行 ○ 危険な操作前のユーザー確認 ○ 実行状態保存 Copyright (C) 2026 Toranoana Lab Inc. All Rights Reserved. 4

5.

AIエージェントについて ● ● ● 以下資料から定義を拝借します ○ https://www.gartner.co.jp/ja/articles/ai-agents `AIの手法を使って状況を把握し、判断し、行動し、目標を達成できる自律(または半 自律)のソフトウェアです。` 具体的な事例 ○ コーディングエージェント ■ Claude Code、Gemini CLI、Codex、GitHub Copilot Agent mode、 Junie、Devin ■ 実装したい機能を与えると自動でファイル読み取り等々をして計画から実 装まで動作してくれる Copyright (C) 2026 Toranoana Lab Inc. All Rights Reserved. 5

6.

12-Factor Agents/Building effective agents ● ● AIエージェントを構築するにあたっていくつかプラクティス的な資料が出ている 12-Factor Agents ○ https://github.com/humanlayer/12-factor-agents ○ AIエージェントを組み立てるにあたって重要な12の要素についてまとめられている資料 ○ 元ネタ:https://12factor.net/ja/ ○ 本番環境に耐えうる信頼性やスケーラビリティを備えたエージェント構築を目的としている ○ 主なポイント: ■ プロンプトとコンテキストの自己管理:プロンプトをコードとして管理し、コンテキストを戦略的に 最適化する(Factor 2, 3) ■ ステートレスな設計と状態の統合:エージェント自体は状態を持たない関数(Stateless Reducer)と して設計し、実行状態などはすべて外部で管理・統合する(Factor 5, 12) ■ 小さく特化させる: 1つの巨大で万能なエージェントを作るのではなく、特定のタスクに特化した小 さなエージェントを組み合わせる(Factor 10) ■ 人間との連携(Human-in-the-loop):人間による確認や介入が必要な場面も、例外処理ではなく 「ツール呼び出しの1つ」として組み込む(Factor 7) Copyright (C) 2026 Toranoana Lab Inc. All Rights Reserved. 6

7.

12-Factor Agents/Building effective agents ● ● ● ● ● Building effective agents Anthropicによる、実際のプロダクトで成功しているエージェント実装のベストプラクティスをまとめた資料 ○ https://www.anthropic.com/engineering/building-effective-agents 複雑なフレームワークに依存するのではなく、シンプルで構成可能なパターンの組み合わせを推奨している 【実装方針】フレームワークへの過度な依存を避ける: ○ 既存のフレームワークは過剰な抽象化によって背後のプロンプトやレスポンスを隠蔽してしまい、デバッ グを困難にする。まずはAPIを直接呼び出すシンプルなコードから始めることを推奨している 「ワークフロー」と「エージェント」の明確な区別: ○ ワークフロー:開発者が定義したコードの経路に従ってLLMやツールを動かす ○ エージェント:LLM自身が動的にプロセスやツール使用を決定する ○ 3つのコア原則: ■ シンプルさの維持 (Simplicity): 必要性が実証されない限り複雑なエージェントシステムは導入せ ず、まずはシンプルなプロンプトやワークフローから始める ■ 透明性の確保 (Transparency): エージェントの計画や推論ステップを明示的に出力させ、プロセス を可視化する ■ ACI(Agent-Computer Interface)の最適化: 人間向けのUI(HCI)に労力をかけるのと同じよう に、AIがツールを使いやすいように設計する。ツールのフォーマットを工夫し、詳細なドキュメン ト化とテストを徹底する Copyright (C) 2026 Toranoana Lab Inc. All Rights Reserved. 7

8.

Denoでの実装 ● ● ● ● これらの記事を参考に以下の方針でDenoでAIエージェントを作ってみる Non-Framework:LangChainといったフレームワーク・ライブラリを利用せず、公式のSDKや APIの実行のみに留める ツールは一旦以下の実装で考える ○ 外部API実行:今回はOpen Weather Map APIでの指定都市の天気取得のみに限定 ○ ファイル読み書き:指定された特定のファイルを読み書きする ○ コード実行:Deno Sandboxで必要なときに必要なコードをその場で作って実行する ○ ユーザー認可:必要なときにユーザーの許可を求める 具体的な実装方法 ○ 12-Factor AgentsやBuilding effective agentsを元にした実装方針をドキュメント化 ○ README.mdやAGENTS.mdにどういったときにどういったファイルを参照するべきか記載 して実装する Copyright (C) 2026 Toranoana Lab Inc. All Rights Reserved. 8

9.

Denoでの実装 ● ● 今回実装の方針 ○ ローカルLLMの活用: OpenAI SDKを利用しつつ、Ollama (qwen3.5:4b等) に接続 ○ ステートフルな設計: Deno KVを活用したセッション状態の永続化 ○ セキュアな実行環境: 重要な操作へのユーザー承認介入(Human-in-the-loop)と、Deno Sandboxによる安全なコード実行 今回のコードは、大きく以下の5つで構成 ○ 状態管理: Deno KVを用いた実行状態の保存・復元 ■ CLIだとあんまり関係ないが、Webアプリとかだと中断、再実行が必要 ○ LLMクライアント: OpenAI互換APIを通じたローカルLLMへの接続 ■ 本番ではOpen AI APIを利用する想定 ○ ツールの定義: LLMが利用可能なアクション(Function Calling)のスキーマ定義 ○ ツールの実行ロジック: 各アクションの具体的な処理と、セキュアな実行制御(execute_step) ○ メイン制御ループ: LLMの推論とツール実行を繰り返すオーケストレーション(runAgent) Copyright (C) 2026 Toranoana Lab Inc. All Rights Reserved. 9

10.

Denoでの実装 Copyright (C) 2026 Toranoana Lab Inc. All Rights Reserved. 10

11.

Denoでの実装 ● ドキュメントの例:基本的に今までの資料の内容を守らせるような内容→突然LangChainなどを採用しないようにとか ## アーキテクチャの基本方針 - **複雑なフレームワークの回避**: 抽象度の高いエージェントフレームワークに依存せず、決定論的なソフトウェアコードによってプロンプト と制御フローを管理する。 - **ワークフロー優先**: 最初から完全自律型エージェントを目指すのではなく、まずはルーティングやプロンプトチェーンを用いた予測可能な ワークフローを構築する。 ## 追加コンテキスト(最重要) **IMPORTANT:** タスクを開始する前に、そのタスクに関連する以下のドキュメントを必ず読み、コンテキストをロードしてから作業を行うこと。 - エージェントの全体設計を行う場合: `docs/architecture/agent-principles.md` - ツール(Tools)の定義や追加を行う場合: `docs/guidelines/tool-design.md` - 状態管理や制御フローを実装する場合: `docs/guidelines/state-and-flow.md` - 評価(Evals)やテストを実装する場合: `docs/guidelines/evals-and-errors.md` - 過去のバグや落とし穴の知見: `docs/gotchas/agent-pitfalls.md` Copyright (C) 2026 Toranoana Lab Inc. All Rights Reserved. 11

12.

Denoでの実装 ● 代表してagent-principles.mdの内容紹介(一部) # エージェント設計の原則 (Agent Principles) このドキュメントは、本プロジェクトにおけるAIエージェントの全体的な構造と設計の方向性を定義します。「12-Factor Agents」および「Anthropicのワークフロー パターン」のベストプラクティスに基づいています。 コーディングエージェントは、新規エージェントの実装やアーキテクチャ設計を行う際、必ず以下の原則に従ってコードを生成してください。 ## 1. 複雑なフレームワークへの依存回避 (Avoid Complex Frameworks) - **原則**: 高度な抽象化を提供するエージェントフレームワーク(LangChainの奥深い機能など)に過度に依存せず、決定論的なアプリケーションコードを用いて制 御フローを記述してください - **理由**: 抽象化層が厚すぎると、LLMへ送信されるプロンプトや制御フローが隠蔽され、本番環境でのデバッグ、チューニング、および意図しない挙動の修正が困 難になるためです ## 2. 小さく特化したエージェント (Small, Focused Agents) - **原則**: あらゆるタスクをこなす「万能(モノリス)エージェント」を作成しないでください - **実装**: 代わりに、3〜10ステップ程度で完了する、特定のタスク(意図分類、データ検索、アクション実行、回答生成など)に特化した「マイクロエージェン ト」を構築してください - **理由**: 責務を小さく保つことで、コンテキストウィンドウの消費を抑え、LLMの幻覚(ハルシネーション)を防ぎ、テストとデバッグを容易にします Copyright (C) 2026 Toranoana Lab Inc. All Rights Reserved. 12

13.
[beta]
Denoでの実装
●

状態管理(Deno KV):タスクの中断、続行ができるようにセッションごとの状態を永続化する機能

export interface AgentState {

export async function saveState(state: AgentState): Promise<void> {

sessionId: string;

const kv = await Deno.openKv();

messages: ChatCompletionMessageParam[];

try {
const stored: AgentStateStored = {

iterations: number;

...state,

status: "running" | "paused" | "completed" | "error";

// Deno KVで扱いやすいようDate型を文字列に変換

createdAt: Date;

createdAt: state.createdAt.toISOString(),

updatedAt: Date;

updatedAt: state.updatedAt.toISOString(),
};

userMessage?: string; // 元のユーザーメッセージ

await kv.set(["agent_sessions", state.sessionId], stored);

}

} finally {
kv.close();
}
}

Copyright (C) 2026 Toranoana Lab Inc. All Rights Reserved.

13

14.

Denoでの実装 ● ● ● ● ● ● ● ツール周りの全体像 現段階では以下7つをツールとして実装 データ取得・計算 ○ get_weather: OpenWeatherMapから天気を取得 ○ add: 数値の加算(ほとんどお試し用) Human-in-the-loop (人間による介入) ○ request_human_input: ユーザーへの質問 ○ request_approval: 実行許可の要求 ファイル・コード操作(要承認) ○ read_file / write_file: ファイルの読み書き ○ code_interpreter: サンドボックス内でのコード実行 次から実装例を提示しますが、ある程度簡略化して紹介します AIだからといって普通の関数実装で良いということがわかればいいかなと思います Copyright (C) 2026 Toranoana Lab Inc. All Rights Reserved. 14

15.
[beta]
Denoでの実装
●

実装例:エージェントの実行 (runAgent)
○
LLMの推論(次に何をするか)と、ツールの実行を繋ぎ合わせる

export async function runAgent(userMessage: string, options:

// 2. ツール呼び出し要求があれば、実行して結果を履歴に積む

RunAgentOptions = {}) {

if (nextStep.tool_calls) {

// ... 状態の初期化・復元 ...

for (const call of nextStep.tool_calls) {

try {

const result = await execute_step(call, state.messages);

while (state.iterations < maxIterations) {

state.messages.push(result);

state.iterations++;

}
} else {

// 1. LLMにこれまでの会話履歴を渡し、次のアクションを決定させる

// 3. ツール呼び出しがなければ、最終回答としてループを抜ける

const nextStep = await determine_next_step(state.messages);

break;

state.messages.push(nextStep);

}
}
} catch (error) {
// エラーハンドリングと状態の保存
}
}
Copyright (C) 2026 Toranoana Lab Inc. All Rights Reserved.

15

16.
[beta]
Denoでの実装
●

実装例:外部APIの呼び出し (get_weather)

export async function get_weather(location: string = "tokyo"): Promise<string> {
const apiKey = Deno.env.get("OPENWEATHERMAP_API_KEY");
const url =
`https://api.openweathermap.org/data/2.5/weather?q=${encodeURIComponent(location)}&appid=${apiKey}&units=metric&lang
=ja`;

const response = await fetch(url);
const data = await response.json();
return `${location}の現在の天気は${data.weather.description}、気温は${data.main.temp}℃です。`;
}

Copyright (C) 2026 Toranoana Lab Inc. All Rights Reserved.

16

17.
[beta]
Denoでの実装
●

実装例:ユーザーへの承認要求 (request_approval)
○
いわゆるHuman-in-the-loopを実現するためのもの。今回は危険な動作の前に確認を行う部分のみ
○
これはy/nだけだが、実際は自由入力も受け入れ可能
export async function request_approval(action: string): Promise<string> {
console.log(`\n[Agent Request] 次のアクションを実行してもよろしいですか?: ${action}`);
const buf = new Uint8Array(1024);
await Deno.stdout.write(new TextEncoder().encode("(y/n) > "));
// 標準入力からユーザーの回答を待つ
const n = await Deno.stdin.read(buf);
const answer = new TextDecoder().decode(buf.subarray(0, n)).trim().toLowerCase();
if (answer === "y" || answer === "yes") return "approved";
return "denied";
}

Copyright (C) 2026 Toranoana Lab Inc. All Rights Reserved.

17

18.
[beta]
Denoでの実装
●

実際のapproveチェック
○
まだ直前のチェックのみなので本来はもっと厳密なチェックが必要
if (function_name === "code_interpreter") {
// 過去のメッセージから、まだ使われていない最新のapproved があるか確認
const hasValidApproval = (function () {
for (let i = messages.length - 1; i >= 0; i--) {
const m = messages[i];
// ユーザーが 'approved' と答えた履歴があれば true
if (isToolMessage(m) && m.name === "request_approval" && m.content === "approved") {
return true;
}
}
return false;
})();
if (!hasValidApproval) {
return { /* ... エラーを返す処理 ... */ };
}
}
Copyright (C) 2026 Toranoana Lab Inc. All Rights Reserved.

18

19.
[beta]
Denoでの実装
●

実装例:セキュアなコード実行(Code Interpreter)
○
コード実行できると色々分析力があがるが、AIがなに生成するかわからないコード実行はそれなりに危険
○
Deno Sandboxを利用することで安全にコードを実行して結果だけを受け取る

import { Sandbox } from "@deno/sandbox";

// 3. サンドボックス内で安全に実行し、結果を取得(※)
// 実際はユーザーのapproveが必要

export async function code_interpreter(code: string): Promise<string> {

// const output = await sandbox.run("main.ts");

const token = Deno.env.get("DENO_DEPLOY_TOKEN");

// return output;

if (!token) return "エラー: DENO_DEPLOY_TOKEN が設定されていません。";

} catch (error) {
return `コード実行中にエラーが発生しました: ${error.message}`;

try {

}

// 1. 隔離されたサンドボックス環境を動的に作成(using で自動クリーン

}

アップ)
await using sandbox = await Sandbox.create();

// 2. LLMが生成したコードをサンドボックス内のファイルとして配置
await sandbox.fs.writeTextFile("main.ts", code);

Copyright (C) 2026 Toranoana Lab Inc. All Rights Reserved.

19

20.

Denoでの実装 ● デモ用プロンプト→動作を見るためにあえて無駄な処理をしてます(次の動画は4倍速です) async function main() { const answer = await runAgent(` sample/cities.txtの中身に記載されている各都市の今日の天気を教えてください。 結果の天気情報はsample/cities_weather.csvとしてCSV形式で保存してください。 再びsample/cities_weather.csvの中身を読み取って結果を報告してください。 さらに、取得したデータから「全都市の平均気温」「最も気温が高い都市と低い都市」を算出し、全体の傾向と してあわせて報告してください。 `); console.log(`[Agent] ${answer}`); } Copyright (C) 2026 Toranoana Lab Inc. All Rights Reserved. 20

21.

Denoでの実装 Copyright (C) 2026 Toranoana Lab Inc. All Rights Reserved. 21

22.

まとめと今後 ● ● ● ● DenoでAIエージェントを作ってみた発表をしました 各種ドキュメントに従った作りをDenoで実践してみました Deno KVによる状態管理や、Sandboxによる安全な実行環境といったDeno特有の機能を活用し ました 今後 ○ 色々甘い部分がある & エージェントとしては片手落ち ■ AI自身に実行計画をさせる必要がある ■ approveのチェックが直近にあるかどうかだけなので甘い ○ Denoのパーミッションシステムをもうちょっと活用できないか考えたい ■ approveも求められるし、パーミッションも求められるので二回チェック来る ■ 片方で片方を巻き取れると活用できそうかも?けど考えるところ多し ● どこまでの範囲をパーミッションするか?どこまで許可するかAI部分との兼ね 合いを考えないといけなさそう Copyright (C) 2026 Toranoana Lab Inc. All Rights Reserved. 22