paint-brush
AI 検索を活用したパーソナル アシスタント アプリを構築する方法@lablab
6,056 測定値
6,056 測定値

AI 検索を活用したパーソナル アシスタント アプリを構築する方法

lablab.ai hackathons28m2023/07/04
Read on Terminal Reader

長すぎる; 読むには

Anthropic は、高度な AI システムの開発に焦点を当てた研究組織です。彼らの最新作である Claude は、役に立つ、正直、そして無害になるように設計された次世代 AI アシスタントです。 LangChain は、エンドツーエンドの言語モデル アプリケーションを構築するためのツールです。言語学習モデルの作成、管理、展開のプロセスを簡素化する堅牢なフレームワークを提供します。
featured image - AI 検索を活用したパーソナル アシスタント アプリを構築する方法
lablab.ai hackathons HackerNoon profile picture
0-item
1-item

この記事では、2 つのテクノロジー ( LangChainAnthropic ) を組み合わせて、検索機能を備えたパーソナル アシスタントを作成する方法を説明します。


ハッカソンの参加者によって、ますます多くの AI 製品が作成されています。開発者にとっての次の機会は、 Google Cloud Vertex AIとの共同ハッカソンであり、各参加者はGoogle Cloudの最新テクノロジーを使用して独自のAI アプリケーションを作成する機会があります。ここでは、ますます重要性を増している AI のアプリケーションの 1 つである検索エンジンについて説明します。



それは何ですか?

検索機能を備えたパーソナル アシスタントは、検索エンジン テクノロジーを使用して、情報の検索、予約、リマインダーの設定、メッセージの送信などのタスクを行うユーザーを支援するデジタル アシスタントです。これらのアシスタントは、検索アルゴリズムを使用してさまざまなソースからデータを収集および分析し、それを有用かつ簡潔な方法でユーザーに表示します。


検索機能を備えたパーソナル アシスタントの代表的な例としては、 Google アシスタントSiriAlexaCortanaがあります。これらのアシスタントは、検索機能を効果的に使用して、正確で関連性の高い情報を提供し、タスクを完了し、ユーザーと対話する際の応答を改善します。


Anthropicのクロードの紹介

Anthropic は、高度な AI システムの開発に焦点を当てた研究組織です。彼らの最新作である Claude は、役に立つ、正直、そして無害になるように設計された次世代 AI アシスタントです。この最先端のモデルは、さまざまなタスクにおいて高度な信頼性と予測可能性を保証します。


クロードの主な特徴は次のとおりです。

  • 多彩な会話機能とテキスト処理機能

  • ユーザーの安全とプライバシーを最優先に維持する


Claude の主な使用例は次のとおりです。

  • 要約

  • 検索

  • 創造的で共同執筆

  • Q&A

  • コーディング支援


これらの機能により、Claude は幅広いアプリケーションにとって理想的な AI ツールとなり、さまざまなドメインにわたるユーザーに力を与えます。

ラングチェーンの紹介

LangChain は、エンドツーエンドの言語モデル アプリケーションを構築するための多用途ツールです。言語学習モデル (LLM) の作成、管理、展開のプロセスを簡素化する堅牢なフレームワークを提供します。 LLM は、さまざまな言語やタスクで人間のようなテキストを理解、生成、操作するように設計された高度な AI モデルです。


LangChain の主な機能は次のとおりです。


  • LLM のプロンプトの効率的な管理

  • 複雑なワークフローのタスクのチェーンを作成する機能

  • AI に状態を追加して、以前のインタラクションからの情報を記憶できるようにする


これらの機能により、LangChain は、さまざまなアプリケーションで言語モデルの可能性を活用するための強力でユーザーフレンドリーなプラットフォームになります。

前提条件

  • Pythonの基礎知識
  • Typescipt や React の基礎知識
  • Anthropic の Claude API へのアクセス
  • SerpApi の Web 検索 API へのアクセス

概要

  1. プロジェクトの初期化
  2. Claude と LangCHain を使用して AI アシスタント アプリのフロントエンドを構築する
  3. プロジェクトファイルの書き込み
  4. AI アシスタント アプリのテスト

