paint-brush
AI 검색 기반 개인 비서 앱을 구축하는 방법~에 의해@lablab
6,040 판독값
6,040 판독값

AI 검색 기반 개인 비서 앱을 구축하는 방법

~에 의해 lablab.ai hackathons28m2023/07/04
Read on Terminal Reader
Read this story w/o Javascript

너무 오래; 읽다

Anthropic은 첨단 AI 시스템 개발에 주력하는 연구 기관입니다. 그들의 최신 창작물인 Claude는 유용하고 정직하며 무해하도록 설계된 차세대 AI 비서입니다. LangChain은 엔드투엔드 언어 모델 애플리케이션을 구축하기 위한 도구입니다. 이는 언어 학습 모델의 생성, 관리 및 배포 프로세스를 단순화하는 강력한 프레임워크를 제공합니다.
featured image - AI 검색 기반 개인 비서 앱을 구축하는 방법
lablab.ai hackathons HackerNoon profile picture
0-item
1-item

이 기사에서는 두 가지 기술( LangChainAnthropic )을 결합하여 검색 기반 개인 비서를 만드는 방법을 보여 드리고자 합니다.


해커톤 참가자들은 점점 더 많은 AI 제품을 만들고 있습니다. 개발자를 위한 다음 기회는 Google Cloud Vertex AI 와의 공동 해커톤입니다. 여기서 각 참가자는 Google Cloud 의 최신 기술을 사용하여 자신만의 AI 애플리케이션을 만들 수 있습니다. 여기서는 점점 더 중요해지고 있는 AI 응용 프로그램 중 하나인 검색 엔진 에 대해 이야기하겠습니다.



그것은 무엇입니까?

검색 기반 개인 비서는 검색 엔진 기술을 사용하여 정보 찾기, 예약, 알림 설정, 메시지 보내기 등의 작업을 사용자에게 돕는 디지털 비서입니다. 이러한 도우미는 검색 알고리즘을 사용하여 다양한 소스에서 데이터를 수집 및 분석하여 유용하고 간결한 방식으로 사용자에게 제공합니다.


검색 기반 개인 비서의 대표적인 예로는 Google Assistant , Siri , AlexaCortana 가 있습니다. 이러한 보조자는 검색 기능을 효과적으로 사용하여 정확하고 관련성 있는 정보를 제공하고, 작업을 완료하며, 사용자와 상호 작용할 때 응답을 개선합니다.


Anthropic의 클로드를 소개합니다

Anthropic은 첨단 AI 시스템 개발에 주력하는 연구 기관입니다. 그들의 최신 창작물인 Claude는 유용하고 정직하며 무해하도록 설계된 차세대 AI 비서입니다. 이 최첨단 모델은 다양한 작업에서 높은 수준의 신뢰성과 예측 가능성을 보장합니다.


Claude의 주요 기능은 다음과 같습니다.

  • 다양한 대화 및 텍스트 처리 기능

  • 사용자의 안전과 개인정보 보호를 최우선으로 생각합니다


Claude의 주요 사용 사례는 다음과 같습니다.

  • 요약

  • 찾다

  • 창의적이고 협력적인 글쓰기

  • Q&A

  • 코딩 지원


이러한 기능을 통해 Claude는 다양한 애플리케이션을 위한 이상적인 AI 도구가 되어 다양한 도메인의 사용자에게 힘을 실어줍니다.

랭체인 소개

LangChain은 엔드투엔드 언어 모델 애플리케이션을 구축하기 위한 다목적 도구입니다. 이는 LLM(언어 학습 모델) 생성, 관리 및 배포 프로세스를 단순화하는 강력한 프레임워크를 제공합니다. LLM은 다양한 언어와 작업에서 인간과 유사한 텍스트를 이해, 생성 및 조작하도록 설계된 고급 AI 모델입니다.


LangChain의 주요 기능은 다음과 같습니다.


  • LLM 프롬프트의 효율적인 관리

  • 복잡한 워크플로를 위한 작업 체인을 생성하는 기능

  • AI에 상태를 추가하여 이전 상호 작용의 정보를 기억할 수 있도록 합니다.


