59
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

前提

前回の記事で次回やりたいと言っていた AI Kit やってみます!

AI Kitやりたい https://docs.amplify.aws/react/ai/

次の記載を見て実際いくらになるんだろ〜と思いながら挑戦してます。 :sweat:
今回はちょっと試すくらいなので大した金額にはならないはず...

大規模言語モデル (LLM) で推論を実行するとコストがかかる場合があります。Amazon Bedrock はサーバーレスサービスなので、使用した分だけ支払いますが、生成 AI アプリケーションの構築に関連するコストに注意してください。詳細については、Bedrock の料金をご覧ください。

できたこと

こんな感じでAIとやりとりできる画面を作ることができました :smiley:
途中バージョンの関係で手こずってしまったんですがそれ以外は大変お手軽でした。

image.png

image.png

手順

sandbox(本番に影響出ないようにするやつ)を起動します

npx ampx sandbox

Set up AI

権限周りの設定

  • IAMへの権限付与
    AmazonBedrockFullAccessを付与しました

image.png

  • 使用するモデルへのアクセス付与

image.png

前回の続きからなので... Build your AI backendあたりから実施します

Build your AI backend

バージョンの関係でしょうか、Claude 3.5 Haikuがありません :thinking:
Claude 3.5 Sonnetの方がnewっぽいしなくなったのだろうか...
image.png
Claude 3.5 Sonnetで進めます。

次のコードを追加しました。

amplify/data/resource.ts
const schema = a.schema({
  // 省略---
+ chat: a.conversation({
+   aiModel: a.ai.model('Claude 3.5 Sonnet'),
+   systemPrompt: 'You are a helpful assistant',
+ }),
    
+ generateRecipe: a.generation({
+   aiModel: a.ai.model('Claude 3.5 Sonnet'),
+   systemPrompt: 'You are a helpful assistant that generates recipes.',
+ })
+ .arguments({
+   description: a.string(),
+ })
+ .returns(
+   a.customType({
+     name: a.string(),
+     ingredients: a.string().array(),
+     instructions: a.string(),
+   })
+ )
+ .authorization((allow) => allow.authenticated()),
});
  // 省略---

Connect your frontend

必要なパッケージを追加します。

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

Configure the librariesはすでにやってあったのでGenerate the data clientからやります。

次のファイルを作成します。

src/client.ts
import { generateClient } from "aws-amplify/api";
import { Schema } from "../amplify/data/resource";
import { createAIHooks } from "@aws-amplify/ui-react-ai";

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

次のファイルを編集します。

src/App.tsx
import * as React from 'react';
import { Flex, TextAreaField, Loader, Text, View, Button } from "@aws-amplify/ui-react"
import { useAIGeneration } from "./client";

export default function App() {
  const [description, setDescription] = React.useState("");
  const [{ data, isLoading }, generateRecipe] =
    useAIGeneration("generateRecipe");

  const handleClick = async () => {
    generateRecipe({ description });
  };

  return (
    <Flex direction="column">
      <Flex direction="row">
        <TextAreaField
          autoResize
          value={description}
          onChange={(e) => setDescription(e.target.value)}
          label="Description"
        />
        <Button onClick={handleClick}>Generate recipe</Button>
      </Flex>
      {isLoading ? (
        <Loader variation="linear" />
      ) : (
        <>
          <Text fontWeight="bold">{data?.name}</Text>
          <View as="ul">
            {data?.ingredients?.map((ingredient) => (
              <View as="li" key={ingredient}>
                {ingredient}
              </View>
            ))}
          </View>
          <Text>{data?.instructions}</Text>
        </>
      )}
    </Flex>
  );
}

まだひと手順残ってますが、ここまでで何ができたのかが気になるので起動してみます。
認証を通り...
何か起動してますね。

image.png

入力してボタン押してみたけどうんともすんとも...

とりあえず次の手順もやってみます。

