Amazon Web Services ブログ

あらゆるデータソースに 会話型インターフェースを追加する

Amazon Bedrockを使用してジェネレーティブAIアプリケーションを構築する際、外部データや機能にアクセスさせることでモデルの機能を強化する必要があることがよくあります。それを行う方法は、Tool(Function Calling と呼ばれることもあります)を使うことです。これらの Tool はブリッジの役割を果たし、モデルの学習データにはない、アプリケーション固有のリアルタイムまたは動的な情報をモデルが取得できるようにします。例えば、一般的な問い合わせに答えるだけでなく、ライブの天気予報やニュースを取得したり、最新の株価を取得したり、社内のデータベースと対話したりできる会話アシスタントを想像してみてください。

LLM Tool の仕組み

Tool の説明

はじめに、LLM は使用可能な Tool について知る必要があります。適切な状況に適切なものを選択するために、名称や説明など、使用可能な Tool に関する情報が必要です。詳細な説明とわかりやすい名称を提供することで、LLM の決定を助け、より良い結果につなげることができます。また、LLM は API 仕様や関数インターフェースのように、 Tool のインプットに何があるかを知る必要があります。Amazon Bedrock Converse API では、Tool の定義は次のようになります:

{
    "tools": [
        {
            "toolSpec": {
                "name": "get_news",
                "description": "Provides the latest news for a given topic.",
                "inputSchema": {
                    "json": {
                        "type": "object",
                        "properties": {
                            "topic": {
                                "type": "string",
                                "description": "The Topic we want the news about."
                            }
                        },
                        "required": [
                            "topic"
                        ]
                    }
                }
            }
        }
    ]
}
JSON

このオブジェクトは、Tool が何を行い、どのような入力を必要とするかを記述します。例えば、「 News API 」Tool はニュースを返すためにトピックを入力として必要とするかもしれません。AWS Amplify AI Kit を使えば、これは大幅に簡略化されるため、このような方法でTool を定義する必要がなくなることは後ほど説明します。

Tool の呼び出し

ユーザーが Amazon Bedrock と対話するとき、モデルはプロンプトを分析し、レスポンスを生成するために Tool が必要かどうかを判断します。もし必要であれば、モデルは使用する Tool を指定し、アプリケーションコードに Tool の使用を依頼しレスポンスを返します。

アプリケーションは Tool のレスポンスを受信し、リクエストを満たすためのコードを実行します。この実行の出力は会話履歴に追加され、モデルに送り返されます。モデルは会話履歴にある Tool の実行結果を用いてこのリクエストを処理し、人間が読めるレスポンスを生成することでユーザーに応答するか、必要であれば別の Tool を呼び出すことを選びます。LLM はステートレスであり、LLM への各リクエストは個別のユニークな API コールであり、会話履歴、システムプロンプト、 Tool 設定はすべて毎回 LLM に送信されます。LLM の外側にあるアプリケーションコードは、会話履歴を管理し、適切なタイミングで Tool を起動し、LLM にプロンプトを出したり再プロンプトを出したりする必要があります。何が起きているのか、簡略化した図を見てみましょう:

LLM Tool の使用は非常に強力ですが、管理もかなり複雑になっています。AWS Amplify AI Kit が、リッチでインタラクティブな生成 AI アプリケーションを構築するための Tool として、どのように簡単に定義できるかを見てみましょう。

AWS Amplify で LLM Tool を利用する

まず、Amplify ライブラリーがインストールされていて、Amplify アプリケーションがセットアップされていることを確認してください。新しい Amplify AI Kit を使ってフルスタック AI アプリを構築する方法については、こちらの記事で詳しく説明しています。

Amplify では、amplify/data/resource.ts ファイルのスキーマで、TypeScript を用いてデータを定義します。カスタムクエリだけでなく、リレーションシップや認可ルールを持つデータモデルを定義することが可能です。データへのアクセスは生成 AI アプリケーションにとって非常に重要なので、生成 AI Tool がアクセスできるデータもこのデータスキーマで定義できます。

LLM Tool を使って、外部APIからデータをフェッチし、そのAPIの上に会話型検索を追加できるカスタムクエリを定義する方法を見てみましょう。

クエリの定義

クエリレスポンスの形を定義することから始めてみましょう。amplify/data/resource.ts ファイルを開き、スキーマに News という customType を追加します。このカスタムタイプは、タイトル、説明、ニュース記事のソースを含めます。

const schema = a.schema({
  News: a.customType({
    source: a.float(),
    title: a.string(),
    description: a.string()
  }),
  //...
})
TypeScript

次に、カスタムクエリを定義します。そのためには、受け取る引数( API への入力)、返却する値(先ほど定義したカスタム型)、ハンドラー関数(実装)、権限(アクセスできる人)を定義する必要があります。

const schema = a.schema({
    //...
    getNews: a
        .query()
        .arguments({ topic: a.string() })
        .returns(a.ref("News").array())
        .handler(a.handler.function(getNews))
        .authorization((allow) => allow.authenticated()).
    //...
})
TypeScript

次にハンドラ関数を定義しましょう。defineFunction を使って、amplify/data/resource.ts ファイルに getNews 関数を定義します。

import { defineFunction, secret } from '@aws-amplify/backend';

export const getNews = defineFunction({
  name: "getNews",
  entry: "./getNews.ts",
  environment: {
    NEWSAPI_API_KEY: secret("NEWSAPI_API_KEY"),
  },
});
TypeScript

