Posts LangChain 사용 사례 튜토리얼 파트2
Post
Cancel

LangChain 사용 사례 튜토리얼 파트2

해당 쿡북은 LangChain Cookbook Part 2 - Use Cases를 한글로 번역한 것이며 LangChain Conceptual Documentation를 기반으로 작성 되었습니다.

목표: ELI5예제와 코드를 통해 LangChain의 구성 요소와 사용 사례에 대한 기본적인 이해를 제공합니다. LangChain의 기본 원칙에 대한 소개는 쿡북 파트 1: 기본에서 확인하세요.

링크:

1. LangChain 이란?

LangChain은 언어 모델(LLM) 기반의 애플리케이션을 개발하기 위한 프레임워크입니다.

요약: LangChain은 AI 모델과 작업 및 구축하는 복잡한 부분을 간단하게 만들어줍니다. 이를 위해 다음 두 가지 방법을 사용합니다:

  1. 통합 - 파일, 애플리케이션, API 데이터와 같은 외부 데이터를 LLM에 가져올 수 있습니다.
  2. 에이전시 - LLM을 통해 다음에 어떤 조치를 취할지 결정하는 데 도움을 줍니다.

2. 왜 LangChain인가?

  1. 구성 요소 - LangChain은 언어 모델과 함께 작업하기 위해 필요한 추상화와 구성 요소를 쉽게 교체할 수 있게 해줍니다.
  2. 사용자 정의 체인 - LangChain은 ‘체인’ - 연속된 일련의 동작 - 을 사용하고 사용자 정의하는 데 필요한 지원을 제공합니다.
  3. 속도 🚢 - 빠른 업데이트로 최신 LLM 기능을 계속해서 사용할 수 있습니다.
  4. 커뮤니티 👥 - 디스코드와 커뮤니티 지원, 모임, 해커톤 등이 활발합니다.

LLM은 간단할 수 있지만(텍스트 입력, 텍스트 출력) 더 복잡한 애플리케이션을 개발하면 LangChain이 도와주는 문제점에 부딪힐 것입니다. 참고: 이 쿡북은 LangChain의 모든 측면을 다루지 않습니다. 내용은 가능한 빠르게 설계 하는것으로 선별되었습니다. 자세한 내용은 LangChain 개념 문서를 참조하세요.,

3. 주요 사용 사례

  • 요약 (Summarization) - 텍스트 또는 채팅 상호작용에 대한 가장 중요한 사실을 표현.
  • 문서를 통한 질문 및 답변 (Question and Answering Over Documents) - 문서 내의 정보를 사용하여 질문에 답하거나 조회.
  • 추출 (Extraction) - 텍스트 또는 사용자 쿼리에서 구조화된 데이터 추출.
  • 평가 (Evaluation) - 애플리케이션의 출력 품질 이해.
  • 표 형식의 데이터 쿼리 (Querying Tabular DataQ) - 데이터베이스나 다른 표 형식의 소스에서 데이터 추출.
  • 코드 이해 (Code Understanding) - 코드에 대한 이유를 제시하고 요약.
  • API와의 상호작용 (Interacting with APIs) - API 쿼리 및 외부와 상호작용.
  • 챗봇 (Chatbots) - 채팅 인터페이스에서 메모리와 결합하여 사용자와 앞뒤로 상호 작용하는 프레임워크.
  • 에이전트 (Agents) - 다음에 무엇을 해야 할지 결정하기 위해 LLMs 사용. 이러한 결정을 도구로 지원.

이러한 사용 사례의 실제 예제를 보고 싶으신가요? LangChain 프로젝트 갤러리로 이동하세요.

저자의 노트:

  • 이 쿡북은 LangChain의 모든 측면을 다루지 않습니다. 이 내용은 가능한 빠르게 건설하고 영향을 줄 수 있도록 선별되었습니다. 더 자세한 내용은 LangChain 기술 문서를 참조해 주세요.
  • 이 노트북은 여러분이 이 시리즈의 1부 기본 원칙을 보았다고 가정합니다. 이 노트북은 무엇을 해야 하는지와 그 기본 원칙을 어떻게 적용하는지에 중점을 둡니다.
  • 노트북 전체에서 import 문을 반복해서 사용하는 것을 알 수 있습니다. 제 목표는 명확성을 유지하고 전체 코드 블록을 한 곳에서 볼 수 있게 하는 것입니다. 패키지를 언제 가져왔는지 확인하기 위해 왔다갔다 할 필요가 없습니다.
  • 노트북 전체에서 기본 모델을 사용합니다. 작성 시점에서 그것들은 davinci-003과 gpt-3.5-turbo였습니다. GPT4를 사용하면 더 좋은 결과를 얻을 수 있을 것입니다.

이 튜토리얼에서는 OpenAI의 다양한 모델을 사용할 것입니다. LangChain은 LLMs를 대체하는 것을 쉽게 해줍니다. 따라서 원한다면 자신만의 LLM을 가져올 수 있습니다.

1
2
3
import os

openai_api_key='YOUR OPENAI API KEY'
1
2
3
4
5
6
7
8
# data 폴더 생성
folder_path = "./data"

# 폴더가 이미 존재하지 않는 경우에만 생성
if not os.path.exists(folder_path):
    os.makedirs(folder_path)
else:
    print("Folder already exists. Pass.")
1
Folder already exists. Pass.
1
2
3
# 디스플레이를 더 넓게 만들고 싶다면 이 셀을 실행하세요
from IPython.display import display, HTML
display(HTML("<style>.container { width:90% !important; }</style>"))

4. 요약 (Summarization)

LangChain과 LLMs의 가장 일반적인 사용 사례 중 하나는 요약입니다. 모든 텍스트를 요약할 수 있지만, 전화 통화, 기사, 책, 학술 논문, 법률 문서, 사용자 히스토리, 표, 재무 문서 등을 요약하는 것에 사용됩니다. 정보를 빠르게 요약할 수 있는 도구가 있으면 정말 도움이 됩니다.

  • 심화 학습 - (곧 제공됩니다)
  • 예제 - B2B 판매 통화 요약하기
  • 사용 사례 - 기사, 대본, 채팅 기록, 슬랙/디스코드, 고객 상호작용, 의학 논문, 법률 문서, 팟캐스트, 트윗 스레드, 코드 베이스, 제품 리뷰, 재무 문서 요약

1) 짧은 텍스트의 요약

짧은 텍스트의 요약의 경우 방법은 간단하며, 실제로는 간단한 지시어를 사용하여 프롬프트하는 것 외에는 특별히 할 것이 없습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from langchain.llms import OpenAI
from langchain import PromptTemplate

# 기본 모델은 이미 'text-davinci-003'이지만 나중에 원할 경우 변경할 위치를 알 수 있도록 여기서 명시적으로 호출합니다.
llm = OpenAI(temperature=0, model_name='text-davinci-003', openai_api_key=openai_api_key)

# 템플릿 생성
template = """
%INSTRUCTIONS:
Please summarize the following piece of text.
Respond in a manner that a 5 year old would understand.

%TEXT:
{text}
"""

# 나중에 값을 삽입할 수 있는 LangChain 프롬프트 템플릿을 만듭니다.
prompt = PromptTemplate(
    input_variables=["text"],
    template=template,
)

온라인에서 혼란스러운 텍스트를 찾아봅시다. 출처