src/App.tsx
import { Authenticator } from "@aws-amplify/ui-react";
import { AIConversation } from '@aws-amplify/ui-react-ai';
import { useAIConversation } from './client';

export default function App() {
  const [
    {
      data: { messages },
      isLoading,
    },
    handleSendMessage,
  ] = useAIConversation('chat');
  // 'chat' is based on the key for the conversation route in your schema.

  return (
    <Authenticator>
      <AIConversation
        messages={messages}
        isLoading={isLoading}
        handleSendMessage={handleSendMessage}
      />
    </Authenticator>
  );
}

こちらも起動して確認してみたんですが次の項目がミュウぐらいの感じで消えてきます。
image.png
image.png

ブラウザではこんなエラーが

chunk-PJEEZAML.js?v=1b728f99:9129 Uncaught TypeError: conversation.onStreamEvent is not a function

うーん? :thinking:

src/App.tsx
import { Authenticator } from "@aws-amplify/ui-react";
import { AIConversation } from '@aws-amplify/ui-react-ai';
- import '@aws-amplify/ui-react/styles.css';
- import { useAIConversation } from './client';

export default function App() {
-  const [
-    {
-      data: { messages },
-      isLoading,
-    },
-    handleSendMessage,
-  ] = useAIConversation('chat');
-  // 'chat' is based on the key for the conversation route in your schema.

export default function App() {
  return (
    <Authenticator>
      <AIConversation
        messages={[]}
        handleSendMessage={() => {}}
-       messages={messages}
-       isLoading={isLoading}
-       handleSendMessage={handleSendMessage}
      />
    </Authenticator>
  );
}

こうしたら消えなくなたったので @aws-amplify/ui-react-ai の問題かもしれません :thinking:

image.png

何やら解決してくれそうなページを発見

下記参考にできそうです

import { generateClient } from 'aws-amplify/data';
import { type Schema } from '../amplify/data/resource'

const client = generateClient<Schema>();

// 1. Create a conversation
const { data: chat, errors } = await client.conversations.chat.create();

// 2. Subscribe to assistant responses
const subscription = chat.onStreamEvent({
  next: (event) => {
    // handle assistant response stream events
    console.log(event);
  },
  error: (error) => {
    // handle errors
    console.error(error);
  },
});

// 3. Send a message to the conversation
const { data: message, errors } = await chat.sendMessage('Hello, world!');

参考にしてちょっと直してみました :arrow_down:

src/App.tsx
import { Authenticator } from "@aws-amplify/ui-react";
import { AIConversation, ConversationMessage, SendMesageParameters } from '@aws-amplify/ui-react-ai';
import '@aws-amplify/ui-react/styles.css';
import { generateClient } from "aws-amplify/api";
import React from "react";
import { Schema } from "../amplify/data/resource";


export default function App() {
  const client = generateClient<Schema>({ authMode: "userPool" });
  const [chat, setChat] = React.useState<any>(null);
  const [messages, setMessages] = React.useState<ConversationMessage[]>([]);
  const [isLoading, setIsLoading] = React.useState(true);

  const handleSendMessage = async (input: SendMesageParameters) => {
    try {
      const response = await chat?.sendMessage(input);
      if (response?.data) {
        setMessages([...messages, response.data]);
      }
    } catch (error) {
      console.error(error);
    }
  };


  const createChat = async () => {
    try {
      const { data } = await client.conversations.chat.create();
      setChat(data);
    } catch (error) {
      console.error(error);
    }
  };

  const fetchMessages = async () => {
    try {
      const response = await chat?.listMessages();
      if (response?.data){
        setMessages(response.data);
      }
      setIsLoading(false);
    } catch (error) {
      console.error(error);
    }
  };

  React.useEffect(() => {
    createChat();
    fetchMessages();
  }, []);

  return (
    <Authenticator>
      <AIConversation
        messages={messages}
        isLoading={isLoading}
        handleSendMessage={handleSendMessage}
      />
    </Authenticator>
  );
}

(SendMesageParameters?)

メッセージが入れてボタン押せるようになりました、でもAIから返答が出ません...
image.png

やはりバージョンの問題?

image.png

ドキュメントやサンプルにあるこれがエラーになるんですよね...
https://github.com/aws-samples/amplify-ai-examples/tree/main をクローンして同じ問題が起こらなければバージョンの問題ということになる? :thinking:

package.jsonの記述合わせてnode_modules、package-lock.json消して再インストールしたけど解決しなかったのでコマンドでバージョン確認します

npm list --depth=0

ちょいちょいズレてるな... node_modules消してinstallしなおしてもこのままなんですよね

こちらのバージョン
├── @aws-amplify/backend-cli@1.4.2
├── @aws-amplify/backend@1.8.0
├── @aws-amplify/ui-react-ai@1.1.0
├── @aws-amplify/ui-react@6.7.1
├── @types/node@20.17.0
├── @types/react-dom@18.3.1
├── @types/react@18.3.12
├── @vitejs/plugin-react@4.3.3
├── aws-amplify@6.10.2
├── aws-cdk-lib@2.172.0
├── aws-cdk@2.172.0
├── constructs@10.4.2
├── esbuild@0.20.2
├── eslint@8.57.1
├── react-dom@18.3.1
├── react@18.3.1
├── tsx@4.19.1
├── typescript@5.6.3
└── vite@5.4.10
amplify-ai-examplesのバージョン
├── @aws-amplify/backend-cli@1.4.2
├── @aws-amplify/backend@1.7.0
├── @aws-amplify/ui-react-ai@1.0.0
├── @aws-amplify/ui-react@6.6.0
├── @types/node@20.17.6
├── @types/react-dom@18.3.1
├── @types/react@18.3.12
├── aws-amplify@6.8.2
├── aws-cdk-lib@2.167.2
├── aws-cdk@2.167.2
├── constructs@10.4.2
├── esbuild@0.24.0
├── eslint-config-next@15.0.2
├── eslint@8.57.1
├── next@14.2.15
├── react-dom@18.3.1
├── react-icons@5.3.0
├── react-markdown@9.0.1
├── react@18.3.1
├── tsx@4.19.2
└── typescript@5.6.3

キャッシュクリアして再インストールします。

npm cache clean --force
rm -rf node_modules package-lock.json
npm install

エラーでなくなりました。

image.png

もしかして一番最初の記述でいけるようになったんじゃないか?
ソース戻し...

src/App.tsx
import { Button, Flex, Loader, Text, TextAreaField, View } from "@aws-amplify/ui-react";
import * as React from 'react';
import { useAIGeneration } from "./client";

export default function App() {
  const [description, setDescription] = React.useState("");
  const [{ data, isLoading }, generateRecipe] =
    useAIGeneration("generateRecipe");

  const handleClick = async () => {
    generateRecipe({ description });
  };

  return (
    <Flex direction="column">
      <Flex direction="row">
        <TextAreaField
          autoResize
          value={description}
          onChange={(e) => setDescription(e.target.value)}
          label="Description"
        />
        <Button onClick={handleClick}>Generate recipe</Button>
      </Flex>
      {isLoading ? (
        <Loader variation="linear" />
      ) : (
        <>
          <Text fontWeight="bold">{data?.name}</Text>
          <View as="ul">
            {data?.ingredients?.map((ingredient) => (
              <View as="li" key={ingredient}>
                {ingredient}
              </View>
            ))}
          </View>
          <Text>{data?.instructions}</Text>
        </>
      )}
    </Flex>
  );
}

う、動くようになった!!!

image.png

人魚はサバに置き換えられてますね〜
急がば回れだったか〜...

image.png

image.png

ちゃんと別の文字を入れると別のレシピになりますね!

会話の方も

src/App.tsx
import { Authenticator } from "@aws-amplify/ui-react";
import { AIConversation } from '@aws-amplify/ui-react-ai';
import { useAIConversation } from './client';

export default function App() {
  const [
    {
      data: { messages },
      isLoading,
    },
    handleSendMessage,
  ] = useAIConversation('chat');
  // 'chat' is based on the key for the conversation route in your schema.

  return (
    <Authenticator>
      <AIConversation
        messages={messages}
        isLoading={isLoading}
        handleSendMessage={handleSendMessage}
      />
    </Authenticator>
  );
}

image.png

動くようになりました〜 ...?
き、消えた... えぇ〜? :thinking:

image.png

バージョンがサンプルとズレてるとこがあったので修正します

├── @aws-amplify/backend@1.8.0
├── @aws-amplify/ui-react@6.7.1

次のコマンドを実行します

npm install @aws-amplify/backend@1.7.0 @aws-amplify/ui-react@6.6.0

しかし... うまくいかず...

サンプルのコード起動してみよう

image.png

あら、なんか左にいっぱい出てますね...

今までの問い合わせたち!

image.png

image.png

なるほどなー... 別の連絡手段を試す、大事ですね :smile:

せっかくなので

systemPromptを編集してこれの振る舞いを変更したいです。
ちょっと難しい要求すぎるかな...

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

const schema = a.schema({
  chat: a.conversation({
    aiModel: a.ai.model("Claude 3.5 Sonnet"),
+   systemPrompt: `あなたは5・7・5でのみ会話ができる俳句のAIです。`,
-   systemPrompt: `You are a helpful assistant`,
  })
    .authorization((allow) => allow.owner()),

  chatNamer: a
    .generation({
      aiModel: a.ai.model("Claude 3 Sonnet"),
      systemPrompt: `You are a helpful assistant that writes descriptive names for conversations. Names should be 2-10 words long`,
    })
    .arguments({
      content: a.string(),
    })
    .returns(
      a.customType({
        name: a.string(),
      })
    )
    .authorization((allow) => [allow.authenticated()]),
});

export type Schema = ClientSchema<typeof schema>;

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

image.png

アレ...?

image.png

ページリロードしたら表示できました。
@aws-amplify/ui-react-aiに問題があるのかな...

image.png

全然575じゃない

systemPrompt: `今から投げられる質問に Yes か No で答えてください。`,

ちょっと変えてみました。思ったようにはならなかった...

image.png

会話の方アレンジするの難しいのかもですね

Generatorをアレンジ

細かい命名とかはちょっと直すのスキップします

amplify/data/resource.ts
  generateRecipe: a.generation({
    aiModel: a.ai.model('Claude 3.5 Haiku'),
-   systemPrompt: 'You are a helpful assistant that generates recipes.',
+   systemPrompt: 'You are a useful assistant in creating travel plans.',
  })
...あれ、うまくいってるgenerateRecipeの方Haikuだな... :thinking: chatの方Sonnetに変えたのが悪かったのかな... と思って変えたけど普通にさっきのエラー出ました。

ちょっとボタンの見た目だけ変えときました。

image.png

おぉ〜計画立ててくれてますね。プロンプト戻して同じこと伝えたらどうなるんだろう?

あんま影響ないみたいですね :thinking:

image.png

単語(台湾)だけを与えた場合は謎のレシピを出してくれます、何のレシピだろうこれ...
image.png

systemPrompt:はAIChatと話すときの最初の会話みたいな感じですかね。
普段AIとのチャットでも次の会話で全然別のこと話すと、話題変わってくれるみたいな感じで、初めに与えるsystemPromptにそこまでの影響力はないかも?

最後に

ここまで読んでくださってありがとうございます!
Qiitaカレンダーは新しいもの試すいい機会です、まだ参加されたことがない方もぜひやってみてください :thumbsup:

59
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
59
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?