議論

プロジェクトの初期化

app.py (Flask アプリのエントリポイント) 🐍

  1. Flask をインストールする: まず、環境に Flask がインストールされていることを確認してください。 pip使用してこれを行うことができます。

     pip install Flask


  2. 新しいディレクトリを作成する: プロジェクト用に新しいディレクトリを作成し、そこに移動します。

     mkdir claude-langchain cd claude-langchain


  3. 仮想環境をセットアップする (オプション) : Python プロジェクトを操作するときは、仮想環境を使用することをお勧めします。 venvまたはその他の任意のツールを使用して作成できます。

     python -m venv venv source venv/bin/activate (Linux/Mac) venv\Scripts\activate (Windows)


  4. main.pyファイルを作成する: Flask アプリケーション コードを記述するためのmain.pyファイルを作成します。

     touch app.py # Linux/Mac echo.>app.py # Windows


  5. Flask アプリケーション コードを作成します。お気に入りのコード エディターでmain.pyファイルを開き、次のコードを追加します。

     from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello, World!' if __name__ == '__main__': app.run()


  6. Flask アプリケーションを実行しますmain.pyファイルを保存し、ターミナル/コマンド プロンプトで次のコマンドを実行します。

     python main.py


  7. ブラウザを開く: 任意の Web ブラウザを開き、 http://127.0.0.1:5000/に移動します。 「Hello, World!」が表示されるはずです。ウェブページに表示されます。


以上です! Flask プロジェクトが正常に初期化され、簡単なアプリケーションが作成されました。

.env 🌏

  1. python-dotenv と langchain をインストールする: .envファイルで環境変数を簡単に管理するには、 python-dotenvパッケージを使用します。同時にlangchainインストールしましょう。 pipを使用して両方のパッケージをインストールします。

     pip install python-dotenv langchain


  2. .envファイルを作成する: プロジェクトのルート ディレクトリに.envファイルを作成します。

     touch .env # Linux/Mac echo.>.env # Windows


  3. 環境変数を追加する: お気に入りのコード エディターで.envファイルを開き、環境変数を追加します。各変数は、KEY=VALUE の形式で新しい行に置く必要があります。

     ANTHROPIC_API_KEY=sk-ant-xxxxxxxxxxxxxxxxx SERPAPI_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

    Anthropic の ClaudeモデルとSerpAPIの Web 検索サービスの両方の API キーが必要であることに注意してください。


  4. 環境変数をロードする: main.pyファイルを変更して、 python-dotenvを使用して.envファイルから環境変数をロードします。 main.pyを次のように更新します。

     import os from flask import Flask from dotenv import load_dotenv load_dotenv() app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello, World!' if __name__ == '__main__': app.run()


  5. バージョン管理の.envファイルを無視する: バージョン管理では秘密キーなどの機密情報を共有しないことが重要です。 Git を使用している場合は、 .gitignoreファイルに次の行を追加します (ファイルがない場合は作成します)。

     .env


これで、 .envファイルの環境変数を使用するように Flask プロジェクトが設定されました。必要に応じてさらに変数を追加し、 os.environ.get('KEY')を使用してアクセスできます。 .envファイルは非公開にし、バージョン管理には決してコミットしないようにしてください。

Claude と LangChain を使用した AI アシスタント アプリのフロントエンドの構築

このチュートリアルは、Node.js、npm、React、Typescript の基本を理解している中級ユーザー向けに設計されています。これらのテクノロジーを含むスタックと、スタイル設定用の Tailwind CSS を使用します。このスタックは、その堅牢性、多用途性、およびこれらのテクノロジーに対するコミュニティの強力なサポートにより選択されました。さらに、Anthropic の Claude モデルと LangChain という 2 つの強力な AI テクノロジーを統合し、アプリが人間のような正確なテキスト応答を生成できるようにします。

Node.js と NPM のインストール

  1. 公式サイトから OS 用の Node.js インストーラーをダウンロードします。

  2. インストール プロンプトに従って、Node.js と npm をインストールします。ほとんどのユーザーには LTS (長期サポート) バージョンが推奨されます。

  3. インストールしたら、ターミナルから Node.js と npm のバージョンを確認して、インストールを確認します。

    ノード -v npm -v