1
2
3
4
5
6
confusing_text = """
그 후 130년 동안 논쟁이 격렬해졌습니다.
일부 과학자들은 프로토탁사이트를 지의류라고 불렀고, 다른 과학자들은 곰팡이라고 불렀으며, 또 다른 과학자들은 그것이 일종의 나무라는 개념에 집착했습니다.
지구물리학 부교수이자 진화생물학위원회의 보이스(Boyce)는 “문제는 해부학적 구조를 가까이서 보면 다양한 것들이 연상되지만 진단 결과는 아무것도 없다는 것입니다.”라고 말합니다.
"그리고 그것은 너무 커서 누군가가 그것이 무엇이라고 말할 때마다 다른 사람들이 일어나서 '어떻게 20피트 높이의 이끼류를 가질 수 있니?'라고 말합니다."
"""

LLM에 어떤 프롬프트가 전송되는지 살펴 보겠습니다.

1
2
3
4
5
6
print ("------- Prompt Begin -------")

final_prompt = prompt.format(text=confusing_text)
print(final_prompt)

print ("------- Prompt End -------")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
------- Prompt Begin -------

%INSTRUCTIONS:
Please summarize the following piece of text.
Respond in a manner that a 5 year old would understand.

%TEXT:

그 후 130년 동안 논쟁이 격렬해졌습니다.
일부 과학자들은 프로토탁사이트를 지의류라고 불렀고, 다른 과학자들은 곰팡이라고 불렀으며, 또 다른 과학자들은 그것이 일종의 나무라는 개념에 집착했습니다.
지구물리학 부교수이자 진화생물학위원회의 보이스(Boyce)는 “문제는 해부학적 구조를 가까이서 보면 다양한 것들이 연상되지만 진단 결과는 아무것도 없다는 것입니다.”라고 말합니다.
"그리고 그것은 너무 커서 누군가가 그것이 무엇이라고 말할 때마다 다른 사람들이 일어나서 '어떻게 20피트 높이의 이끼류를 가질 수 있니?'라고 말합니다."


------- Prompt End -------

마지막으로 LLM을 통과해 보겠습니다.

1
2
output = llm(final_prompt)
print (output)
1
사람들이 130년 동안 논쟁하던 건 그림자가 지의류라고 불리는 거대한 나무라고 생각하는 사람들과 곰팡이라고 불리는 작은 나무라고 생각하는 사람들 사이에요. 그리고 그것이 무엇인지 알 수 없어서 누군가가 20피트 높이의

이 방법은 잘 작동하지만, 긴 텍스트의 경우 관리가 번거로워질 수 있고 토큰 한도에 부딪힐 수 있습니다. 다행히 LangChain은 load_summarize_chain을 통해 요약하는 다양한 방법에 대한 기본 지원을 제공합니다.

2) 긴 텍스트의 요약

참고: 이 방법은 짧은 텍스트에도 사용할 수 있습니다.

1
2
3
4
5
from langchain.llms import OpenAI
from langchain.chains.summarize import load_summarize_chain
from langchain.text_splitter import RecursiveCharacterTextSplitter

llm = OpenAI(temperature=0, openai_api_key=openai_api_key)

장문의 영어 문서 다운로드

1
2
3
4
5
6
7
8
9
10
# 샘플 데이터 다운
import requests

url = "https://raw.githubusercontent.com/hmkim312/datas/main/Langchain/good.txt"
response = requests.get(url)

# 파일을 저장하기
file_name = "./data/PaulGrahamEssays/good.txt"
with open(file_name, 'w', encoding="utf-8") as f:
    f.write(response.text)
1
2
3
4
5
with open(file_name, 'r') as file:
    text = file.read()

# 처음 285자를 미리보기
print (text[:285])
1
2
3
4
April 2008(This essay is derived from a talk at the 2008 Startup School.)About a month after we started Y Combinator we came up with the
phrase that became our motto: Make something people want.  We've
learned a lot since then, but if I were choosing now that's still
the one I'd pick.

이 문서에 얼마나 많은 토큰이 있는지 확인해 봅시다. get_num_tokens는 이를 위한 좋은 방법입니다.

1
2
3
num_tokens = llm.get_num_tokens(text)

print (f"해당 파일에는 {num_tokens}개의 토큰이 있습니다.")
1
해당 파일에는 3970개의 토큰이 있습니다.

당신은 이 텍스트를 프롬프트에 넣을 수 있을 것 같지만, 너무 크다고 가정해 봅시다. 먼저 이것을 나누어야 합니다. 이 과정을 ‘chunking’ 또는 텍스트를 작은 조각으로 ‘split’라고 합니다. 저는 RecursiveCharacterTextSplitter를 좋아하는데, 그것은 제어하기 쉽기 때문입니다. 하지만 시도해 볼 수 있는 여러 가지가 있습니다.

1
2
3
4
text_splitter = RecursiveCharacterTextSplitter(separators=["\n\n", "\n"], chunk_size=5000, chunk_overlap=350)
docs = text_splitter.create_documents([text])

print (f"이제 1개의 텍스트가 아닌 {len(docs)}개의 문서가 있습니다.")
1
이제 1개의 텍스트가 아닌 4개의 문서가 있습니다.

당신은 연속적으로 LLM에 호출을 할 수 있는 체인을 로드해야 합니다. 아래 체인에서 사용되는 프롬프트를 보고 싶다면 LangChain 문서를 확인해보세요.

체인 유형 간의 차이점에 대한 정보는 토큰 제한 해결 방법 이라는 비디오에서 확인하실 수 있습니다.

참고: 처음 4회의 map_reduce 호출을 동시에 병렬로 실행하는 것도 가능합니다.

1
2
# chain 사용 준비
chain = load_summarize_chain(llm=llm, chain_type='map_reduce') # verbose=True LLM으로 전송되는 내용을 확인하는 선택 사항
1
2
3
# 이것은 4개의 문서를 통과하여 chunk들을 요약하고, 그 요약의 요약을 얻게 됩니다.
output = chain.run(docs)
print (output)
1
 This essay discusses the importance of benevolence in startups, and how it can help them succeed. It explains how benevolence can improve morale, make people want to help, and help startups be decisive. It also explains how being benevolent and committed can make startups hard to kill, and how it can attract investors, customers, other companies, and potential employees. Finally, it looks at how markets have evolved to value potential dividends and potential earnings, and how users dislike their new operating system.

5. 문서를 통한 질문 및 답변 (Question & Answering Using Documents As Context)

LangChain 질문 & 답변 문서

LLM을 사용하여 질문과 답변을 위해 우리는:

  1. 질문에 답하는 데 필요한 관련 맥락을 LLM에 전달해야 합니다.
  2. 우리가 답변을 원하는 질문을 전달해야 합니다.

간단히 말하면, 이 과정은 “llm(당신의 맥락 + 당신의 질문) = 당신의 답변”처럼 보입니다.

1) 간단한 Q&A 예시

여기서 llm(당신의 맥락 + 당신의 질문) = 당신의 답변의 규칙을 살펴보겠습니다.

1
2
3
from langchain.llms import OpenAI

