取り組みの背景 — なぜ「エージェント型AI」がモバイルアプリに必要なのか
「チャットボット」と「AIエージェント」は似て非なるものです。チャットボットは質問に答えるだけですが、AIエージェントは目標を達成するために自律的に複数のツールを使い、計画を立て、実行します。たとえば「来週の旅行プランを立てて、ホテルを調べて、カレンダーに登録して」という指示を一連の流れで処理できるのがエージェントです。
2026年、Google の Gemini Agents SDK (旧称: Google AI Agent Development Kit、ADK)がモバイル開発の世界でも注目を集めています。React Native をベースとする Rork / Rork Max でこのSDKを活用すれば、単なるテキスト生成を超えた「行動するAI」をアプリに組み込めます。
対象読者は、Rorkでの基本的なAI統合経験があり、より高度なエージェント型AIを実装したい開発者です。
Gemini Agents SDK の中核アーキテクチャを理解する
エージェントの三要素:Model・Tools・Instructions
Gemini Agents SDK でエージェントを構成する要素は3つです。
Model(モデル) : 推論エンジン。gemini-2.0-flash(高速・低コスト)またはgemini-2.5-pro(高精度)を用途に応じて選択します。モバイルアプリではレスポンス速度が重要なため、基本はgemini-2.0-flashを推奨します。
Tools(ツール) : エージェントが呼び出せる機能群。大きく分けて以下の3種類があります。
Function Calling : 開発者が定義した任意のJavaScript関数を呼び出す
Code Execution : Pythonコードを動的に生成・実行してデータ分析を行う
Grounding with Google Search : リアルタイムのWeb検索結果を根拠として回答を生成する
Instructions(指示) : エージェントの役割・制約・振る舞いを定義するシステムプロンプト。ここの品質がエージェントの実用性を左右します。
Function Calling の内部動作
Function Calling が実際にどう動くかを理解しておくことは、デバッグや最適化に不可欠です。
ユーザー入力 → モデルが "このツールを使うべき" と判断
→ ツール名と引数を JSON で出力(実際の実行はしない)
→ SDK が JSON を解析して実際の関数を実行
→ 関数の戻り値をモデルに返す
→ モデルが結果を解釈して最終回答を生成
この「判断→実行→解釈」サイクルが複数回繰り返されることで、マルチステップの複雑なタスクを処理できます。
開発環境のセットアップ
前提条件
Rork または Rork Max プロジェクト(React Native / Expo ベース)
Google AI Studio にて Gemini API キーを取得済み
Node.js 20 以上
パッケージのインストール
# Gemini AI SDK(Google Generative AI の最新版)
npm install @google/generative-ai
# エージェント機能拡張(ADK互換ラッパー)
npm install @google/generative-ai-agents
# セッション管理と永続化
npm install @react-native-async-storage/async-storage
# ストリーミング対応のUIコンポーネント
npm install react-native-reanimated
環境変数の設定
Rork プロジェクトの .env ファイルに以下を追加します。APIキーは必ずサーバーサイドの環境変数として管理し、クライアントコードに直接埋め込まないでください。
# .env(バージョン管理に含めないこと)
GEMINI_API_KEY = YOUR_GEMINI_API_KEY_HERE
# Expo の場合(クライアント公開は非推奨、バックエンド経由を推奨)
EXPO_PUBLIC_API_BASE_URL = https://your-api.workers.dev
⚠️ セキュリティ注意 : モバイルアプリのバンドルにAPIキーを含めると、逆コンパイルで漏洩する危険があります。本番環境では必ず Cloudflare Workers などのバックエンドプロキシ経由でAPIを呼び出してください。
基本的なエージェントの実装
ツール定義:Function Calling の設計
良いツール定義は、エージェントが「いつそのツールを使うべきか」を正確に理解できるような説明文を持ちます。
// src/agents/tools/weatherTool.ts
import { FunctionDeclaration, Type } from "@google/generative-ai" ;
export const weatherTool : FunctionDeclaration = {
name: "get_current_weather" ,
description:
"指定した都市・地域の現在の天気情報を取得する。" +
"ユーザーが天気について質問したり、外出・旅行の計画を立てる際に使用する。" ,
parameters: {
type: Type. OBJECT ,
properties: {
location: {
type: Type. STRING ,
description: "都市名または地域名(例: '東京', '大阪', 'Tokyo, Japan')" ,
},
unit: {
type: Type. STRING ,
enum: [ "celsius" , "fahrenheit" ],
description: "温度の単位。デフォルトは celsius" ,
},
},
required: [ "location" ],
},
};
// 実際のAPI呼び出し処理
export async function executeWeatherTool ( args : {
location : string ;
unit ?: string ;
}) : Promise < object > {
// Open-Meteo など無料APIを活用
const geocodeRes = await fetch (
`https://geocoding-api.open-meteo.com/v1/search?name=${ encodeURIComponent ( args . location ) }&count=1&language=ja`
);
const geocodeData = await geocodeRes. json ();
if ( ! geocodeData.results?. length ) {
return { error: `"${ args . location }" の位置情報が見つかりませんでした` };
}
const { latitude , longitude , name , country } = geocodeData.results[ 0 ];
const weatherRes = await fetch (
`https://api.open-meteo.com/v1/forecast?latitude=${ latitude }&longitude=${ longitude }¤t=temperature_2m,weather_code,wind_speed_10m&timezone=auto`
);
const weatherData = await weatherRes. json ();
const current = weatherData.current;
return {
location: `${ name }, ${ country }` ,
temperature: current.temperature_2m,
unit: args.unit || "celsius" ,
wind_speed: current.wind_speed_10m,
weather_code: current.weather_code,
// weather_code: 0=晴れ, 1-3=曇り, 61-67=雨, 71-77=雪 (WMO規格)
};
}
エージェントコアの実装
// src/agents/GeminiAgent.ts
import {
GoogleGenerativeAI,
GenerativeModel,
ChatSession,
FunctionCallingMode,
Part,
} from "@google/generative-ai" ;
import { weatherTool, executeWeatherTool } from "./tools/weatherTool" ;
// ツール実行マップ(名前 → 実行関数)
const TOOL_HANDLERS : Record < string , ( args : any ) => Promise < any >> = {
get_current_weather: executeWeatherTool,
};
export interface AgentMessage {
role : "user" | "model" ;
content : string ;
toolCalls ?: Array <{ name : string ; args : object ; result : object }>;
timestamp : number ;
}
export class GeminiAgent {
private model : GenerativeModel ;
private chat : ChatSession | null = null ;
private messageHistory : AgentMessage [] = [];
constructor ( private apiKey : string ) {
const genAI = new GoogleGenerativeAI ( this .apiKey);
this .model = genAI. getGenerativeModel ({
model: "gemini-2.0-flash" ,
systemInstruction: this . buildSystemInstruction (),
tools: [{ functionDeclarations: [weatherTool] }],
toolConfig: {
functionCallingConfig: {
// AUTO: モデルが自律的にツール使用を判断(推奨)
mode: FunctionCallingMode. AUTO ,
},
},
generationConfig: {
maxOutputTokens: 2048 ,
temperature: 0.7 ,
},
});
}
private buildSystemInstruction () : string {
return `あなたは親切で有能なAIアシスタントです。
ユーザーの質問に対して、必要に応じてツールを使って正確な情報を提供してください。
ガイドライン:
- ツールを使った場合は、その結果を自然な日本語で説明してください
- 不確かな情報は「〜と思われます」のように断定を避けてください
- ユーザーのプライバシーを尊重し、個人情報を不必要に収集しないでください
- 複雑な質問は段階的に分解して回答してください` ;
}
// チャットセッションを初期化(または既存セッションを継続)
startSession ( history ?: Array <{ role : string ; parts : Part [] }>) {
this .chat = this .model. startChat ({
history: history || [],
});
this .messageHistory = [];
}
async sendMessage ( userMessage : string ) : Promise < AgentMessage > {
if ( ! this .chat) this . startSession ();
const userEntry : AgentMessage = {
role: "user" ,
content: userMessage,
timestamp: Date. now (),
};
this .messageHistory. push (userEntry);
let result = await this .chat ! . sendMessage (userMessage);
const toolCalls : AgentMessage [ "toolCalls" ] = [];
// Function Calling ループ(モデルがツール呼び出しを要求する間継続)
while ( true ) {
const response = result.response;
const functionCalls = response. functionCalls ();
if ( ! functionCalls || functionCalls. length === 0 ) {
// ツール呼び出しなし → 最終回答
break ;
}
// 複数ツールを並列実行
const toolResults = await Promise . all (
functionCalls. map ( async ( call ) => {
const handler = TOOL_HANDLERS [call.name];
if ( ! handler) {
return {
name: call.name,
response: { error: `Unknown tool: ${ call . name }` },
};
}
const toolResult = await handler (call.args);
toolCalls. push ({ name: call.name, args: call.args, result: toolResult });
return { name: call.name, response: toolResult };
})
);
// ツール結果をモデルに返して続きを生成
result = await this .chat ! . sendMessage (
toolResults. map (( tr ) => ({
functionResponse: { name: tr.name, response: tr.response },
}))
);
}
const modelEntry : AgentMessage = {
role: "model" ,
content: result.response. text (),
toolCalls: toolCalls. length > 0 ? toolCalls : undefined ,
timestamp: Date. now (),
};
this .messageHistory. push (modelEntry);
return modelEntry;
}
getHistory () : AgentMessage [] {
return [ ... this .messageHistory];
}
}
マルチステップエージェントの実装パターン
セッション管理と状態の永続化
モバイルアプリではアプリを閉じてもセッションを継続できることが鍵になります。AsyncStorageを使ってセッションを永続化します。
// src/agents/AgentSessionManager.ts
import AsyncStorage from "@react-native-async-storage/async-storage" ;
import { AgentMessage } from "./GeminiAgent" ;
interface Session {
id : string ;
title : string ;
messages : AgentMessage [];
createdAt : number ;
updatedAt : number ;
}
const SESSION_KEY_PREFIX = "agent_session_" ;
const SESSION_LIST_KEY = "agent_session_list" ;
const MAX_SESSIONS = 20 ; // 古いセッションを自動削除
export class AgentSessionManager {
static async saveSession ( session : Session ) : Promise < void > {
const key = `${ SESSION_KEY_PREFIX }${ session . id }` ;
await AsyncStorage. setItem (key, JSON . stringify (session));
// セッション一覧の更新
const list = await this . getSessionList ();
const updatedList = [
session.id,
... list. filter (( id ) => id !== session.id),
]. slice ( 0 , MAX_SESSIONS );
await AsyncStorage. setItem ( SESSION_LIST_KEY , JSON . stringify (updatedList));
// 古いセッションの削除
const toDelete = list. slice ( MAX_SESSIONS );
await Promise . all (
toDelete. map (( id ) =>
AsyncStorage. removeItem ( `${ SESSION_KEY_PREFIX }${ id }` )
)
);
}
static async loadSession ( sessionId : string ) : Promise < Session | null > {
const data = await AsyncStorage. getItem (
`${ SESSION_KEY_PREFIX }${ sessionId }`
);
return data ? JSON . parse (data) : null ;
}
static async getSessionList () : Promise < string []> {
const data = await AsyncStorage. getItem ( SESSION_LIST_KEY );
return data ? JSON . parse (data) : [];
}
static async getAllSessions () : Promise < Session []> {
const list = await this . getSessionList ();
const sessions = await Promise . all (list. map (( id ) => this . loadSession (id)));
return sessions. filter (( s ) : s is Session => s !== null );
}
// メッセージ履歴をGemini APIのhistory形式に変換
static toGeminiHistory (
messages : AgentMessage []
) : Array <{ role : string ; parts : any [] }> {
return messages. map (( msg ) => ({
role: msg.role,
parts: [{ text: msg.content }],
}));
}
// セッションタイトルを最初のユーザーメッセージから自動生成
static generateTitle ( messages : AgentMessage []) : string {
const firstUserMsg = messages. find (( m ) => m.role === "user" );
if ( ! firstUserMsg) return "新しい会話" ;
return firstUserMsg.content. slice ( 0 , 30 ) + (firstUserMsg.content. length > 30 ? "…" : "" );
}
}
ストリーミングレスポンスの実装
長い回答を生成する際、ストリーミングでリアルタイムに表示することでUXが大幅に向上します。
// src/agents/GeminiAgentStream.ts(ストリーミング対応版)
export class GeminiAgentStream extends GeminiAgent {
async sendMessageStream (
userMessage : string ,
onChunk : ( text : string ) => void ,
onComplete : ( message : AgentMessage ) => void ,
onError : ( error : Error ) => void
) : Promise < void > {
if ( ! this .chat) this . startSession ();
try {
const result = await this .chat ! . sendMessageStream (userMessage);
let fullText = "" ;
for await ( const chunk of result.stream) {
const chunkText = chunk. text ();
if (chunkText) {
fullText += chunkText;
onChunk (chunkText);
}
}
const modelEntry : AgentMessage = {
role: "model" ,
content: fullText,
timestamp: Date. now (),
};
onComplete (modelEntry);
} catch (error) {
onError (error instanceof Error ? error : new Error ( String (error)));
}
}
}
実践:AIタスクアシスタントアプリの構築
ここでは実用的なAIタスクアシスタントの実装例を見ていきます。ユーザーが自然言語でタスクを管理できるアプリです。
カスタムツールの定義(タスク管理)
// src/agents/tools/taskTools.ts
import { FunctionDeclaration, Type } from "@google/generative-ai" ;
// タスクCRUDツールの定義
export const taskTools : FunctionDeclaration [] = [
{
name: "create_task" ,
description:
"新しいタスクを作成する。ユーザーが「〜をやる」「〜をタスクに追加して」などと言った場合に使う。" ,
parameters: {
type: Type. OBJECT ,
properties: {
title: { type: Type. STRING , description: "タスクのタイトル" },
due_date: {
type: Type. STRING ,
description: "期限日(ISO 8601形式、例: '2026-04-10')。指定なければ省略" ,
},
priority: {
type: Type. STRING ,
enum: [ "high" , "medium" , "low" ],
description: "優先度。デフォルトは medium" ,
},
},
required: [ "title" ],
},
},
{
name: "list_tasks" ,
description:
"タスク一覧を取得する。完了状態やフィルター条件で絞り込める。" ,
parameters: {
type: Type. OBJECT ,
properties: {
status: {
type: Type. STRING ,
enum: [ "all" , "pending" , "completed" ],
description: "タスクのステータスフィルター" ,
},
priority: {
type: Type. STRING ,
enum: [ "high" , "medium" , "low" , "all" ],
description: "優先度フィルター" ,
},
},
},
},
{
name: "complete_task" ,
description: "指定したタスクを完了済みにマークする。" ,
parameters: {
type: Type. OBJECT ,
properties: {
task_id: { type: Type. STRING , description: "タスクのID" },
},
required: [ "task_id" ],
},
},
];
Rork UIコンポーネントとの統合
// src/screens/AIAssistantScreen.tsx
import React, { useState, useRef, useCallback } from "react" ;
import {
View,
Text,
TextInput,
FlatList,
TouchableOpacity,
ActivityIndicator,
KeyboardAvoidingView,
Platform,
StyleSheet,
} from "react-native" ;
import { GeminiAgent, AgentMessage } from "../agents/GeminiAgent" ;
import { AgentSessionManager } from "../agents/AgentSessionManager" ;
const API_BASE = process.env. EXPO_PUBLIC_API_BASE_URL ! ;
// APIキーはバックエンドプロキシ経由で取得(直接埋め込み禁止)
async function fetchApiKey () : Promise < string > {
const res = await fetch ( `${ API_BASE }/api/agent-token` , {
headers: { Authorization: `Bearer ${ await getUserToken () }` },
});
const { token } = await res. json ();
return token; // 短命トークン(15分有効)
}
export function AIAssistantScreen () {
const [ messages , setMessages ] = useState < AgentMessage []>([]);
const [ inputText , setInputText ] = useState ( "" );
const [ isLoading , setIsLoading ] = useState ( false );
const [ streamingText , setStreamingText ] = useState ( "" );
const agentRef = useRef < GeminiAgent | null >( null );
const listRef = useRef < FlatList >( null );
const initAgent = useCallback ( async () => {
if ( ! agentRef.current) {
const apiKey = await fetchApiKey ();
agentRef.current = new GeminiAgent (apiKey);
agentRef.current. startSession ();
}
}, []);
const sendMessage = async () => {
if ( ! inputText. trim () || isLoading) return ;
const text = inputText. trim ();
setInputText ( "" );
setIsLoading ( true );
// ユーザーメッセージを即座に表示
const userMsg : AgentMessage = {
role: "user" ,
content: text,
timestamp: Date. now (),
};
setMessages (( prev ) => [ ... prev, userMsg]);
try {
await initAgent ();
const response = await agentRef.current ! . sendMessage (text);
setMessages (( prev ) => [ ... prev, response]);
// セッションを自動保存
const allMessages = [ ... messages, userMsg, response];
await AgentSessionManager. saveSession ({
id: "current" ,
title: AgentSessionManager. generateTitle (allMessages),
messages: allMessages,
createdAt: allMessages[ 0 ].timestamp,
updatedAt: Date. now (),
});
} catch (error) {
const errMsg : AgentMessage = {
role: "model" ,
content: "申し訳ありません、エラーが発生しました。もう一度お試しください。" ,
timestamp: Date. now (),
};
setMessages (( prev ) => [ ... prev, errMsg]);
} finally {
setIsLoading ( false );
listRef.current?. scrollToEnd ({ animated: true });
}
};
const renderMessage = ({ item } : { item : AgentMessage }) => (
< View
style = {[
styles.messageBubble,
item.role === "user" ? styles.userBubble : styles.modelBubble,
]}
>
{item.toolCalls && item.toolCalls.length > 0 && (
<View style={styles.toolCallBadge}>
<Text style={styles.toolCallText}>
🔧 {item.toolCalls.map((t) => t.name).join(", ")}
</Text>
</View>
)}
<Text
style={item.role === "user" ? styles.userText : styles.modelText}
>
{ item . content }
</ Text >
</ View >
);
return (
< KeyboardAvoidingView
style = {styles.container}
behavior = {Platform. OS === "ios" ? "padding" : "height" }
>
< FlatList
ref = {listRef}
data = {messages}
keyExtractor = {(item) => item.timestamp.toString()}
renderItem = {renderMessage}
contentContainerStyle = {styles.messageList}
/>
{ isLoading && (
< View style = {styles.loadingContainer} >
< ActivityIndicator size = "small" color = "#4285F4" />
< Text style = {styles.loadingText} > AIが考えています ...</ Text >
</ View >
)}
< View style = {styles.inputContainer} >
< TextInput
style = {styles.input}
value = {inputText}
onChangeText = {setInputText}
placeholder = "メッセージを入力..."
multiline
maxLength = { 1000 }
onSubmitEditing = {sendMessage}
/>
< TouchableOpacity
style = {[styles.sendButton, ( ! inputText. trim () || isLoading) && styles.sendButtonDisabled]}
onPress={sendMessage}
disabled={!inputText.trim() || isLoading}
>
<Text style={styles.sendButtonText}>送信</Text>
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
);
}
const styles = StyleSheet.create({
container: { flex: 1 , backgroundColor: "#f5f5f5" },
messageList: { padding: 16 , paddingBottom: 8 },
messageBubble: {
maxWidth: "85%" ,
borderRadius: 16 ,
padding: 12 ,
marginBottom: 8 ,
},
userBubble: {
alignSelf: "flex-end" ,
backgroundColor: "#4285F4" ,
borderBottomRightRadius: 4 ,
},
modelBubble: {
alignSelf: "flex-start" ,
backgroundColor: "#ffffff" ,
borderBottomLeftRadius: 4 ,
shadowColor: "#000" ,
shadowOffset: { width: 0 , height: 1 },
shadowOpacity: 0.1 ,
shadowRadius: 2 ,
elevation: 2 ,
},
userText: { color: "#ffffff" , fontSize: 15 , lineHeight: 22 },
modelText: { color: "#1a1a1a" , fontSize: 15 , lineHeight: 22 },
toolCallBadge: {
backgroundColor: "#e8f0fe" ,
borderRadius: 8 ,
padding: 4 ,
marginBottom: 6 ,
},
toolCallText: { color: "#4285F4" , fontSize: 11 , fontWeight: "600" },
loadingContainer: {
flexDirection: "row" ,
alignItems: "center" ,
padding: 12 ,
gap: 8 ,
},
loadingText: { color: "#666" , fontSize: 13 },
inputContainer: {
flexDirection: "row" ,
padding: 12 ,
backgroundColor: "#fff" ,
borderTopWidth: 1 ,
borderTopColor: "#e0e0e0" ,
gap: 8 ,
alignItems: "flex-end" ,
},
input: {
flex: 1 ,
borderWidth: 1 ,
borderColor: "#ddd" ,
borderRadius: 20 ,
paddingHorizontal: 16 ,
paddingVertical: 10 ,
fontSize: 15 ,
maxHeight: 100 ,
backgroundColor: "#fafafa" ,
},
sendButton: {
backgroundColor: "#4285F4" ,
borderRadius: 20 ,
paddingHorizontal: 20 ,
paddingVertical: 10 ,
justifyContent: "center" ,
},
sendButtonDisabled: { backgroundColor: "#b0c4f8" },
sendButtonText: { color: "#fff" , fontSize: 15 , fontWeight: "600" },
});
エラーハンドリングと本番運用
よくある問題と対処法
問題1: レート制限(429エラー)
Gemini API には1分あたりのリクエスト数制限があります。エクスポネンシャルバックオフで自動リトライする仕組みを実装しましょう。
// src/utils/apiRetry.ts
export async function withRetry < T >(
fn : () => Promise < T >,
maxRetries = 3 ,
baseDelayMs = 1000
) : Promise < T > {
let lastError : Error ;
for ( let attempt = 0 ; attempt <= maxRetries; attempt ++ ) {
try {
return await fn ();
} catch ( error : any ) {
lastError = error;
if (error?.status === 429 && attempt < maxRetries) {
// エクスポネンシャルバックオフ + ジッター
const delay = baseDelayMs * Math. pow ( 2 , attempt) + Math. random () * 500 ;
await new Promise (( r ) => setTimeout (r, delay));
continue ;
}
throw error;
}
}
throw lastError ! ;
}
問題2: コンテキストウィンドウの超過
長い会話は token 上限(Gemini 2.0 Flash: 128k tokens)を超える場合があります。古いメッセージを要約して圧縮する戦略が有効です。
// メッセージ履歴が長くなったら要約して圧縮
async function compressHistory (
messages : AgentMessage [],
agent : GeminiAgent
) : Promise < AgentMessage []> {
if (messages. length <= 20 ) return messages; // 短い場合はそのまま
// 古いメッセージを要約
const toSummarize = messages. slice ( 0 , - 10 );
const summaryRequest = `以下の会話を3〜5文で要約してください: \n ${ toSummarize . map (( m ) => `${ m . role }: ${ m . content }` ). join ( " \n " ) }` ;
const summary = await agent. sendMessage (summaryRequest);
return [
{
role: "model" ,
content: `[以前の会話の要約]: ${ summary . content }` ,
timestamp: Date. now (),
},
... messages. slice ( - 10 ),
];
}
問題3: ツールタイムアウト
外部APIを呼び出すツールはネットワーク遅延で失敗することがあります。適切なタイムアウトを設定しましょう。
// タイムアウト付きフェッチ
async function fetchWithTimeout (
url : string ,
options : RequestInit = {},
timeoutMs = 5000
) : Promise < Response > {
const controller = new AbortController ();
const timeoutId = setTimeout (() => controller. abort (), timeoutMs);
try {
return await fetch (url, { ... options, signal: controller.signal });
} finally {
clearTimeout (timeoutId);
}
}
本番環境でのモニタリング
AIエージェントの品質を継続的に改善するためには、以下のメトリクスを収集・分析することが要点になります。
Tool Call Rate : 全リクエスト中にツールが呼び出された割合
Tool Success Rate : ツール呼び出しの成功/失敗比率
Latency Breakdown : モデル推論時間 vs ツール実行時間
Token Usage : コスト管理のための入出力トークン数
User Satisfaction : サムアップ/ダウンのフィードバック
// src/analytics/agentAnalytics.ts
interface AgentEvent {
type : "message_sent" | "tool_called" | "error" | "session_started" ;
metadata : Record < string , any >;
timestamp : number ;
}
export function trackAgentEvent ( event : AgentEvent ) {
// Firebase Analytics / Amplitude / Mixpanel などに送信
// 個人情報(メッセージ内容)は送信しないこと
console. log ( "[AgentAnalytics]" , event.type, event.metadata);
}
コスト最適化の戦略
Gemini APIのコストを最小化しながら品質を維持するための実践的な戦略を紹介します。
キャッシング : 同じ質問への回答をキャッシュする(15分有効など)ことで、APIコールを大幅に削減できます。特に「今日の天気は?」のような頻繁に聞かれる質問は効果的です。
モデルの使い分け : シンプルな意図分類には軽量モデル(gemini-2.0-flash-lite)を使い、複雑な推論が必要な場合だけ高精度モデルに切り替えるハイブリッド戦略が効果的です。
コンテキスト圧縮 : 前述の履歴圧縮戦略により、長い会話でも入力トークン数を抑えられます。
バッチ処理 : 複数のツール呼び出しを並列実行することで、往復のレイテンシを削減できます(前述の Promise.all パターン)。
個人開発者の視点から(実体験メモ)
ここまでの要点
ここで扱うのはGemini Agents SDK を使ったマルチステップAIエージェントの Rork への統合を、アーキテクチャ設計から本番運用まで体系的に解説しました。
重要なポイントを振り返ります。まず、Function Calling の仕組みを理解して、エージェントが自律的にツールを使えるように設計することが出発点です。次に、セッション管理と AsyncStorage を組み合わせて、ユーザーが会話を継続できる体験を実現します。そして、レート制限・タイムアウト・コンテキスト超過などの本番特有の課題に対処するための実装パターンを押さえておくが肝心です。
AIエージェントは「考えるだけのAI」から「行動するAI」への進化です。Rork の直感的なUI構築能力と組み合わせれば、ユーザーの生活を本当に便利にするアプリを短期間で市場に投入できます。
エージェント型AIの設計パターンについてさらに深く
さらに高度なAI機能として、複数エージェントを協調させるマルチエージェントシステムの実装については Rork マルチAIオーケストレーション完全ガイド も参考にしてください。