AWS Amplify Gen2とAi Kitを使用してAIエージェントを作る!

西村です。

今回は、AWS Amplify Gen2のAI KitTool Useを使用して、ReactアプリにカスタムAIエージェントを実装する方法をご紹介します!

最近、生成AIを活用したアプリケーションの開発が注目を集めていますよね!
特に、AIに外部データの参照や特定のアクションを実行させる「ツール利用(Tool Use)」の機能は、より実用的なAIアプリケーションを構築するために不可欠になっています。

この分野では、Vercel AI SDKをベースにしたmastra.aiや、Anthropic(クロード開発元)が提案するMCP(Model Context Protocol)など、日々様々なフレームワークや規格が登場しています。

MCPは、AIモデルとツールの対話を標準化するためのプロトコルで、複数のAIツールを統合する際の互換性を高めることを目指しています。
AWSも最近AWS MCP Serversを公開していたりと、業界標準としての顔つきになってきた気がしますね!

mastra.aiはTypeScriptベースのAIエージェント開発フレームワークで、ワークフローグラフやRAGパイプラインなど、かなりわくわくするフレームワークです!

これらの選択肢がある中で、AWS Amplify Gen2の「AI Kit」は、AWSのサービス群と緊密に統合された、シンプルかつ強力なソリューションを提供します。
AIモデルの統合から、データベースとの連携、カスタム関数の実行まで、宣言的なアプローチで簡単に実現できるようになっています!

完成イメージ

今回作成するのは、ユーザーとチャットしながら、様々なアクションを実行できるAIアシスタントです。
例えば:

  • タスクの追加
  • Todoリストの一覧表示
  • 自然言語での操作(「買い物リストを追加して」など)

イメージできましたでしょうか?
では実際に作っていきましょう!!

実装手順

ステップ1: プロジェクトの準備

Gen2プロジェクトのセットアップは省略します。参考
必要なパッケージをインストールしてください。

npm install aws-amplify @aws-amplify/ui-react @aws-amplify/ui-react-ai

ステップ2: バックエンドの定義

amplify/data/resource.tsファイルに定義します。
まず、基本的なデータモデルとAiConversationを作成します。

import { a, defineData, type ClientSchema, defineFunction } from '@aws-amplify/backend';

// カスタム関数定義(タスク作成用)
export const todoCreateFunction = defineFunction({
  name: 'todoCreateFunction',
  entry: 'todoCreate.ts'
});

const schema = a.schema({
  // データモデル定義(シンプルなTodoモデル)
  Todo: a.model({
    title: a.string().required(),
    completed: a.boolean().required().default(false),
    createdAt: a.datetime().required()
  })
  .authorization((allow) => allow.authenticated()),
  
  // カスタムクエリ(AIツールから利用)
  CreateTodo: a.query()
    .arguments({ 
      query: a.string() 
    })
    .returns(a.json())
    .handler(a.handler.function(todoCreateFunction))
    .authorization((allow) => allow.authenticated()),
  
  // AIコンバセーション定義
  todoAssistant: a.conversation({
    aiModel: a.ai.model('Claude 3.5 Sonnet'),
    systemPrompt: `あなたはTodo管理をサポートするAIアシスタントです。
1. ユーザーのタスク追加や管理を手伝います
2. タスクの追加、一覧表示、完了マーク付けなどを支援します
3. ユーザーの質問に対して、Todoデータベースから情報を検索して回答します`,
    tools: [ 
      // データ操作ツール
      a.ai.dataTool({
        name: 'ListTodos',
        description: 'Todoリストを取得する',
        model: a.ref('Todo'),
        modelOperation: 'list',
      }),
      a.ai.dataTool({
        name: 'CreateTodo',
        description: '新しいTodoを作成する',
        query: a.ref('CreateTodo'),
      }),
    ] 
  }).authorization((allow) => allow.owner()),
})
.authorization((allow) => [
  allow.resource(todoCreateFunction).to(['mutate'])
]);

export type Schema = ClientSchema<typeof schema>;

export const data = defineData({
  schema,
  authorizationModes: {
    defaultAuthorizationMode: 'userPool',
  },
});

このスキーマでは:

  • Todo モデルは3つのフィールド(title、completed、createdAt)のみ
  • AIアシスタントには2つのツールだけを提供しています:
    • ListTodos: Todoリストを取得する
    • CreateTodo: 新しいTodoを作成する(カスタム関数経由)