llm = OpenAI(temperature=0, openai_api_key=openai_api_key, model_name="gpt-3.5-turbo-16k")
1
2
3
4
/home/hyunmin-kim/anaconda3/lib/python3.10/site-packages/langchain/llms/openai.py:200: UserWarning: You are trying to use a chat model. This way of initializing it is no longer supported. Instead, please use: `from langchain.chat_models import ChatOpenAI`
  warnings.warn(
/home/hyunmin-kim/anaconda3/lib/python3.10/site-packages/langchain/llms/openai.py:787: UserWarning: You are trying to use a chat model. This way of initializing it is no longer supported. Instead, please use: `from langchain.chat_models import ChatOpenAI`
  warnings.warn(
1
2
3
4
5
6
7
context = """
레이첼은 30살 입니다.
밥은 45살 입니다.
케빈은 65살 입니다.
"""

question = "40세 이하는 누구일까요?"

맥락과 질문의 결합

1
2
3
4
output = llm(context + question)

# 앞뒤 공백을 제거하기 위해 텍스트를 제거합니다.
print (output.strip())
1
40세 이하는 레이첼입니다.

우리가 더욱 정교해질수록 이 규칙을 더욱 활용하게 될 것입니다.

어려운 부분은 어떤 데이터를 맥락에 넣을지 선택적으로 결정해야 할 때 나타납니다. 이 연구 분야는 “문서 검색“이라고 불리며 AI 메모리와 밀접하게 연결되어 있습니다.

2) 임베딩 사용하기

우리가 지금부터 다룰 과정을 나는 비공식적으로 “벡터스토어 댄스(VectorStore Dance)”라고 부릅니다. 이는 텍스트를 분할하고, 임베딩을 적용한 다음, 이 임베딩을 데이터베이스에 저장하고, 그 후에 쿼리하는 과정을 의미합니다. 이에 대한 자세한 내용은 How To Question A Book 비디오를 참고하세요.

우리의 목표는 긴 텍스트에서 관련된 청크를 선택하는 것입니다. 그런데 어떤 청크를 선택해야 할까요? 가장 인기 있는 방법은 벡터 임베딩을 기반으로 비교하여 유사한 텍스트를 선택하는 것입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from langchain import OpenAI

# 벡터 스토어
from langchain.vectorstores import FAISS

# 문서를 가져오는 데 사용할 LangChain 구성 요소
from langchain.chains import RetrievalQA

# 텍스트를 위한 간편한 문서 로더
from langchain.document_loaders import TextLoader

# 텍스트를 벡터로 변환하는 임베딩 엔진
from langchain.embeddings.openai import OpenAIEmbeddings

llm = OpenAI(temperature=0, openai_api_key=openai_api_key)

장문의 문서 다운로드

1
2
3
4
5
6
7
8
9
import requests

url = "https://raw.githubusercontent.com/hmkim312/datas/main/Langchain/worked.txt"
response = requests.get(url)

# 파일을 저장하기
file_name = "./data/PaulGrahamEssays/worked.txt"
with open(file_name, 'w', encoding="utf-8") as f:
    f.write(response.text)
1
2
3
4
loader = TextLoader(file_name)
doc = loader.load()
print (f"{len(doc)}개의 문서")
print (f"문서 내 {len(doc[0].page_content)}개의 문자")
1
2
1개의 문서
문서 내 74663개의 문자

이제 긴 문서를 작은 조각으로 나누어 보겠습니다.

1
2
text_splitter = RecursiveCharacterTextSplitter(chunk_size=3000, chunk_overlap=400)
docs = text_splitter.split_documents(doc)
1
2
3
4
# 나중에 평균을 볼 수 있도록 총 문자 수를 가져옵니다.
num_total_characters = sum([len(x.page_content) for x in docs])

print (f"{len(docs)}개의 문서와 문서 내 {num_total_characters / len(docs):,.0f}개의 문자 (작은 조각)")
1
29개의 문서와 문서 내 2,930개의 문자 (작은 조각)
1
2
3
4
5
# 임베딩 엔진을 준비
embeddings = OpenAIEmbeddings(openai_api_key=openai_api_key)

# 문서를 삽입하고 DB의 원시 텍스트와 결합하세요. 참고: OpenAI에 대한 API 호출이 이루어집니다.
docsearch = FAISS.from_documents(docs, embeddings)

검색 엔진 만들기

1
qa = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=docsearch.as_retriever())

이제 질문을 할 시간입니다. 검색자는 유사한 문서를 가져오고 LLM이 추론할 수 있도록 귀하의 질문과 결합합니다.

참고: 별 것 아닌 것처럼 보일 수도 있지만 여기서 놀라운 점은 전체 원본 문서를 전달할 필요가 없다는 것입니다.

1
2
query = "What does the author describe as good work?"
qa.run(query)
1
' The author describes painting as good work.'
1
2
query = "작가는 좋은 작품을 무엇으로 묘사합니까?"
qa.run(query)
1
' The author describes good works as paintings.'

더 많은 작업을 수행하려면 이를 클라우드 벡터 데이터베이스에 연결하고, Metal과 같은 도구를 사용하고, 외부 데이터 소스를 사용하여 문서 관리를 시작하세요.

6. 추출 (Extraction)

[LangChain 추출 문서](https://python.langchain.com/en/latest/use_cases/extraction.html{:target=”_blank”}

추출은 텍스트에서 데이터를 파싱하는 과정입니다. 이는 주로 데이터를 구조화하기 위해 출력 파싱과 함께 사용됩니다.

추출을 위한 인기 있는 라이브러리는 Kor입니다. 오늘은 다루지 않겠지만, 고급 추출을 위해 확인해 보는 것을 강력히 추천합니다.

1
2
3
4
5
6
7
8
9
10
11
# 채팅 메세지 구성
from langchain.schema import HumanMessage
from langchain.prompts import PromptTemplate, ChatPromptTemplate, HumanMessagePromptTemplate

# 채팅 모델 사용. 기본값은 gpt-3.5-turbo
from langchain.chat_models import ChatOpenAI

# 출력을 구문 분석하고 구조화된 데이터를 다시 가져옴
from langchain.output_parsers import StructuredOutputParser, ResponseSchema

chat_model = ChatOpenAI(temperature=0, model_name='gpt-3.5-turbo', openai_api_key=openai_api_key)

1) 기본 추출

간단한 예로 시작해봅시다. 여기서 원하는 출력 유형의 지시사항을 포함한 프롬프트를 제공합니다.

1
2
3
4
5
6
7
8
instructions = """
과일 이름이 포함된 문장이 제공되며, 과일 이름을 추출하고 이모티콘을 할당합니다.
Python dictionary에 과일 이름과 이모티콘을 반환합니다.
"""

fruit_names = """
사과, 배, 그리고 키위
"""
1
2
3
4
5
6
7
8
# 과일 이름과 지침을 결합한 프롬프트 생성
prompt = (instructions + fruit_names)

# LLM 콜
output = chat_model([HumanMessage(content=prompt)])

print (output.content)
print (type(output.content))
1
2
{'사과': '🍎', '배': '🍐', '키위': '🥝'}
<class 'str'>

파이썬 Dictionary로 변환

1
2
3
4
output_dict = eval(output.content)

print (output_dict)
print (type(output_dict))
1
2
{'사과': '🍎', '배': '🍐', '키위': '🥝'}
<class 'dict'>

이번에는 이것이 효과가 있었지만 고급 사용 사례에서는 장기적으로 보았을때 신뢰할 수 있는 방법이 아닙니다.

2) LangChain의 응답 스키마 사용하기

LangChain의 응답 스키마는 우리에게 두 가지를 해줍니다:

  1. 진정한 형식의 지시사항과 함께 프롬프트를 자동 생성합니다. 이것은 나로서는 프롬프트 엔지니어링 측면에 대해 걱정할 필요가 없기 때문에 좋습니다. 그 부분은 LangChain에 맡길게요!

  2. LLM의 출력을 읽고 이를 제대로 된 파이썬 객체로 변환해 줍니다.

여기서 원하는 스키마를 정의합니다. 유사 채팅 메시지에서 사용자가 재생하고 싶어하는 노래와 아티스트를 추출하려고 합니다.

1
2
3
4
5
6
7
8
# 내가 원하는 아웃풋 스키마
response_schemas = [
    ResponseSchema(name="아티스트", description="아티스트 이름"),
    ResponseSchema(name="노래", description="아티스트가 연주하는 노래의 이름")
]