プロジェクト環境のセットアップ

Create React アプリのインストール

Create React App (CRA) は、新しい React.js アプリケーションの作成を支援するコマンドライン ユーティリティです。 npm 経由でグローバルにインストールします。

 npm install -g create-react-app
Typescript を使用して新しい React プロジェクトを作成する

CRA と Typescript テンプレートを使用してai-assistant-claudeという名前の新しいプロジェクトを作成します。

 npx create-react-app ai-assistant-claude --template typescript

このコマンドは、現在のディレクトリにai-assistant-claudeという新しいディレクトリを作成し、Typescript をサポートする新しい React アプリケーションを格納します。

TailwindCSS の統合

TailwindCSS のインストール

このチュートリアルで従う手順は、 Tailwind CSS の公式ドキュメントに基づいています。最新の手順については、これらのドキュメントを参照してください。

まず、TailwindCSS をインストールし、プロジェクト内のライブラリを初期化します。

 npm install -D tailwindcss npx tailwindcss init
テンプレートパスの構成

次に、テンプレート パスをtailwind.config.jsファイルに追加して構成します。 ++は、追加する行を示します。

 /** @type {import('tailwindcss').Config} */ module.exports = { -- content: [], ++ content: [ ++ "./src/**/*.{js,jsx,ts,tsx}", ++ ], theme: { extend: {}, }, plugins: [], }
Tailwind ディレクティブの追加

最後に、Tailwind の各レイヤーの@tailwindディレクティブを./src/index.cssファイルに追加します。

 @tailwind base; @tailwind components; @tailwind utilities;

そして出来上がり! Tailwind CSS がプロジェクトに統合されました。

必要なライブラリのインストール

コーディングセクションに進む前に、 fontawesomereact-markdownaxiosreact-hook-formなどの必要なライブラリをインストールして準備を完了しましょう。

アイコン用の FontAwesome のインストール
npm i --save @fortawesome/fontawesome-svg-core npm install --save @fortawesome/free-solid-svg-icons npm install --save @fortawesome/react-fontawesome
Markdown コンテンツをレンダリングするための React Markdown のインストール
npm install --save react-markdown

これらの手順が完了すると、プロジェクトには必要なツールとライブラリがすべてセットアップされます。次に、Anthropic の Claude API と LangChain を使用して正確で人間のようなテキスト応答を生成する AI アシスタント アプリの構築を開始します。

トラブルシューティング

インストールまたはセットアップ中に問題が発生した場合の一般的な解決策をいくつか示します。

  • Node.js または npm に関連する問題については、再インストールしてみるか、 Node.jsおよびnpmの公式ドキュメントを確認してください。
  • Create React App で問題が発生した場合は、 CRA のドキュメントを参照してください。
  • Tailwind CSS の問題については、 Tailwind CSS のドキュメントを参照してください。


その他の問題については、それぞれのライブラリのドキュメントを参照するか、StackOverflow または関連する GitHub リポジトリに問題を投稿してください。

プロジェクトファイルの書き込み

このセクションでは、前に初期化した Flask アプリに戻り、 /ask/searchなどの新しいエンドポイントを追加します。これらは、シンプルなチャット機能と高度なチャット機能 (後者は Google 検索結果を利用します) のエンドポイントとして機能します。

必要なモジュールをインポートすることから始めましょう。


 from flask import Flask, jsonify, request from dotenv import load_dotenv from langchain.chat_models import ChatAnthropic from langchain.chains import ConversationChain from langchain.agents import Tool from langchain.agents import AgentType from langchain.utilities import SerpAPIWrapper from langchain.agents import initialize_agent from langchain.memory import ConversationBufferMemory from langchain.prompts.chat import ( ChatPromptTemplate, MessagesPlaceholder, SystemMessagePromptTemplate, AIMessagePromptTemplate, HumanMessagePromptTemplate, ) load_dotenv() app = Flask(__name__)

上記のセクションでは、必要なパッケージをすべてインポートし、Flask アプリケーションを初期化します。