API キーは、AWS Systems Manager Parameter Store に保存される Amplify secrets を使用して保存することを推奨します。サンドボックス環境にシークレットを追加するには、以下のコマンドを使用します:

npx ampx sandbox secret set NEWSAPI_API_KEY
Bash

あとは amplify/data/getNews.ts ファイルに getNews 関数を記述するだけです。この例では、https://newsapi.org/ API を使用して、トピックに関する最新のニュースを取得します。

import { env } from "$amplify/env/getNews";
import type { Schema } from "./resource";

export const handler: Schema["getNews"]["functionHandler"] = async (event) => {
    const res = await fetch(`https://newsapi.org/v2/everything?q=${encodeURIComponent(
        event.arguments.topic ?? ""
    )}&apiKey=${env.NEWSAPI_API_KEY}`);

    const news = await res.json();

    const result = news.articles.slice(0, 3).map((article: any) => ({
        source: article.source.name,
        title: article.title,
        description: article.description,
      }));
    
    return result;
};
TypeScript

これで、トピックを受け取り、最新のニュース記事を取得するカスタムクエリがデータスキーマに定義されました。あとは、このクエリを LLM Tool として使うだけです。そのためには、a.conversation() を使用して、データスキーマに AI 会話ルートを定義します。使用したい AI モデル、システムプロンプト、アクセスできる Tool を指定します。LLM Tool を定義するには、a.ai.dataTool を使用し、名前と説明、および先ほど定義したカスタムクエリへの参照を指定します。

const schema = a.schema({
    //...
    chat: a.conversation({
        aiModel: a.ai.model("Claude 3.5 Sonnet"),
        systemPrompt: `
        You are a helpful assistant.
        `,
        tools: [
        a.ai.dataTool({
            name: "get_news",
            description: "Provides the latest news for a given topic.",
            query: a.ref("getNews"),
        }),
        ],
    })
    .authorization((allow) => allow.owner()),
})
TypeScript

Amplify sandbox を使用している場合、データリソースファイルを保存すると、変更が個人のクラウドサンドボックスにデプロイされるはずです。または、Amplify コンソールで、Amplify アプリケーションに接続されている git ブランチに変更をプッシュしてデプロイすることもできます。

会話型インターフェースの構築

Amplify クライアントライブラリーと React コンポーネントをインストールします:

npm i aws-amplify @aws-amplify/ui-react @aws-amplify/ui-react-ai react-markdown
Bash

次に、サンドボックスを使用する際に Amplify が生成する amplify_outputs.json ファイルを使用して Amplify.configure を呼び出すか、Amplify コンソールからファイルをダウンロードして、フロントエンドのコードに Amplify ライブラリが設定されていることを確認してください。

import { Amplify } from 'aws-amplify';
import '@aws-amplify/ui-react/styles.css';
import outputs from '../amplify_outputs.json';

Amplify.configure(outputs);
TypeScript

次に、useAIConversation React フックを使用して、チャット AI の会話ルートに接続し、メッセージと送信メッセージハンドラを AIConversation React コンポーネントに渡します。会話がユーザーに閲覧されてしまうため、必ず Authenticator コンポーネントでラップしてください。

import { generateClient } from "aws-amplify/api";
import { Authenticator } from '@aws-amplify/ui-react';
import { createAIHooks, AIConversation } from "@aws-amplify/ui-react-ai";
import { Schema } from "../amplify/data/resource";

export const client = generateClient<Schema>({ authMode: "userPool" });
export const { useAIConversation } = createAIHooks(client);

function App() {
  const [
    {
      data: { messages },
      isLoading,
    },
    handleSendMessage,
  ] = useAIConversation('chat');


  return (
    <Authenticator>
      <AIConversation
        messageRenderer={{
          text: ({ text }) => <ReactMarkdown>{text}</ReactMarkdown>
        }}
        messages={messages}
        isLoading={isLoading}
        handleSendMessage={handleSendMessage}
      />
    </Authenticator>
  );
}
TypeScript

データモデルの取得

また、データスキーマで定義したデータモデルに LLM がアクセスできるようにすることもできます。例えば、メモを取るアプリケーションを作成していて、会話アシスタントにメモだけでなく現在のニュース記事にもアクセスさせたい場合、そのようにすることができます。

まず、データスキーマにデータモデルを追加します:

const schema = a.schema({
    //...
    Note: a.model({
        title: a.string(),
        content: a.string()
    })
    .authorization((allow) => [allow.owner()])
    //...
});
TypeScript

次に、会話ルートの定義に新しい Tool を追加します:

tools: [
   //...
   a.ai.dataTool({
        name: 'SearchNotes',
        description: 'Used to search for notes the user has written',
        model: a.ref('Note'),
        modelOperation: 'list'
   })
]
TypeScript

これで会話アシスタントは、ユーザーが作成したメモや最新のニュース記事を用いた応答ができるようになるでしょう。

クリーンアップ

サンドボックスを実行している場合は、ターミナルで以下のコマンドを実行することで、サンドボックスと作成されたすべてのバックエンド環境を削除できます。

npx ampx sandbox delete
Bash

まとめ

このブログ記事では、LLM Tool とは何か、AWS Amplify で外部 API コールや AWS AppSync を使用してデータベースからデータを取得するためにどのように使い方ができるのかを学びました。もっと詳しく知りたい場合は、AI のスタートガイドを見るか、サンプルコードを確認してください。

本記事は、Add a Conversational Interface to any Data Source を翻訳したものです。翻訳は Solutions Architect の 瀬高 が担当しました。