In diesem Artikel möchten wir Ihnen zeigen, wie Sie zwei Technologien ( LangChain und Anthropic ) kombinieren und einen suchbasierten persönlichen Assistenten erstellen.
Immer mehr KI-Produkte werden von Teilnehmern unserer Hackathons erstellt. Die nächste Gelegenheit für Entwickler ist unser gemeinsamer Hackathon mit Google Cloud Vertex AI , bei dem jeder Teilnehmer die Chance hat, seine eigene KI-Anwendung mit der neuesten Technologie von Google Cloud zu erstellen. Hier sprechen wir über eine Anwendung von KI, die immer wichtiger wird: Suchmaschinen .
Ein suchgesteuerter persönlicher Assistent ist ein digitaler Assistent, der Suchmaschinentechnologie nutzt, um Benutzer bei Aufgaben wie der Suche nach Informationen, der Durchführung von Reservierungen, dem Einrichten von Erinnerungen und dem Versenden von Nachrichten zu unterstützen. Mithilfe von Suchalgorithmen sammeln und analysieren diese Assistenten Daten aus verschiedenen Quellen und präsentieren sie den Benutzern auf nützliche und prägnante Weise.
Prominente Beispiele für suchgesteuerte persönliche Assistenten sind Google Assistant , Siri , Alexa und Cortana . Diese Assistenten nutzen ihre Suchfunktionen effektiv, um genaue und relevante Informationen bereitzustellen, Aufgaben zu erledigen und ihre Antworten zu verbessern, während sie mit dem Benutzer interagieren.
Anthropic ist eine Forschungsorganisation, die sich auf die Entwicklung fortschrittlicher KI-Systeme konzentriert. Ihre neueste Kreation, Claude, ist ein KI-Assistent der nächsten Generation, der hilfreich, ehrlich und harmlos sein soll. Dieses hochmoderne Modell gewährleistet ein hohes Maß an Zuverlässigkeit und Vorhersehbarkeit bei verschiedenen Aufgaben.
Zu den Hauptmerkmalen von Claude gehören:
Vielseitige Konversations- und Textverarbeitungsfunktionen
Die Wahrung der Sicherheit und Privatsphäre der Benutzer hat oberste Priorität
Claudes Hauptanwendungsfälle sind:
Zusammenfassung
Suchen
Kreatives und kollaboratives Schreiben
Fragen und Antworten
Codierungsunterstützung
Diese Funktionen machen Claude zu einem idealen KI-Tool für eine Vielzahl von Anwendungen und unterstützen Benutzer in verschiedenen Domänen.
LangChain ist ein vielseitiges Tool zum Erstellen von End-to-End-Sprachmodellanwendungen. Es bietet ein robustes Framework, das den Prozess der Erstellung, Verwaltung und Bereitstellung von Sprachlernmodellen (Language Learning Models, LLMs) vereinfacht. LLMs sind fortschrittliche KI-Modelle, die darauf ausgelegt sind, menschenähnlichen Text in verschiedenen Sprachen und Aufgaben zu verstehen, zu generieren und zu manipulieren.
Effizientes Management von Eingabeaufforderungen für LLMs
Die Möglichkeit, Aufgabenketten für komplexe Arbeitsabläufe zu erstellen
Der KI wird ein Zustand hinzugefügt, der es ihr ermöglicht, sich Informationen aus früheren Interaktionen zu merken
Diese Fähigkeiten machen LangChain zu einer leistungsstarken und benutzerfreundlichen Plattform zur Nutzung des Potenzials von Sprachmodellen in verschiedenen Anwendungen.
Flask installieren : Stellen Sie zunächst sicher, dass Flask in Ihrer Umgebung installiert ist. Sie können dies mit pip
tun:
pip install Flask
Erstellen Sie ein neues Verzeichnis : Erstellen Sie ein neues Verzeichnis für Ihr Projekt und navigieren Sie dorthin:
mkdir claude-langchain cd claude-langchain
Richten Sie eine virtuelle Umgebung ein (optional) : Es empfiehlt sich, bei der Arbeit mit Python-Projekten eine virtuelle Umgebung zu verwenden. Sie können eines mit venv
oder einem anderen Tool Ihrer Wahl erstellen:
python -m venv venv source venv/bin/activate (Linux/Mac) venv\Scripts\activate (Windows)
Erstellen Sie eine main.py
Datei: Erstellen Sie eine main.py
Datei, um Ihren Flask-Anwendungscode zu schreiben:
touch app.py # Linux/Mac echo.>app.py # Windows
Schreiben Sie Ihren Flask-Anwendungscode : Öffnen Sie die Datei main.py
in Ihrem bevorzugten Code-Editor und fügen Sie den folgenden Code hinzu:
from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello, World!' if __name__ == '__main__': app.run()
Führen Sie die Flask-Anwendung aus : Speichern Sie die Datei main.py
und führen Sie den folgenden Befehl in Ihrem Terminal/Eingabeaufforderung aus:
python main.py
Öffnen Sie Ihren Browser : Öffnen Sie Ihren bevorzugten Webbrowser und navigieren Sie zu http://127.0.0.1:5000/ . Sie sollten „Hello, World!“ sehen. auf der Webseite angezeigt.
Und das ist es! Sie haben ein Flask-Projekt erfolgreich initialisiert und eine einfache Anwendung erstellt.
Installieren Sie python-dotenv und langchain : Um Umgebungsvariablen einfach mit einer .env
Datei zu verwalten, verwenden wir das Paket python-dotenv
. Gleichzeitig installieren wir auch langchain
. Installieren Sie beide Pakete mit pip
:
pip install python-dotenv langchain
Erstellen Sie eine .env
Datei: Erstellen Sie eine .env
Datei im Stammverzeichnis Ihres Projekts:
touch .env # Linux/Mac echo.>.env # Windows
Umgebungsvariablen hinzufügen : Öffnen Sie die .env
Datei in Ihrem bevorzugten Code-Editor und fügen Sie Ihre Umgebungsvariablen hinzu. Jede Variable sollte in einer neuen Zeile im Format SCHLÜSSEL=WERT stehen:
ANTHROPIC_API_KEY=sk-ant-xxxxxxxxxxxxxxxxx SERPAPI_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Bitte beachten Sie, dass Sie sowohl einen API-Schlüssel für das Claude-Modell von Anthropic als auch für den Websuchdienst von SerpAPI benötigen.
Umgebungsvariablen laden : Ändern Sie Ihre main.py
Datei, um die Umgebungsvariablen mit python-dotenv
aus der .env
Datei zu laden. Aktualisieren Sie Ihre main.py
wie folgt:
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()
Ignorieren Sie die .env
Datei in der Versionskontrolle: Es ist wichtig, keine vertraulichen Informationen wie geheime Schlüssel in der Versionskontrolle weiterzugeben. Wenn Sie Git verwenden, fügen Sie die folgende Zeile zu Ihrer .gitignore
Datei hinzu (erstellen Sie eine, falls Sie sie nicht haben):
.env
Jetzt ist Ihr Flask-Projekt für die Verwendung von Umgebungsvariablen aus der .env
Datei eingerichtet. Sie können nach Bedarf weitere Variablen hinzufügen und mit os.environ.get('KEY')
darauf zugreifen. Denken Sie daran, die .env
Datei privat zu halten und sie niemals der Versionskontrolle zu unterwerfen.
Dieses Tutorial richtet sich an fortgeschrittene Benutzer, die über grundlegende Kenntnisse von Node.js, npm, React und Typescript verfügen. Wir werden einen Stack verwenden, der diese Technologien sowie Tailwind CSS für das Styling enthält. Dieser Stack wurde aufgrund seiner Robustheit, Vielseitigkeit und der starken Community-Unterstützung für diese Technologien ausgewählt. Darüber hinaus werden wir das Claude-Modell und LangChain von Anthropic integrieren, zwei leistungsstarke KI-Technologien, die es unserer App ermöglichen, genaue, menschenähnliche Textantworten zu generieren.
Laden Sie das Node.js-Installationsprogramm für Ihr Betriebssystem von der offiziellen Website herunter.
Befolgen Sie die Installationsanweisungen, um Node.js und npm zu installieren. Für die meisten Benutzer wird die LTS-Version (Long Term Support) empfohlen.
Überprüfen Sie nach der Installation die Installation, indem Sie die Versionen von Node.js und npm auf Ihrem Terminal überprüfen:
Knoten -v npm -v
Create React App (CRA) ist ein Befehlszeilendienstprogramm, das uns beim Erstellen einer neuen React.js-Anwendung unterstützt. Wir installieren es global über npm:
npm install -g create-react-app
Wir verwenden CRA mit der Typescript-Vorlage, um ein neues Projekt mit dem Namen ai-assistant-claude
zu erstellen.
npx create-react-app ai-assistant-claude --template typescript
Dieser Befehl erstellt ein neues Verzeichnis namens ai-assistant-claude
in unserem aktuellen Verzeichnis, das eine neue React-Anwendung mit Typescript-Unterstützung enthält.
Die in diesem Tutorial befolgten Schritte basieren auf der offiziellen Tailwind CSS-Dokumentation . Weitere aktuelle Anweisungen finden Sie in diesen Dokumenten.
Wir beginnen mit der Installation von TailwindCSS und der Initialisierung der Bibliothek in unserem Projekt:
npm install -D tailwindcss npx tailwindcss init
Als Nächstes konfigurieren wir unsere Vorlagenpfade, indem wir sie zur Datei tailwind.config.js
hinzufügen. Das ++
kennzeichnet die Zeilen, die Sie hinzufügen:
/** @type {import('tailwindcss').Config} */ module.exports = { -- content: [], ++ content: [ ++ "./src/**/*.{js,jsx,ts,tsx}", ++ ], theme: { extend: {}, }, plugins: [], }
Zuletzt fügen wir die @tailwind
Anweisungen für jede Ebene von Tailwind zu unserer Datei ./src/index.css
hinzu:
@tailwind base; @tailwind components; @tailwind utilities;
Und voilà! Tailwind CSS ist jetzt in unser Projekt integriert.
Bevor wir mit dem Codierungsabschnitt fortfahren, schließen wir unsere Vorbereitungen ab, indem wir die erforderlichen Bibliotheken wie fontawesome
, react-markdown
, axios
und react-hook-form
installieren.
npm i --save @fortawesome/fontawesome-svg-core npm install --save @fortawesome/free-solid-svg-icons npm install --save @fortawesome/react-fontawesome
npm install --save react-markdown
Nach Abschluss dieser Schritte ist Ihr Projekt mit allen erforderlichen Tools und Bibliotheken ausgestattet. Als Nächstes beginnen wir mit der Entwicklung der KI-Assistenten-App, die die Claude API und LangChain von Anthropic nutzt, um genaue und menschenähnliche Textantworten zu generieren.
Wenn bei der Installation oder Einrichtung Probleme auftreten, finden Sie hier einige gängige Lösungen:
Bei allen anderen Problemen konsultieren Sie die Dokumentation der jeweiligen Bibliotheken oder veröffentlichen Sie Ihre Probleme auf StackOverflow oder relevanten GitHub-Repositories.
In diesem Abschnitt kehren wir zur Flask-App zurück, die wir zuvor initialisiert haben, und fügen neue Endpunkte hinzu, z. B. /ask
und /search
. Diese dienen als Endpunkte für unsere einfachen Chat- und erweiterten Chat-Funktionen (letztere basieren auf den Google-Suchergebnissen).
Beginnen wir mit dem Import unserer notwendigen Module:
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__)
Der obige Abschnitt importiert alle erforderlichen Pakete und initialisiert unsere Flask-Anwendung.
Wir beginnen mit der Erstellung eines Basisendpunkts ( /
), um zu testen, ob unser Server ordnungsgemäß läuft:
@app.route('/') def hello_world(): return 'Hello, World!'
Wenn wir die Root-URL besuchen, sollten wir die Antwort „Hello, World!“ erhalten, die anzeigt, dass unser Server wie erwartet läuft.
/ask
EndpunktsDieser Endpunkt verarbeitet Nachrichten für die grundlegende Chat-Funktion unserer Anwendung. Es liest JSON-Daten aus der Anfrage, verarbeitet die Nachrichten und generiert eine Antwort mithilfe des Claude-Modells von Anthropic und LangChain.
@app.route('/ask', methods=['POST']) def ask_assistant(): # The code for /ask endpoint goes here
Extrahieren von Nachrichten aus der Anfrage
Zunächst müssen wir prüfen, ob Daten bereitgestellt werden, und die Nachrichten daraus extrahieren.
data = request.get_json() if not data: return jsonify({"error": "No data provided"}), 400 messages = data.get("message")
Generieren der Antwort
Das folgende Codesegment generiert die Chat-Antwort mithilfe ChatAnthropic()
Modells von LangChain und der ChatPromptTemplate
, um unsere Konversation zu strukturieren. Der Konversationsverlauf wird mithilfe von ConversationBufferMemory
gespeichert.
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)
Nachdem wir die Antwort generiert haben, ersetzen wir Zeilenumbrüche im Ergebnis und geben es als JSON-Objekt zurück.
print(result) return jsonify({"status": "success", "message": result})
/search
Endpunkts Der /search
Endpunkt ähnelt /ask
verfügt jedoch über Suchfunktionen, um detailliertere Antworten bereitzustellen. Wir verwenden den SerpAPIWrapper
, um diese Funktion hinzuzufügen.
@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})
Schließlich fügen wir das Standard-Boilerplate hinzu, um unsere Flask-App auszuführen.
if __name__ == '__main__': app.run()
Sollte alles gut gehen, finden Sie hier unseren endgültigen Backend-Code.
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()
Jetzt testen wir unsere App. Führen Sie die Backend-App mit diesem Befehl aus. Stellen Sie außerdem sicher, dass unsere virtuelle Umgebung aktiviert ist.
flask run
Wir wissen, dass alles gut wird, wenn unser Terminal diese Ausgabe zurückgibt.
Testen wir ohne weiteres unsere beiden Endpunkte /ask
und /search
. Um die beiden zu unterscheiden, senden wir ihnen jeweils eine ähnliche Nutzlast. Öffnen Sie Ihre REST-API-Test- und Dokumentationssoftware oder verwenden Sie einfach cURL. Aber in diesem Tutorial verwende ich Insomnia .
Rufen wir zunächst den /ask
-Endpunkt mit der folgenden Nutzlast auf. Nehmen wir an, ich habe nach der Fortsetzung des Videospiels „ The Legend of Zelda: Breath of the Wild “ gefragt. Was es falsch beantworten wird. Dies ist zu erwarten, da das Modell Ende 2021 einen Trainingsstopp hat, in dem es noch keine Ankündigungen zur Fortsetzung gab.
Wie wäre es mit dem /search
Endpunkt? Wenn Sie unseren Code von früher bemerken, wird dieser Endpunkt mit einer ausgefeilteren Kette behandelt, die Agent verwendet.
Durch den Einsatz von Agent können wir der KI mehr Macht bei der Entscheidungsfindung verleihen, indem wir ihr mehr Werkzeuge als nur ihr eigenes Modell zur Verfügung stellen, das, wie wir bereits gezeigt haben, seine eigenen Mängel aufweist.
Um zu demonstrieren, wie der /search
Endpunkt funktioniert, rufen wir ihn mit derselben Nutzlast wie zuvor auf.
Dieses Mal hat es gut geklappt! Wie funktioniert es unter der Haube? Schauen wir uns die Ausgabe in unserem Terminal an.
Interessanterweise antwortete das KI-Modell dieses Mal nicht sofort, sondern schien darüber nachzudenken, was es tun sollte, um zu antworten. Es entschied sich zu einer Antwort, nachdem es anhand der Websuchergebnisse festgestellt hatte, dass „der Nachrichtensuche zufolge eine Fortsetzung mit dem Titel „The Legend of Zelda: Tears of the Kingdom“ angekündigt wurde“.
Sollten wir also nach dieser Logik nicht einfach den Endpunkt /search
verwenden? Wie ist es genauer und daher intelligenter? Nicht ganz. Normalerweise wird in einer Chatbot-basierten App erwartet, dass der Bot den Kontext früherer Konversationen behält, sodass er Antworten geben kann, als ob er sich an den gesamten Kontext der Konversation „merken“ würde. Mal sehen, ob der /search
Endpunkt genau das kann.
Testen wir, ob der /search
Endpunkt sich an das erinnern kann, was wir gerade gefragt haben.
Dies geschah, weil die LangChain-Bibliothek zwar über eine Speicherkettenfunktion verfügt, mit der frühere Konversationen gespeichert werden können, der Webserver und der darauf aufbauende REST-API-Dienst jedoch von Natur aus zustandslos sind. Das bedeutet, dass der Webserver jede Anfrage als neue Anfrage behandelt.
Aber haben wir die vorherigen Gespräche nicht als Nutzlast einbezogen? Das ist eine gute Frage. Es stellt sich heraus, dass die in LangChain verwendete Agentenkette derzeit die Verarbeitung zusammengesetzter Eingabeaufforderungen, die sowohl aus Anfragen als auch aus Antworten sowohl des Benutzers als auch der KI bestehen, nicht unterstützt. Wir verwenden dies hauptsächlich, um Beispielgespräche für das Modell bereitzustellen und das Modell weiter auf unsere gewünschte Reaktion abzustimmen.
Der Agent hingegen arbeitet, indem er eine einzelne Anweisung erhält und seine Gedankenketten darauf aufbauend entwickelt. Aus diesem Grund antwortet der Agent, egal wie lange unsere Gespräche dauern, immer nur auf die letzte Anfrage.
Testen wir unseren /ask
Endpunkt, um den Unterschied festzustellen.
Diesmal antwortete es mit unseren vergangenen Gesprächen als zusätzlichen Kontext! Mittlerweile ist uns klar geworden, dass wir beide Endpunkte benötigen, um unsere AI Assistant-App zu erstellen. Aber wie integrieren wir den veralteten, aber stets im Gedächtnis haftenden Endpunkt /ask
mit dem vergesslichen, aber gründlichen und aktuellen Endpunkt /search
? Natürlich durch den Aufbau des Frontends!
Navigieren wir zurück zum Projektverzeichnis ai-assistant-claude
. In diesem Projekt beginnen wir mit der Änderung unserer React-Komponenten, beginnend mit der Einstiegspunktdatei App.tsx
.
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;
Im obigen Codeausschnitt importieren wir die ChatClient
Komponente, die in einem nachfolgenden Schritt erstellt wird. Anschließend fügen wir die <ChatClient/>
Komponente in ein <div>
-Element ein. Dadurch wird die Struktur für unsere KI-Assistenten-App mithilfe von React-Komponenten eingerichtet.
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> ); };
Diese Komponente dient als primäre Benutzeroberfläche für unseren KI-Assistenten. Wie gezeigt, enthält es eine ChatHistory
Komponente zum Anzeigen des Konversationsverlaufs und eine ChatInput
Komponente zum Eingeben von Text.
Die Komponente verarbeitet Eingaben der ChatInput
Komponente, sendet mithilfe dieser Eingaben Anfragen an das Backend und zeigt dann einen Ladestatus an. Wenn die Anfrage erfolgreich verarbeitet wurde, zeigt die Komponente auch die vom Backend erhaltene Antwort an.
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> ); };
Glücklicherweise bietet TailwindCSS integrierte Hilfsklassen für einfache Animationen, wie zum Beispiel animate-pulse
. Diese Klasse hilft auf elegante Weise anzuzeigen, dass eine Anfrage auf eine Antwort wartet. In dieser Komponente unterscheiden wir auch die Positionierung von Nachrichten vom „Benutzer“ und vom „Assistenten“.
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> ); };
Zuletzt haben wir unserer Texteingabe zwei Schaltflächen hinzugefügt. Mit der ersten Schaltfläche wird die Eingabe an den /ask
Endpunkt gesendet, der die Eingabe mithilfe des KI-Modells ohne zusätzliche Verbesserungen verarbeitet.
Dieser Endpunkt ist kontextsensitiv. Die zweite Schaltfläche mit dem treffenden Namen „Ask and Search“ sendet die Eingabe an den /search
Endpunkt. Zusätzlich zur Verarbeitung der Eingaben über das KI-Modell löst dieser Button bei Bedarf auch eine KI-gesteuerte Websuche aus.
<!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>
Als letzten Schliff aktualisieren wir den Titel unserer App auf der Seite index.html
, indem wir ihn von „ React App “ in „ Claude AI Assistant “ ändern.
{ "name": "ai-assistant-claude", "version": "0.1.0", "private": true, ++ "proxy": "http://localhost:5000", "dependencies": {
Zuletzt fügen wir der Datei package.json
eine Proxy-Konfiguration hinzu und legen sie auf http://localhost:5000
fest. Dies hilft uns, CORS-Einschränkungen zu umgehen, die sich aus der Verwendung verschiedener Ports ergeben.
Um mit dem Testen zu beginnen, stellen Sie zunächst sicher, dass die Backend-App (claude-langchain) bereits ausgeführt wird.
Als nächstes wechseln Sie das Verzeichnis zur Frontend-App (ai-assistant-claude) und starten die App mit dem folgenden Befehl:
npm start
Die Erstellung der App kann einen Moment dauern. Sobald es fertig ist, wird es automatisch in Ihrem Browser unter localhost:3000
geöffnet.
Testen wir sowohl die Kontexterkennung als auch die Suchfunktion! Erkundigen wir uns zunächst nach einem weiteren Videospiel, dessen Veröffentlichung im Jahr 2021 noch nicht angekündigt wurde – der Fortsetzung des neuesten Yakuza-Spiels von SEGA . Außerdem fragen wir, ob in diesem Spiel der beliebte Charakter Kazuma Kiryu aus früheren Spielen enthalten ist. Klicken Sie dazu auf die Schaltfläche „ Fragen “.
Nehmen Sie sich ein paar Sekunden Zeit zum Nachdenken ...
Überraschenderweise antwortete die KI falsch. Yakuza: Like a Dragon ist zwar das neueste Yakuza-Spiel, hat aber einen anderen Protagonisten, Ichiban Kasuga. Lassen Sie uns die Frage umformulieren und dieses Mal die Schaltfläche „ Fragen und Suchen “ verwenden.
Jetzt brauchte die KI länger, um zu entscheiden, ob sie das Web durchsuchen musste. Nachdem festgestellt wurde, dass eine Websuche erforderlich war und eine zufriedenstellende Antwort gefunden wurde, wurden die Informationen an den Client/Frontend zurückgegeben.
Diesmal gibt es den Titel „Like a Dragon Gaiden: The Man Who Erased His Name“, in dem tatsächlich Kazuma Kiryu die Hauptrolle spielt.
Faszinierend, nicht wahr? Der auf einem Large Language Model basierende Agent sagt voraus, welche Aktionen angesichts der verfügbaren Ressourcen (Suche) erforderlich sind, und liefert eine zufriedenstellende Antwort. LangChain erleichtert die Verbindung und „Verkettung“ des Modells, der Eingabeaufforderungen (Anweisungen zum Modell) und anderer Funktionen wie Agenten und Tools.
Wir hoffen, dass es Ihnen Spaß gemacht hat, diese App zu erstellen und mehr über diese Technologien zu erfahren. Wer tiefer eintauchen möchte, kann auf das fertige Projekt für Front-End und Backend zugreifen.
Und wenn Sie mit den neuesten Technologien unserer Partner bauen möchten, nehmen Sie an der nächsten KI-Herausforderung teil!
*please see lablab.ai for all terms and conditions