バックエンドの開発

基本エンドポイントの作成

まず、基本的なエンドポイント ( / ) を作成して、サーバーが正しく実行されているかどうかをテストします。

 @app.route('/') def hello_world(): return 'Hello, World!'

ルート URL にアクセスすると、「Hello, World!」という応答が返され、サーバーが期待どおりに実行されていることを示します。

/askエンドポイントの作成

このエンドポイントは、アプリケーションの基本的なチャット機能のメッセージを処理します。リクエストから JSON データを読み取り、メッセージを処理し、Anthropic の Claude モデルと LangChain を使用してレスポンスを生成します。

 @app.route('/ask', methods=['POST']) def ask_assistant(): # The code for /ask endpoint goes here


リクエストからのメッセージの抽出

まず、データが提供されているかどうかを確認し、そこからメッセージを抽出する必要があります。

 data = request.get_json() if not data: return jsonify({"error": "No data provided"}), 400 messages = data.get("message")


応答の生成

次のコード セグメントは、LangChain のChatAnthropic()モデルとChatPromptTemplateを使用してチャット応答を生成し、会話を構造化します。会話履歴はConversationBufferMemoryを使用して保存されます。


 llm = ChatAnthropic() input = "" message_list = [] for message in messages: if message['role'] == 'user': message_list.append( HumanMessagePromptTemplate.from_template(message['content']) ) input = message['content'] elif message['role'] == 'assistant': message_list.append( AIMessagePromptTemplate.from_template(message['content']) ) # Adding SystemMessagePromptTemplate at the beginning of the message_list message_list.insert(0, SystemMessagePromptTemplate.from_template( "The following is a friendly conversation between a human and an AI. The AI is talkative and " "provides lots of specific details from its context. The AI will respond with plain string, replace new lines with \\n which can be easily parsed and stored into JSON, and will try to keep the responses condensed, in as few lines as possible." )) message_list.insert(1, MessagesPlaceholder(variable_name="history")) message_list.insert(-1, HumanMessagePromptTemplate.from_template("{input}")) prompt = ChatPromptTemplate.from_messages(message_list) memory = ConversationBufferMemory(return_messages=True) conversation = ConversationChain(memory=memory, prompt=prompt, llm=llm) result = conversation.predict(input=input)

応答の送信

応答を生成した後、結果内の改行を置換し、JSON オブジェクトとして返します。

 print(result) return jsonify({"status": "success", "message": result})
/searchエンドポイントの作成

/searchエンドポイントは/askに似ていますが、より詳細な応答を提供する検索機能が含まれています。この機能を追加するにはSerpAPIWrapperを使用します。

 @app.route('/search', methods=['POST']) def search_with_assistant(): data = request.get_json() if not data: return jsonify({"error": "No data provided"}), 400 messages = data.get("message") llm = ChatAnthropic() # Get the last message with 'user' role user_messages = [msg for msg in messages if msg['role'] == 'user'] last_user_message = user_messages[-1] if user_messages else None # If there is no user message, return an error response if not last_user_message: return jsonify({"error": "No user message found"}), 400 input = last_user_message['content'] search = SerpAPIWrapper() tools = [ Tool( name = "Current Search", func=search.run, description="useful for when you need to answer questions about current events or the current state of the world" ), ] chat_history = MessagesPlaceholder(variable_name="chat_history") memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True) agent_chain = initialize_agent( tools, llm, agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION, verbose=True, memory=memory, agent_kwargs = { "memory_prompts": [chat_history], "input_variables": ["input", "agent_scratchpad", "chat_history"] } ) result = agent_chain.run(input=input) print(result) return jsonify({"status": "success", "message": result})


Flask アプリの実行

最後に、 Flask アプリを実行するための標準ボイラープレートを追加します。

 if __name__ == '__main__': app.run()
バックエンドのテスト