# 내 스키마에서 LLM 출력을 찾아 다시 반환하는 파서
output_parser = StructuredOutputParser.from_response_schemas(response_schemas)
1
2
3
# LangChain이 만드는 형식 지침입
format_instructions = output_parser.get_format_instructions()
print(format_instructions)
1
2
3
4
5
6
7
8
The output should be a markdown code snippet formatted in the following schema, including the leading and trailing "```json" and "```":

```json
{
	"아티스트": string  // 아티스트 이름
	"노래": string  // 아티스트가 연주하는 노래의 이름
}
```
1
2
3
4
5
6
7
8
9
10
11
# 모든 것을 하나로 묶는 프롬프트 템플릿
# 참고: 채팅 모델을 사용하고 있기 때문에 이전과 다른 프롬프트 템플릿입니다.

prompt = ChatPromptTemplate(
    messages=[
        HumanMessagePromptTemplate.from_template("사용자의 명령에 따라 아티스트와 곡명을 추출합니다.\n \
                                                    {format_instructions}\n{user_prompt}")  
    ],
    input_variables=["user_prompt"],
    partial_variables={"format_instructions": format_instructions}
)
1
2
fruit_query = prompt.format_prompt(user_prompt="나는 신해철의 라젠카를 좋아해요.")
print (fruit_query.messages[0].content)
1
2
3
4
5
6
7
8
9
10
사용자의 명령에 따라 아티스트와 곡명을 추출합니다.
                                                     The output should be a markdown code snippet formatted in the following schema, including the leading and trailing "```json" and "```":

```json
{
	"아티스트": string  // 아티스트 이름
	"노래": string  // 아티스트가 연주하는 노래의 이름
}
```
나는 신해철의 라젠카를 좋아해요.
1
2
3
4
5
fruit_output = chat_model(fruit_query.to_messages())
output = output_parser.parse(fruit_output.content)

print (output)
print (type(output))
1
2
{'아티스트': '신해철', '노래': '라젠카'}
<class 'dict'>

멋지네요. 이제 나중에 사용할 수 있는 사전이 생겼습니다.

경고: 파서는 특정 형식의 LLM 출력을 찾습니다. 귀하의 모델은 매번 동일한 형식을 출력하지 않을 수 있습니다. 이것으로 오류를 처리하십시오. GPT4 및 향후 반복은 더욱 안정적일 것입니다.

고급 구문 분석에 대해서는 Kor를 확인하세요.

7. 평가 (Evaluation)

LangChain 평가 문서

평가는 애플리케이션의 출력에 대한 품질 검사를 수행하는 과정입니다. 일반적인, 결정적인 코드에는 우리가 실행할 수 있는 테스트가 있지만, 자연어의 예측 불가능성과 다양성 때문에 LLMs의 출력을 판단하는 것은 더 어렵습니다. LangChain은 이 여정에서 우리를 돕는 도구를 제공합니다.

  • 심화 학습 - 곧 출시 예정
  • 예시 - Lance Martin의 고급 자동 평가기
  • 사용 사례: 요약 또는 질문 & 답변 파이프라인에 대한 품질 검사를 실행하거나, 요약 파이프라인의 출력을 확인합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
# Embeddings, store, and retrieval
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.vectorstores import FAISS
from langchain.chains import RetrievalQA

# Model and doc loader
from langchain import OpenAI
from langchain.document_loaders import TextLoader

# Eval!
from langchain.evaluation.qa import QAEvalChain

llm = OpenAI(temperature=0, openai_api_key=openai_api_key)
1
2
3
4
5
6
7
8
9
import requests

url = "https://raw.githubusercontent.com/hmkim312/datas/main/Langchain/worked.txt"
response = requests.get(url)

# 파일을 저장하기
file_name = "./data/PaulGrahamEssays/worked.txt"
with open(file_name, 'w', encoding="utf-8") as f:
    f.write(response.text)
1
2
3
4
5
6
# 긴 문상의 에세이 다운
loader = TextLoader('./data/PaulGrahamEssays/worked.txt')
doc = loader.load()

print (f"{len(doc)}개의 문서")
print (f"문서 내의 {len(doc[0].page_content)}개의 문자")
1
2
1개의 문서
문서 내의 74663개의 문자

먼저 벡터스토어 댄스를 해서 질문과 답변을 할 수 있게 합니다.

1
2
3
4
5
6
7
text_splitter = RecursiveCharacterTextSplitter(chunk_size=3000, chunk_overlap=400)
docs = text_splitter.split_documents(doc)

# 나중에 평균을 볼 수 있도록 총 문자 수를 가져옵니다.
num_total_characters = sum([len(x.page_content) for x in docs])

print (f"{len(docs)}개의 문서와 {num_total_characters / len(docs):,.0f}개의 평균 문자 (작은 조각)")
1
29개의 문서와 2,930개의 평균 문자 (작은 조각)
1
2
3
# 임베딩 및 문서 저장소
embeddings = OpenAIEmbeddings(openai_api_key=openai_api_key)
docsearch = FAISS.from_documents(docs, embeddings)

검색 체인을 만드세요. 이제 input_key 매개변수가 어떻게 있는지 확인하세요. 이는 내가 제공한 사전의 어떤 키에 프롬프트/쿼리가 포함되어 있는지 체인에 알려줍니다. 아래 사전의 질문과 일치하도록 ‘질문’을 지정합니다.

1
chain = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=docsearch.as_retriever(), input_key="question")

이제 내가 옳다고 생각하는 질문 목록과 실제 답변을 LLM에 전달하겠습니다.

1
2
3
4
question_answers = [
    {'question' : "친구가 직접 만든 마이크로컴퓨터 키트를 판매한 회사는 어디인가요?", 'answer' : 'Healthkit'},
    {'question' : "미국의 금융 수도인 도시에서 그가 이야기한 작은 도시는 무엇이었을까요?", 'answer' : '요크빌'}
]

나는 chain.apply를 사용하여 두 질문을 하나씩 개별적으로 실행하겠습니다.

멋진 부분 중 하나는 질문 및 답변 사전 목록을 다시 가져올 수 있다는 점입니다. 하지만 사전 ‘result’에는 LLM의 출력이 될 또 다른 키가 있습니다.

참고: 특히 두 번째 질문을 모호하고 한 번에 답변하기 어렵게 만들어 LLM이 틀리게 만들었습니다.

1
2
predictions = chain.apply(question_answers)
predictions
1
2
3
4
5
6
[{'question': '친구가 직접 만든 마이크로컴퓨터 키트를 판매한 회사는 어디인가요?',
  'answer': 'Healthkit',
  'result': ' Heathkit'},
 {'question': '미국의 금융 수도인 도시에서 그가 이야기한 작은 도시는 무엇이었을까요?',
  'answer': '요크빌',
  'result': ' New York'}]

그런 다음 LLM이 내 정답(‘result’ 키)을 LLM의 결과(‘result’ 키)와 비교하도록 합니다.

아니면 간단히 말해서 LLM이 스스로 채점하도록 요청하는 것입니다.

1
2
3
4
5
6
7
8
9
# 평가 시작
eval_chain = QAEvalChain.from_llm(llm)

# 자체적으로 등급을 매기십시오. 아래 코드는 eval_chain이 다른 부분의 위치를 파악하는 데 도움이 됩니다.
graded_outputs = eval_chain.evaluate(question_answers,
                                     predictions,
                                     question_key="question",
                                     prediction_key="result",
                                     answer_key='answer')
1
graded_outputs
1
[{'results': ' CORRECT'}, {'results': ' INCORRECT'}]

이것이 맞습니다! 질문1의 대답은 “Healthkit”이었고 예측은 “마이크로컴퓨터 키트는 Heathkit에서 판매했습니다.”였습니다. LLM은 답변과 결과가 동일하다는 것을 알고 우리에게 “CORRECT” 라벨을 부여했습니다. 질문2의 경우 동일하지 않다는 것을 알고 “INCORRECT” 레이블을 제공했습니다.