이러한 기능을 통해 LangChain은 다양한 애플리케이션에서 언어 모델의 잠재력을 활용할 수 있는 강력하고 사용자 친화적인 플랫폼이 됩니다.

전제 조건

  • Python에 대한 기본 지식
  • Typescipt 및/또는 React에 대한 기본 지식
  • Anthropic의 Claude API에 액세스
  • SerpApi의 웹 검색 API에 액세스

개요

  1. 프로젝트 초기화
  2. Claude 및 LangCHain과 함께 AI 보조 앱용 프런트 엔드 구축
  3. 프로젝트 파일 작성
  4. AI 도우미 앱 테스트

논의

프로젝트 초기화

app.py(Flask 앱 진입점) 🐍

  1. Flask 설치 : 시작하려면 환경에 Flask가 설치되어 있는지 확인하세요. pip 사용하여 이 작업을 수행할 수 있습니다.

     pip install Flask


  2. 새 디렉터리 만들기 : 프로젝트에 대한 새 디렉터리를 만들고 해당 디렉터리로 이동합니다.

     mkdir claude-langchain cd claude-langchain


  3. 가상 환경 설정(선택 사항) : Python 프로젝트 작업 시 가상 환경을 사용하는 것이 좋습니다. venv 또는 원하는 다른 도구를 사용하여 하나를 만들 수 있습니다.

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


  4. main.py 파일 만들기 : Flask 애플리케이션 코드를 작성하기 위한 main.py 파일을 만듭니다.

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


  5. Flask 애플리케이션 코드 작성 : 원하는 코드 편집기에서 main.py 파일을 열고 다음 코드를 추가합니다.

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


  6. Flask 애플리케이션 실행 : main.py 파일을 저장하고 터미널/명령 프롬프트에서 다음 명령을 실행합니다.

     python main.py


  7. 브라우저 열기 : 선호하는 웹 브라우저를 열고 http://127.0.0.1:5000/ 으로 이동합니다. "Hello, World!"가 표시되어야 합니다. 웹페이지에 표시됩니다.


그리고 그게 다야! Flask 프로젝트를 성공적으로 초기화하고 간단한 애플리케이션을 만들었습니다.

.env 🌏

  1. python-dotenv 및 langchain 설치 : .env 파일로 환경 변수를 쉽게 관리하기 위해 python-dotenv 패키지를 사용하겠습니다. 동시에 langchain 도 설치해 봅시다. pip 사용하여 두 패키지를 모두 설치합니다.

     pip install python-dotenv langchain


  2. .env 파일 만들기 : 프로젝트의 루트 디렉터리에 .env 파일을 만듭니다.

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


  3. 환경 변수 추가 : 즐겨 사용하는 코드 편집기에서 .env 파일을 열고 환경 변수를 추가합니다. 각 변수는 KEY=VALUE 형식으로 새 줄에 있어야 합니다.

     ANTHROPIC_API_KEY=sk-ant-xxxxxxxxxxxxxxxxx SERPAPI_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

    Anthropic의 Claude 모델과 SerpAPI 의 웹 검색 서비스를 위해서는 API Key가 모두 필요하다는 점 참고하시기 바랍니다.


  4. 환경 변수 로드 : python-dotenv 사용하여 .env 파일에서 환경 변수를 로드하도록 main.py 파일을 수정합니다. 다음과 같이 main.py 업데이트하세요.

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


  5. 버전 제어에서 .env 파일 무시 : 버전 제어에서 비밀 키와 같은 민감한 정보를 공유하지 않는 것이 중요합니다. Git을 사용하는 경우 .gitignore 파일에 다음 줄을 추가합니다(없는 경우 하나 생성).

     .env


이제 Flask 프로젝트가 .env 파일의 환경 변수를 사용하도록 설정되었습니다. 필요에 따라 더 많은 변수를 추가하고 os.environ.get('KEY') 사용하여 해당 변수에 액세스할 수 있습니다. .env 파일을 비공개로 유지하고 버전 제어에 커밋하지 마세요.