すべてがうまくいけば、これが最終的なバックエンド コードです。

 from flask import Flask, jsonify, request from dotenv import load_dotenv from langchain.chat_models import ChatAnthropic from langchain.chains import ConversationChain from langchain.agents import Tool from langchain.agents import AgentType from langchain.utilities import SerpAPIWrapper from langchain.agents import initialize_agent from langchain.memory import ConversationBufferMemory from langchain.prompts.chat import ( ChatPromptTemplate, MessagesPlaceholder, SystemMessagePromptTemplate, AIMessagePromptTemplate, HumanMessagePromptTemplate, ) load_dotenv() app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello, World!' @app.route('/ask', methods=['POST']) def ask_assistant(): data = request.get_json() if not data: return jsonify({"error": "No data provided"}), 400 messages = data.get("message") llm = ChatAnthropic() input = "" message_list = [] for message in messages: if message['role'] == 'user': message_list.append( HumanMessagePromptTemplate.from_template(message['content']) ) input = message['content'] elif message['role'] == 'assistant': message_list.append( AIMessagePromptTemplate.from_template(message['content']) ) # Adding SystemMessagePromptTemplate at the beginning of the message_list message_list.insert(0, SystemMessagePromptTemplate.from_template( "The following is a friendly conversation between a human and an AI. The AI is talkative and " "provides lots of specific details from its context. The AI will respond with plain string, replace new lines with \\n which can be easily parsed and stored into JSON, and will try to keep the responses condensed, in as few lines as possible." )) message_list.insert(1, MessagesPlaceholder(variable_name="history")) message_list.insert(-1, HumanMessagePromptTemplate.from_template("{input}")) prompt = ChatPromptTemplate.from_messages(message_list) memory = ConversationBufferMemory(return_messages=True) conversation = ConversationChain(memory=memory, prompt=prompt, llm=llm) result = conversation.predict(input=input) print(result) return jsonify({"status": "success", "message": result}) @app.route('/search', methods=['POST']) def search_with_assistant(): data = request.get_json() if not data: return jsonify({"error": "No data provided"}), 400 messages = data.get("message") llm = ChatAnthropic() # Get the last message with 'user' role user_messages = [msg for msg in messages if msg['role'] == 'user'] last_user_message = user_messages[-1] if user_messages else None # If there is no user message, return an error response if not last_user_message: return jsonify({"error": "No user message found"}), 400 input = last_user_message['content'] search = SerpAPIWrapper() tools = [ Tool( name = "Current Search", func=search.run, description="useful for when you need to answer questions about current events or the current state of the world" ), ] chat_history = MessagesPlaceholder(variable_name="chat_history") memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True) agent_chain = initialize_agent( tools, llm, agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION, verbose=True, memory=memory, agent_kwargs = { "memory_prompts": [chat_history], "input_variables": ["input", "agent_scratchpad", "chat_history"] } ) result = agent_chain.run(input=input) print(result) return jsonify({"status": "success", "message": result}) if __name__ == '__main__': app.run()


それでは、アプリをテストしてみましょう。このコマンドを使用してバックエンド アプリを実行します。また、仮想環境がアクティブ化されていることを確認してください。

 flask run


ターミナルがこの出力を返せば、すべてがうまくいくことがわかります。


早速、2 つのエンドポイント/ask/searchをテストしてみましょう。 2 つを区別するために、それぞれに同様のペイロードを送信してみましょう。 REST API テストおよびドキュメント ソフトウェアを開くか、単に cURL を使用します。ただし、このチュートリアルではInsomniaを使用します。


まず、次のペイロードを使用して/askエンドポイントを呼び出してみましょう。ビデオゲーム「 ゼルダの伝説 ブレス オブ ザ ワイルド」の続編について質問したとします。それは不正確に答えます。このモデルは 2021 年末の時点でトレーニングが終了しており、続編についてはまだ発表されていないため、これは予想されることです。


/searchエンドポイントはどうですか?先ほどのコードにお気づきかと思いますが、このエンドポイントは、エージェントを利用するより洗練されたチェーンで処理されます。


エージェントを使用することで、AI に独自のモデル (すでに示したように、独自の欠陥がある) だけでなく、より多くのツールを提供することで、AI の意思決定においてより大きな力を与えることができます。


/searchエンドポイントがどのように機能するかを示すために、前と同じペイロードを使用して呼び出してみましょう。