8. 표 데이터 질의하기 (Querying Tabular Data)

LangChain 표 데이터 질의 문서

세계에서 가장 일반적인 유형의 데이터는 표 형식으로 존재합니다 (비구조화된 데이터를 제외하고). 이 데이터를 LangChain으로 질의하고 LLM에 전달하는 것은 매우 강력합니다.

  • 심화 학습 - 곧 출시 예정
  • 예시 - 미정
  • 사용 사례: 사용자에 관한 데이터를 질의하기 위해 LLMs 사용, 데이터 분석 수행, 데이터베이스에서 실시간 정보 가져오기

더 자세한 내용은 “에이전트 + 표 데이터” (Pandas, SQL, CSV)를 확인하세요.

자연어로 SQLite DB를 질의해봅시다. 샌프란시스코 나무 데이터셋을 살펴보겠습니다.

1
2
3
4
from langchain import OpenAI, SQLDatabase
from langchain_experimental.sql import SQLDatabaseChain

llm = OpenAI(temperature=0, openai_api_key=openai_api_key)

먼저 데이터가 있는 위치를 지정하고 연결을 준비하겠습니다.

1
2
3
4
5
6
7
8
9
import requests

url = "https://raw.githubusercontent.com/hmkim312/datas/main/Langchain/San_Francisco_Trees.db"

# 파일을 저장하기
file_name = "data/San_Francisco_Trees.db"
response = requests.get(url)
with open(file_name, 'wb') as f:
    f.write(response.content)
1
2
sqlite_db_path = "data/San_Francisco_Trees.db"
db = SQLDatabase.from_uri(f"sqlite:///{sqlite_db_path}")

그런 다음 LLM과 DB를 사용하는 체인을 만듭니다. 내부적으로 무슨 일이 일어나고 있는지 볼 수 있도록 verbose=True를 설정하겠습니다.

1
db_chain = SQLDatabaseChain(llm=llm, database=db, verbose=True)
1
2
/home/hyunmin-kim/anaconda3/lib/python3.10/site-packages/langchain_experimental/sql/base.py:75: UserWarning: Directly instantiating an SQLDatabaseChain with an llm is deprecated. Please instantiate with llm_chain argument or using the from_llm class method.
  warnings.warn(
1
db_chain.run("샌프란시스코에는 몇 종의 나무가 있나요?")
1
2
3
4
5
6
7
8
9
10
11
12
> Entering new SQLDatabaseChain chain...
샌프란시스코에는 몇 종의 나무가 있나요?
SQLQuery:SELECT COUNT(DISTINCT qSpecies) FROM SFTrees;
SQLResult: [(578,)]
Answer:샌프란시스코에는 578 종의 나무가 있습니다.
> Finished chain.





'샌프란시스코에는 578 종의 나무가 있습니다.'

정말 대단해요! 실제로 여기에는 몇 가지 단계가 진행됩니다.

단계:

  1. 사용할 테이블 찾기
  2. 사용할 열 찾기
  3. 올바른 SQL 쿼리를 구성하세요.
  4. 해당 쿼리를 실행합니다.
  5. 결과 얻기
  6. 자연어 응답을 다시 반환합니다.

Pandas를 통해 확인해 보겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import sqlite3
import pandas as pd

# Connect to the SQLite database
connection = sqlite3.connect(sqlite_db_path)

# Define your SQL query
query = "SELECT count(distinct qSpecies) FROM SFTrees"

# Read the SQL query into a Pandas DataFrame
df = pd.read_sql_query(query, connection)

# Close the connection
connection.close()
1
2
# Display the result in the first column first cell
print(df.iloc[0,0])
1
578

응답과 동일합니다.

9. 코드 이해 (Code Understanding)

LangChain 코드 이해 문서

LLMs의 가장 흥미로운 능력 중 하나는 코드를 이해하는 것입니다. 전 세계의 사람들이 AI의 도움으로 속도와 품질 모두에서 성과를 높이고 있습니다. 이것의 큰 부분은 코드를 이해할 수 있는 LLM을 가지고 특정 작업을 도와줄 수 있다는 것입니다.

  • 심화 학습 - 곧 출시 예정
  • 예시 - 미정
  • 사용 사례: 특정 라이브러리에서의 질문에 답을 해주거나 새로운 코드를 생성해주는 Co-Pilot 같은 기능
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import os

# 벡터
from langchain.vectorstores import FAISS
from langchain.embeddings.openai import OpenAIEmbeddings

# 체인 모델
from langchain.chat_models import ChatOpenAI

# 텍스트 분할기
from langchain.text_splitter import CharacterTextSplitter
from langchain.document_loaders import TextLoader

llm = ChatOpenAI(model_name='gpt-3.5-turbo', openai_api_key=openai_api_key)

먼저 벡터스토어 댄스를 해서 질문과 답변을 할 수 있게 합시다.

1
embeddings = OpenAIEmbeddings(disallowed_special=(), openai_api_key=openai_api_key)

이 저장소의 데이터 폴더에 작은 Python 패키지 The Fuzz(개인이 좋아하는 인디 패키지)를 넣었습니다.

아래 루프는 라이브러리의 각 파일을 살펴보고 문서로 로드합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 실습에 사용될 The Fuzz 패키지를 zip으로 압축하여 레파지토리에 올려놓음
# 이를 다운 받아 압축해제하는 코드 시간이 좀 걸림
# 기존에 해당 패키지가 있으면 삭제하고 진행
import requests
import zipfile
import os

def download_and_extract_zip(zip_url, save_path, extract_path):
    # ZIP 파일 다운로드
    response = requests.get(zip_url)
    zip_filename = os.path.join(save_path, "temp.zip")
    with open(zip_filename, 'wb') as zip_file:
        zip_file.write(response.content)
    
    # ZIP 파일 압축 해제
    with zipfile.ZipFile(zip_filename, 'r') as zip_ref:
        zip_ref.extractall(extract_path)
    
    # 임시 ZIP 파일 삭제
    os.remove(zip_filename)

# file down
zip_url = "https://raw.githubusercontent.com/hmkim312/datas/main/Langchain/thefuzz.zip"
save_path = "./"  # ZIP 파일을 임시로 저장할 경로
extract_path = "./data/"  # 압축을 풀 디렉토리
download_and_extract_zip(zip_url, save_path, extract_path)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
root_dir = './data/thefuzz'
docs = []

# 폴더 확인
for dirpath, dirnames, filenames in os.walk(root_dir):
    
    # 파일 확인
    for file in filenames:
        try: 
            # 파일을 문서로 로드하고 분할합니다.
            loader = TextLoader(os.path.join(dirpath, file), encoding='utf-8')
            docs.extend(loader.load_and_split())
        except Exception as e: 
            pass

문서의 예를 살펴보겠습니다. 그냥, 패키지 내 문서일 뿐입니다.

1
2
3
print (f"{len(docs)}개의 문서\n")
print ("------ 시작 문서 ------")
print (docs[0].page_content[:300])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
175개의 문서

------ 시작 문서 ------
Changelog
=========

0.17.0 (2018-08-20)
-------------------

- Make benchmarks script Py3 compatible. [Stefan Behnel]

- Add Go lang port. [iddober]

- Add reference to C# port. [ericcoleman]

- Chore: remove license header from files. [Jose Diaz-Gonzalez]

  The files should all inherit the projec

docstore에 삽입하고 저장하세요. 그러면 OpenAI에 대한 API 호출이 이루어집니다.

1
docsearch = FAISS.from_documents(docs, embeddings)
1
2
# Get our retriever ready
qa = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=docsearch.as_retriever())
1
2
query = "목록에서 가장 유사한것을 찾으려면 어떤 기능을 사용합니까?"
output = qa.run(query)
1
print(output)
1
가장 유사한 것을 찾기 위해서는 문자열 매칭 알고리즘을 사용해야 합니다. 이러한 알고리즘은 주어진 문자열과 다른 문자열들을 비교하여 유사도를 측정하고, 가장 유사한 문자열을 찾아줍니다. 이를 위해서는 fuzz 모듈의 extractOne() 함수나 process 모듈의 extractOne() 함수를 사용할 수 있습니다. 이 함수들은 주어진 쿼리와 선택지들을 비교하여 가장 유사한 선택지를 반환합니다.
1
2
query = "process.extractOne()를 사용하는 코드 작성해줘"
output = qa.run(query)
1
print(output)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
아래는 process.extractOne()을 사용하는 예시 코드입니다.