Claude 및 LangChain과 함께 AI 보조 앱용 프런트 엔드 구축

이 튜토리얼은 Node.js, npm, React 및 Typescript에 대한 기본적인 이해가 있는 중급 사용자를 위해 설계되었습니다. 우리는 스타일링을 위해 Tailwind CSS와 함께 이러한 기술을 포함하는 스택을 사용할 것입니다. 이 스택은 견고성, 다양성 및 이러한 기술에 대한 강력한 커뮤니티 지원을 위해 선택되었습니다. 또한, 우리는 앱이 정확하고 인간과 같은 텍스트 응답을 생성할 수 있게 해주는 두 가지 강력한 AI 기술인 Anthropic의 Claude 모델과 LangChain을 통합할 것입니다.

Node.js 및 NPM 설치

  1. 공식 사이트 에서 해당 OS에 맞는 Node.js 설치 프로그램을 다운로드하세요.

  2. 설치 프롬프트에 따라 Node.js 및 npm을 설치합니다. 대부분의 사용자에게는 LTS(장기 지원) 버전이 권장됩니다.

  3. 설치가 완료되면 터미널에서 Node.js 및 npm 버전을 확인하여 설치를 확인하세요.

    노드 -v npm -v

프로젝트 환경 설정

Create React 앱 설치

CRA(Create React App)는 새로운 React.js 애플리케이션을 만드는 데 도움이 되는 명령줄 유틸리티입니다. npm을 통해 전역적으로 설치하겠습니다.

 npm install -g create-react-app
Typescript를 사용하여 새 React 프로젝트 만들기

Typescript 템플릿과 함께 CRA를 사용하여 ai-assistant-claude 라는 새 프로젝트를 만듭니다.

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

이 명령은 현재 디렉토리에 ai-assistant-claude 라는 새 디렉토리를 생성하고 Typescript를 지원하는 새로운 React 애플리케이션을 수용합니다.

TailwindCSS 통합

TailwindCSS 설치

이 튜토리얼에서 따르는 단계는 공식 Tailwind CSS 문서를 기반으로 합니다. 최신 지침은 이 문서를 참조하세요.

TailwindCSS를 설치하고 프로젝트에서 라이브러리를 초기화하는 것부터 시작하겠습니다.

 npm install -D tailwindcss npx tailwindcss init
템플릿 경로 구성

다음으로 tailwind.config.js 파일에 템플릿 경로를 추가하여 구성합니다. ++ 추가할 줄을 나타냅니다.

 /** @type {import('tailwindcss').Config} */ module.exports = { -- content: [], ++ content: [ ++ "./src/**/*.{js,jsx,ts,tsx}", ++ ], theme: { extend: {}, }, plugins: [], }
Tailwind 지시문 추가

마지막으로 Tailwind의 각 레이어에 대한 @tailwind 지시문을 ./src/index.css 파일에 추가합니다.

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

그리고 짜잔! Tailwind CSS가 이제 프로젝트에 통합되었습니다.

필수 라이브러리 설치

코딩 섹션을 진행하기 전에, fontawesome , react-markdown , axios , react-hook-form 등 필요한 라이브러리를 설치하여 준비를 마무리하겠습니다.

아이콘용 FontAwesome 설치
 npm i --save @fortawesome/fontawesome-svg-core npm install --save @fortawesome/free-solid-svg-icons npm install --save @fortawesome/react-fontawesome
마크다운 콘텐츠 렌더링을 위해 React Markdown 설치
 npm install --save react-markdown

이 단계가 완료되면 프로젝트는 필요한 모든 도구와 라이브러리로 설정됩니다. 다음으로 정확하고 인간과 유사한 텍스트 응답을 생성하기 위해 Anthropic의 Claude API와 LangChain을 사용하는 AI 보조 앱 구축을 시작하겠습니다.

문제 해결

설치 또는 설정 중에 문제가 발생하는 경우 몇 가지 일반적인 해결 방법은 다음과 같습니다.

  • Node.js 또는 npm과 관련된 문제의 경우 다시 설치하거나 공식 Node.jsnpm 문서를 확인하세요.
  • Create React App에 문제가 있는 경우 CRA 설명서를 참조하여 도움을 받으세요.
  • Tailwind CSS 문제는 Tailwind CSS 문서를 참조하세요.