今回もうまくいきました!ボンネットの下ではどのように機能するのでしょうか?ターミナルの出力を見てみましょう。

興味深いことに、今回の AI モデルはすぐには答えませんでしたが、答えるために何をすべきかについて「熟考」したようでした。同社はウェブ検索結果から「ニュース検索によると、『ゼルダの伝説 涙の王国』という続編が発表されたようだ」との見解を示したことから回答を決定した。


したがって、その論理によれば、 /searchエンドポイントを使用するだけでよいのではないでしょうか?それはより正確であり、したがってより賢明だからでしょうか?完全ではありません。通常、チャットボットベースのアプリでは、ボットは以前の会話のコンテキストを保持することが期待されているため、会話のコンテキスト全体を「覚えている」かのように応答を提供できます。 /searchエンドポイントでそれができるかどうかを見てみましょう。


/searchエンドポイントが今尋ねた内容を思い出せるかどうかをテストしてみましょう。


これは、LangChain ライブラリには過去の会話、Web サーバー、およびその上に構築された REST API サービスを保持するために使用できるメモリ チェーン機能が本質的にステートレスであるために発生しました。つまり、Web サーバーは各リクエストを新しいリクエストとして扱います。


しかし、以前の会話をペイロードとして含めていませんか?それは良い質問です。現在、LangChain で使用されているエージェント チェーンは、ユーザーと AI の両方のリクエストと応答で構成される、合成プロンプトの処理をサポートしていないことがわかりました。これは主に、モデルに会話の例を提供するために使用され、望ましい応答に合わせてモデルをさらに調整します。


一方、エージェントは単一の指示を受け取ることによって動作し、それを中心に思考の連鎖を展開します。そのため、会話がどれだけ長くても、エージェントは最新のリクエストのみに応答します。


/askエンドポイントをテストして違いを確認してみましょう。


今回は、私たちの過去の会話を追加のコンテキストとして使用して答えてくれました。ここまでで、AI アシスタント アプリを構築するには両方のエンドポイントが必要であることがわかりました。しかし、時代遅れだが常に覚えている/askエンドポイントと、忘れっぽいが徹底的で最新の/searchエンドポイントの両方をどのように組み込むのでしょうか?もちろん、フロントエンドを構築することによってです。

フロントエンドの開発


App.tsx

ai-assistant-claudeプロジェクト ディレクトリに戻りましょう。このプロジェクトでは、エントリ ポイント ファイルApp.tsxから始めて、React コンポーネントの変更を開始します。

 import React from 'react'; import logo from './logo.svg'; import './App.css'; import { ChatClient } from './ChatClient'; function App() { return ( <div> <ChatClient/> </div> ); } export default App;

上記のコード スニペットでは、後の手順で作成されるChatClientコンポーネントをインポートします。次に、 <ChatClient/>コンポーネントを<div>要素内に含めます。これにより、React コンポーネントを使用した AI アシスタント アプリの構造が設定されます。

