En este artículo nos gustaría mostrarle cómo combinar dos tecnologías ( LangChain y Anthropic ) y crear un asistente personal con motor de búsqueda.
Los participantes en nuestros hackatones crean cada vez más productos de IA. La próxima oportunidad para los desarrolladores es nuestro hackathon conjunto con Google Cloud Vertex AI , donde cada participante tiene la oportunidad de crear su propia aplicación de IA utilizando la última tecnología de Google Cloud . Aquí hablaremos de una aplicación de IA que se está volviendo cada vez más importante: los motores de búsqueda .
Un asistente personal con motor de búsqueda es un asistente digital que utiliza tecnología de motor de búsqueda para ayudar a los usuarios con tareas como encontrar información, hacer reservas, configurar recordatorios y enviar mensajes. Estos asistentes utilizan algoritmos de búsqueda para recopilar y analizar datos de varias fuentes, presentándolos a los usuarios de manera útil y concisa.
Ejemplos destacados de asistentes personales con motor de búsqueda son Google Assistant , Siri , Alexa y Cortana . Estos asistentes utilizan eficazmente sus capacidades de búsqueda para proporcionar información precisa y relevante, completar tareas y mejorar sus respuestas a medida que interactúan con el usuario.
Anthropic es una organización de investigación centrada en el desarrollo de sistemas avanzados de IA. Su última creación, Claude, es un asistente de inteligencia artificial de próxima generación diseñado para ser útil, honesto e inofensivo. Este modelo de vanguardia garantiza un alto grado de confiabilidad y previsibilidad en diversas tareas.
Las características clave de Claude incluyen:
Capacidades versátiles de procesamiento de texto y conversacional
Mantener la seguridad y privacidad del usuario como su máxima prioridad
Los principales casos de uso de Claude son:
resumen
Buscar
Escritura creativa y colaborativa.
Preguntas y respuestas
Asistencia de codificación
Estas características hacen de Claude una herramienta de IA ideal para una amplia gama de aplicaciones, lo que permite a los usuarios en diferentes dominios.
LangChain es una herramienta versátil para crear aplicaciones de modelo de lenguaje de extremo a extremo. Proporciona un marco sólido que simplifica el proceso de creación, gestión e implementación de modelos de aprendizaje de idiomas (LLM). Los LLM son modelos avanzados de IA diseñados para comprender, generar y manipular texto similar al humano en varios idiomas y tareas.
Gestión eficiente de avisos para LLM
La capacidad de crear cadenas de tareas para flujos de trabajo complejos
Agregar estado a la IA, lo que le permite recordar información de interacciones anteriores
Estas capacidades hacen de LangChain una plataforma poderosa y fácil de usar para aprovechar el potencial de los modelos de lenguaje en diversas aplicaciones.
Instale Flask : para comenzar, asegúrese de tener Flask instalado en su entorno. Puedes hacer esto usando pip
:
pip install Flask
Crear un nuevo directorio : cree un nuevo directorio para su proyecto y navegue hasta él:
mkdir claude-langchain cd claude-langchain
Configurar un entorno virtual (opcional) : es una buena práctica usar un entorno virtual cuando se trabaja con proyectos de Python. Puede crear uno usando venv
o cualquier otra herramienta de su elección:
python -m venv venv source venv/bin/activate (Linux/Mac) venv\Scripts\activate (Windows)
Cree un archivo main.py
: cree un archivo main.py
para escribir el código de su aplicación Flask:
touch app.py # Linux/Mac echo.>app.py # Windows
Escribe el código de tu aplicación Flask : abre el archivo main.py
en tu editor de código favorito y agrega el siguiente código:
from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello, World!' if __name__ == '__main__': app.run()
Ejecute la aplicación Flask : guarde el archivo main.py
y ejecute el siguiente comando en su terminal/símbolo del sistema:
python main.py
Abra su navegador : Abra su navegador web preferido y vaya a http://127.0.0.1:5000/ . Deberías ver "¡Hola, mundo!" que se muestra en la página web.
¡Y eso es! Ha inicializado con éxito un proyecto de Flask y ha creado una aplicación sencilla.
Instale python-dotenv y langchain : para administrar fácilmente las variables de entorno con un archivo .env
, usaremos el paquete python-dotenv
. Al mismo tiempo, también instalemos langchain
. Instale ambos paquetes usando pip
:
pip install python-dotenv langchain
Cree un archivo .env
: cree un archivo .env
en el directorio raíz de su proyecto:
touch .env # Linux/Mac echo.>.env # Windows
Agregue variables de entorno : abra el archivo .env
en su editor de código favorito y agregue sus variables de entorno. Cada variable debe estar en una nueva línea, en el formato CLAVE=VALOR:
ANTHROPIC_API_KEY=sk-ant-xxxxxxxxxxxxxxxxx SERPAPI_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Tenga en cuenta que debe tener la clave API para el modelo Claude de Anthropic y el servicio de búsqueda web de SerpAPI .
Cargue variables de entorno : modifique su archivo main.py
para cargar las variables de entorno desde el archivo .env
usando python-dotenv
. Actualice su main.py
de la siguiente manera:
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()
Ignore el archivo .env
en el control de versiones: es importante no compartir información confidencial como claves secretas en el control de versiones. Si está utilizando Git, agregue la siguiente línea a su archivo .gitignore
(cree uno si no lo tiene):
.env
Ahora, su proyecto Flask está configurado para usar variables de entorno del archivo .env
. Puede agregar más variables según sea necesario y acceder a ellas usando os.environ.get('KEY')
. Recuerde mantener el archivo .env
privado y nunca enviarlo al control de versiones.
Este tutorial está diseñado para usuarios intermedios que tienen conocimientos básicos de Node.js, npm, React y Typescript. Usaremos una pila que incluye estas tecnologías, junto con Tailwind CSS para diseñar. Esta pila fue elegida por su robustez, versatilidad y el fuerte apoyo de la comunidad para estas tecnologías. Además, integraremos el modelo Claude de Anthropic y LangChain, dos potentes tecnologías de IA que permitirán que nuestra aplicación genere respuestas de texto precisas y similares a las humanas.
Descargue el instalador de Node.js para su sistema operativo desde el sitio oficial .
Siga las indicaciones de instalación para instalar Node.js y npm. La versión LTS (soporte a largo plazo) se recomienda para la mayoría de los usuarios.
Una vez instalado, verifica la instalación revisando las versiones de Node.js y npm desde tu terminal:
nodo -v npm -v
Create React App (CRA) es una utilidad de línea de comandos que nos ayuda a crear una nueva aplicación React.js. Lo instalaremos globalmente a través de npm:
npm install -g create-react-app
Usaremos CRA con la plantilla Typescript para crear un nuevo proyecto llamado ai-assistant-claude
.
npx create-react-app ai-assistant-claude --template typescript
Este comando crea un nuevo directorio llamado ai-assistant-claude
en nuestro directorio actual, que alberga una nueva aplicación React compatible con Typescript.
Los pasos seguidos en este tutorial están basados en la documentación oficial de Tailwind CSS . Consulte estos documentos para obtener instrucciones más actualizadas.
Comenzaremos instalando TailwindCSS e inicializando la biblioteca en nuestro proyecto:
npm install -D tailwindcss npx tailwindcss init
A continuación, configuramos las rutas de nuestras plantillas agregándolas al archivo tailwind.config.js
. El ++
significa las líneas que agregará:
/** @type {import('tailwindcss').Config} */ module.exports = { -- content: [], ++ content: [ ++ "./src/**/*.{js,jsx,ts,tsx}", ++ ], theme: { extend: {}, }, plugins: [], }
Por último, agregaremos las directivas @tailwind
para cada una de las capas de Tailwind a nuestro archivo ./src/index.css
:
@tailwind base; @tailwind components; @tailwind utilities;
¡Y voilá! Tailwind CSS ahora está integrado en nuestro proyecto.
Antes de continuar con la sección de codificación, finalicemos nuestros preparativos instalando las bibliotecas necesarias, como fontawesome
, react-markdown
, axios
y react-hook-form
.
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
Con estos pasos completados, su proyecto está configurado con todas las herramientas y bibliotecas necesarias. A continuación, comenzaremos a crear la aplicación de asistente de IA que utiliza la API Claude de Anthropic y LangChain para generar respuestas de texto precisas y similares a las humanas.
Si tiene algún problema durante la instalación o configuración, aquí hay algunas soluciones comunes:
Para cualquier otro problema, consulte la documentación de las bibliotecas respectivas o publique sus problemas en StackOverflow o en los repositorios relevantes de GitHub.
En esta sección, volveremos a la aplicación Flask que inicializamos anteriormente y agregaremos nuevos puntos finales, como /ask
y /search
. Estos servirán como puntos finales para nuestras funciones de chat simple y chat avanzado (esta última funciona con los resultados de búsqueda de Google).
Comencemos con la importación de nuestros módulos necesarios:
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__)
La sección anterior importa todos los paquetes necesarios e inicializa nuestra aplicación Flask.
Comenzaremos creando un punto final básico ( /
) para probar si nuestro servidor está funcionando correctamente:
@app.route('/') def hello_world(): return 'Hello, World!'
Al visitar la URL raíz, deberíamos recibir la respuesta "¡Hola, mundo!", lo que indica que nuestro servidor está funcionando como se esperaba.
/ask
Este punto final procesa mensajes para la función básica de chat de nuestra aplicación. Lee datos JSON de la solicitud, procesa los mensajes y genera una respuesta utilizando el modelo Claude de Anthropic y LangChain.
@app.route('/ask', methods=['POST']) def ask_assistant(): # The code for /ask endpoint goes here
Extracción de mensajes de la solicitud
Primero, debemos verificar si se proporciona algún dato y extraer los mensajes de él.
data = request.get_json() if not data: return jsonify({"error": "No data provided"}), 400 messages = data.get("message")
Generación de la respuesta
El siguiente segmento de código genera la respuesta de chat utilizando el modelo ChatAnthropic()
de LangChain y ChatPromptTemplate
para estructurar nuestra conversación. El historial de conversaciones se almacena mediante 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)
Después de generar la respuesta, reemplazamos líneas nuevas en el resultado y lo devolvemos como un objeto JSON.
print(result) return jsonify({"status": "success", "message": result})
/search
El punto final /search
es similar a /ask
pero incluye la funcionalidad de búsqueda para proporcionar respuestas más detalladas. Usamos SerpAPIWrapper
para agregar esta característica.
@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})
Finalmente, agregamos el modelo estándar para ejecutar nuestra aplicación Flask.
if __name__ == '__main__': app.run()
Si todo va bien, aquí está nuestro código backend final.
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()
Ahora, probemos nuestra aplicación. Ejecute la aplicación backend con este comando, también asegúrese de que nuestro entorno virtual esté activado.
flask run
Sabremos que todo va a estar bien si nuestro terminal devuelve esta salida.
Sin más preámbulos, probemos nuestros dos extremos, /ask
y /search
. Para distinguir entre los dos, enviemos a cada uno de ellos una carga útil similar. Abra su software de prueba y documentación de API REST, o simplemente use cURL. Pero en este tutorial, usaré Insomnia .
Primero llamemos al punto final /ask
con la siguiente carga útil. Digamos que he preguntado por la secuela del videojuego " The Legend of Zelda: Breath of the Wild ". Que responderá incorrectamente. Esto es de esperar ya que el modelo tiene un límite de entrenamiento a partir de finales de 2021, en el que aún no hubo anuncios sobre la secuela.
¿Qué hay del punto final /search
? Si observa nuestro código anterior, este punto final se maneja con una cadena más sofisticada, que utiliza Agent.
Al usar Agent, podemos darle a la IA más poder en la toma de decisiones, brindándole más herramientas que solo su propio modelo, que como ya demostramos, tiene sus propias fallas.
Para demostrar cómo funciona el punto final /search
, llamémoslo con la misma carga útil que antes.
¡Esta vez, funcionó muy bien! ¿Cómo funciona bajo el capó? veamos la salida en nuestra terminal.
Curiosamente, esta vez el modelo de IA no respondió de inmediato, sino que pareció "reflexionar" sobre qué hacer para responder. Decidió responder después de hacer la observación del resultado de la búsqueda web de que "según la búsqueda de noticias, parece que se anunció una secuela llamada The Legend of Zelda: Tears of the Kingdom".
Entonces, según esa lógica, ¿no deberíamos simplemente usar el punto final /search
entonces? como es más precisa, y por lo tanto, más inteligente? No exactamente. Normalmente, en una aplicación basada en chatbot, se espera que el bot conserve el contexto de conversaciones anteriores, de modo que pueda proporcionar respuestas como si "recordara" todo el contexto de la conversación. Veamos si /search
endpoint puede hacer precisamente eso.
Probemos si el punto final /search
puede recordar lo que le acabamos de preguntar.
Esto sucedió porque, si bien la biblioteca LangChain tiene una función de cadena de memoria que se puede usar para retener conversaciones pasadas, el servidor web y el servicio API REST integrado en él no tienen estado. Lo que significa que el servidor web tratará cada solicitud como una nueva solicitud.
Pero, ¿no hemos incluido las conversaciones anteriores como payload? Buena pregunta. Resulta que la cadena de agentes utilizada en LangChain actualmente no admite el manejo de avisos compuestos, que consisten en solicitudes y respuestas tanto del usuario como de la IA. Usamos esto principalmente para proporcionar conversaciones de ejemplo para el modelo, ajustando aún más el modelo a nuestra respuesta deseada.
El Agente, en cambio, trabaja recibiendo una sola instrucción y desarrolla sus cadenas de pensamiento en torno a ella. Por eso, por muy largas que sean nuestras conversaciones, el agente solo responderá a la última petición.
Probemos nuestro punto final /ask
para notar la diferencia.
¡Esta vez, respondió usando nuestras conversaciones pasadas como contexto adicional! A estas alturas, nos dimos cuenta de que necesitamos ambos puntos finales para construir nuestra aplicación AI Assistant. Pero, ¿cómo incorporamos el punto final obsoleto pero siempre recordado /ask
con el punto final olvidadizo pero minucioso y actualizado /search
? ¡Construyendo el front-end, por supuesto!
Naveguemos de regreso al directorio del proyecto ai-assistant-claude
. En este proyecto, comenzaremos a modificar nuestros componentes de React, comenzando con el archivo de punto de entrada, 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;
En el fragmento de código anterior, importamos el componente ChatClient
, que se creará en un paso posterior. Luego incluimos el componente <ChatClient/>
dentro de un elemento <div>
. Esto configura la estructura de nuestra aplicación de asistente de IA utilizando componentes React.
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> ); };
Este componente sirve como interfaz de usuario principal para nuestro asistente de IA. Como se muestra, incorpora un componente ChatHistory
para mostrar el historial de conversaciones y un componente ChatInput
para ingresar texto.
El componente procesa la entrada del componente ChatInput
, envía solicitudes al backend usando esta entrada y luego muestra un estado de carga. Si la solicitud se procesa con éxito, el componente también mostrará la respuesta recibida del backend.
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> ); };
Afortunadamente, TailwindCSS ofrece clases de utilidad integradas para animaciones simples, como animate-pulse
. Esta clase ayuda elegantemente a indicar que una solicitud está esperando una respuesta. En este componente también diferenciamos el posicionamiento de los mensajes del "usuario" y el "asistente".
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> ); };
Por último, agregamos dos botones a nuestra entrada de texto. El primer botón se usa para enviar la entrada al punto final /ask
, que procesa la entrada usando el modelo de IA sin mejoras adicionales.
Este punto final es consciente del contexto. El segundo botón, llamado acertadamente "Preguntar y buscar", envía la entrada al punto final /search
. Además de procesar la entrada a través del modelo de IA, este botón también activa una búsqueda web impulsada por IA si la situación lo requiere.
<!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>
Como toque final, actualizamos el título de nuestra aplicación en la página index.html
cambiándolo de " React App " a " Claude AI Assistant ".
{ "name": "ai-assistant-claude", "version": "0.1.0", "private": true, ++ "proxy": "http://localhost:5000", "dependencies": {
Por último, agregamos una configuración de proxy al archivo package.json
y lo configuramos en http://localhost:5000
. Esto nos ayuda a eludir las limitaciones de CORS que surgen del uso de diferentes puertos.
Para comenzar la prueba, primero asegúrese de que la aplicación de back-end (claude-langchain) ya se esté ejecutando.
A continuación, cambie el directorio a la aplicación frontal (ai-assistant-claude) e inicie la aplicación con el siguiente comando:
npm start
La aplicación puede tardar un momento en compilarse. Una vez que esté listo, se abrirá automáticamente en su navegador en localhost:3000
.
¡Probemos tanto el conocimiento del contexto como la función de búsqueda! Primero, indaguemos sobre otro videojuego cuyo lanzamiento aún no se ha anunciado en 2021: la secuela del último juego Yakuza de SEGA . Además, preguntaremos si este juego presenta al querido personaje Kazuma Kiryu de juegos anteriores. Para hacer esto, haga clic en el botón " Preguntar ".
Dale unos segundos para pensar...
Sorprendentemente, la IA respondió incorrectamente. Yakuza: Like a Dragon es, de hecho, el último juego de Yakuza, pero está protagonizado por un protagonista diferente, Ichiban Kasuga. Reformulemos la pregunta y usemos el botón " Preguntar y buscar " esta vez.
Ahora, la IA tardó más en decidir si necesitaba buscar en la web. Después de determinar que era necesaria una búsqueda en la web y encontrar una respuesta satisfactoria, devolvió la información al cliente/front-end.
Esta vez, regresa el título "Like a Dragon Gaiden: The Man Who Erased His Name", que de hecho presenta a Kazuma Kiryu como protagonista.
Fascinante, ¿no? El agente, impulsado por un modelo de lenguaje grande, predice qué acciones son necesarias dados los recursos disponibles (búsqueda) y proporciona una respuesta satisfactoria. LangChain facilita la conexión y "encadenamiento" del modelo, indicaciones (instrucciones para el modelo) y otras funciones, como agentes y herramientas.
Esperamos que haya disfrutado creando esta aplicación y aprendiendo sobre estas tecnologías. Para aquellos interesados en profundizar más, pueden acceder al proyecto completo para el front-end y el backend .
Y si desea construir con las últimas tecnologías desarrolladas por nuestros socios, ¡únase a nosotros en el próximo desafío de IA!
*please see lablab.ai for all terms and conditions