다른 문제가 있는 경우 해당 라이브러리의 문서를 참조하거나 StackOverflow 또는 관련 GitHub 리포지토리에 문제를 게시하세요.

프로젝트 파일 작성

이 섹션에서는 이전에 초기화한 Flask 앱으로 돌아가서 /ask/search 와 같은 새 엔드포인트를 추가합니다. 이는 간단한 채팅 및 고급 채팅 기능(후자는 Google 검색 결과를 기반으로 함)의 종점 역할을 합니다.

필요한 모듈을 가져오는 것부터 시작해 보겠습니다.


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

위 섹션에서는 필요한 모든 패키지를 가져오고 Flask 애플리케이션을 초기화합니다.

백엔드 개발

기본 엔드포인트 생성

서버가 올바르게 실행되고 있는지 테스트하기 위해 기본 끝점( / )을 만드는 것부터 시작하겠습니다.

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

루트 URL을 방문하면 서버가 예상대로 실행되고 있음을 나타내는 "Hello, World!" 응답을 받아야 합니다.

/ask 엔드포인트 생성

이 엔드포인트는 애플리케이션의 기본 채팅 기능에 대한 메시지를 처리합니다. 요청에서 JSON 데이터를 읽고, 메시지를 처리하고, Anthropic의 Claude 모델과 LangChain을 사용하여 응답을 생성합니다.

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


요청에서 메시지 추출

먼저, 제공되는 데이터가 있는지 확인하고 그로부터 메시지를 추출해야 합니다.

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


응답 생성

다음 코드 부분은 LangChain의 ChatAnthropic() 모델과 ChatPromptTemplate 사용하여 대화를 구성하는 채팅 응답을 생성합니다. 대화 기록은 ConversationBufferMemory 사용하여 저장됩니다.


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

응답 보내기

응답을 생성한 후 결과의 줄 바꿈을 바꾸고 이를 JSON 개체로 반환합니다.

 print(result) return jsonify({"status": "success", "message": result})
/search 엔드포인트 생성

/search 엔드포인트는 /ask 와 유사하지만 더 자세한 응답을 제공하는 검색 기능을 포함합니다. 이 기능을 추가하기 위해 SerpAPIWrapper 사용합니다.

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


플라스크 앱 실행

마지막으로 Flask 앱을 실행하기 위한 표준 상용구를 추가합니다.

 if __name__ == '__main__': app.run()
백엔드 테스트

모든 것이 순조롭게 진행된다면 여기에 최종 백엔드 코드가 있습니다.

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


이제 앱을 테스트해 보겠습니다. 이 명령으로 백엔드 앱을 실행하고, 가상 환경이 활성화되어 있는지도 확인하세요.

 flask run


터미널이 이 출력을 반환하면 모든 것이 잘 될 것임을 알 수 있습니다.


더 이상 고민하지 않고 두 엔드포인트인 /ask/search 테스트해 보겠습니다. 둘을 구별하기 위해 각각 유사한 페이로드를 보내도록 하겠습니다. REST API 테스트 및 문서화 소프트웨어를 열거나 cURL을 사용하세요. 하지만 이 튜토리얼에서는 Insomnia 를 사용하겠습니다.


먼저 다음 페이로드를 사용하여 /ask 엔드포인트를 호출해 보겠습니다. 내가 비디오 게임 " The Legend of Zelda: Breath of the Wild "의 속편에 대해 질문했다고 가정해 보겠습니다. 그것은 잘못 대답할 것입니다. 이는 아직 속편에 대한 발표가 없었던 2021년 말 현재 모델의 훈련이 중단되었기 때문에 예상되는 것입니다.


/search 엔드포인트는 어떻습니까? 이전 코드를 보면 이 엔드포인트는 Agent를 활용하는 보다 정교한 체인으로 처리됩니다.