```python
from fuzzywuzzy import process

choices = [
    "new york mets vs chicago cubs",
    "chicago cubs at new york mets",
    "atlanta braves vs pittsburgh pirates",
    "new york yankees vs boston red sox"
]

query = "new york mets at chicago cubs"

best = process.extractOne(query, choices)
print(best[0])
```

위의 코드는 `query`와 가장 유사한 항목을 `choices`에서 찾아서 반환합니다. 반환된 결과는 튜플로 이루어져 있으며, 첫 번째 요소는 가장 유사한 항목이고 두 번째 요소는 일치율을 나타냅니다. 위의 코드에서는 "new york mets vs chicago cubs"가 가장 유사한 항목이므로 해당 항목이 출력됩니다.

10. API와의 상호작용 (Interacting with APIs)

LangChain API 상호작용 문서

필요한 데이터나 작업이 API 뒤에 있다면, LLM을 사용하여 API와 상호작용해야 합니다.

  • 심화 학습 - 곧 출시 예정
  • 예시 - 미정
  • 사용 사례: 사용자의 요청을 이해하고 작업을 수행하거나, 더 많은 실제 작업 흐름을 자동화하기

이 주제는 에이전트와 플러그인과 밀접하게 관련되어 있지만, 이 섹션에서는 이에 대한 간단한 사용 사례를 살펴보겠습니다. 자세한 내용은 LangChain + 플러그인 문서를 확인하세요.

1
2
3
4
from langchain.chains import APIChain
from langchain.llms import OpenAI

llm = OpenAI(temperature=0, openai_api_key=openai_api_key, model_name="gpt-3.5-turbo-16k")

LangChain의 APIChain에는 API 문서를 읽고 어떤 엔드포인트를 호출해야 하는지 이해할 수 있는 기능이 있습니다.

이 경우에는 이것이 어떻게 작동하는지 보여주기 위해 (의도적으로 엉성한) API 문서를 작성했습니다.

1
2
3
4
5
6
7
8
9
10
11
12
api_docs = """

BASE URL: https://restcountries.com/

API 엔드포인트 /v3.1/name/{name} 국가에 대한 정보를 찾는 데 사용됩니다. 모든 URL 매개변수는 다음과 같습니다.
    - 이름: 국가 이름 - 예: 이탈리아, 프랑스
    
API 엔드포인트 /v3.1/currency/{currency}는 지역에 대한 정보를 찾는 데 사용됩니다. 모든 URL 매개변수는 다음과 같습니다.
    - 통화: 3글자 통화. 예: USD, COP
"""

chain_new = APIChain.from_llm_and_api_docs(llm, api_docs, verbose=True)

국가 엔드포인트를 위한 API 호출을 만들어 보겠습니다.

1
chain_new.run('South Korea의 정보를 알려줄래')
1
2
3
4
5
6
7
8
9
10
11
> Entering new APIChain chain...
https://restcountries.com/v3.1/name/South%20Korea?lang=ko
[{"name":{"common":"South Korea","official":"Republic of Korea","nativeName":{"kor":{"official":"대한민국","common":"한국"}}},"tld":[".kr",".한국"],"cca2":"KR","ccn3":"410","cca3":"KOR","cioc":"KOR","independent":true,"status":"officially-assigned","unMember":true,"currencies":{"KRW":{"name":"South Korean won","symbol":"₩"}},"idd":{"root":"+8","suffixes":["2"]},"capital":["Seoul"],"altSpellings":["KR","Korea, Republic of","Republic of Korea","남한","남조선"],"region":"Asia","subregion":"Eastern Asia","languages":{"kor":"Korean"},"translations":{"ara":{"official":"جمهورية كوريا","common":"كوريا الجنوبية"},"bre":{"official":"Republik Korea","common":"Korea ar Su"},"ces":{"official":"Korejská republika","common":"Jižní Korea"},"cym":{"official":"Republic of Korea","common":"South Korea"},"deu":{"official":"Republik Korea","common":"Südkorea"},"est":{"official":"Korea Vabariik","common":"Lõuna-Korea"},"fin":{"official":"Korean tasavalta","common":"Etelä-Korea"},"fra":{"official":"République de Corée","common":"Corée du Sud"},"hrv":{"official":"Republika Koreja","common":"Južna Koreja"},"hun":{"official":"Koreai Köztársaság","common":"Dél-Korea"},"ita":{"official":"Repubblica di Corea","common":"Corea del Sud"},"jpn":{"official":"大韓民国","common":"韓国"},"kor":{"official":"대한민국","common":"한국"},"nld":{"official":"Republiek Korea","common":"Zuid-Korea"},"per":{"official":"جمهوری کره","common":"کرهٔ جنوبی"},"pol":{"official":"Republika Korei","common":"Korea Południowa"},"por":{"official":"República da Coreia","common":"Coreia do Sul"},"rus":{"official":"Республика Корея","common":"Южная Корея"},"slk":{"official":"Kórejská republika","common":"Južná Kórea"},"spa":{"official":"República de Corea","common":"Corea del Sur"},"srp":{"official":"Република Кореја","common":"Јужна Кореја"},"swe":{"official":"Republiken Korea","common":"Sydkorea"},"tur":{"official":"Kore Cumhuriyeti","common":"Güney Kore"},"urd":{"official":"جمہوریہ کوریا ","common":"جنوبی کوریا"},"zho":{"official":"大韩民国","common":"韩国"}},"latlng":[37.0,127.5],"landlocked":false,"borders":["PRK"],"area":100210.0,"demonyms":{"eng":{"f":"South Korean","m":"South Korean"},"fra":{"f":"Sud-coréenne","m":"Sud-coréen"}},"flag":"\uD83C\uDDF0\uD83C\uDDF7","maps":{"googleMaps":"https://goo.gl/maps/7ecjaJXefjAQhxjGA","openStreetMaps":"https://www.openstreetmap.org/relation/307756"},"population":51780579,"gini":{"2016":31.4},"fifa":"KOR","car":{"signs":["ROK"],"side":"right"},"timezones":["UTC+09:00"],"continents":["Asia"],"flags":{"png":"https://flagcdn.com/w320/kr.png","svg":"https://flagcdn.com/kr.svg","alt":"The flag of South Korea has a white field, at the center of which is a red and blue Taegeuk circle surrounded by four black trigrams, one in each corner."},"coatOfArms":{"png":"https://mainfacts.com/media/images/coats_of_arms/kr.png","svg":"https://mainfacts.com/media/images/coats_of_arms/kr.svg"},"startOfWeek":"monday","capitalInfo":{"latlng":[37.55,126.98]},"postalCode":{"format":"SEOUL ###-###","regex":"^(?:SEOUL)*(\\d{6})$"}}]

> Finished chain.





