Introduction
LangChain은 인공 지능(AI) 및 그 기계 학습 하위 집합으로 작업하는 소프트웨어 개발자가 대규모 언어 모델을 다른 외부 구성 요소와 결합하여 LLM 기반 애플리케이션을 개발할 수 있는 오픈 소스 프레임워크입니다. LangChain의 목표는 OpenAI의 GPT-3.5 및 GPT-4와 같은 강력한 LLM을 다양한 외부 데이터 소스에 연결하여 자연어 처리(NLP) 애플리케이션의 이점을 활용하고 생성하는 것입니다.
파이썬, 자바스크립트 또는 타입스크립트 프로그래밍 언어에 대한 경험이 있는 개발자, 소프트웨어 엔지니어, 데이터 과학자는 해당 언어로 제공되는 LangChain의 패키지를 사용할 수 있습니다. LangChain은 2022년 공동 창립자인 해리슨 체이스와 안쿠시 골라에 의해 오픈 소스 프로젝트로 시작되었으며, 같은 해에 초기 버전이 출시되었습니다.
LangChain은 생성형 AI 애플리케이션 인터페이스를 만드는 과정을 간소화하는 프레임워크입니다. 이러한 유형의 인터페이스를 개발하는 개발자는 다양한 도구를 사용하여 고급 NLP 앱을 만들지만, LangChain은 이 과정을 간소화합니다. 예를 들어, LLM은 대량의 빅 데이터에 액세스해야 하므로 LangChain은 이러한 대량의 데이터를 쉽게 액세스할 수 있도록 구성합니다.
LangChain 공식 문서에서는 LangChain에 대해서 아래와 같이 소개합니다.
LangChain is a framework for developing applications powered by language models. It enables applications that:
- Are context-aware: connect a language model to sources of context (prompt instructions, few shot examples, content to ground its response in, etc.)
- Reason: rely on a language model to reason (about how to answer based on provided context, what actions to take, etc.)
Component of LangChain
LangChain은 NLP 어플리케이션을 개발하기 위한 다양한 도구를 포함한 프레임워크입니다. LangChain 프레임워크는 NLP 어플리케이션을 개발하는 관점에서 뿐만 아니라, 운영하고 배포하는 등의 다양한 파트를 제공합니다:
- LangChain Libraries: 파이썬 및 자바스크립트 라이브러리. 수많은 구성 요소에 대한 인터페이스와 통합, 이러한 구성 요소를 체인 및 에이전트로 결합하기 위한 기본 런타임, 체인 및 에이전트의 상용 구현이 포함되어 있습니다.
- LangChain Template: 다양한 작업을 위해 쉽게 배포할 수 있는 참조 아키텍처 모음입니다.
- LangServe: LangChain 체인을 REST API로 배포하기 위한 라이브러리.
- LangSmith: 모든 LLM 프레임워크에 구축된 체인을 디버그, 테스트, 평가, 모니터링할 수 있는 개발자 플랫폼으로 LangChain과 원활하게 통합됩니다.
이러한 프레임워크 구성요소를 모두 사용하면 전체 애플리케이션 수명 주기를 간소화할 수 있습니다:
- Develop: LangChain/LangChain.js로 애플리케이션을 작성, 템플릿을 참조하여 바로 실행할 수 있음
- Productionize: LangSmith를 사용하여 체인을 검사, 테스트 및 모니터링하여 지속적으로 개선하고 자신 있게 배포할 수 있음
- Deploy: 구현된 모든 체인을 LangServe로 간단하게 API 전환 가능
LangChain Libraries
LangChain 프레임워크는 위에서 소개한대로, NLP 애플리케이션을 개발, 배포 및 운영하기 위한 다양한 컴포넌트를 제공합니다. 그 중에서 본 포스트 시리즈에서는 LangChain을 활용하여 NLP 애플리케이션을 개발하기 위한 LangChain Libraries에 초점을 맞추고자 합니다.
공식 문서에 소개하는 LangChain Libraries의 주요한 가치는 다음과 같습니다:
- Components: 언어 모델 작업을 위한 구성 가능한 도구 및 통합. 컴포넌트는 모듈식이며 다른 LangChain 프레임워크의 사용 여부와 관계없이 사용하기 쉽습니다. 기존 체인을 쉽게 사용자 정의하고 새로운 체인을 구축할 수 있습니다.
- Off-the-shelf chains: LangChain에는 다양한 chain들이 구현되어 built-in되어 있습니다. 이러한 off-the-shelf chain을 활용하여 NLP 애플리케이션 개발을 쉽게 시작할 수 있습니다.
LangChain Libraries 자체는 여러 가지 패키지로 구성되어 있습니다. 기본 추상화 및 LangChain Expression Language(LCEL)을 포함한 langchain-core, Third party integrations을 위한 langchain-community, 그리고 Chain, Agent, retrieval strategies 등의 애플리케이션을 구성하는 모듈을 포함하는 langchain 패키지가 있습니다.
그 중 LangChain 패키지는 NLP 어플리케이션을 원활하게 만들기 위해서 필요한 여러 모듈을 제공합니다:
- Model I/O: 이 모듈을 통해 LangChain은 모든 언어 모델과 상호 작용하고, 모델에 대한 입력을 관리하고 출력에서 정보를 추출하는 등의 작업을 수행할 수 있습니다.
- Data connection and retrieval: LLM이 액세스하는 데이터를 변환하여 데이터베이스에 저장하고 이 모듈을 사용한 쿼리를 통해 해당 데이터베이스에서 검색할 수 있습니다.
- Chain: LangChain을 사용하여 더 복잡한 애플리케이션을 구축할 때 다른 컴포넌트나 하나 이상의 LLM이 필요할 수 있습니다. 이 모듈은 여러 LLM을 다른 컴포넌트 또는 LLM과 연결합니다. 이를 LLM 체인이라고 합니다.
- Agent: 에이전트 모듈은 LLM이 문제를 해결하기 위해 취할 수 있는 최선의 단계나 조치를 결정할 수 있게 해줍니다. 에이전트 모듈은 일련의 복잡한 명령을 LLM 및 기타 도구에 오케스트레이션하여 특정 요청에 응답하도록 함으로써 이를 수행합니다.
- Memory: 메모리 모듈은 LLM이 사용자와의 상호작용의 맥락을 기억하는 데 도움이 됩니다. 특정 용도에 따라 단기 메모리와 장기 메모리를 모두 모델에 추가할 수 있습니다.
- Callback: LangChain은 LLM 애플리케이션의 다양한 단계에 연결할 수 있는 콜백 시스템을 제공합니다. 이는 로깅, 모니터링, 스트리밍 및 기타 작업에 유용합니다.
QuickStart
LangChain을 사용하면 외부 데이터 및 계산 소스를 LLM에 연결하는 애플리케이션을 구축할 수 있습니다. 이 QuickStart에서는 이를 수행하는 몇 가지 방법을 살펴보겠습니다. QuickStart에서는 다음 내용들을 다룹니다:
- LLM Chain: 먼저 프롬프트 템플릿의 정보에만 의존하여 응답하는 간단한 LLM 체인부터 시작하겠습니다.
- Retrival Chain: 다음으로, 별도의 데이터베이스에서 데이터를 가져와 프롬프트 템플릿에 전달하는 검색 체인을 구축하겠습니다.
- Converation Retrival Chain: 그런 다음 채팅 기록을 추가하여 대화 검색 체인을 만들 것입니다. 이렇게 하면 이 LLM과 채팅 방식으로 상호 작용할 수 있으므로 이전 질문을 기억할 수 있습니다.
- Agent: 마지막으로 LLM을 활용하여 질문에 답하기 위해 데이터를 가져와야 하는지 여부를 결정하는 에이전트를 구축합니다.
LLM Chain (OpenAI)
먼저 LangChain x OpenAI 통합 패키지를 설치합니다.
pip install langchain-openai
API에 액세스하려면 API 키가 필요합니다. API 키는 https://platform.openai.com/account/api-keys에서 계정을 만들고 얻을 수 있습니다. 키가 있으면 다음을 실행하여 이를 환경 변수로 설정합니다.
export OPENAI_API_KEY="..."
그러면 우리는 모델을 초기화할 수 있습니다.
from langchain_openai import ChatOpenAI
llm = ChatOpenAI()
초기화된 모델은 아래와 같이 invoke 함수를 통해 사용해볼 수 있습니다.
llm.invoke("how can langsmith help with testing?")
PromptTemplate은 raw user input을 LLM에 대한 더 나은 입력으로 변환하는 데 사용됩니다. 아래는 Chatbot에서 사용할 수 있는 프롬프트를 생성하기 위한 ChatPromptTemplate을 사용하여 user input을 입력받는 PromptTemplate입니다.
from langchain_core.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_messages([
("system", "You are world class technical documentation writer."),
("user", "{input}")
])
위에서 생성한 prompt와 llm을 아래와 같이 간단하게 chain으로 구성할 수 있습니다.
chain = prompt | llm
chain.invoke({"input": "how can langsmith help with testing?"})
또한 추가적으로 output에 대한 parsing이 필요하다면 OutputParser를 chain에 엮을 수 있습니다.
from langchain_core.output_parsers import StrOutputParser
output_parser = StrOutputParser()
chain = prompt | llm | output_parser
chain.invoke({"input": "how can langsmith help with testing?"})
LLM Chain (local)
OpenAI에서 제공하지 않거나, 로컬에서 모델을 띄워서 사용하고 싶은 경우에는 별도로 llm을 초기화합니다. langchain_community에서 이미 구현된 모델을 사용할 수 있습니다.
from langchain_community.llms import Ollama
llm = Ollama(model="llama2")
Retrieval Chain
이 과정에서 retrieval에서 관련 문서를 조회한 다음 프롬프트에 전달합니다. retrieval는 SQL 테이블, 인터넷 등 무엇이든 뒷받침할 수 있지만, 여기서는 벡터 저장소를 채우고 이를 retrieval로 사용하겠습니다.
먼저, 인덱싱하려는 데이터를 로드해야 합니다. 이를 위해 WebBaseLoader를 사용합니다.
from langchain_community.document_loaders import WebBaseLoader
loader = WebBaseLoader("https://docs.smith.langchain.com/overview")
docs = loader.load()
docs를 찍어보면 다음과 같은 내용이 로드된 것을 확인 할 수 있습니다.
그런 다음, 위에서 했던 것처럼 ollama embedding 모델을 로딩합니다.
from langchain_community.embeddings import OllamaEmbeddings
embeddings = OllamaEmbeddings()
여기서 우리는 준비된 별도의 vector store가 없기 때문에, local vector store로 faiss를 사용합니다.
위에서 로드한 docs를 text_splitter를 통해 여러 도큐먼트로 쪼개고, 쪼개진 도큐먼트를 ollama embedding 모델을 사용하여 벡터화한 뒤, faiss vector store에 index를 생성합니다.
from langchain_community.vectorstores import FAISS
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter()
documents = text_splitter.split_documents(docs)
vector = FAISS.from_documents(documents, embeddings)
이제 이 데이터가 vector store에 색인되었으므로 검색 체인을 만들겠습니다. 이 체인은 들어오는 질문을 받아 관련 문서를 조회한 다음, 그 문서를 원래 질문과 함께 LLM에 전달하고 원래 질문에 대한 답변을 요청합니다.
먼저 질문과 검색된 문서를 받아 답을 생성하는 체인을 설정해 보겠습니다.
from langchain.chains.combine_documents import create_stuff_documents_chain
prompt = ChatPromptTemplate.from_template("""Answer the following question based only on the provided context:
<context>
{context}
</context>
Question: {input}""")
document_chain = create_stuff_documents_chain(llm, prompt)
from langchain_core.documents import Document
document_chain.invoke({
"input": "how can langsmith help with testing?",
"context": [Document(page_content="langsmith can let you visualize test results")]
})
그러나 우리는 방금 설정한 retriever에서 먼저 문서를 가져오길 원합니다. 그렇게 하면 주어진 질문에 대해 검색기를 사용하여 가장 관련성이 높은 문서를 동적으로 선택하고 전달할 수 있습니다.
from langchain.chains import create_retrieval_chain
retriever = vector.as_retriever()
retrieval_chain = create_retrieval_chain(retriever, document_chain)
이제, 이 chain을 호출하면 아래와 같은 응답이 반환됩니다.
response = retrieval_chain.invoke({"input": "how can langsmith help with testing?"})
print(response["answer"])
# LangSmith offers several features that can help with testing:...
Conversation Retrieval Chain
지금까지 만든 체인은 단 하나의 질문에만 답할 수 있습니다. 사람들이 구축하는 LLM 애플리케이션의 주요 유형 중 하나는 챗봇입니다. 그렇다면 이 체인을 후속 질문에 답할 수 있는 체인으로 어떻게 바꾸는지 다루어 보겠습니다.
retrieval을 업데이트하기 위해 새 체인을 생성합니다. 이 체인은 가장 최근 입력(input)과 대화 기록(chat_history)을 받아들이고 LLM을 사용하여 search query를 생성합니다.
from langchain.chains import create_history_aware_retriever
from langchain_core.prompts import MessagesPlaceholder
# First we need a prompt that we can pass into an LLM to generate this search query
prompt = ChatPromptTemplate.from_messages([
MessagesPlaceholder(variable_name="chat_history"),
("user", "{input}"),
("user", "Given the above conversation, generate a search query to look up in order to get information relevant to the conversation")
])
retriever_chain = create_history_aware_retriever(llm, retriever, prompt)
사용자가 후속 질문을 하는 인스턴스를 전달하여 이를 테스트할 수 있습니다.
from langchain_core.messages import HumanMessage, AIMessage
chat_history = [HumanMessage(content="Can LangSmith help test my LLM applications?"), AIMessage(content="Yes!")]
retriever_chain.invoke({
"chat_history": chat_history,
"input": "Tell me how"
})
이제 이 새로운 retrieval가 있으므로 검색된 문서를 염두에 두고 대화를 계속하기 위해 새 체인을 만들 수 있습니다.
prompt = ChatPromptTemplate.from_messages([
("system", "Answer the user's questions based on the below context:\n\n{context}"),
MessagesPlaceholder(variable_name="chat_history"),
("user", "{input}"),
])
document_chain = create_stuff_documents_chain(llm, prompt)
retrieval_chain = create_retrieval_chain(retriever_chain, document_chain)
이제 이것을 end-to-end로 테스트 해볼 수 있습니다.
chat_history = [HumanMessage(content="Can LangSmith help test my LLM applications?"), AIMessage(content="Yes!")]
retrieval_chain.invoke({
"chat_history": chat_history,
"input": "Tell me how"
})
Agent
지금까지 각 단계가 미리 알려진 체인의 예를 만들었습니다. QuickStart에서 다루는 마지막 것은 LLM이 수행할 단계를 결정하는 에이전트입니다.
에이전트를 구축할 때 가장 먼저 해야 할 일 중 하나는 에이전트가 액세스할 수 있는 tool을 결정하는 것입니다. 이 예에서는 상담원에게 두 가지 tool에 대한 액세스 권한을 부여합니다.
먼저 방금 만든 retrieval에 대한 tool을 설정해 보겠습니다.
from langchain.tools.retriever import create_retriever_tool
retriever_tool = create_retriever_tool(
retriever,
"langsmith_search",
"Search for information about LangSmith. For any questions about LangSmith, you must use this tool!",
)
여기서 사용할 검색 tool은 Tavily입니다. 이를 위해서는 API 키가 필요하므로 해당 플랫폼에서 생성한 후 환경 변수로 설정해야 합니다.
from langchain_community.tools.tavily_search import TavilySearchResults
search = TavilySearchResults()
이제 tools 리스트를 만들 수 있습니다.
tools = [retriever_tool, search]
이제 tool이 있으므로 이를 사용할 에이전트를 만들 수 있습니다.
from langchain_openai import ChatOpenAI
from langchain import hub
from langchain.agents import create_openai_functions_agent
from langchain.agents import AgentExecutor
# Get the prompt to use - you can modify this!
prompt = hub.pull("hwchase17/openai-functions-agent")
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
agent = create_openai_functions_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
만들어진 agent_executor를 활용해 다음과 같이 응답을 실행할 수 있습니다.
# invokes
agent_executor.invoke({"input": "how can langsmith help with testing?"})
agent_executor.invoke({"input": "what is the weather in SF?"})
# conversations
chat_history = [HumanMessage(content="Can LangSmith help test my LLM applications?"), AIMessage(content="Yes!")]
agent_executor.invoke({
"chat_history": chat_history,
"input": "Tell me how"
})
'AI > 어플리케이션 개발' 카테고리의 다른 글
LangChain을 활용한 Tool Calling # 2 (3) | 2024.11.29 |
---|---|
LangChain을 활용한 Tool Calling # 1 (2) | 2024.11.29 |
LLM 어플리케이션에서의 Tool Calling: AI가 더 똑똑해지는 방법 (0) | 2024.11.29 |
LLM 애플리케이션 개발 훑어보기 - LangChain #3 Model I/O (1) | 2024.01.23 |
LLM 애플리케이션 개발 훑어보기 - LangChain #2 LangChain Expression Language (LCEL) (0) | 2024.01.22 |