에이전트를 사용하면 이미 설명했듯이 결함이 있는 자체 모델보다 더 많은 도구를 AI에 제공함으로써 AI의 의사 결정에 더 많은 권한을 부여할 수 있습니다.


/search 엔드포인트가 어떻게 작동하는지 보여주기 위해 이전과 동일한 페이로드를 사용하여 이를 호출해 보겠습니다.


이번에는 잘 작동했습니다! 내부적으로는 어떻게 작동하나요? 터미널의 출력을 살펴보겠습니다.

흥미롭게도 이번에는 AI 모델이 바로 답변을 하지는 않고, 답변을 어떻게 해야 할지 '고민'하는 것 같았다. 웹 검색 결과 "뉴스 검색 결과 '젤다의 전설:왕국의 눈물'이라는 속편이 발표된 것으로 보인다"는 관측이 나온 뒤 답변을 결정했다.


그렇다면 해당 논리에 따르면 /search 엔드포인트를 사용해야 하지 않나요? 더 정확하고 따라서 더 똑똑합니까? 좀 빠지는. 일반적으로 챗봇 기반 앱에서는 봇이 이전 대화의 맥락을 유지하여 대화의 전체 맥락을 '기억'하는 것처럼 응답할 수 있을 것으로 기대합니다. /search 엔드포인트가 그렇게 할 수 있는지 살펴보겠습니다.


/search 엔드포인트가 방금 요청한 내용을 기억할 수 있는지 테스트해 보겠습니다.


이는 LangChain 라이브러리에 과거 대화를 유지하는 데 사용할 수 있는 메모리 체인 기능이 있지만 웹 서버 및 이에 구축된 REST API 서비스는 본질적으로 상태 비저장이기 때문에 발생했습니다. 즉, 웹 서버는 각 요청을 새로운 요청으로 처리합니다.


그런데 이전 대화 내용을 페이로드에 포함시키지 않았나요? 그건 좋은 질문이야. 현재 LangChain에서 사용되는 에이전트 체인은 사용자와 AI의 요청과 응답으로 구성된 구성된 프롬프트 처리를 지원하지 않습니다. 우리는 주로 이를 모델에 대한 예제 대화를 제공하고 원하는 응답에 맞게 모델을 추가로 조정하는 데 사용합니다.


반면에 에이전트는 단일 지침을 수신하여 작업하고 이를 중심으로 사고의 사슬을 개발합니다. 그렇기 때문에 대화가 아무리 길어도 에이전트는 최신 요청에만 응답합니다.


차이점을 알아보기 위해 /ask 엔드포인트를 테스트해 보겠습니다.


이번에는 과거 대화를 추가된 맥락으로 활용하여 답변했습니다! 지금까지 우리는 AI Assistant 앱을 구축하려면 두 엔드포인트가 모두 필요하다는 것을 깨달았습니다. 하지만 구식이지만 항상 기억하는 /ask 엔드포인트와 잊어버리지만 철저하고 최신의 /search 엔드포인트를 어떻게 통합할 수 있을까요? 물론 프런트엔드를 구축함으로써 말이죠!

프론트엔드 개발


앱.tsx

ai-assistant-claude 프로젝트 디렉터리로 다시 이동해 보겠습니다. 이 프로젝트에서는 App.tsx 진입점 파일부터 시작하여 React 구성 요소를 수정하기 시작합니다.

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

위의 코드 조각에서는 후속 단계에서 생성될 ChatClient 구성 요소를 가져옵니다. 그런 다음 <div> 요소 내에 <ChatClient/> 구성 요소를 포함합니다. 이는 React 구성 요소를 사용하여 AI 보조 앱의 구조를 설정합니다.

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

이 구성 요소는 AI 도우미의 기본 사용자 인터페이스 역할을 합니다. 표시된 대로 대화 기록을 표시하는 ChatHistory 구성 요소와 텍스트 입력을 위한 ChatInput 구성 요소를 통합합니다.


구성 요소는 ChatInput 구성 요소의 입력을 처리하고 이 입력을 사용하여 백엔드로 요청을 보낸 다음 로드 상태를 표시합니다. 요청이 성공적으로 처리되면 구성 요소에는 백엔드에서 받은 응답도 표시됩니다.

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

