paint-brush
So erstellen Sie eine KI-gestützte persönliche Assistenten-Appvon@lablab
6,056 Lesungen
6,056 Lesungen

So erstellen Sie eine KI-gestützte persönliche Assistenten-App

von lablab.ai hackathons28m2023/07/04
Read on Terminal Reader
Read this story w/o Javascript

Zu lang; Lesen

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. LangChain ist ein Tool zum Erstellen von End-to-End-Sprachmodellanwendungen. Es bietet ein robustes Framework, das den Prozess der Erstellung, Verwaltung und Bereitstellung von Sprachlernmodellen vereinfacht.
featured image - So erstellen Sie eine KI-gestützte persönliche Assistenten-App
lablab.ai hackathons HackerNoon profile picture
0-item
1-item

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 .



Was ist es?

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.


Wir stellen vor: Claude von Anthropic

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.

Einführung in LangChain

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.


Zu den Hauptmerkmalen von LangChain gehören:


  • 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.

Voraussetzungen

  • Grundkenntnisse in Python
  • Grundkenntnisse in Typescipt und/oder React
  • Zugriff auf die Claude-API von Anthropic
  • Zugriff auf die Websuch-API von SerpApi

Gliederung

  1. Initialisieren des Projekts
  2. Erstellen des Front-Ends für eine KI-Assistenten-App mit Claude und LangCHain
  3. Schreiben der Projektdateien
  4. Testen der AI Assistant App

Diskussion

Initialisieren des Projekts

app.py (Flask-App-Einstiegspunkt) 🐍

  1. Flask installieren : Stellen Sie zunächst sicher, dass Flask in Ihrer Umgebung installiert ist. Sie können dies mit pip tun:

     pip install Flask


  2. Erstellen Sie ein neues Verzeichnis : Erstellen Sie ein neues Verzeichnis für Ihr Projekt und navigieren Sie dorthin:

     mkdir claude-langchain cd claude-langchain


  3. 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)


  4. 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


  5. 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()


  6. 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


  7. Ö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.

.env 🌏

  1. 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


  2. Erstellen Sie eine .env Datei: Erstellen Sie eine .env Datei im Stammverzeichnis Ihres Projekts:

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


  3. 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.


  4. 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()


  5. 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.

Erstellen des Front-Ends für eine KI-Assistenten-App mit Claude und LangChain

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.

Node.js und NPM installieren

  1. Laden Sie das Node.js-Installationsprogramm für Ihr Betriebssystem von der offiziellen Website herunter.

  2. Befolgen Sie die Installationsanweisungen, um Node.js und npm zu installieren. Für die meisten Benutzer wird die LTS-Version (Long Term Support) empfohlen.

  3. Ü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

Einrichten der Projektumgebung

Installieren der Create React App

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
Erstellen eines neuen React-Projekts mit Typescript

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.

TailwindCSS integrieren

TailwindCSS installieren

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
Konfigurieren von Vorlagenpfaden

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: [], }
Hinzufügen von Rückenwind-Anweisungen

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.

Erforderliche Bibliotheken installieren

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.

Installieren von FontAwesome für Icons
 npm i --save @fortawesome/fontawesome-svg-core npm install --save @fortawesome/free-solid-svg-icons npm install --save @fortawesome/react-fontawesome
Installieren von React Markdown zum Rendern von Markdown-Inhalten
 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.

Fehlerbehebung

Wenn bei der Installation oder Einrichtung Probleme auftreten, finden Sie hier einige gängige Lösungen:

  • Versuchen Sie bei Problemen im Zusammenhang mit Node.js oder npm, diese neu zu installieren oder sehen Sie sich die offiziellen Node.js- und npm -Dokumente an.
  • Wenn Sie Probleme mit Create React App haben, konsultieren Sie die CRA-Dokumentation , um Hilfe zu erhalten.
  • Informationen zu Tailwind-CSS-Problemen finden Sie in der Tailwind-CSS-Dokumentation .


Bei allen anderen Problemen konsultieren Sie die Dokumentation der jeweiligen Bibliotheken oder veröffentlichen Sie Ihre Probleme auf StackOverflow oder relevanten GitHub-Repositories.

Schreiben der Projektdateien

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.

Entwicklung des Backends

Erstellen der grundlegenden Endpunkte

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.

Erstellen des /ask Endpunkts

Dieser 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)

Senden der Antwort

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})
Erstellen des /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})


Ausführen der Flask-App

Schließlich fügen wir das Standard-Boilerplate hinzu, um unsere Flask-App auszuführen.

 if __name__ == '__main__': app.run()
Testen des Backends

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!

Entwicklung des Frontends


App.tsx

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.

ChatClient.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> ); };

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.

ChatHistory.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> ); };

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“.


ChatInput.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> ); };

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.


index.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>

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.


package.json

 { "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.

Testen der AI Assistant App

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.

Abschluss

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