チャットクライアント.tsx

 import React, { useState } from 'react'; import { ChatInput } from './ChatInput'; import { ChatHistory } from './ChatHistory'; export interface Message { content: string; role: string; } export const ChatClient: React.FC = () => { const [messages, setMessages] = useState<Array<Message>>([]); const [isLoading, setIsLoading] = useState(false) const handleSimpleChat = (message: string) => { // Send the message and past chat history to the backend // Update messages state with the new message let newMessages = [...messages, { content: message, role: 'user' }] setMessages(newMessages); let postData = { message: newMessages } setIsLoading(true) fetch('/ask', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(postData), }) .then((response) => response.json()) .then((data) => { if (data.status === "success") { setMessages([...newMessages, { content: data.message, role: 'assistant' }]) } setIsLoading(false) console.log('Success:', data); }) .catch((error) => { console.error('Error:', error); setIsLoading(false) }); }; const handleAdvancedChat = (message: string) => { // Trigger AI agent with Google Search functionality // Update messages state with the new message and AI response let newMessages = [...messages, { content: message, role: 'user' }] setMessages(newMessages); let postData = { message: newMessages } setIsLoading(true) fetch('/search', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(postData), }) .then((response) => response.json()) .then((data) => { if (data.status === "success") { setMessages([...newMessages, { content: data.message, role: 'assistant' }]) } console.log('Success:', data); setIsLoading(false) }) .catch((error) => { console.error('Error:', error); setIsLoading(false) }); }; return ( <div className="h-screen bg-gray-100 dark:bg-gray-900 flex items-center justify-center"> <div className='flex flex-col items-center gap-2'> <h1 className='text-white text-xl'>AI Assistant with Claude and LangChain</h1> <div className="w-full max-w-md h-[80vh] bg-white dark:bg-gray-800 rounded-lg shadow-md overflow-hidden flex flex-col"> <ChatHistory messages={messages} isLoading={isLoading} /> <ChatInput onSimpleChat={handleSimpleChat} onAdvancedChat={handleAdvancedChat} /> </div> </div> </div> ); };

このコンポーネントは、AI アシスタントの主要なユーザー インターフェイスとして機能します。図に示すように、会話履歴を表示するChatHistoryコンポーネントとテキストを入力するChatInputコンポーネントが組み込まれています。


このコンポーネントは、 ChatInputコンポーネントからの入力を処理し、この入力を使用してバックエンドにリクエストを送信し、読み込みステータスを表示します。リクエストが正常に処理された場合、コンポーネントはバックエンドから受信した応答も表示します。

チャット履歴.tsx

 import React from 'react'; import { ReactMarkdown } from 'react-markdown/lib/react-markdown'; import { Message } from './ChatClient'; interface ChatHistoryProps { messages: Array<Message>; isLoading: boolean } export const ChatHistory: React.FC<ChatHistoryProps> = ({ messages, isLoading }) => { return ( <div className="p-4 h-full overflow-y-auto"> {messages.map((message, index) => ( <div key={index} className={`mb-3 ${ message.role === 'user' ? 'text-right' : 'text-left' }`} > <ReactMarkdown className={`inline-block px-3 py-2 rounded-md ${ index % 2 === 0 ? 'bg-gray-300 dark:bg-gray-700' : 'bg-blue-200 dark:bg-blue-900' }`} > {message.content} </ReactMarkdown> </div> ))} {isLoading && ( <div className="mb-3 text-left"> <ReactMarkdown className="inline-block px-3 py-2 rounded-md bg-blue-200 dark:bg-blue-900 animate-pulse" > {/* Put your desired loading content here */} Thinking... </ReactMarkdown> </div> )} </div> ); };

幸いなことに、TailwindCSS には、 animate-pulseなどの単純なアニメーション用の組み込みユーティリティ クラスが用意されています。このクラスは、リクエストが応答を待っていることをエレガントに示すのに役立ちます。このコンポーネントでは、「ユーザー」と「アシスタント」からのメッセージの位置付けも区別します。


チャット入力.tsx

 import React, { useState } from 'react'; interface ChatInputProps { onSimpleChat: (message: string) => void; onAdvancedChat: (message: string) => void; } export const ChatInput: React.FC<ChatInputProps> = ({ onSimpleChat, onAdvancedChat }) => { const [input, setInput] = useState(''); const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => { setInput(event.target.value); }; const handleSubmit = (handler: (message: string) => void) => { handler(input); setInput(''); }; return ( <div className="flex p-4 border-t border-gray-200 dark:border-gray-700"> <input type="text" value={input} onChange={handleInputChange} placeholder="Type your message..." className="flex-grow px-3 py-2 rounded-md bg-gray-200 text-gray-900 dark:bg-gray-700 dark:text-gray-100 focus:outline-none" /> <button onClick={() => handleSubmit(onSimpleChat)} className="ml-2 px-4 py-2 font-semibold text-gray-600 bg-white dark:text-gray-400 dark:bg-gray-800 border border-gray-300 rounded-md hover:bg-gray-200 dark:hover:bg-gray-700 focus:outline-none" > Ask </button> <button onClick={() => handleSubmit(onAdvancedChat)} className="ml-2 px-4 py-2 font-semibold text-white bg-blue-500 border border-blue-600 rounded-md hover:bg-blue-400 focus:outline-none" > Ask and Search </button> </div> ); };

最後に、テキスト入力に 2 つのボタンを追加しました。最初のボタンは、入力を/askエンドポイントに送信するために使用され、追加の機能拡張を行わずに AI モデルを使用して入力を処理します。


このエンドポイントはコンテキスト認識型です。 2 番目のボタンは、「Ask and Search」という適切な名前が付けられており、入力を/searchエンドポイントに送信します。このボタンは、AI モデルによる入力の処理に加えて、状況に応じて AI 駆動の Web 検索もトリガーします。


インデックス.html

 <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="theme-color" content="#000000" /> <meta name="description" content="Web site created using create-react-app" /> <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> <!-- manifest.json provides metadata used when your web app is installed on a user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/ --> <link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> <!-- Notice the use of %PUBLIC_URL% in the tags above. It will be replaced with the URL of the `public` folder during the build. Only files inside the `public` folder can be referenced from the HTML. Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. --> -- <title>React App</title> ++ <title>Claude AI Assistant</title> </head> <body> <noscript>You need to enable JavaScript to run this app.</noscript> <div id="root"></div> <!-- This HTML file is a template. If you open it directly in the browser, you will see an empty page. You can add webfonts, meta tags, or analytics to this file. The build step will place the bundled scripts into the <body> tag. To begin the development, run `npm start` or `yarn start`. To create a production bundle, use `npm run build` or `yarn build`. --> </body> </html>

最後の仕上げとして、 index.htmlページ内のアプリのタイトルを「 React App 」から「 Claude AI Assistant 」に変更して更新します。


パッケージ.json

 { "name": "ai-assistant-claude", "version": "0.1.0", "private": true, ++ "proxy": "http://localhost:5000", "dependencies": {

最後に、プロキシ構成をpackage.jsonファイルに追加し、 http://localhost:5000に設定します。これにより、別のポートの使用によって生じる CORS 制限を回避できます。

AI アシスタント アプリのテスト

テストを開始するには、まずバックエンド アプリ (claude-langchain) がすでに実行されていることを確認します。


次に、ディレクトリをフロントエンド アプリ (ai-assistant-claude) に変更し、次のコマンドを使用してアプリを起動します。

 npm start


アプリのビルドには少し時間がかかる場合があります。準備が完了すると、ブラウザでlocalhost:3000に自動的に開きます。


コンテキスト認識と検索機能の両方をテストしてみましょう。まず、2021 年のリリースがまだ発表されていない別のビデオ ゲーム、つまりSEGAの最新龍が如くゲームの続編について聞いてみましょう。さらに、このゲームには過去のゲームで人気のキャラクターである桐生一馬が登場するかどうかも尋ねます。これを行うには、「質問する」ボタンをクリックします。


数秒考えてください...



驚いたことに、AIは不正解でした。 『龍が如く:龍が如く』は確かに最新の龍が如くゲームですが、主役は別の主人公、春日一番です。質問を言い換えて、今度は「質問して検索」ボタンを使用してみましょう。



現在、AI はウェブを検索する必要があるかどうかを判断するのに時間がかかりました。 Web 検索が必要であると判断し、満足のいく答えが見つかった後、情報をクライアント/フロントエンドに返します。


今回はタイトルを『龍が如く 外伝 名前を消した男』に戻り、まさに桐生一馬が主人公となっている。


魅力的ですね。大規模言語モデルを利用したエージェントは、利用可能なリソース (検索) を考慮してどのようなアクションが必要かを予測し、満足のいく答えを提供します。 LangChain を使用すると、モデル、プロンプト (モデルへの指示)、エージェントやツールなどのその他の機能の接続と「チェーン」が簡単になります。

結論

このアプリの構築とこれらのテクノロジーについての学習を楽しんでいただければ幸いです。さらに詳しく知りたい場合は、フロントエンドバックエンドの完成したプロジェクトにアクセスできます。


また、パートナーが実現した最新テクノロジーを使用して構築したい場合は、次の AI チャレンジに参加してください。


*please see lablab.ai for all terms and conditions