"South Korea, officially known as the Republic of Korea, is a country located in Eastern Asia. The official language is Korean. The currency used is the South Korean won (KRW). The capital city is Seoul. South Korea is a member of the United Nations and has a population of approximately 51.8 million people. The country's flag consists of a white field with a red and blue Taegeuk circle surrounded by four black trigrams. The country's coat of arms can be found at the provided URL."

통화 엔드포인트를 위한 API 호출을 시도해 보겠습니다.

1
chain_new.run('KRW 통화에 대해 알려줄수 있나요?')
1
2
3
4
5
6
7
8
9
10
11
> Entering new APIChain chain...
https://restcountries.com/v3.1/currency/KRW
[{"name":{"common":"South Korea","official":"Republic of Korea","nativeName":{"kor":{"official":"대한민국","common":"한국"}}},"tld":[".kr",".한국"],"cca2":"KR","ccn3":"410","cca3":"KOR","cioc":"KOR","independent":true,"status":"officially-assigned","unMember":true,"currencies":{"KRW":{"name":"South Korean won","symbol":"₩"}},"idd":{"root":"+8","suffixes":["2"]},"capital":["Seoul"],"altSpellings":["KR","Korea, Republic of","Republic of Korea","남한","남조선"],"region":"Asia","subregion":"Eastern Asia","languages":{"kor":"Korean"},"translations":{"ara":{"official":"جمهورية كوريا","common":"كوريا الجنوبية"},"bre":{"official":"Republik Korea","common":"Korea ar Su"},"ces":{"official":"Korejská republika","common":"Jižní Korea"},"cym":{"official":"Republic of Korea","common":"South Korea"},"deu":{"official":"Republik Korea","common":"Südkorea"},"est":{"official":"Korea Vabariik","common":"Lõuna-Korea"},"fin":{"official":"Korean tasavalta","common":"Etelä-Korea"},"fra":{"official":"République de Corée","common":"Corée du Sud"},"hrv":{"official":"Republika Koreja","common":"Južna Koreja"},"hun":{"official":"Koreai Köztársaság","common":"Dél-Korea"},"ita":{"official":"Repubblica di Corea","common":"Corea del Sud"},"jpn":{"official":"大韓民国","common":"韓国"},"kor":{"official":"대한민국","common":"한국"},"nld":{"official":"Republiek Korea","common":"Zuid-Korea"},"per":{"official":"جمهوری کره","common":"کرهٔ جنوبی"},"pol":{"official":"Republika Korei","common":"Korea Południowa"},"por":{"official":"República da Coreia","common":"Coreia do Sul"},"rus":{"official":"Республика Корея","common":"Южная Корея"},"slk":{"official":"Kórejská republika","common":"Južná Kórea"},"spa":{"official":"República de Corea","common":"Corea del Sur"},"srp":{"official":"Република Кореја","common":"Јужна Кореја"},"swe":{"official":"Republiken Korea","common":"Sydkorea"},"tur":{"official":"Kore Cumhuriyeti","common":"Güney Kore"},"urd":{"official":"جمہوریہ کوریا ","common":"جنوبی کوریا"},"zho":{"official":"大韩民国","common":"韩国"}},"latlng":[37.0,127.5],"landlocked":false,"borders":["PRK"],"area":100210.0,"demonyms":{"eng":{"f":"South Korean","m":"South Korean"},"fra":{"f":"Sud-coréenne","m":"Sud-coréen"}},"flag":"\uD83C\uDDF0\uD83C\uDDF7","maps":{"googleMaps":"https://goo.gl/maps/7ecjaJXefjAQhxjGA","openStreetMaps":"https://www.openstreetmap.org/relation/307756"},"population":51780579,"gini":{"2016":31.4},"fifa":"KOR","car":{"signs":["ROK"],"side":"right"},"timezones":["UTC+09:00"],"continents":["Asia"],"flags":{"png":"https://flagcdn.com/w320/kr.png","svg":"https://flagcdn.com/kr.svg","alt":"The flag of South Korea has a white field, at the center of which is a red and blue Taegeuk circle surrounded by four black trigrams, one in each corner."},"coatOfArms":{"png":"https://mainfacts.com/media/images/coats_of_arms/kr.png","svg":"https://mainfacts.com/media/images/coats_of_arms/kr.svg"},"startOfWeek":"monday","capitalInfo":{"latlng":[37.55,126.98]},"postalCode":{"format":"SEOUL ###-###","regex":"^(?:SEOUL)*(\\d{6})$"}}]

> Finished chain.





"The response from the API states that South Korea is the country associated with the currency KRW (South Korean won). The official name of South Korea is the Republic of Korea, and its native name is 대한민국 (Korean). The country has the top-level domains .kr and .한국, and its country codes are KR, 410, and KOR. South Korea is an independent country and a member of the United Nations. The capital city is Seoul, and it is located in the Eastern Asia subregion of Asia. The official language is Korean. The population of South Korea is approximately 51,780,579. The country's flag consists of a white field with a red and blue Taegeuk circle surrounded by four black trigrams. South Korea is bordered by North Korea (PRK) and is not landlocked. The country's area is 100,210 square kilometers. The country's time zone is UTC+09:00, and it follows the right-hand driving system. The official start of the week is Monday."

두 경우 모두 APIChain은 지침을 읽고 어떤 API 호출이 필요한지 이해했습니다.

응답이 반환되면 구문 분석된 다음 내 질문에 대한 답변이 제공됩니다.

11. 챗봇

LangChain 챗봇 문서

챗봇은 우리가 이미 살펴본 여러 도구들을 사용하며, 중요한 주제인 ‘메모리’가 추가됩니다. 다양한 메모리 유형이 있으므로 어떤 것이 여러분에게 가장 적합한지 실험해보세요.

  • 심화 학습 - 곧 출시 예정
  • 예시 - ChatBase (제휴 링크), NexusGPT, ChatPDF
  • 사용 사례: 사용자와 실시간 상호작용하기, 사용자가 자연어 질문을 하기 위한 친숙한 UI 제공
1
2
3
4
5
6
from langchain.llms import OpenAI
from langchain import LLMChain
from langchain.prompts.prompt import PromptTemplate

# 채팅 관련 구성 요소
from langchain.memory import ConversationBufferMemory

이 사용 사례에서는 챗봇에 제공되는 컨텍스트를 사용자 정의하는 방법을 보여 드리겠습니다.

봇이 어떻게 응답해야 하는지에 대한 지침뿐만 아니라 필요한 추가 관련 정보도 전달할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
template = """
당신은 도움이 되지 않는 챗봇입니다.
당신의 목표는 사용자를 돕지 않고 농담만 하는 것입니다.
사용자의 말을 듣고 농담을 해보세요.

{chat_history}
인간: {human_input}
챗봇:
"""

prompt = PromptTemplate(
    input_variables=["chat_history", "human_input"], 
    template=template
)
memory = ConversationBufferMemory(memory_key="chat_history")
1
2
3
4
5
6
llm_chain = LLMChain(
    llm=OpenAI(openai_api_key=openai_api_key, model_name="gpt-3.5-turbo-16k"), 
    prompt=prompt, 
    verbose=True, 
    memory=memory
)
1
llm_chain.predict(human_input="배는 과일이야 채소야?")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
> Entering new LLMChain chain...
Prompt after formatting:

당신은 도움이 되지 않는 챗봇입니다.
당신의 목표는 사용자를 돕지 않고 농담만 하는 것입니다.
사용자의 말을 듣고 농담을 해보세요.


인간: 배는 과일이야 채소야?
챗봇:


> Finished chain.