다행히 TailwindCSS는 animate-pulse 와 같은 간단한 애니메이션을 위한 내장 유틸리티 클래스를 제공합니다. 이 클래스는 요청이 응답을 기다리고 있음을 나타내는 데 도움이 됩니다. 이 구성 요소에서는 "사용자"와 "보조자"의 메시지 위치도 구분합니다.


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

마지막으로 텍스트 입력에 두 개의 버튼을 추가했습니다. 첫 번째 버튼은 추가 개선 없이 AI 모델을 사용하여 입력을 처리하는 /ask 엔드포인트로 입력을 보내는 데 사용됩니다.


이 끝점은 컨텍스트를 인식합니다. "질문 및 검색"이라는 적절한 이름의 두 번째 버튼은 입력을 /search 엔드포인트로 보냅니다. AI 모델을 통해 입력을 처리하는 것 외에도 이 버튼은 상황에 따라 AI 기반 웹 검색을 트리거합니다.


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>

마무리 작업으로 index.html 페이지에서 앱 제목을 " React App "에서 " Claude AI Assistant "로 변경하여 업데이트합니다.


패키지.json

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

마지막으로 package.json 파일에 프록시 구성을 추가하고 이를 http://localhost:5000 으로 설정합니다. 이는 다른 포트 사용으로 인해 발생하는 CORS 제한을 우회하는 데 도움이 됩니다.

AI 도우미 앱 테스트

테스트를 시작하려면 먼저 백엔드 앱(claude-langchain)이 이미 실행 중인지 확인하세요.


그런 다음 디렉터리를 프런트 엔드 앱(ai-assistant-claude)으로 변경하고 다음 명령을 사용하여 앱을 시작합니다.

 npm start


앱을 빌드하는 데 잠시 시간이 걸릴 수 있습니다. 준비가 되면 브라우저의 localhost:3000 에서 자동으로 열립니다.


상황인식과 검색 기능을 모두 테스트해보겠습니다! 먼저, 2021년에 아직 출시가 발표되지 않은 또 다른 비디오 게임, 즉 SEGA 의 최신 Yakuza 게임의 속편에 대해 문의해 보겠습니다. 또한, 이 게임에 이전 게임에서 사랑받았던 캐릭터인 키류 카즈마가 포함되어 있는지 물어볼 것입니다. 이렇게 하려면 " 질문 " 버튼을 클릭하세요.


몇 초 동안 생각할 시간을 주세요...



놀랍게도 AI가 잘못 대답했습니다. Yakuza: Like a Dragon은 실제로 최신 Yakuza 게임이지만 다른 주인공인 Ichiban Kasuga가 출연합니다. 이번에는 질문을 바꿔서 " 질문 및 검색 " 버튼을 사용해 보겠습니다.



이제 AI가 웹 검색이 필요한지 여부를 결정하는 데 더 오랜 시간이 걸렸습니다. 웹 검색이 필요하다고 판단하고 만족스러운 답변을 찾은 후 정보를 클라이언트/프론트 엔드에 반환했습니다.


이번에는 키류 카즈마가 주인공인 '용과 같이 이름을 지운 남자'라는 제목으로 돌아왔습니다.


정말 흥미롭지 않나요? 대규모 언어 모델을 기반으로 하는 에이전트는 사용 가능한 리소스(검색)를 고려하여 어떤 작업이 필요한지 예측하고 만족스러운 답변을 제공합니다. LangChain을 사용하면 모델, 프롬프트(모델에 대한 지침), 에이전트 및 도구와 같은 기타 기능을 쉽게 연결하고 "체인"할 수 있습니다.

결론

이 앱을 구축하고 이러한 기술에 대해 배우는 것이 즐거웠기를 바랍니다. 더 자세히 알아보는 데 관심이 있는 경우 프런트엔드백엔드 에 대해 완성된 프로젝트에 액세스할 수 있습니다.


파트너가 실현한 최신 기술로 구축하고 싶다면 다음 AI 챌린지에 참여하세요!


*please see lablab.ai for all terms and conditions