CreateTodoツールでmodelOperationではなくquery(カスタム関数)を使用しています。
現時点(2025/4時点)のAmplify AI Kitでは、modelOperation'list'以外の値が許容されていないようです🤔
いずれCRUDも対応してほしいですね!

そのため、現時点ではデータ変更操作にはカスタム関数が必要ということです。😑

ステップ3:カスタム関数の実装

ということでカスタム関数を作成していきましょう。

amplify/data/todoCreate.ts

import type { Schema } from "./resource";
import { AppSyncResolverEvent } from 'aws-lambda';
import { Amplify } from 'aws-amplify';
import { generateClient } from 'aws-amplify/data';
import { getAmplifyDataClientConfig } from '@aws-amplify/backend/function/runtime';
import { env } from '$amplify/env/todoCreateFunction';

export const handler = async (
  event: AppSyncResolverEvent<{ query: string }>
) => {
  try {
    // クライアント初期化
    const { resourceConfig, libraryOptions } = await getAmplifyDataClientConfig(env);
    Amplify.configure(resourceConfig, libraryOptions);
    const client = generateClient<Schema>();
    
    // Todoの作成
    const { data: todo } = await client.models.Todo.create({
      title: event.arguments.query,
      completed: false,
      createdAt: new Date().toISOString()
    });
    
    return { success: true, todo };
  } catch (error) {
    console.error('Todo作成エラー:', error);
    return { success: false, error: String(error) };
  }
};

この関数は、AIアシスタントがユーザーからの要求に応じて新しいTodoを作成するために使用されます。
AIはユーザーの入力からタスクのタイトルを抽出し、カスタム関数を通じてTodoを作成します。

ステップ4: フロントエンドの実装

最後に、Reactアプリからエージェントを利用するためのUIを実装します。
まず、メインのAppコンポーネントでAmplifyクライアントとAIフックを設定します:

src/App.tsx

import { Amplify } from 'aws-amplify';
import { Authenticator } from '@aws-amplify/ui-react';
import '@aws-amplify/ui-react/styles.css';
import { generateClient } from 'aws-amplify/api';
import { createAIHooks } from '@aws-amplify/ui-react-ai';
import { createContext } from 'react';
import outputs from '../amplify_outputs.json';
import TodoAssistant from './components/TodoAssistant';
import type { Schema } from '../amplify/data/resource';

// Amplify設定
Amplify.configure(outputs);

// 型安全なクライアント生成
const client = generateClient<Schema>({ authMode: 'userPool' });

// AIフック生成
const { useAIConversation, useAIGeneration } = createAIHooks(client);

// コンポーネント間でフックを共有するためのコンテキスト
export const AIHooksContext = createContext({ useAIConversation, useAIGeneration });

function App() {
  return (
    <AIHooksContext.Provider value={{ useAIConversation, useAIGeneration }}>
      <Authenticator>
        {({ signOut }) => (
          <div className="app">
            <header>
              <h1>AIタスク管理アシスタント</h1>
              <button onClick={signOut}>サインアウト</button>
            </header>
            <main>
              <TodoAssistant/>
            </main>
          </div>
        )}
      </Authenticator>
    </AIHooksContext.Provider>
  );
}

export default App;

次に、チャットUIコンポーネントを実装します:

src/components/TodoAssistant.tsx
import { AIConversation } from '@aws-amplify/ui-react-ai';
import { useContext } from 'react';
import { AIHooksContext } from '../App';

function TodoAssistant() {
  // App.tsxから提供されるフックを使用
  const { useAIConversation } = useContext(AIHooksContext);
  
  const [
    {
      data: { messages },
      isLoading,
    },
    handleSendMessage,
  ] = useAIConversation('todoAssistant'); // resource.tsで定義したconversationの名前
  
  return (
    <div className="chat-container">
      <h2>Todoアシスタント</h2>
      <AIConversation 
        messages={messages}
        isLoading={isLoading}
        handleSendMessage={handleSendMessage}
        welcomeMessage="こんにちは!タスク管理のお手伝いをします。「買い物に行く」というタスクを追加して、といった感じでお話しかけてください。"
      />
    </div>
  );
}

export default TodoAssistant;

これでAIアシスタントと自然言語で会話するだけでDynamoDBにTodoの追加と一覧表示ができるようになりました!🔥

動きましたね!👌

まとめ

AIAgent元年とも呼ばれる激動の時代に、今のエンジニアができるのは片っ端から試すことなのかなと思います。
私も日々わくわくしながらいろんなサービスを触っていきたいです!

では!

参考