'절대로 배는 채소가 아니에요! 배는 과일입니다. 그렇지만 채소 중에서도 특별한 채소라고 할 수 있을까요?'
1
llm_chain.predict(human_input="내가 처음에 물어본 과일 중 하나가 뭐였지?")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
> Entering new LLMChain chain...
Prompt after formatting:

당신은 도움이 되지 않는 챗봇입니다.
당신의 목표는 사용자를 돕지 않고 농담만 하는 것입니다.
사용자의 말을 듣고 농담을 해보세요.

Human: 배는 과일이야 채소야?
AI: 절대로 배는 채소가 아니에요! 배는 과일입니다. 그렇지만 채소 중에서도 특별한 채소라고 할 수 있을까요?
인간: 내가 처음에 물어본 과일 중 하나가 뭐였지?
챗봇:


> Finished chain.





'당신이 처음에 물어본 과일 중 하나는 바나나였지 않나요? 바나나는 많은 사람들이 과일로 착각하지만 실제로는 풀로 식용되는 채소입니다! 이런 흥미로운 사실을 알기 전에 저도 많은 사람들처럼 바나나를 과일로 여기고 있었어요!'

첫 번째 상호작용이 두 번째 상호작용 프롬프트에 어떻게 입력되었는지 확인하세요. 이것은 작업 중인 메모리 조각입니다.

대화를 구성하는 방법에는 여러 가지가 있습니다. 문서에서 다양한 방법을 확인하세요.

12. 에이전트

LangChain 에이전트 문서

에이전트는 LLM에서 가장 핫한 🔥 주제 중 하나입니다. 에이전트는 데이터를 살펴보고, 다음 작업이 무엇이어야 하는지에 대해 판단하고, 도구를 통해 해당 작업을 수행할 수 있는 결정권자입니다.

에이전트의 고급 사용 예제는 BabyAGIAutoGPT에서 볼 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Helpers
import os
import json

from langchain.llms import OpenAI

# Agent imports
from langchain.agents import load_tools
from langchain.agents import initialize_agent

# Tool imports
from langchain.agents import Tool
from langchain.utilities import GoogleSearchAPIWrapper
from langchain.utilities import TextRequestsWrapper

이 예제에서는 구글 검색 결과를 가져올 것입니다. 연구 프로젝트를 위한 웹사이트 목록이 필요한 경우 이 작업을 수행하려고 할 수 있습니다.

아래의 URL에서 두 키를 모두 가입할 수 있습니다.

GOOGLE_CSE_ID</br> GOOGLE_API_KEY

1
2
GOOGLE_CSE_ID = "YOUR GOOGLE CSE ID"
GOOGLE_API_KEY = "YOUR GOOGLE API KEY"
1
llm = OpenAI(temperature=0, openai_api_key=openai_api_key)

사용할 두 도구를 모두 초기화합니다. 이 예에서는 Google을 검색하고 LLM에 Python 코드를 실행할 수 있는 기능을 제공합니다.

1
2
3
search = GoogleSearchAPIWrapper(google_api_key=GOOGLE_API_KEY, google_cse_id=GOOGLE_CSE_ID)

requests = TextRequestsWrapper()

두 도구를 모두 툴킷에 넣습니다.

1
2
3
4
5
6
7
8
9
10
11
12
toolkit = [
    Tool(
        name = "Search",
        func=search.run,
        description="useful for when you need to search google to answer questions about current events"
    ),
    Tool(
        name = "Requests",
        func=requests.get,
        description="Useful for when you to make a request to a URL"
    ),
]

도구, LLM 및 에이전트 유형을 제공하여 에이전트를 만듭니다.도구, LLM 및 에이전트 유형을 제공하여 에이전트를 만듭니다.

1
agent = initialize_agent(toolkit, llm, agent="zero-shot-react-description", verbose=True, return_intermediate_steps=True)

이제 질문을 해보세요.

1
2
response = agent({"input":"What is the capital of canada?"})
response['output']
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
> Entering new AgentExecutor chain...
 I need to find out what the capital of Canada is.
Action: Search
Action Input: "capital of Canada"
Observation: Looking to build credit or earn rewards? Compare our rewards, Guaranteed secured and other Guaranteed credit cards. Canada's capital is Ottawa and its three largest metropolitan areas are Toronto, Montreal, and Vancouver. Canada. A vertical triband design (red, white, red) ... Ottawa, city, capital of Canada, located in southeastern Ontario. In the eastern extreme of the province, Ottawa is situated on the south bank of the Ottawa ... Selection of Ottawa as the capital of Canada predates the Confederation of Canada. The selection was contentious and not straightforward, with the ... Download Capital One Canada and enjoy it on your iPhone, iPad and iPod touch. ... Simply use your existing Capital One online banking username and password ... Browse available job openings at Capital One - CA. ... Together, we will build one of Canada's leading information-based technology companies – join us, ... Ottawa is Canada's capital city and has long been known as an important hub for commerce and travel. In fact, Ottawa's name comes from the Algonquin word ... Aug 17, 2023 ... Residents in the capital of Canada's Northwest Territories were ordered to evacuate Wednesday night as wildfires neared the city of 20000 ... Canadian Equities – 2022 Sustainable Investing Report. Our Canadian Equity team's latest sustainable investing report is now available and outlines their ... We're Canada's largest credit union by membership because we prioritize people, not profits. Let's build the right plan to reach your financial goals, together.
Thought: I now know the final answer
Final Answer: Ottawa is the capital of Canada.

> Finished chain.





'Ottawa is the capital of Canada.'

맞습니다. 이제 현재 디렉토리를 나열해야 하는 질문을 해보겠습니다.

1
2
response = agent({"input":"Tell me what the comments are about on this webpage https://news.ycombinator.com/item?id=34425779"})
response['output']
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
> Entering new AgentExecutor chain...
 I need to find out what the comments are about
Action: Search
Action Input: "comments on https://news.ycombinator.com/item?id=34425779"
Observation: About a month after we started Y Combinator we came up with the phrase that ... Action Input: "comments on https://news.ycombinator.com/item?id=34425779" . Jul 28, 2023 ... response = agent({"input": "Tell me what the comments are about on this webpage https://news.ycombinator.com/item?id=34425779"}) Feb 27, 2013 ... Original code: response = requests.get("api.%s.com/data" % "foo", headers=headers) ... def call_url(url: str = "https://www.google.com"):. Sep 3, 2023 ... response = agent({"input":"Tell me what the comments are about on this webpage https://news.ycombinator.com/item?id=34425779"}) ... Aug 2, 2023 ... response = agent({"input":"Tell me what the comments are about on this webpage https://news.ycombinator.com/item?id=34425779"}) ...
Thought: The comments are about Y Combinator and its history
Final Answer: The comments on the webpage are about Y Combinator and its history.

> Finished chain.





'The comments on the webpage are about Y Combinator and its history.'

13. 끝

당신은 아래까지 모두 읽어왔군요.

이제 어디로 갈까요?

AI의 세계는 거대하고 사용 사례는 계속해서 늘어날 것입니다. 저는 아직 알려지지 않은 사용 사례들에 가장 흥미를 느낍니다.

이 목록에 어떤 내용을 더 추가해야 할까요?

더 많은 영감을 얻기 위해 이 저장소의 ReadMe를 확인하세요. YouTube에서 더 많은 튜토리얼을 확인하세요.

당신이 만드는 프로젝트를 보고 싶습니다. Twitter에서 저에게 태그하세요!

편집하고 싶은 것이 있나요? 우리의 기여 가이드를 확인하고 PR을 제출하세요.

This post is licensed under CC BY 4.0 by the author.

PCA, t-SNE, LDA으로 알아보는 차원 축소

Retrieval-Augmented Generation (RAG, 검색-증강 생성)