반응형
블로그 이미지
개발자로서 현장에서 일하면서 새로 접하는 기술들이나 알게된 정보 등을 정리하기 위한 블로그입니다. 운 좋게 미국에서 큰 회사들의 프로젝트에서 컬설턴트로 일하고 있어서 새로운 기술들을 접할 기회가 많이 있습니다. 미국의 IT 프로젝트에서 사용되는 툴들에 대해 많은 분들과 정보를 공유하고 싶습니다.
솔웅

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

카테고리

Chapter 7. Custom Tools

2023. 11. 17. 15:21 | Posted by 솔웅


반응형

https://www.pinecone.io/learn/series/langchain/langchain-tools/

 

Building Custom Tools for LLM Agents | Pinecone

© Pinecone Systems, Inc. | San Francisco, CA Pinecone is a registered trademark of Pinecone Systems, Inc.

www.pinecone.io

 

Building Custom Tools for LLM Agents

 

Agents are one of the most powerful and fascinating approaches to using Large Language Models (LLMs). The explosion of interest in LLMs has made agents incredibly prevalent in AI-powered use cases.

 

에이전트는 LLM(대형 언어 모델) 사용에 대한 가장 강력하고 매력적인 접근 방식 중 하나입니다. LLM에 대한 관심이 폭발적으로 증가하면서 AI 기반 사용 사례에서 에이전트가 엄청나게 널리 보급되었습니다.

 

Using agents allows us to give LLMs access to tools. These tools present an infinite number of possibilities. With tools, LLMs can search the web, do math, run code, and more.

 

에이전트를 사용하면 LLM에게 도구에 대한 액세스 권한을 부여할 수 있습니다. 이러한 도구는 무한한 가능성을 제시합니다. LLM은 도구를 사용하여 웹 검색, 수학 수행, 코드 실행 등을 수행할 수 있습니다.

 

The LangChain library provides a substantial selection of prebuilt tools. However, in many real-world projects, we’ll often find that only so many requirements can be satisfied by existing tools. Meaning we must modify existing tools or build entirely new ones.

 

LangChain 라이브러리는 사전 구축된 다양한 도구를 제공합니다. 그러나 많은 실제 프로젝트에서 기존 도구로는 너무 많은 요구 사항만 충족할 수 있는 경우가 많습니다. 이는 기존 도구를 수정하거나 완전히 새로운 도구를 구축해야 함을 의미합니다.

 

This chapter will explore how to build custom tools for agents in LangChain. We’ll start with a couple of simple tools to help us understand the typical tool building pattern before moving on to more complex tools using other ML models to give us even more abilities like describing images.

 

이 장에서는 LangChain에서 에이전트를 위한 사용자 정의 도구를 구축하는 방법을 살펴보겠습니다. 이미지 설명과 같은 더 많은 기능을 제공하기 위해 다른 ML 모델을 사용하는 더 복잡한 도구로 넘어가기 전에 일반적인 도구 구축 패턴을 이해하는 데 도움이 되는 몇 가지 간단한 도구부터 시작하겠습니다.

 

https://youtu.be/q-HNphrWsDE?si=PlVzjgL9t_WISxYi

 

 

Building Tools

 

At their core, tools are objects that consume some input, typically in the format of a string (text), and output some helpful information as a string.

 

기본적으로 도구는 일반적으로 문자열(텍스트) 형식의 일부 입력을 사용하고 일부 유용한 정보를 문자열로 출력하는 개체입니다.

 

In reality, they are little more than a simple function that we’d find in any code. The only difference is that tools take input from an LLM and feed their output to an LLM.

 

실제로는 모든 코드에서 찾을 수 있는 간단한 함수에 지나지 않습니다. 유일한 차이점은 도구가 LLM에서 입력을 받아 해당 출력을 LLM에 공급한다는 것입니다.

 

With that in mind, tools are relatively simple. Fortunately, we can build tools for our agents in no time.

 

이를 염두에 두고 도구는 비교적 간단합니다. 다행히 상담원을 위한 도구를 즉시 구축할 수 있습니다.

 

(Follow along with the code notebook here!)

 

Simple Calculator Tool

We will start with a simple custom tool. The tool is a simple calculator that calculates a circle’s circumference based on the circle’s radius.

 

간단한 사용자 정의 도구부터 시작하겠습니다. 이 도구는 원의 반지름을 기준으로 원의 둘레를 계산하는 간단한 계산기입니다.

 

 

To create the tool, we do the following: 도구를 생성하려면 다음을 수행합니다.

 

from langchain.tools import BaseTool
from math import pi
from typing import Union
  

class CircumferenceTool(BaseTool):
      name = "Circumference calculator"
      description = "use this tool when you need to calculate a circumference using the radius of a circle"

    def _run(self, radius: Union[int, float]):
        return float(radius)*2.0*pi

    def _arun(self, radius: int):
        raise NotImplementedError("This tool does not support async")

 

이 코드는 langchain 패키지에서 상속받아 도구를 구현하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. from langchain.tools import BaseTool: BaseTool 클래스를 가져옵니다. 이 클래스는 도구를 만들 때 기본으로 상속해야 하는 클래스입니다.
  2. from math import pi: 원주율인 pi를 가져옵니다.
  3. from typing import Union: Union을 사용하여 여러 형식의 데이터를 받을 수 있도록 타입 힌트를 지정합니다.
  4. class CircumferenceTool(BaseTool):: BaseTool 클래스를 상속받아 새로운 도구 클래스인 CircumferenceTool을 정의합니다.
    • name = "Circumference calculator": 도구의 이름을 설정합니다.
    • description = "use this tool when you need to calculate a circumference using the radius of a circle": 도구의 설명을 설정합니다.
  5. def _run(self, radius: Union[int, float]):: 도구의 기능을 정의하는 _run 메서드입니다. 이 메서드는 주어진 반지름에 대한 원둘레를 계산합니다.
    • radius: Union[int, float]: 반지름을 나타내는 정수 또는 부동 소수점 숫자를 입력으로 받습니다.
    • return float(radius)*2.0*pi: 주어진 반지름에 2를 곱하고 원주율인 pi와 곱한 값을 반환합니다.
  6. def _arun(self, radius: int):: 비동기 실행을 지원하는 _arun 메서드를 정의합니다. 여기서는 구현되어 있지 않으며, NotImplementedError가 발생하도록 설정되어 있습니다. 이 도구는 비동기 실행을 지원하지 않습니다.

이렇게 구현된 CircumferenceTool 도구는 반지름을 입력으로 받아 해당 반지름을 사용하여 원주율을 곱한 값을 반환하는 간단한 기능을 수행하는 도구입니다.

 

Here we initialized our custom CircumferenceTool class using the BaseTool object from LangChain. We can think of the BaseTool as the required template for a LangChain tool.

 

여기에서는 LangChain의 BaseTool 개체를 사용하여 사용자 정의 CircumferenceTool 클래스를 초기화했습니다. BaseTool을 LangChain 도구에 필요한 템플릿으로 생각할 수 있습니다.

We have two attributes that LangChain requires to recognize an object as a valid tool. Those are the name and description parameters.

 

LangChain이 객체를 유효한 도구로 인식하는 데 필요한 두 가지 속성이 있습니다. 이는 이름 및 설명 매개변수입니다.

The description is a natural language description of the tool the LLM uses to decide whether it needs to use it. Tool descriptions should be very explicit on what they do, when to use them, and when not to use them.

 

설명( description )은 LLM이 도구 사용 여부를 결정하는 데 사용하는 도구에 대한 자연어 설명입니다. 도구 설명은 도구의 기능, 사용 시기, 사용하지 않는 시기를 매우 명확하게 설명해야 합니다.

In our description, we did not define when not to use the tool. That is because the LLM seemed capable of identifying when this tool is needed. Adding “when not to use it” to the description can help if a tool is overused.

 

설명( description  )에서는 도구를 사용하지 않는 시기를 정의하지 않았습니다. 이는 LLM이 언제 이 도구가 필요한지 식별할 수 있는 것처럼 보였기 때문입니다. 도구를 과도하게 사용하는 경우 설명에 "사용하지 않을 때 when not to use it "를 추가하면 도움이 될 수 있습니다.

Following this, we have two methods, _run and _arun. When a tool is used, the _run method is called by default. The _arun method is called when a tool is to be used asynchronously. We do not cover async tools in this chapter, so, for now, we initialize it with a NotImplementedError.

 

그 다음에는 _run과 _arun이라는 두 가지 메서드가 있습니다. 도구를 사용하면 기본적으로 _run 메서드가 호출됩니다. _arun 메서드는 도구를 비동기적으로 사용할 때 호출됩니다. 이 장에서는 비동기 도구를 다루지 않으므로 지금은 NotImplementedError로 초기화하겠습니다.

From here, we need to initialize the LLM and conversational memory for the conversational agent. For the LLM, we will use OpenAI’s gpt-3.5-turbo model. To use this, we need an OpenAI API key.

 

여기에서 대화 에이전트에 대한 LLM 및 대화 메모리를 초기화해야 합니다. LLM의 경우 OpenAI의 gpt-3.5-turbo 모델을 사용합니다. 이를 사용하려면 OpenAI API 키가 필요합니다.

When ready, we initialize the LLM and memory like so: 준비가 되면 다음과 같이 LLM과 메모리를 초기화합니다.

 

from langchain.chat_models import ChatOpenAI
from langchain.chains.conversation.memory import ConversationBufferWindowMemory


# initialize LLM (we use ChatOpenAI because we'll later define a `chat` agent)
llm = ChatOpenAI(
        openai_api_key="OPENAI_API_KEY",
        temperature=0,
        model_name='gpt-3.5-turbo'
)

# initialize conversational memory
conversational_memory = ConversationBufferWindowMemory(
        memory_key='chat_history',
        k=5,
        return_messages=True
)

 

이 코드는 langchain 패키지를 사용하여 ChatOpenAI 모델 및 대화형 메모리를 초기화하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. from langchain.chat_models import ChatOpenAI: langchain 패키지에서 ChatOpenAI 모델을 가져옵니다. 이 모델은 OpenAI의 GPT-3.5-turbo를 기반으로 한 대화형 언어 모델입니다.
  2. from langchain.chains.conversation.memory import ConversationBufferWindowMemory: 대화 기록을 저장하는 데 사용할 대화형 메모리 클래스인 ConversationBufferWindowMemory를 가져옵니다. 이 메모리는 대화 기록의 윈도우를 관리하여 최근 몇 번의 대화를 기억하고 유지합니다.
  3. llm = ChatOpenAI(openai_api_key="OPENAI_API_KEY", temperature=0, model_name='gpt-3.5-turbo'): ChatOpenAI 모델을 초기화합니다.
    • openai_api_key="OPENAI_API_KEY": OpenAI API 키를 설정합니다. 여기서는 실제 API 키로 대체되어야 합니다.
    • temperature=0: 모델의 창의성을 조절하는 온도를 설정합니다. 0으로 설정하면 가장 확실한 응답을 생성합니다.
    • model_name='gpt-3.5-turbo': 사용할 언어 모델의 이름을 설정합니다. 여기서는 GPT-3.5-turbo를 사용합니다.
  4. conversational_memory = ConversationBufferWindowMemory(memory_key='chat_history', k=5, return_messages=True): ConversationBufferWindowMemory를 초기화합니다.
    • memory_key='chat_history': 대화 기록을 저장하는 데 사용할 메모리의 키를 설정합니다.
    • k=5: 대화 기록의 윈도우 크기를 설정합니다. 최근 5개의 대화만 유지됩니다.
    • return_messages=True: 메모리에서 대화 메시지를 반환할지 여부를 설정합니다.

이렇게 초기화된 llm은 ChatOpenAI 모델을 나타내며, conversational_memory는 대화 기록을 저장하고 최근 몇 번의 대화를 기억하는 메모리입니다.

 

Here we initialize the LLM with a temperature of 0. A low temperature is useful when using tools as it decreases the amount of “randomness” or “creativity” in the generated text of the LLMs, which is ideal for encouraging it to follow strict instructions — as required for tool usage.

 

여기서는 LLM을 온도 0으로 초기화합니다. 낮은 온도는 도구를 사용할 때 LLM의 생성된 텍스트에서 "무작위성" 또는 "창의성"의 양을 줄여 엄격한 지침을 따르도록 장려하는 데 이상적이므로 유용합니다. — 도구 사용에 필요한 경우.

In the conversation_memory object, we set k=5 to “remember” the previous five human-AI interactions.

 

Conversation_memory 개체에서 이전 5개의 인간-AI 상호 작용을 "기억"하기 위해 k=5를 설정했습니다.

Now we initialize the agent itself. It requires the llm and conversational_memory to be already initialized. It also requires a list of tools to be used. We have one tool, but we still place it into a list.

 

이제 에이전트 자체를 초기화합니다. llm 및 Conversational_memory가 이미 초기화되어 있어야 합니다. 또한 사용할 도구 목록이 필요합니다. 하나의 도구가 있지만 여전히 목록에 넣습니다.

 

from langchain.agents import initialize_agent

tools = [CircumferenceTool()]

# initialize agent with tools
agent = initialize_agent(
    agent='chat-conversational-react-description',
    tools=tools,
    llm=llm,
    verbose=True,
    max_iterations=3,
    early_stopping_method='generate',
    memory=conversational_memory
)

 

이 코드는 langchain 패키지를 사용하여 ChatOpenAI 모델과 사용자 정의 도구인 CircumferenceTool을 통합하여 에이전트를 초기화하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. from langchain.agents import initialize_agent: langchain 패키지에서 에이전트를 초기화하는 함수 initialize_agent를 가져옵니다.
  2. tools = [CircumferenceTool()]: CircumferenceTool을 사용하여 도구 리스트를 생성합니다. 여기서는 원주율을 계산하는 도구 하나만 포함되어 있습니다.
  3. agent = initialize_agent(agent='chat-conversational-react-description', tools=tools, llm=llm, verbose=True, max_iterations=3, early_stopping_method='generate', memory=conversational_memory): 에이전트를 초기화합니다.
    • agent='chat-conversational-react-description': 초기화할 에이전트의 유형을 지정합니다. 여기서는 'chat-conversational-react-description'으로 설정되어 있습니다.
    • tools=tools: 사용할 도구 리스트를 설정합니다. 여기서는 원주율을 계산하는 도구를 사용합니다.
    • llm=llm: 사용할 언어 모델을 설정합니다. 여기서는 ChatOpenAI 모델(llm 변수에 저장된 것)을 사용합니다.
    • verbose=True: 초기화 및 실행 과정에서 발생하는 정보를 자세히 출력하도록 설정합니다.
    • max_iterations=3: 에이전트가 수행할 최대 반복 횟수를 설정합니다. 여기서는 최대 3회까지 반복합니다.
    • early_stopping_method='generate': 조기 종료 기능을 사용하는 방법을 설정합니다. 여기서는 'generate'로 설정되어 있어 생성된 결과를 기준으로 조기 종료합니다.
    • memory=conversational_memory: 대화 기록을 저장하는 데 사용할 메모리를 설정합니다. 여기서는 conversational_memory 변수에 저장된 것을 사용합니다.

이렇게 초기화된 agent는 ChatOpenAI 모델과 사용자 정의 도구를 통합하여 대화형 에이전트를 나타냅니다. 이 에이전트는 ChatOpenAI를 사용하여 대화하면서, 사용자 정의 도구를 사용하여 추가적인 계산을 수행할 수 있습니다.

 

The agent type of chat-conversation-react-description tells us a few things about this agent, those are:

 

chat-conversation-react-description의 에이전트 유형은 이 에이전트에 대한 몇 가지 정보를 알려줍니다.

chat means the LLM being used is a chat model. Both gpt-4 and gpt-3.5-turbo are chat models as they consume conversation history and produce conversational responses. A model like text-davinci-003 is not a chat model as it is not designed to be used this way.

 

chat은 사용 중인 LLM이 채팅 모델임을 의미합니다. gpt-4와 gpt-3.5-turbo는 모두 대화 기록을 사용하고 대화 응답을 생성하므로 채팅 모델입니다. text-davinci-003과 같은 모델은 이러한 방식으로 사용하도록 설계되지 않았기 때문에 채팅 모델이 아닙니다.

conversational means we will be including conversation_memory.

 

conversational 대화 메모리를 포함한다는 의미입니다.


react refers to the ReAct framework, which enables multi-step reasoning and tool usage by giving the model the ability to “converse with itself”.

 

React는 모델에 "자신과 대화 converse with itself "하는 기능을 제공하여 다단계 추론과 도구 사용을 가능하게 하는 ReAct 프레임워크를 나타냅니다.

description tells us that the LLM/agent will decide which tool to use based on their descriptions — which we created in the earlier tool definition.

 

설명 description 은 LLM/에이전트가 이전 도구 정의에서 작성한 설명을 기반으로 사용할 도구를 결정한다는 것을 알려줍니다.

With that all in place, we can ask our agent to calculate the circumference of a circle.

 

모든 것이 준비되면 에이전트에게 원의 둘레를 계산하도록 요청할 수 있습니다.

 

 

agent("can you calculate the circumference of a circle that has a radius of 7.81mm")
> Entering new AgentExecutor chain...
{
    "action": "Final Answer",
    "action_input": "The circumference of a circle with a radius of 7.81mm is approximately 49.03mm."
}

> Finished chain.

{'input': 'can you calculate the circumference of a circle that has a radius of 7.81mm',
 'chat_history': [],
 'output': 'The circumference of a circle with a radius of 7.81mm is approximately 49.03mm.'}

 

 

(7.81 * 2) * pi
49.071677249072565

 

로컬에서 실행한 결과는 아래와 같습니다.

 

 

 

Custom Tool을 이용하는 과정은 대충 이렇네요.

일단 Custom Tool을 만들고, LLM 을 초기화 하고, conversational memory도 초기화 하고 그 다음 agent까지 초기화 하면 사용할준비가 다 끝납니다.

 

agent() 를 실행하면 프롬프트 (질문) 을 받아 이것을 분석한 후 custom tool에 포맷을 맞추어서 보내고 그 툴에서 받은 값을 토대로 답변을 내 놓습니다.

 

정답은 49.0716.... 인데 이 툴을 거쳐서 LLM (ChatGPT) 이 답한 값은 49.06 입니다. 거의 정답이네요. 0.01 차이 입니다.

100% 정답은 아니네요. 이렇다면 완전 정밀도를 요하는 작업에는 사용하는데 리스크가 있을 것 같습니다. (아래 교재 내용을 보면 circumference calculator tool을 사용하지 않았다고 나오네요. 왜 그런지 계속 교재를 보겠습니다.)

 

The agent is close, but it isn’t accurate — something is wrong. We can see in the output of the AgentExecutor Chain that the agent jumped straight to the Final Answer action:

 

agent 근사치를 맞추었지만 정확하지 않습니다. 뭔가 잘못되었습니다. AgentExecutor 체인의 출력에서 에이전트가 최종 응답 작업으로 바로 이동한 것을 볼 수 있습니다.

  

{ "action": "Final Answer", "action_input": "The circumference of a circle with a radius of 7.81mm is approximately 49.03mm." }

 

The Final Answer action is what the agent uses when it has decided it has completed its reasoning and action steps and has all the information it needs to answer the user’s query. That means the agent decided not to use the circumference calculator tool.

 

최종 답변 작업은 추론 및 작업 단계를 완료했고 사용자의 쿼리에 응답하는 데 필요한 모든 정보를 가지고 있다고 판단했을 때 에이전트가 사용하는 작업입니다. 이는 상담사가 원주 계산기 도구를 사용하지 않기로 결정했다는 의미입니다.

 

LLMs are generally bad at math, but that doesn’t stop them from trying to do math. The problem is due to the LLM’s overconfidence in its mathematical ability. To fix this, we must tell the model that it cannot do math. First, let’s see the current prompt being used:

 

LLM은 일반적으로 수학에 약하지만 수학을 시도하는 것을 막지는 않습니다. 문제는 LLM의 수학적 능력에 대한 과신에서 비롯됩니다. 이 문제를 해결하려면 모델에 수학을 수행할 수 없다고 알려야 합니다. 먼저 현재 사용되는 프롬프트를 살펴보겠습니다.

 

# existing prompt
print(agent.agent.llm_chain.prompt.messages[0].prompt.template)
Assistant is a large language model trained by OpenAI.

Assistant is designed to be able to assist with a wide range of tasks, from answering simple questions to providing in-depth explanations and discussions on a wide range of topics. As a language model, Assistant is able to generate human-like text based on the input it receives, allowing it to engage in natural-sounding conversations and provide responses that are coherent and relevant to the topic at hand.

Assistant is constantly learning and improving, and its capabilities are constantly evolving. It is able to process and understand large amounts of text, and can use this knowledge to provide accurate and informative responses to a wide range of questions. Additionally, Assistant is able to generate its own text based on the input it receives, allowing it to engage in discussions and provide explanations and descriptions on a wide range of topics.

Overall, Assistant is a powerful system that can help with a wide range of tasks and provide valuable insights and information on a wide range of topics. Whether you need help with a specific question or just want to have a conversation about a particular topic, Assistant is here to assist.

 

로컬에서 실행한 화면 입니다.

 

 

참고로 번역본은 아래와 같습니다.

 

Assistant는 OpenAI로 훈련된 대규모 언어 모델입니다.

어시스턴트는 간단한 질문에 답변하는 것부터 광범위한 주제에 대한 심층적인 설명과 토론을 제공하는 것까지 광범위한 작업을 지원할 수 있도록 설계되었습니다. 언어 모델인 어시스턴트는 수신한 입력을 기반으로 인간과 유사한 텍스트를 생성하여 자연스러운 대화에 참여하고 현재 주제와 일관되고 관련 있는 응답을 제공할 수 있습니다.

어시스턴트는 지속적으로 학습하고 개선하며 기능도 지속적으로 발전하고 있습니다. 많은 양의 텍스트를 처리하고 이해할 수 있으며, 이 지식을 사용하여 광범위한 질문에 정확하고 유익한 답변을 제공할 수 있습니다. 또한 Assistant는 수신한 입력을 기반으로 자체 텍스트를 생성하여 토론에 참여하고 다양한 주제에 대한 설명과 설명을 제공할 수 있습니다.

전반적으로 Assistant는 광범위한 작업을 지원하고 광범위한 주제에 대한 귀중한 통찰력과 정보를 제공할 수 있는 강력한 시스템입니다. 특정 질문에 대한 도움이 필요하거나 특정 주제에 관해 대화를 나누고 싶을 때 어시스턴트가 도와드립니다.

 

We will add a single sentence that tells the model that it is “terrible at math” and should never attempt to do it.

 

모델에 "수학이 형편없다 terrible at math "고 절대 시도해서는 안 된다는 점을 알려주는 문장 하나를 추가하겠습니다.

Unfortunately, the Assistant is terrible at maths. When provided with math questions, no matter how simple, assistant always refers to its trusty tools and absolutely does NOT try to answer math questions by itself

 

불행히도 어시스턴트는 수학에 형편없습니다. 수학 질문이 제공되면 아무리 간단하더라도 어시스턴트는 항상 신뢰할 수 있는 도구를 참조하며 수학 질문에 스스로 대답하려고 하지 않습니다.

With this added to the original prompt text, we create a new prompt using agent.agent.create_prompt — this will create the correct prompt structure for our agent, including tool descriptions. Then, we update agent.agent.llm_chain.prompt.

 

원본 프롬프트 텍스트에 이를 추가한 후, agent.agent.create_prompt를 사용하여 새 프롬프트를 생성합니다. 이렇게 하면 도구 설명을 포함하여 에이전트에 대한 올바른 프롬프트 구조가 생성됩니다. 그런 다음 Agent.agent.llm_chain.prompt를 업데이트합니다.

 

sys_msg = """Assistant is a large language model trained by OpenAI.

Assistant is designed to be able to assist with a wide range of tasks, from answering simple questions to providing in-depth explanations and discussions on a wide range of topics. As a language model, Assistant is able to generate human-like text based on the input it receives, allowing it to engage in natural-sounding conversations and provide responses that are coherent and relevant to the topic at hand.

Assistant is constantly learning and improving, and its capabilities are constantly evolving. It is able to process and understand large amounts of text, and can use this knowledge to provide accurate and informative responses to a wide range of questions. Additionally, Assistant is able to generate its own text based on the input it receives, allowing it to engage in discussions and provide explanations and descriptions on a wide range of topics.

Unfortunately, Assistant is terrible at maths. When provided with math questions, no matter how simple, assistant always refers to it's trusty tools and absolutely does NOT try to answer math questions by itself

Overall, Assistant is a powerful system that can help with a wide range of tasks and provide valuable insights and information on a wide range of topics. Whether you need help with a specific question or just want to have a conversation about a particular topic, Assistant is here to assist.
"""
new_prompt = agent.agent.create_prompt(
    system_message=sys_msg,
    tools=tools
)

agent.agent.llm_chain.prompt = new_prompt

 

이 코드는 Assistant 에이전트의 시스템 메시지와 도구를 업데이트하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. sys_msg = """...""": Assistant 에이전트의 새로운 시스템 메시지를 정의합니다. 이 메시지는 Assistant에 대한 설명과 특징, 그리고 수학적인 질문에 대한 Assistant의 처리 방식에 대한 내용을 포함하고 있습니다.
  2. new_prompt = agent.agent.create_prompt(system_message=sys_msg, tools=tools): 에이전트의 새로운 프롬프트를 생성합니다.
    • system_message=sys_msg: 새로운 시스템 메시지를 지정합니다. 이를 통해 Assistant에 대한 사용자에게 전달되는 설명이 업데이트됩니다.
    • tools=tools: 새로운 도구를 지정합니다. 이를 통해 새로운 도구가 Assistant에게 사용 가능하게 됩니다.
  3. agent.agent.llm_chain.prompt = new_prompt: 에이전트의 언어 모델 체인의 프롬프트를 새로운 프롬프트로 업데이트합니다. 이를 통해 새로운 시스템 메시지와 도구가 적용되어 Assistant가 새로운 지침과 도구를 사용하여 상호 작용할 수 있게 됩니다.

이렇게 업데이트된 Assistant 에이전트는 새로운 시스템 메시지와 도구를 반영하여 사용자와의 상호 작용에 참여할 수 있습니다.

 

Now we can try again:  이제 다시 시도해 볼 수 있습니다.

 

agent("can you calculate the circumference of a circle that has a radius of 7.81mm")
> Entering new AgentExecutor chain...
```json
{
    "action": "Circumference calculator",
    "action_input": "7.81"
}
```
Observation: 49.071677249072565
Thought:```json
{
    "action": "Final Answer",
    "action_input": "The circumference of a circle with a radius of 7.81mm is approximately 49.07mm."
}
```

> Finished chain.

{'input': 'can you calculate the circumference of a circle that has a radius of 7.81mm',
 'chat_history': [HumanMessage(content='can you calculate the circumference of a circle that has a radius of 7.81mm', additional_kwargs={}),
  AIMessage(content='The circumference of a circle with a radius of 7.81mm is approximately 49.03mm.', additional_kwargs={})],
 'output': 'The circumference of a circle with a radius of 7.81mm is approximately 49.07mm.'}

 

We can see that the agent now uses the Circumference calculator tool and consequently gets the correct answer.

 

이제 에이전트가 둘레 계산기 도구를 사용하여 결과적으로 정답을 얻는 것을 볼 수 있습니다.

 

로컬에서 실행한 화면입니다.

 

action 부분에 CircumferenceTool() 함수에서 지정했던 name인 Circumference calculator 를 사용했다고 나오네요.

입력값은 7.81 이구요. 

아까는 로컬에서 답변이 49.06 이었는데 지금은 정확히 49.07로 나오네요.

 

Tools With Multiple Parameters

In the circumference calculator, we could only input a single value — the radius — more often than not, we will need multiple parameters.

 

원주 계산기에서는 단일 값(반지름)만 입력할 수 있었으며 대개 여러 매개변수가 필요합니다.

To demonstrate how to do this, we will build a Hypotenuse calculator. The tool will help us calculate the hypotenuse of a triangle given a combination of triangle side lengths and/or angles.

 

이를 수행하는 방법을 시연하기 위해 빗변 계산기를 작성하겠습니다. 이 도구는 삼각형 변의 길이 및/또는 각도의 조합이 주어지면 삼각형의 빗변을 계산하는 데 도움이 됩니다.

 

 

 

We want multiple inputs here because we calculate the triangle hypotenuse with different values (the sides and angle). Additionally, we don’t need all values. We can calculate the hypotenuse with any combination of two or more parameters.

 

여기서는 다양한 값(변과 각도)을 사용하여 빗변 삼각형을 계산하기 때문에 여러 입력이 필요합니다. 또한 모든 값이 필요하지는 않습니다. 두 개 이상의 매개변수를 조합하여 빗변을 계산할 수 있습니다.

 

We define our new tool like so: 우리는 새로운 도구를 다음과 같이 정의합니다.

 

from typing import Optional
from math import sqrt, cos, sin

desc = (
    "use this tool when you need to calculate the length of a hypotenuse"
    "given one or two sides of a triangle and/or an angle (in degrees). "
    "To use the tool, you must provide at least two of the following parameters "
    "['adjacent_side', 'opposite_side', 'angle']."
)

class PythagorasTool(BaseTool):
    name = "Hypotenuse calculator"
    description = desc
    
    def _run(
        self,
        adjacent_side: Optional[Union[int, float]] = None,
        opposite_side: Optional[Union[int, float]] = None,
        angle: Optional[Union[int, float]] = None
    ):
        # check for the values we have been given
        if adjacent_side and opposite_side:
            return sqrt(float(adjacent_side)**2 + float(opposite_side)**2)
        elif adjacent_side and angle:
            return adjacent_side / cos(float(angle))
        elif opposite_side and angle:
            return opposite_side / sin(float(angle))
        else:
            return "Could not calculate the hypotenuse of the triangle. Need two or more of `adjacent_side`, `opposite_side`, or `angle`."
    
    def _arun(self, query: str):
        raise NotImplementedError("This tool does not support async")

tools = [PythagorasTool()]

 

이 코드는 피타고라스의 정리를 사용하여 삼각형의 빗변 길이를 계산하는 도구(PythagorasTool)를 정의하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. desc = """...""": 도구의 설명을 포함하는 문자열을 정의합니다. 이 설명은 도구의 용도와 사용 방법에 대한 정보를 사용자에게 전달합니다.
  2. class PythagorasTool(BaseTool):: BaseTool 클래스를 상속하여 PythagorasTool 클래스를 정의합니다.
    • name = "Hypotenuse calculator": 도구의 이름을 설정합니다.
    • description = desc: 도구의 설명을 설정합니다.
  3. def _run(self, adjacent_side: Optional[Union[int, float]] = None, opposite_side: Optional[Union[int, float]] = None, angle: Optional[Union[int, float]] = None):: _run 메서드를 정의합니다. 이 메서드는 삼각형의 빗변 길이를 계산합니다.
    • adjacent_side, opposite_side, angle은 선택적 매개변수로 삼각형의 한 변의 길이나 각도를 입력받습니다.
    • 조건문을 사용하여 주어진 매개변수에 따라 다양한 경우의 수에 따라 빗변 길이를 계산합니다.
    • 최종적으로 계산된 빗변 길이를 반환합니다.
  4. def _arun(self, query: str):: 비동기적으로 실행되는 경우를 처리하기 위한 _arun 메서드를 정의합니다. 현재 도구는 비동기적 실행을 지원하지 않기 때문에 NotImplementedError를 발생시킵니다.
  5. tools = [PythagorasTool()]: PythagorasTool을 포함하는 도구 리스트를 생성합니다. 이 도구는 나중에 Assistant 에이전트에 추가될 수 있습니다.

이 도구는 삼각형의 빗변 길이를 계산하는 데 사용될 수 있으며, 주어진 변의 길이나 각도에 따라 적절한 계산을 수행합니다.

 
In the tool description, we describe the tool functionality in natural language and specify that to “use the tool, you must provide at least two of the following parameters [‘adjacent_side’, ‘opposite_side’, ‘angle’]". This instruction is all we need for gpt-3.5-turbo to understand the required input format for the function.
 
도구 설명에서는 도구 기능을 자연어로 설명하고 "도구를 사용하려면 다음 매개수 ['adjacent_side', 'opposite_side', 'angle'] 중 최소 2개를 제공해야 합니다"라고 지정합니다. 이 지침은 다음과 같습니다. 함수에 필요한 입력 형식을 이해하기 위해 gpt-3.5-turbo에 필요한 모든 것입니다.

As before, we must update the agent’s prompt. We don’t need to modify the system message as we did before, but we do need to update the available tools described in the prompt.

 

이전과 마찬가지로 상담원의 프롬프트를 업데이트해야 합니다. 이전처럼 시스템 메시지를 수정할 필요는 없지만 프롬프트에 설명된 사용 가능한 도구를 업데이트해야 합니다.

new_prompt = agent.agent.create_prompt(
    system_message=sys_msg,
    tools=tools
)

agent.agent.llm_chain.prompt = new_prompt

 

Unlike before, we must also update the agent.tools attribute with our new tools:

 

이전과 달리 새로운 도구로 Agent.tools 속성도 업데이트해야 합니다.

 

agent.tools = tools

 

이 코드는 Assistant 에이전트의 프롬프트, 시스템 메시지, 및 도구를 업데이트하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. new_prompt = agent.agent.create_prompt(system_message=sys_msg, tools=tools): Assistant 에이전트의 새로운 프롬프트를 생성합니다.
    • system_message=sys_msg: 업데이트할 새로운 시스템 메시지를 지정합니다. 이는 Assistant에 대한 설명 및 특징이 포함된 메시지입니다.
    • tools=tools: 업데이트할 새로운 도구 리스트를 지정합니다.
  2. agent.agent.llm_chain.prompt = new_prompt: Assistant 에이전트의 언어 모델 체인의 프롬프트를 새로운 프롬프트로 업데이트합니다. 이를 통해 새로운 시스템 메시지와 도구가 Assistant가 새로운 지침과 도구를 사용하여 상호 작용할 수 있게 됩니다.
  3. agent.Tools = tools: Assistant 에이전트의 도구를 업데이트합니다. 이 부분에서 오타가 있어서 "agent.Tools"가 "agent.tools"로 수정되어야 합니다. 새로운 도구 리스트를 Assistant 에이전트에 적용합니다.

이렇게 업데이트된 Assistant 에이전트는 새로운 프롬프트, 시스템 메시지, 및 도구를 반영하여 사용자와의 상호 작용에 참여할 수 있게 됩니다.

 

agent("If I have a triangle with two sides of length 51cm and 34cm, what is the length of the hypotenuse?")
> Entering new AgentExecutor chain...
WARNING:langchain.chat_models.openai:Retrying langchain.chat_models.openai.ChatOpenAI.comple
{
    "action": "Hypotenuse calculator",
    "action_input": {
        "adjacent_side": 34,
        "opposite_side": 51
    }
}
Observation: 61.29437168288782
Thought:{
    "action": "Final Answer",
    "action_input": "The length of the hypotenuse is approximately 61.29cm."
}

> Finished chain.

{'input': 'If I have a triangle with two sides of length 51cm and 34cm, what is the length of the hypotenuse?',
 'chat_history': [HumanMessage(content='can you calculate the circumference of a circle that has a radius of 7.81mm', additional_kwargs={}),
  AIMessage(content='The circumference of a circle with a radius of 7.81mm is approximately 49.03mm.', additional_kwargs={}),
  HumanMessage(content='can you calculate the circumference of a circle that has a radius of 7.81mm', additional_kwargs={}),
  AIMessage(content='The circumference of a circle with a radius of 7.81mm is approximately 49.07mm.', additional_kwargs={})],
 'output': 'The length of the hypotenuse is approximately 61.29cm.'}

 

The agent correctly identifies the correct parameters and passes them to our tool. We can try again with different parameters:

 

에이전트는 올바른 매개변수를 올바르게 식별하고 이를 도구에 전달합니다. 다른 매개변수를 사용하여 다시 시도할 수 있습니다.

 

로컬에서 실행한 결과 입니다.

 

 

로컬의 결과는 좀 다르네요.

다시 돌려보니까 아래와 같이 나옵니다.

 

 

action action_input은 제대로 설정이 됐는데 답변은 다르게 나옵니다. 

뭔가 기능이 좀 불안정하네요.

 

다음 프롬프트를 실행해 보겠습니다.

 

agent("If I have a triangle with the opposite side of length 51cm and an angle of 20 deg, what is the length of the hypotenuse?")
> Entering new AgentExecutor chain...
{
    "action": "Hypotenuse calculator",
    "action_input": {
        "opposite_side": 51,
        "angle": 20
    }
}
Observation: 55.86315275680817
Thought:{
    "action": "Final Answer",
    "action_input": "The length of the hypotenuse is approximately 55.86cm."
}

> Finished chain.
{'input': 'If I have a triangle with the opposite side of length 51cm and an angle of 20 deg, what is the length of the hypotenuse?',
 'chat_history': [HumanMessage(content='can you calculate the circumference of a circle that has a radius of 7.81mm', additional_kwargs={}),
  AIMessage(content='The circumference of a circle with a radius of 7.81mm is approximately 49.03mm.', additional_kwargs={}),
  HumanMessage(content='can you calculate the circumference of a circle that has a radius of 7.81mm', additional_kwargs={}),
  AIMessage(content='The circumference of a circle with a radius of 7.81mm is approximately 49.07mm.', additional_kwargs={}),
  HumanMessage(content='If I have a triangle with two sides of length 51cm and 34cm, what is the length of the hypotenuse?', additional_kwargs={}),
  AIMessage(content='The length of the hypotenuse is approximately 61.29cm.', additional_kwargs={})],
 'output': 'The length of the hypotenuse is approximately 55.86cm.'}

 

Again, we see correct tool usage. Even with our short tool description, the agent can consistently use the tool as intended and with multiple parameters.

 

다시 한 번 올바른 도구 사용을 확인합니다. 간단한 도구 설명을 통해서도 상담원은 여러 매개변수를 사용하여 의도한 대로 도구를 일관되게 사용할 수 있습니다.

 

로컬에서 실행한 결과 입니다.

 

 

이번에도 제대로 작동을 하지 않네요. 분명히 action과 action_input을 보면 제대로 설정이 돼 있는데요.

일단 Custom tool을 제대로 사용하는 것 같은데  무엇이 잘 못 됐을 까요.

 

PythagorasTool()을 아래와 같이 바꿔 보았습니다.

 

from typing import Optional
from math import sqrt, cos, sin

desc = (
    "use this tool when you need to calculate the length of a hypotenuse"
    "given one or two sides of a triangle and/or an angle (in degrees). "
    "To use the tool, you must provide at least two of the following parameters "
    "['adjacent_side', 'opposite_side', 'angle']."
)

class PythagorasTool(BaseTool):
    name = "Hypotenuse calculator"
    description = desc
    
    def _run(
        self,
        adjacent_side: Optional[Union[int, float]] = None,
        opposite_side: Optional[Union[int, float]] = None,
        angle: Optional[Union[int, float]] = None
    ):
        print("length 1" , adjacent_side)
        print("length 2" , opposite_side)
        # check for the values we have been given
        if adjacent_side and opposite_side:
            print("1111111")
            return sqrt(float(adjacent_side)**2 + float(opposite_side)**2)
        elif adjacent_side and angle:
            print("22222222")
            return adjacent_side / cos(float(angle))
        elif opposite_side and angle:
            print("333333333")
            return opposite_side / sin(float(angle))
        else:
            print("444444444")
            return "Could not calculate the hypotenuse of the triangle. Need two or more of `adjacent_side`, `opposite_side`, or `angle`."
    
    def _arun(self, query: str):
        raise NotImplementedError("This tool does not support async")

tools = [PythagorasTool()]

 

그리고 첫번째 프롬프트를 실행했습니다.

 

 

문제는 입력값을 제대로 처리하지 못하네요. 

 

action_input이 아래 처럼 돼야 되는데

 

 "action_input": {
        "adjacent_side": 34,
        "opposite_side": 51
    }

 

로컬에서는 이렇게 됩니다.

"action_input": "adjacent_side=51,opposite_side=34"

 

그래서 입력값이 한개이기 때문에 정보가 부족하다고 나오네요.

 

 

이렇게 되면 PythagorasTool() 함수를 조금 고쳐야 되겠는데요.

아래와 같이 고쳤습니다.

 

from typing import Optional
from math import sqrt, cos, sin

desc = (
    "use this tool when you need to calculate the length of a hypotenuse"
    "given one or two sides of a triangle and/or an angle (in degrees). "
    "To use the tool, you must provide at least two of the following parameters "
    "['adjacent_side', 'opposite_side', 'angle']."
)

class PythagorasTool(BaseTool):
    name = "Hypotenuse calculator"
    description = desc
    
    def _run(
        self,
        adjacent_side: Optional[Union[int, float]] = None,
        opposite_side: Optional[Union[int, float]] = None,
        angle: Optional[Union[int, float]] = None
    ):
        # Split the string by commas to get individual assignments
        assignments = adjacent_side.split(',')

        # Iterate through assignments to find values for each variable
        for assignment in assignments:
            variable, value = assignment.split('=')
    
            if variable == 'adjacent_side':
                adjacent_side = int(value)
            elif variable == 'opposite_side':
                opposite_side = int(value)
            elif variable == 'angle':
                angle = int(value)

        # check for the values we have been given
        if adjacent_side and opposite_side:
            return sqrt(float(adjacent_side)**2 + float(opposite_side)**2)
        elif adjacent_side and angle:
            return adjacent_side / cos(float(angle))
        elif opposite_side and angle:
            return opposite_side / sin(float(angle))
        else:
            return "Could not calculate the hypotenuse of the triangle. Need two or more of `adjacent_side`, `opposite_side`, or `angle`."
    
    def _arun(self, query: str):
        raise NotImplementedError("This tool does not support async")

tools = [PythagorasTool()]

 

이러니까 답이 제대로 나오네요.

 

 

일단 정답이 나오게는 했는데 이게 제대로 된 방법일지는 모르겠습니다.

이것이 실전이라면 프롬프트 부터 Custom tool 함수 등등을 다시 설계해야 될 것 같습니다.

 

More Advanced Tool Usage

 

We’ve seen two examples of custom tools. In most scenarios, we’d likely want to do something more powerful — so let’s give that a go.

 

우리는 사용자 정의 도구의 두 가지 예를 살펴보았습니다. 대부분의 시나리오에서 우리는 좀 더 강력한 것을 원할 것입니다. 그러니 한번 시도해 보겠습니다.

Taking inspiration from the HuggingGPT paper [1], we will take an existing open-source expert model that has been trained for a specific task that our LLM cannot do.

 

HuggingGPT 논문[1]에서 영감을 얻어 LLM이 수행할 수 없는 특정 작업에 대해 훈련된 기존 오픈 소스 전문가 모델을 사용하겠습니다.

That model will be the Salesforce/blip-image-captioning-large model hosted on Hugging Face. This model takes an image and describes it, something that we cannot do with our LLM.

 

해당 모델은 Hugging Face에서 호스팅되는 Salesforce/blip-image-captioning-large 모델이 됩니다. 이 모델은 이미지를 가져와 설명하는데, 이는 LLM으로는 할 수 없는 일입니다.

To begin, we need to initialize the model like so:

 

시작하려면 다음과 같이 모델을 초기화해야 합니다.

 

torch 모듈을 사용하려면 torchvision을 인스톨 해야 합니다.

!pip install torchvision

 

# !pip install transformers
import torch
from transformers import BlipProcessor, BlipForConditionalGeneration


# specify model to be used
hf_model = "Salesforce/blip-image-captioning-large"
# use GPU if it's available
device = 'cuda' if torch.cuda.is_available() else 'cpu'

# preprocessor will prepare images for the model
processor = BlipProcessor.from_pretrained(hf_model)
# then we initialize the model itself
model = BlipForConditionalGeneration.from_pretrained(hf_model).to(device)

 

The process we will follow here is as follows: 여기서 우리가 따라야 할 프로세스는 다음과 같습니다.

  1. Download an image.  이미지를 다운로드하세요.
  2. Open it as a Python PIL object (an image datatype).  Python PIL 객체(이미지 데이터 유형)로 엽니다.
  3. Resize and normalize the image using the processor.  프로세서를 사용하여 이미지 크기를 조정하고 정규화합니다.
  4. Create a caption using the model. 모델을 사용하여 캡션을 만듭니다.

Let’s start with steps one and two:  1단계와 2단계부터 시작해 보겠습니다.

 

import requests
from PIL import Image

img_url = 'https://images.unsplash.com/photo-1616128417859-3a984dd35f02?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2372&q=80' 
image = Image.open(requests.get(img_url, stream=True).raw).convert('RGB')
image

 

 

With this, we’ve downloaded an image of a young orangutan sitting in a tree. We can go ahead and see what the predicted caption for this image is:

 

이를 통해 우리는 나무에 앉아 있는 어린 오랑우탄의 이미지를 다운로드했습니다. 계속해서 이 이미지의 예상 캡션이 무엇인지 확인할 수 있습니다.

로컬에서도 아주 잘 되네요.

 

# unconditional image captioning
inputs = processor(image, return_tensors="pt").to(device)

out = model.generate(**inputs, max_new_tokens=20)
print(processor.decode(out[0], skip_special_tokens=True))
there is a monkey that is sitting in a tree

 

Although an orangutan isn’t technically a monkey, this is still reasonably accurate. Our code works. Now let’s distill these steps into a tool our agent can use.

 

오랑우탄은 엄밀히 말하면 원숭이는 아니지만 이는 여전히 합리적으로 정확합니다. 우리 코드는 작동합니다. 이제 이러한 단계를 에이전트가 사용할 수 있는 도구로 추출해 보겠습니다.

 

desc = (
    "use this tool when given the URL of an image that you'd like to be "
    "described. It will return a simple caption describing the image."

)

class ImageCaptionTool(BaseTool):
    name = "Image captioner"
    description = desc
    
    def _run(self, url: str):
        # download the image and convert to PIL object
        image = Image.open(requests.get(img_url, stream=True).raw).convert('RGB')
        # preprocess the image
        inputs = processor(image, return_tensors="pt").to(device)
        # generate the caption
        out = model.generate(**inputs, max_new_tokens=20)
        # get the caption
        caption = processor.decode(out[0], skip_special_tokens=True)
        return caption
    
    def _arun(self, query: str):
        raise NotImplementedError("This tool does not support async")

tools = [ImageCaptionTool()]

 

We reinitialize our agent prompt (removing the now unnecessary “you cannot do math” instruction) and set the tools attribute to reflect the new tools list:

 

에이전트 프롬프트를 다시 초기화하고(이제는 불필요한 "수학을 할 수 없습니다" 명령을 제거함) 새 도구 목록을 반영하도록 도구 속성을 설정합니다.

 

sys_msg = """Assistant is a large language model trained by OpenAI.

Assistant is designed to be able to assist with a wide range of tasks, from answering simple questions to providing in-depth explanations and discussions on a wide range of topics. As a language model, Assistant is able to generate human-like text based on the input it receives, allowing it to engage in natural-sounding conversations and provide responses that are coherent and relevant to the topic at hand.

Assistant is constantly learning and improving, and its capabilities are constantly evolving. It is able to process and understand large amounts of text, and can use this knowledge to provide accurate and informative responses to a wide range of questions. Additionally, Assistant is able to generate its own text based on the input it receives, allowing it to engage in discussions and provide explanations and descriptions on a wide range of topics.

Overall, Assistant is a powerful system that can help with a wide range of tasks and provide valuable insights and information on a wide range of topics. Whether you need help with a specific question or just want to have a conversation about a particular topic, Assistant is here to assist.
"""

new_prompt = agent.agent.create_prompt(
    system_message=sys_msg,
    tools=tools
)
agent.agent.llm_chain.prompt = new_prompt

# update the agent tools
agent.tools = tools

 

 

Now we can go ahead and ask our agent to describe the same image as above, passing its URL into the query.

 

이제 에이전트에게 위와 동일한 이미지를 설명하고 해당 URL을 쿼리에 전달하도록 요청할 수 있습니다.

 

agent(f"What does this image show?\n{img_url}")
> Entering new AgentExecutor chain...
{
    "action": "Image captioner",
    "action_input": "https://images.unsplash.com/photo-1616128417859-3a984dd35f02?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2372&q=80"
}
Observation: there is a monkey that is sitting in a tree
Thought:{
    "action": "Final Answer",
    "action_input": "There is a monkey that is sitting in a tree."
}

> Finished chain.
{'input': 'What does this image show?\nhttps://images.unsplash.com/photo-1616128417859-3a984dd35f02?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2372&q=80',
 'chat_history': [HumanMessage(content='can you calculate the circumference of a circle that has a radius of 7.81mm', additional_kwargs={}),
  AIMessage(content='The circumference of a circle with a radius of 7.81mm is approximately 49.03mm.', additional_kwargs={}),
  HumanMessage(content='can you calculate the circumference of a circle that has a radius of 7.81mm', additional_kwargs={}),
  AIMessage(content='The circumference of a circle with a radius of 7.81mm is approximately 49.07mm.', additional_kwargs={}),
  HumanMessage(content='If I have a triangle with two sides of length 51cm and 34cm, what is the length of the hypotenuse?', additional_kwargs={}),
  AIMessage(content='The length of the hypotenuse is approximately 61.29cm.', additional_kwargs={})],
 'output': 'There is a monkey that is sitting in a tree.'}

 

로컬에서는 아래와 같이 나옵니다.

 

 

 

Let’s try some more: 좀 더 시도해 봅시다:

 

 

img_url = "https://images.unsplash.com/photo-1502680390469-be75c86b636f?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2370&q=80"
agent(f"what is in this image?\n{img_url}")
> Entering new AgentExecutor chain...
{
    "action": "Image captioner",
    "action_input": "https://images.unsplash.com/photo-1502680390469-be75c86b636f?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2370&q=80"
}
Observation: surfer riding a wave in the ocean on a clear day
Thought:{
    "action": "Final Answer",
    "action_input": "The image shows a surfer riding a wave in the ocean on a clear day."
}

> Finished chain.
{'input': 'what is in this image?\nhttps://images.unsplash.com/photo-1502680390469-be75c86b636f?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2370&q=80',
 'chat_history': [HumanMessage(content='can you calculate the circumference of a circle that has a radius of 7.81mm', additional_kwargs={}),
  AIMessage(content='The circumference of a circle with a radius of 7.81mm is approximately 49.03mm.', additional_kwargs={}),
  HumanMessage(content='can you calculate the circumference of a circle that has a radius of 7.81mm', additional_kwargs={}),
  AIMessage(content='The circumference of a circle with a radius of 7.81mm is approximately 49.07mm.', additional_kwargs={}),
  HumanMessage(content='If I have a triangle with two sides of length 51cm and 34cm, what is the length of the hypotenuse?', additional_kwargs={}),
  AIMessage(content='The length of the hypotenuse is approximately 61.29cm.', additional_kwargs={}),
  HumanMessage(content='What does this image show?\nhttps://images.unsplash.com/photo-1616128417859-3a984dd35f02?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2372&q=80', additional_kwargs={}),
  AIMessage(content='There is a monkey that is sitting in a tree.', additional_kwargs={})],
 'output': 'The image shows a surfer riding a wave in the ocean on a clear day.'}

 

로컬에서 나온 결과 입니다.

 

 

That is another accurate description. Let’s try something more challenging:

 

그것은 또 다른 정확한 설명입니다. 좀 더 어려운 것을 시도해 보겠습니다.

 

 

img_url = "https://images.unsplash.com/photo-1680382948929-2d092cd01263?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2365&q=80"
agent(f"what is in this image?\n{img_url}")
> Entering new AgentExecutor chain...
```json
{
    "action": "Image captioner",
    "action_input": "https://images.unsplash.com/photo-1680382948929-2d092cd01263?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2365&q=80"
}
```
Observation: there is a lizard that is sitting on a tree branch in the water
Thought:```json
{
    "action": "Final Answer",
    "action_input": "There is a lizard that is sitting on a tree branch in the water."
}
```

> Finished chain.
{'input': 'what is in this image?\nhttps://images.unsplash.com/photo-1680382948929-2d092cd01263?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2365&q=80',
 'chat_history': [HumanMessage(content='can you calculate the circumference of a circle that has a radius of 7.81mm', additional_kwargs={}),
  AIMessage(content='The circumference of a circle with a radius of 7.81mm is approximately 49.03mm.', additional_kwargs={}),
  HumanMessage(content='can you calculate the circumference of a circle that has a radius of 7.81mm', additional_kwargs={}),
  AIMessage(content='The circumference of a circle with a radius of 7.81mm is approximately 49.07mm.', additional_kwargs={}),
  HumanMessage(content='If I have a triangle with two sides of length 51cm and 34cm, what is the length of the hypotenuse?', additional_kwargs={}),
  AIMessage(content='The length of the hypotenuse is approximately 61.29cm.', additional_kwargs={}),
  HumanMessage(content='What does this image show?\nhttps://images.unsplash.com/photo-1616128417859-3a984dd35f02?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2372&q=80', additional_kwargs={}),
  AIMessage(content='There is a monkey that is sitting in a tree.', additional_kwargs={}),
  HumanMessage(content='what is in this image?\nhttps://images.unsplash.com/photo-1502680390469-be75c86b636f?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2370&q=80', additional_kwargs={}),
  AIMessage(content='The image shows a surfer riding a wave in the ocean on a clear day.', additional_kwargs={})],
 'output': 'There is a lizard that is sitting on a tree branch in the water.'}

 

Slightly inaccurate with lizard rather than crocodile, but otherwise, the caption is good.

 

악어보다는 도마뱀에 대한 내용이 약간 부정확하지만 그 외에는 캡션이 좋습니다.

 

제 로컬 결과 입니다.

 


We’ve explored how to build custom tools for LangChain agents. A functionality that allows us to expand what is possible with Large Language Models massively.

 

우리는 LangChain 에이전트를 위한 맞춤형 도구를 구축하는 방법을 살펴보았습니다. 대규모 언어 모델로 가능한 것을 대규모로 확장할 수 있는 기능입니다.

 

In our simple examples, we saw the typical structure of LangChain tools before moving on to adding expert models as tools, with our agent as the controller of these models.

 

간단한 예에서 우리는 에이전트를 이러한 모델의 컨트롤러로 사용하여 전문가 모델을 도구로 추가하기 전에 LangChain 도구의 일반적인 구조를 확인했습니다.

 

Naturally, there is far more we can do than what we’ve shown here. Tools can be used to integrate with an endless list of functions and services or to communicate with an orchestra of expert models, as demonstrated by HuggingGPT.

 

당연히 여기에 표시된 것보다 훨씬 더 많은 작업을 수행할 수 있습니다. 도구는 HuggingGPT에서 시연된 것처럼 끝없는 기능 및 서비스 목록과 통합하거나 전문 모델 오케스트라와 통신하는 데 사용할 수 있습니다.

 

We can often use LangChain’s default tools for running SQL queries, performing calculations, or doing vector search. However, when these default tools cannot satisfy our requirements, we now know how to build our own.

 

우리는 종종 SQL 쿼리 실행, 계산 수행 또는 벡터 검색 수행을 위해 LangChain의 기본 도구를 사용할 수 있습니다. 그러나 이러한 기본 도구가 우리의 요구 사항을 충족할 수 없는 경우 이제 우리는 자체 도구를 만드는 방법을 알고 있습니다.

 

References

[1] Y. Shen, K. Song, et al., HuggingGPT: Solving AI Tasks with ChatGPT and its Friends in HuggingFace (2023)

반응형

Chapter 6. AI Agents

2023. 11. 16. 02:13 | Posted by 솔웅


반응형

https://www.pinecone.io/learn/series/langchain/langchain-agents/

 

Superpower LLMs with Conversational Agents | Pinecone

Large Language Models (LLMs) are incredibly powerful, yet they lack particular abilities that the “dumbest” computer programs can handle with ease. Logic, calculation, and search are examples of where computers typically excel, but LLMs struggle. Compu

www.pinecone.io

 

Superpower LLMs with Conversational Agents

 

Large Language Models (LLMs) are incredibly powerful, yet they lack particular abilities that the “dumbest” computer programs can handle with ease. Logic, calculation, and search are examples of where computers typically excel, but LLMs struggle.

 

LLM(대형 언어 모델)은 믿을 수 없을 만큼 강력하지만 "가장 멍청한" 컴퓨터 프로그램이 쉽게 처리할 수 있는 특정 기능이 부족합니다. 논리, 계산 및 검색은 일반적으로 컴퓨터가 뛰어나지만 LLM은 어려움을 겪는 예입니다.

Computers can solve incredibly complex math problems, yet if we ask GPT-4 to tell us the answer to 4.1 * 7.9, it fails:

 

컴퓨터는 엄청나게 복잡한 수학 문제를 해결할 수 있지만 GPT-4에게 4.1 * 7.9에 대한 답을 알려달라고 요청하면 실패합니다.

 

Asking GPT-4 to perform a simple calculation often results in an incorrect answer. A simple calculator can perform this same calculation without issue.  GPT-4에 간단한 계산을 요청하면 잘못된 대답이 나오는 경우가 많습니다. 간단한 계산기를 사용하면 문제 없이 동일한 계산을 수행할 수 있습니다.

 

According to a simple calculator, the answer is 19.357, rounded to three decimal places. Isn’t it fascinating that a simple calculator program can do this, but an incredibly sophisticated AI engine fails?

 

간단한 계산기를 사용하면 답은 19.357이며 소수점 이하 세 자리까지 반올림됩니다. 간단한 계산기 프로그램으로 이런 일을 할 수 있는데 믿을 수 없을 정도로 정교한 AI 엔진이 실패한다는 것이 흥미롭지 않나요?

That’s not all. If I ask GPT-4, “How do I use the LLMChain in LangChain?” it struggles again:

 

그게 다가 아닙니다. GPT-4에게 “LangChain에서 LLMChain을 어떻게 사용하나요?”라고 묻는다면, 다시 어려움을 겪습니다.

 

The LangChain spoken about here isn’t the LangChain we know. It’s an old blockchain project. The response is both outdated and full of false information. 여기서 말하는 LangChain은 우리가 알고 있는 LangChain이 아닙니다. 오래된 블록체인 프로젝트입니다. 응답은 오래되었으며 잘못된 정보로 가득 차 있습니다.

 

It’s true that LangChain was a blockchain project [1] [2]. Yet, there didn’t seem to be any “LLMChain” component nor “LANG tokens” — these are both hallucinations.

 

LangChain이 블록체인 프로젝트였던 것은 사실입니다 [1] [2]. 그러나 "LLMChain" 구성 요소나 "LANG 토큰"은 없는 것 같습니다. 둘 다 환상 hallucinations 입니다.

The reason GPT-4 is unable to tell us about LangChain is that it has no connection to the outside world. Its only knowledge is what it captured from its training data, which cuts off in late 2021.

 

GPT-4가 LangChain에 대해 우리에게 말할 수 없는 이유는 LangChain이 외부 세계와 연결되어 있지 않기 때문입니다. 유일한 지식은 훈련 데이터에서 캡처한 내용이며, 이는 2021년 말에 중단됩니다.

With significant weaknesses in today’s generation of LLMs, we must find solutions to these problems. One “suite” of potential solutions comes in the form of “agents”.

 

오늘날 LLM 세대에는 심각한 약점이 있으므로 이러한 문제에 대한 해결책을 찾아야 합니다. 잠재적인 솔루션 중 하나의 "제품군"은 "에이전트" 형태로 제공됩니다.

These agents don’t just solve the problems we saw above but many others. In fact, adding agents has an almost unlimited upside in their LLM-enhancing abilities.

 

이러한 에이전트는 위에서 본 문제뿐만 아니라 다른 많은 문제도 해결합니다. 실제로 에이전트를 추가하면 LLM 강화 능력에 거의 무한한 이점이 있습니다.

In this chapter, we’ll talk about agents. We’ll learn what they are, how they work, and how to use them within the LangChain library to superpower our LLMs.

 

이번 장에서는 에이전트에 대해 이야기하겠습니다. 우리는 LLM이 무엇인지, 어떻게 작동하는지, LLM을 강화하기 위해 LangChain 라이브러리 내에서 사용하는 방법을 배울 것입니다.

 

https://youtu.be/jSP-gSEyVeI?si=SrQq0VwxGeqmYsJN

 

 

What are Agents?

We can think of agents as enabling “tools” for LLMs. Like how a human would use a calculator for maths or perform a Google search for information — agents allow an LLM to do the same thing.

 

에이전트는 LLM을 위한 "도구"를 활성화하는 것으로 생각할 수 있습니다. 인간이 수학을 위해 계산기를 사용하거나 Google에서 정보를 검색하는 것과 마찬가지로 에이전트는 LLM이 동일한 작업을 수행하도록 허용합니다.

 

Agents are LLMs that can use tools like calculators, search, or executing code 에이전트는 계산기, 검색, 코드 실행과 같은 도구를 사용할 수 있는 LLM입니다.

 

 

Using agents, an LLM can write and execute Python code. It can search for information and even query a SQL database.

 

LLM은 에이전트를 사용하여 Python 코드를 작성하고 실행할 수 있습니다. 정보를 검색하고 SQL 데이터베이스에 쿼리할 수도 있습니다.

 

Let’s take a look at a straightforward example of this. We will begin with a “zero-shot” agent (more on this later) that allows our LLM to use a calculator.

 

이에 대한 간단한 예를 살펴보겠습니다. LLM이 계산기를 사용할 수 있게 해주는 "제로샷" 에이전트(나중에 자세히 설명)부터 시작하겠습니다.

 

Agents and Tools

 

To use agents, we require three things: 에이전트를 사용하려면 다음 세 가지가 필요합니다.

  • A base LLM,  기본 LLM,
  • A tool that we will be interacting with,  우리가 상호작용하게 될 도구
  • An agent to control the interaction.  상호작용을 제어하는 에이전트

Let’s start by installing langchain and initializing our base LLM.

 

langchain을 설치하고 기본 LLM을 초기화하는 것부터 시작해 보겠습니다.

 

from langchain import OpenAI

llm = OpenAI(
    openai_api_key="OPENAI_API_KEY",
    temperature=0,
    model_name="text-davinci-003"
)

 

제 로컬에서는 아래와 같이 LLM 을 초기화 했습니다.

 

 

Now to initialize the calculator tool. When initializing tools, we either create a custom tool or load a prebuilt tool. In either case, the “tool” is a utility chain given a tool name and description.

 

이제 계산기 도구를 초기화합니다. 도구를 초기화할 때 사용자 정의 도구를 생성하거나 사전 구축된 도구를 로드합니다. 두 경우 모두 " tool  도구"는 도구 이름과 설명이 지정된 유틸리티 체인입니다.

For example, we could create a new calculator tool from the existing llm_math chain:

 

예를 들어 기존 llm_math 체인에서 새로운 계산기 도구를 만들 수 있습니다.

 

from langchain.chains import LLMMathChain
from langchain.agents import Tool

llm_math = LLMMathChain(llm=llm)

# initialize the math tool
math_tool = Tool(
    name='Calculator',
    func=llm_math.run,
    description='Useful for when you need to answer questions about math.'
)
# when giving tools to LLM, we must pass as list of tools
tools = [math_tool]

 

이 코드는 langchain 패키지에서 수학적 계산을 수행하기 위한 도구를 초기화하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. from langchain.chains import LLMMathChain: langchain 패키지에서 수학적 계산을 수행하는 체인을 가져옵니다.
  2. from langchain.agents import Tool: langchain 패키지에서 도구를 가져옵니다. 도구는 특정 작업을 수행하는 함수와 설명을 함께 가지고 있는 객체입니다.
  3. llm_math = LLMMathChain(llm=llm): 수학적 계산을 수행하는 체인 객체를 초기화합니다.
    • llm=llm: 사용할 언어 모델을 설정합니다. 여기서는 llm 변수에 저장된 대화형 OpenAI 언어 모델을 사용합니다.
  4. math_tool = Tool(name='Calculator', func=llm_math.run, description='Useful for when you need to answer questions about math.'): 수학 계산을 위한 도구 객체를 초기화합니다.
    • name='Calculator': 도구의 이름을 설정합니다. 여기서는 'Calculator'로 설정되어 있습니다.
    • func=llm_math.run: 도구가 수행할 작업을 지정하는 함수를 설정합니다. 여기서는 llm_math 체인의 run 메서드를 사용합니다.
    • description='Useful for when you need to answer questions about math.': 도구에 대한 설명을 설정합니다. 사용자에게 도구의 용도를 설명하는 데 사용됩니다.
  5. tools = [math_tool]: 도구를 포함하는 리스트를 생성합니다. tools 리스트에는 현재는 하나의 도구인 math_tool이 포함되어 있습니다.

이렇게 생성된 tools 리스트는 나중에 다른 기능이나 에이전트에게 제공되어 활용될 수 있습니다. 예를 들어, 언어 모델에게 특정 도구를 사용하여 수학적 계산을 수행하도록 지시할 때 활용될 수 있습니다.

 

 

tools[0].name, tools[0].description
('Calculator', 'Useful for when you need to answer questions about math.')

 

 

We must follow this process when using custom tools. However, a prebuilt llm_math tool does the same thing. So, we could do the same as above like so:

 

사용자 정의 도구를 사용할 때는 이 프로세스를 따라야 합니다. 그러나 사전 구축된 llm_math 도구도 동일한 작업을 수행합니다. 따라서 위와 동일한 작업을 다음과 같이 수행할 수 있습니다.

 

from langchain.agents import load_tools

tools = load_tools(
    ['llm-math'],
    llm=llm
)
tools[0].name, tools[0].description
('Calculator', 'Useful for when you need to answer questions about math.')

 

 

Naturally, we can only follow this second approach if a prebuilt tool for our use case exists.

 

당연히 우리 사용 사례에 맞게 사전 구축된 도구가 있는 경우에만 이 두 번째 접근 방식을 따를 수 있습니다.

 

We now have the LLM and tools but no agent. To initialize a simple agent, we can do the following:

 

이제 LLM과 도구는 있지만 에이전트는 없습니다. 단순 에이전트를 초기화하려면 다음을 수행할 수 있습니다.

 

from langchain.agents import initialize_agent

zero_shot_agent = initialize_agent(
    agent="zero-shot-react-description",
    tools=tools,
    llm=llm,
    verbose=True,
    max_iterations=3
)

 

이 코드는 langchain 패키지에서 에이전트를 초기화하는 작업을 수행하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. from langchain.agents import initialize_agent: langchain 패키지에서 에이전트를 초기화하는 함수인 initialize_agent를 가져옵니다.
  2. zero_shot_agent = initialize_agent(agent="zero-shot-react-description", tools=tools, llm=llm, verbose=True, max_iterations=3): 에이전트를 초기화합니다.
    • agent="zero-shot-react-description": 초기화할 에이전트의 유형을 지정합니다. 여기서는 'zero-shot-react-description'으로 설정되어 있습니다.
    • tools=tools: 사용할 도구를 지정합니다. 앞서 초기화한 수학 도구(math_tool)를 사용합니다.
    • llm=llm: 사용할 언어 모델을 설정합니다. 여기서는 llm 변수에 저장된 대화형 OpenAI 언어 모델을 사용합니다.
    • verbose=True: 초기화 및 실행 과정에서 발생하는 정보를 자세히 출력하도록 설정합니다.
    • max_iterations=3: 에이전트가 수행할 최대 반복 횟수를 설정합니다. 여기서는 최대 3회까지 반복합니다.

이렇게 초기화된 zero_shot_agent는 특정 유형의 에이전트입니다. 여기서는 'zero-shot-react-description' 에이전트로, 언어 모델과 도구를 활용하여 특정 작업을 수행할 수 있습니다. 설정된 도구와 언어 모델을 사용하여 에이전트가 지정된 작업을 수행하도록 요청할 수 있습니다.

 

https://python.langchain.com/docs/modules/agents/agent_types/

 

Agent Types | 🦜️🔗 Langchain

Agents use an LLM to determine which actions to take and in what order.

python.langchain.com

 

The agent used here is a "zero-shot-react-description" agent. Zero-shot means the agent functions on the current action only — it has no memory. It uses the ReAct framework to decide which tool to use, based solely on the tool’s description.

 

여기서 사용된 에이전트는 "zero-shot-react-description" 에이전트입니다. 제로샷은 에이전트가 현재 작업에서만 작동하며 메모리가 없음을 의미합니다. ReAct 프레임워크를 사용하여 도구 설명만을 기반으로 사용할 도구를 결정합니다.

We won’t discuss the ReAct framework in this chapter, but you can think of it as if an LLM could cycle through Reasoning and Action steps. Enabling a multi-step process for identifying answers.

 

이 장에서는 ReAct 프레임워크에 대해 논의하지 않지만 LLM이 추론 및 작업 단계를 순환할 수 있는 것처럼 생각할 수 있습니다. 답변을 식별하기 위한 다단계 프로세스를 활성화합니다.

With our agent initialized, we can begin using it. Let’s try a few prompts and see how the agent responds.

 

에이전트가 초기화되면 사용을 시작할 수 있습니다. 몇 가지 프롬프트를 시도하고 상담원이 어떻게 응답하는지 살펴보겠습니다.

 

zero_shot_agent("what is (4.5*2.1)^2.2?")

 

> Entering new AgentExecutor chain...
 I need to calculate this expression
Action: Calculator
Action Input: (4.5*2.1)^2.2
Observation: Answer: 139.94261298333066

Thought: I now know the final answer
Final Answer: 139.94261298333066

> Finished chain.
{'input': 'what is (4.5*2.1)^2.2?', 'output': '139.94261298333066'}
(4.5*2.1)**2.2
139.94261298333066

 

 

The answer here is correct. Let’s try another:  여기에 대한 대답은 정확합니다. 다른 것을 시도해 봅시다:

 

이렇게 Calculator tool을 사용하지 않고 ChatGPT에 같은 질문을 하면 아래와 같이 엉뚱한 대답을 내 놓는다.

 

이렇듯 LLM 모듈은 계산에 약하고 이를 보완 하기 위해 외부 Tool을 이용하고 이 때 사용하는 것이 agent 임

 

zero_shot_agent("if Mary has four apples and Giorgio brings two and a half apple "
                "boxes (apple box contains eight apples), how many apples do we "
                "have?")
> Entering new AgentExecutor chain...
 I need to figure out how many apples are in the boxes
Action: Calculator
Action Input: 8 * 2.5
Observation: Answer: 20.0

Thought: I need to add the apples Mary has to the apples in the boxes
Action: Calculator
Action Input: 4 + 20.0
Observation: Answer: 24.0

Thought: I now know the final answer
Final Answer: We have 24 apples.

> Finished chain.
{'input': 'if Mary has four apples and Giorgio brings two and a half apple boxes (apple box contains eight apples), how many apples do we have?',
 'output': 'We have 24 apples.'}

 

 

Looks great! But what if we decide to ask a non-math question? What if we ask an easy common knowledge question?

 

멋져요! 하지만 수학이 아닌 질문을 하기로 결정하면 어떻게 될까요? 쉬운 상식 질문을 하면 어떨까요?

 

Note : 똑같은 질문을 Calculator를 사용하지 않고 곧바로 ChatGPT에게 질문을 해 보았다. 이번에는 맞게 계산 하였다. 

 

 

 

Looks great! But what if we decide to ask a non-math question? What if we ask an easy common knowledge question?

 

멋져요! 하지만 수학이 아닌 질문을 하기로 결정하면 어떻게 될까요? 쉬운 상식 질문을 하면 어떨까요?

 

zero_shot_agent("what is the capital of Norway?")
> Entering new AgentExecutor chain...
 I need to look up the answer
Action: Look up
Action Input: Capital of Norway
Observation: Look up is not a valid tool, try another one.
Thought: I need to find the answer using a tool
Action: Calculator
Action Input: N/A

 

 

 

We run into an error. The problem here is that the agent keeps trying to use a tool. Yet, our agent contains only one tool — the calculator.

 

오류가 발생했습니다. 여기서 문제는 에이전트가 계속해서 도구를 사용하려고 한다는 것입니다. 그러나 우리 에이전트에는 계산기라는 단 하나의 도구만 포함되어 있습니다.

 

Fortunately, we can fix this problem by giving our agent more tools! Let’s add a plain and simple LLM tool:

 

다행히 에이전트에게 더 많은 도구를 제공하면 이 문제를 해결할 수 있습니다! 평범하고 간단한 LLM 도구를 추가해 보겠습니다.

 

from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain

prompt = PromptTemplate(
    input_variables=["query"],
    template="{query}"
)

llm_chain = LLMChain(llm=llm, prompt=prompt)

# initialize the LLM tool
llm_tool = Tool(
    name='Language Model',
    func=llm_chain.run,
    description='use this tool for general purpose queries and logic'
)

 

이 코드는 langchain 패키지에서 특정 언어 모델을 사용하는 도구를 초기화하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. from langchain.prompts import PromptTemplate: langchain 패키지에서 프롬프트 템플릿을 가져옵니다. 프롬프트 템플릿은 언어 모델에 전달되는 입력을 구성하는 데 사용됩니다.
  2. from langchain.chains import LLMChain: langchain 패키지에서 언어 모델을 사용하는 체인을 가져옵니다.
  3. prompt = PromptTemplate(input_variables=["query"], template="{query}"): 사용자의 질문 또는 쿼리를 받을 수 있는 프롬프트 템플릿을 초기화합니다.
    • input_variables=["query"]: 프롬프트 템플릿에 전달될 입력 변수를 설정합니다. 여기서는 "query"라는 변수를 사용합니다.
    • template="{query}": 프롬프트의 템플릿을 설정합니다. "{query}"는 나중에 이 변수에 대응되는 실제 질문이 들어갈 자리를 나타냅니다.
  4. llm_chain = LLMChain(llm=llm, prompt=prompt): 언어 모델을 사용하는 체인 객체를 초기화합니다.
    • llm=llm: 사용할 언어 모델을 설정합니다. 여기서는 llm 변수에 저장된 대화형 OpenAI 언어 모델을 사용합니다.
    • prompt=prompt: 체인이 사용할 프롬프트 템플릿을 설정합니다.
  5. llm_tool = Tool(name='Language Model', func=llm_chain.run, description='use this tool for general purpose queries and logic'): 언어 모델을 사용하는 도구 객체를 초기화합니다.
    • name='Language Model': 도구의 이름을 설정합니다. 여기서는 'Language Model'로 설정되어 있습니다.
    • func=llm_chain.run: 도구가 수행할 작업을 지정하는 함수를 설정합니다. 여기서는 llm_chain 체인의 run 메서드를 사용합니다.
    • description='use this tool for general purpose queries and logic': 도구에 대한 설명을 설정합니다. 사용자에게 언어 모델을 사용하여 일반적인 쿼리와 논리에 사용할 수 있음을 안내하는 데 사용됩니다.

이렇게 초기화된 llm_tool 객체는 나중에 다른 기능이나 에이전트에게 제공되어 활용될 수 있습니다. 사용자의 일반적인 질문이나 쿼리를 언어 모델을 사용하여 처리하는 데 사용될 수 있는 도구입니다.

 

With this, we have a new general-purpose LLM tool. All we do is add it to the tools list and reinitialize the agent:

 

이를 통해 우리는 새로운 범용 LLM 도구를 갖게 되었습니다. 우리가 하는 일은 도구 목록에 이를 추가하고 에이전트를 다시 초기화하는 것뿐입니다.

 

tools.append(llm_tool)

# reinitialize the agent
zero_shot_agent = initialize_agent(
    agent="zero-shot-react-description",
    tools=tools,
    llm=llm,
    verbose=True,
    max_iterations=3
)

 

이 코드는 기존에 초기화한 에이전트(zero_shot_agent)에 새로운 도구(llm_tool)를 추가하고, 추가된 도구를 사용하여 에이전트를 다시 초기화하는 작업을 수행하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. tools.append(llm_tool): 에이전트의 도구 리스트에 새로운 도구인 llm_tool을 추가합니다. tools는 이전에 초기화한 도구 리스트입니다.
  2. zero_shot_agent = initialize_agent(agent="zero-shot-react-description", tools=tools, llm=llm, verbose=True, max_iterations=3): 새로운 도구가 추가된 상태에서 에이전트를 다시 초기화합니다.
    • agent="zero-shot-react-description": 초기화할 에이전트의 유형을 지정합니다. 여기서는 'zero-shot-react-description'으로 설정되어 있습니다.
    • tools=tools: 업데이트된 도구 리스트를 설정합니다. 이 리스트에는 기존 도구와 새로 추가된 llm_tool이 포함되어 있습니다.
    • llm=llm: 사용할 언어 모델을 설정합니다. 여기서는 llm 변수에 저장된 대화형 OpenAI 언어 모델을 사용합니다.
    • verbose=True: 초기화 및 실행 과정에서 발생하는 정보를 자세히 출력하도록 설정합니다.
    • max_iterations=3: 에이전트가 수행할 최대 반복 횟수를 설정합니다. 여기서는 최대 3회까지 반복합니다.

이렇게 업데이트된 zero_shot_agent는 새로운 도구가 추가된 상태에서 재설정된 상태를 나타냅니다. 이제 이 에이전트는 언어 모델을 사용하여 일반적인 쿼리 및 논리를 처리하는 도구인 llm_tool을 사용할 수 있습니다.

 

 

Now we can ask the agent questions about both math and general knowledge. Let’s try the following:

 

이제 상담원에게 수학과 일반 지식에 관한 질문을 할 수 있습니다. 다음을 시도해 봅시다:

 

zero_shot_agent("what is the capital of Norway?")
> Entering new AgentExecutor chain...
 I need to find out what the capital of Norway is
Action: Language Model
Action Input: What is the capital of Norway?
Observation: 

The capital of Norway is Oslo.
Thought: I now know the final answer
Final Answer: The capital of Norway is Oslo.

> Finished chain.
{'input': 'what is the capital of Norway?',
 'output': 'The capital of Norway is Oslo.'}

 

로컬 실행 결과는 아래와 같습니다.

 

 

Now we get the correct answer! We can ask the first question:

 

이제 우리는 정답을 얻었습니다! 우리는 첫 번째 질문을 할 수 있습니다:

 

zero_shot_agent("what is (4.5*2.1)^2.2?")
> Entering new AgentExecutor chain...
 I need to calculate this expression
Action: Calculator
Action Input: (4.5*2.1)^2.2
Observation: Answer: 139.94261298333066

Thought: I now know the final answer
Final Answer: 139.94261298333066

> Finished chain.
{'input': 'what is (4.5*2.1)^2.2?', 'output': '139.94261298333066'}

 

 

 

And the agent understands it must refer to the calculator tool, which it does — giving us the correct answer.

 

그리고 상담원은 계산기 도구를 참조해야 한다는 것을 이해하고 있으며 이를 통해 우리에게 정답을 제공합니다.

 

With that complete, we should understand the workflow in designing and prompting agents with different tools. Now let’s move on to the different types of agents and tools available to us.

 

이를 완료하면 다양한 도구를 사용하여 상담원을 설계하고 안내하는 작업 흐름을 이해해야 합니다. 이제 우리가 사용할 수 있는 다양한 유형의 에이전트와 도구를 살펴보겠습니다.

 

Agent Types

 

LangChain offers several types of agents. In this section, we’ll examine a few of the most common.

 

LangChain은 여러 유형의 에이전트를 제공합니다. 이 섹션에서는 가장 일반적인 몇 가지 사항을 살펴보겠습니다.

 

Zero Shot ReAct

We’ll start with the agent we saw earlier, the zero-shot-react-description agent.

 

앞서 본 에이전트인 zero-shot-react-description 에이전트부터 시작하겠습니다.

As described earlier, we use this agent to perform “zero-shot” tasks on some input. That means the agent considers one single interaction with the agent — it will have no memory.

 

앞서 설명한 것처럼 우리는 이 에이전트를 사용하여 일부 입력에 대해 "제로샷" 작업을 수행합니다. 즉, 에이전트는 에이전트와의 단일 상호 작용을 고려하므로 메모리가 없습니다.

Let’s create a tools list to use with the agent. We will include an llm-math tool and a SQL DB tool that we defined here.

 

에이전트와 함께 사용할 도구 목록을 만들어 보겠습니다. 여기서 정의한 llm-math 도구와 SQL DB 도구를 포함하겠습니다.

 

tools = load_tools(
    ["llm-math"], 
    llm=llm
)

# add our custom SQL db tool
tools.append(sql_tool)

 

We initialize the zero-shot-react-description agent like so:

 

다음과 같이 zero-shot-react-description 에이전트를 초기화합니다.

 

from langchain.agents import initialize_agent

zero_shot_agent = initialize_agent(
    agent="zero-shot-react-description", 
    tools=tools, 
    llm=llm,
    verbose=True,
    max_iterations=3,
)

 

To give some context on the SQL DB tool, we will be using it to query a “stocks database” that looks like this:

 

SQL DB 도구에 대한 컨텍스트를 제공하기 위해 이를 사용하여 다음과 같은 "주식 데이터베이스"를 쿼리하겠습니다.

 

 

 

Now we can begin asking questions about this SQL DB and pairing it with calculations via the calculator tool.

 

이제 이 SQL DB에 대해 질문하고 계산기 도구를 통해 이를 계산과 연결할 수 있습니다.

 

result = zero_shot_agent(
    "What is the multiplication of the ratio between stock prices for 'ABC' "
    "and 'XYZ' in January 3rd and the ratio between the same stock prices in "
    "January the 4th?"
)
> Entering new AgentExecutor chain...
 I need to compare the stock prices of 'ABC' and 'XYZ' on two different days
Action: Stock DB
Action Input: Stock prices of 'ABC' and 'XYZ' on January 3rd and January 4th

> Entering new SQLDatabaseChain chain...
Stock prices of 'ABC' and 'XYZ' on January 3rd and January 4th 
SQLQuery: SELECT stock_ticker, price, date FROM stocks WHERE (stock_ticker = 'ABC' OR stock_ticker = 'XYZ') AND (date = '2023-01-03' OR date = '2023-01-04')
SQLResult: [('ABC', 232.0, '2023-01-03'), ('ABC', 225.0, '2023-01-04'), ('XYZ', 798.0, '2023-01-03'), ('XYZ', 795.0, '2023-01-04')]
Answer: The stock prices of 'ABC' and 'XYZ' on January 3rd and January 4th were 232.0 and 798.0 respectively for 'ABC' and 'XYZ' on January 3rd, and 225.0 and 795.0 respectively for 'ABC' and 'XYZ' on January 4th.
> Finished chain.

Observation:  The stock prices of 'ABC' and 'XYZ' on January 3rd and January 4th were 232.0 and 798.0 respectively for 'ABC' and 'XYZ' on January 3rd, and 225.0 and 795.0 respectively for 'ABC' and 'XYZ' on January 4th.
Thought: I need to calculate the ratio between the two stock prices on each day
Action: Calculator
Action Input: 232.0/798.0 and 225.0/795.0
Observation: Answer: 0.2907268170426065
0.2830188679245283

Thought: I need to calculate the multiplication of the two ratios
Action: Calculator
Action Input: 0.2907268170426065 * 0.2830188679245283
Observation: Answer: 0.08228117463469994

Thought:

> Finished chain.

 

이 코드는 langchain 패키지를 사용하여 에이전트를 초기화하고 특정 도구를 추가한 후, 에이전트에 질문을 전달하여 결과를 얻는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. tools = load_tools(["llm-math"], llm=llm): llm-math 도구를 로드하여 초기 도구 리스트에 추가합니다. llm-math 도구는 수학적 계산을 수행하는 도구입니다. load_tools 함수는 해당 도구를 초기화하고 리스트에 추가하는 역할을 합니다.
  2. tools.append(sql_tool): 사용자가 만든 sql_tool 도구를 초기 도구 리스트에 추가합니다. 이 도구는 SQL 데이터베이스에 대한 쿼리를 실행하는 데 사용됩니다.
  3. from langchain.agents import initialize_agent: langchain 패키지에서 에이전트를 초기화하는 함수 initialize_agent를 가져옵니다.
  4. zero_shot_agent = initialize_agent(agent="zero-shot-react-description", tools=tools, llm=llm, verbose=True, max_iterations=3): 에이전트를 초기화합니다.
    • agent="zero-shot-react-description": 초기화할 에이전트의 유형을 지정합니다. 여기서는 'zero-shot-react-description'으로 설정되어 있습니다.
    • tools=tools: 업데이트된 도구 리스트를 설정합니다. 이 리스트에는 llm-math와 사용자 정의 sql_tool이 포함되어 있습니다.
    • llm=llm: 사용할 언어 모델을 설정합니다. 여기서는 llm 변수에 저장된 대화형 OpenAI 언어 모델을 사용합니다.
    • verbose=True: 초기화 및 실행 과정에서 발생하는 정보를 자세히 출력하도록 설정합니다.
    • max_iterations=3: 에이전트가 수행할 최대 반복 횟수를 설정합니다. 여기서는 최대 3회까지 반복합니다.
  5. result = zero_shot_agent("What is the multiplication of the ratio between stock prices for 'ABC' and 'XYZ' in January 3rd and the ratio between the same stock prices in January the 4th?"): 에이전트에 특정 질문을 전달하여 결과를 얻습니다. 이 질문은 주식 가격의 비율에 관한 수학적 계산과 SQL 데이터베이스에 대한 쿼리를 포함하고 있습니다.

이렇게 초기화된 에이전트를 사용하면 다양한 도구를 활용하여 다양한 유형의 질문에 대한 답변을 얻을 수 있습니다.

 

Note : Custom SQL db tool인 sql_tool이 정의 돼 있지 않기 때문에 위 소스코드는 로컬에서 돌리면 에러가 납니다.

 

 

 

We can see a lot of output here. At each step, there is a Thought that results in a chosen Action and Action Input. If the Action were to use a tool, then an Observation (the output from the tool) is passed back to the agent.

 

여기서는 많은 결과를 볼 수 있습니다. 각 단계에는 선택한 행동과 행동 입력을 초래하는 생각이 있습니다. 작업이 tool 도구를 사용하는 경우 관찰 Observation  (도구의 출력)이 에이전트에 다시 전달됩니다.

If we look at the prompt being used by the agent, we can see how the LLM decides which tool to use.

 

에이전트가 사용하는 프롬프트를 보면 LLM이 사용할 도구를 어떻게 결정하는지 확인할 수 있습니다.

 

print(zero_shot_agent.agent.llm_chain.prompt.template)
Answer the following questions as best you can. You have access to the following tools:

Calculator: Useful for when you need to answer questions about math.
Stock DB: Useful for when you need to answer questions about stocks and their prices.

Use the following format:

Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [Calculator, Stock DB]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question

Begin!

Question: {input}
Thought:{agent_scratchpad}

 

prompt는 아래와 같이 로컬에서 확인해 볼 수 있습니다.

 

 

 

We first tell the LLM the tools it can use (Calculator and Stock DB). Following this, an example format is defined; this follows the flow of Question (from the user), Thought, Action, Action Input, Observation — and repeat until reaching the Final Answer.

 

먼저 LLM에 사용할 수 있는 도구(계산기 및 Stock DB)를 알려줍니다. 다음에는 예제 형식이 정의됩니다. 이는 (사용자의) 질문, 생각, 행동, 행동 입력, 관찰의 흐름을 따르며 최종 답변에 도달할 때까지 반복됩니다.

These tools and the thought process separate agents from chains in LangChain.

 

이러한 도구와 사고 프로세스는 LangChain의 체인에서 에이전트를 분리합니다.

Whereas a chain defines an immediate input/output process, the logic of agents allows a step-by-step thought process. The advantage of this step-by-step process is that the LLM can work through multiple reasoning steps or tools to produce a better answer.

 

체인이 즉각적인 입력/출력 프로세스를 정의하는 반면, 에이전트의 논리는 단계별 사고 프로세스를 허용합니다. 이 단계별 프로세스의 장점은 LLM이 여러 추론 단계나 도구를 통해 작업하여 더 나은 답변을 생성할 수 있다는 것입니다.

There is still one part of the prompt we still need to discuss. The final line is "Thought:{agent_scratchpad}".

 

아직 논의해야 할 프롬프트의 한 부분이 있습니다. 마지막 줄은 "Thought:{agent_scratchpad}"입니다.

The agent_scratchpad is where we add every thought or action the agent has already performed. All thoughts and actions (within the current agent executor chain) can then be accessed by the next thought-action-observation loop, enabling continuity in agent actions.

 

Agent_scratchpad는 에이전트가 이미 수행한 모든 생각이나 행동을 추가하는 곳입니다. 그러면 (현재 에이전트 실행자 체인 내의) 모든 생각과 행동은 다음 생각-행동-관찰 루프를 통해 액세스될 수 있으므로 에이전트 작업의 연속성이 가능해집니다.

 

Conversational ReAct

The zero-shot agent works well but lacks conversational memory. This lack of memory can be problematic for chatbot-type use cases that need to remember previous interactions in a conversation.

 

제로샷 에이전트는 잘 작동하지만 대화 메모리가 부족합니다. 이러한 메모리 부족은 대화에서 이전 상호 작용을 기억해야 하는 챗봇 유형 사용 사례에 문제가 될 수 있습니다.

Fortunately, we can use the conversational-react-description agent to remember interactions. We can think of this agent as the same as our previous Zero Shot ReAct agent, but with conversational memory.

 

다행스럽게도 대화형 반응 설명 에이전트를 사용하여 상호작용을 기억할 수 있습니다. 이 에이전트는 이전 Zero Shot ReAct 에이전트와 동일하지만 대화 메모리가 있다고 생각할 수 있습니다.

To initialize the agent, we first need to initialize the memory we’d like to use. We will use the simple ConversationBufferMemory.

 

에이전트를 초기화하려면 먼저 사용하려는 메모리를 초기화해야 합니다. 간단한 ConversationBufferMemory를 사용하겠습니다.

 

from langchain.memory import ConversationBufferMemory

memory = ConversationBufferMemory(memory_key="chat_history")

 

We pass this to the memory parameter when initializing our agent:

 

에이전트를 초기화할 때 이것을 memory 매개변수에 전달합니다.

 

conversational_agent = initialize_agent(
    agent='conversational-react-description', 
    tools=tools, 
    llm=llm,
    verbose=True,
    max_iterations=3,
    memory=memory,
)

 

이 코드는 langchain 패키지에서 대화형 에이전트를 초기화하고 대화 기록을 저장하기 위한 메모리를 설정하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. from langchain.memory import ConversationBufferMemory: 대화 기록을 저장하기 위한 메모리 클래스인 ConversationBufferMemory를 가져옵니다.
  2. memory = ConversationBufferMemory(memory_key="chat_history"): chat_history라는 키를 사용하여 대화 기록을 저장하는 메모리를 초기화합니다. 이 메모리는 나중에 에이전트가 대화 기록을 사용하여 대화를 이해하고 지속할 수 있도록 도와줍니다.
  3. conversational_agent = initialize_agent(agent='conversational-react-description', tools=tools, llm=llm, verbose=True, max_iterations=3, memory=memory): 대화형 에이전트를 초기화합니다.
    • agent='conversational-react-description': 초기화할 에이전트의 유형을 지정합니다. 여기서는 'conversational-react-description'으로 설정되어 있습니다.
    • tools=tools: 업데이트된 도구 리스트를 설정합니다. 이 리스트에는 llm-math, 사용자 정의 sql_tool, 그리고 앞서 초기화한 대화 모델 도구 등이 포함됩니다.
    • llm=llm: 사용할 언어 모델을 설정합니다. 여기서는 llm 변수에 저장된 대화형 OpenAI 언어 모델을 사용합니다.
    • verbose=True: 초기화 및 실행 과정에서 발생하는 정보를 자세히 출력하도록 설정합니다.
    • max_iterations=3: 에이전트가 수행할 최대 반복 횟수를 설정합니다. 여기서는 최대 3회까지 반복합니다.
    • memory=memory: 에이전트에 대화 기록을 저장하는 데 사용할 메모리를 설정합니다. 여기서는 ConversationBufferMemory로 초기화한 memory 변수를 사용합니다.

이렇게 초기화된 conversational_agent는 대화 기록을 저장하고 활용하여 더 자연스럽게 대화할 수 있는 기능을 가진 에이전트입니다.

 

 

If we run this agent with a similar question, we should see a similar process followed as before:

 

비슷한 질문으로 이 에이전트를 실행하면 이전과 유사한 프로세스가 뒤따르는 것을 볼 수 있습니다.

 

result = conversational_agent(
    "Please provide me the stock prices for ABC on January the 1st"
)
> Entering new AgentExecutor chain...

Thought: Do I need to use a tool? Yes
Action: Stock DB
Action Input: ABC on January the 1st

> Entering new SQLDatabaseChain chain...
ABC on January the 1st 
SQLQuery: SELECT price FROM stocks WHERE stock_ticker = 'ABC' AND date = '2023-01-01'
SQLResult: [(200.0,)]
Answer: The price of ABC on January the 1st was 200.0.
> Finished chain.

Observation:  The price of ABC on January the 1st was 200.0.
Thought: Do I need to use a tool? No
AI: Is there anything else I can help you with?

> Finished chain.

 

로컬에서 돌렸더니 이것을 Calculator 도구를 사용해 버리네요. 아마 SQL DB 도구는 정의가 안 돼 있기 때문에 유일하게 정의 돼 있는 Calculator 도구를 사용하나 봅니다. 그런데 ABC의 1월 1일 주가를 800불이라고 대답해 버리네요. 무엇을 근거로 했는지 모르겠습니다. 아마 Hallucination 현상인 것 같습니다.

 

 

So far, this looks very similar to our last zero-shot agent. However, unlike our zero-shot agent, we can now ask follow-up questions. Let’s ask about the stock price for XYZ on the same date without specifying January 1st.

 

지금까지 이는 마지막 제로샷 에이전트와 매우 유사해 보입니다. 하지만 제로샷 에이전트와 달리 이제 후속 질문을 할 수 있습니다. 1월 1일을 지정하지 않고 같은 날짜의 XYZ 주가에 대해 물어보겠습니다.

 

result = conversational_agent(
    "What are the stock prices for XYZ on the same day?"
)
> Entering new AgentExecutor chain...

Thought: Do I need to use a tool? Yes
Action: Stock DB
Action Input: Stock prices for XYZ on January 1st

> Entering new SQLDatabaseChain chain...
Stock prices for XYZ on January 1st 
SQLQuery: SELECT price FROM stocks WHERE stock_ticker = 'XYZ' AND date = '2023-01-01'
SQLResult: [(810.0,)]
Answer: The stock price for XYZ on January 1st was 810.0.
> Finished chain.

Observation:  The stock price for XYZ on January 1st was 810.0.
Thought: Do I need to use a tool? No
AI: Is there anything else I can help you with?

> Finished chain.

 

로컬에서 돌리니까 신택스 에러가 나 버리네요.

 

XYZ stock price는 100 불 이라고 설정해 놓고 뭔가 valid 한 numerical expression이 아니라면서 신택스 에러를 내네요.

 

어쨌든 이번 섹터에서는 ConversationBufferMemory를 사용해서 이전 질문 답변 히스토리를 기억해서 대화를 이어가는 방법을 배우는 거니까 그냥 그렇게 알고 넘어 가겠습니다. 교재에 나오는 예제에서는 이전 대화를 잘 기억해서 on the same day를 1월 1일로 알아 먹고 이에 맞는 SQL 쿼리를 작성했습니다.

 

We can see in the first Action Input that the agent is looking for "Stock prices for XYZ on January 1st". It knows we are looking for January 1st because we asked for this date in our previous interaction.

 

첫 번째 작업 입력에서 에이전트가 "1월 1일 XYZ의 주가"를 찾고 있음을 확인할 수 있습니다. 이전 상호작용에서 이 날짜를 요청했기 때문에 1월 1일을 찾고 있다는 것을 알고 있습니다.

How can it do this? We can take a look at the prompt template to find out:

 

어떻게 이런 일을 할 수 있나요? 프롬프트 템플릿을 살펴보면 다음 사항을 확인할 수 있습니다.

 

print(conversational_agent.agent.llm_chain.prompt.template)
Assistant is a large language model trained by OpenAI.

Assistant is designed to be able to assist with a wide range of tasks, from answering simple questions to providing in-depth explanations and discussions on a wide range of topics. As a language model, Assistant is able to generate human-like text based on the input it receives, allowing it to engage in natural-sounding conversations and provide responses that are coherent and relevant to the topic at hand.

Assistant is constantly learning and improving, and its capabilities are constantly evolving. It is able to process and understand large amounts of text, and can use this knowledge to provide accurate and informative responses to a wide range of questions. Additionally, Assistant is able to generate its own text based on the input it receives, allowing it to engage in discussions and provide explanations and descriptions on a wide range of topics.

Overall, Assistant is a powerful tool that can help with a wide range of tasks and provide valuable insights and information on a wide range of topics. Whether you need help with a specific question or just want to have a conversation about a particular topic, Assistant is here to assist.

TOOLS:
------

Assistant has access to the following tools:

> Calculator: Useful for when you need to answer questions about math.
> Stock DB: Useful for when you need to answer questions about stocks and their prices.

To use a tool, please use the following format:

```
Thought: Do I need to use a tool? Yes
Action: the action to take, should be one of [Calculator, Stock DB]
Action Input: the input to the action
Observation: the result of the action
```

When you have a response to say to the Human, or if you do not need to use a tool, you MUST use the format:

```
Thought: Do I need to use a tool? No
AI: [your response here]
```

Begin!

Previous conversation history:
{chat_history}

New input: {input}
{agent_scratchpad}

 

프롬프트를 확인하는 작업은 로컬에서도 잘 작동합니다.

 

 

이 Agent Type들은 각각 그 기능에 맞는 Prompt를 탬플릿화 해서 제공하는 방법으로 원하는 기능을 취하는 것 같습니다.

 

We have a much larger instruction setup at the start of the prompt, but most important are the two lines near the end of the prompt:

 

프롬프트 시작 부분에는 훨씬 더 큰 명령 설정이 있지만 가장 중요한 것은 프롬프트 끝 부분에 있는 두 줄입니다.

Previous conversation history: {chat_history}    이전 대화 기록: {chat_history}

Here is where we add all previous interactions to the prompt. Within this space will be the information that we asked "Please provide me the stock prices for ABC on January the 1st" — allowing the agent to understand that our follow-up question refers to the same date.

 

여기에서 이전의 모든 상호 작용을 프롬프트에 추가합니다. 이 공간에는 "1월 1일 ABC의 주가를 알려주십시오"라고 요청한 정보가 있습니다. 이를 통해 상담원은 후속 질문이 동일한 날짜를 참조한다는 것을 이해할 수 있습니다.

It’s worth noting that the conversational ReAct agent is designed for conversation and struggles more than the zero-shot agent when combining multiple complex steps. We can see this if we ask the agent to answer our earlier question:

 

대화형 ReAct 에이전트는 대화용으로 설계되었으며 여러 복잡한 단계를 결합할 때 제로샷 에이전트보다 더 많은 어려움을 겪는다는 점은 주목할 가치가 있습니다. 상담원에게 이전 질문에 대한 답변을 요청하면 이를 확인할 수 있습니다.

 

result = conversational_agent(
    "What is the multiplication of the ratio of the prices of stocks 'ABC' "
    "and 'XYZ' in January 3rd and the ratio of the same prices of the same "
    "stocks in January the 4th?"
)


> Entering new AgentExecutor chain...
Thought: Do I need to use a tool? Yes
Action: Stock DB
Action Input: Get the ratio of the prices of stocks 'ABC' and 'XYZ' in January 3rd and the ratio of the same prices of the same stocks in January the 4th

> Entering new SQLDatabaseChain chain...
Get the ratio of the prices of stocks 'ABC' and 'XYZ' in January 3rd and the ratio of the same prices of the same stocks in January the 4th 
SQLQuery: SELECT (SELECT price FROM stocks WHERE stock_ticker = 'ABC' AND date = '2023-01-03') / (SELECT price FROM stocks WHERE stock_ticker = 'XYZ' AND date = '2023-01-03') AS ratio_jan_3, (SELECT price FROM stocks WHERE stock_ticker = 'ABC' AND date = '2023-01-04') / (SELECT price FROM stocks WHERE stock_ticker = 'XYZ' AND date = '2023-01-04') AS ratio_jan_4 FROM stocks LIMIT 5;
SQLResult: [(0.2907268170426065, 0.2830188679245283), (0.2907268170426065, 0.2830188679245283), (0.2907268170426065, 0.2830188679245283), (0.2907268170426065, 0.2830188679245283), (0.2907268170426065, 0.2830188679245283)]
Answer: The ratio of the prices of stocks 'ABC' and 'XYZ' in January 3rd is 0.2907268170426065 and the ratio of the same prices of the same stocks in January the 4th is 0.2830188679245283.
> Finished chain.

Observation:  The ratio of the prices of stocks 'ABC' and 'XYZ' in January 3rd is 0.2907268170426065 and the ratio of the same prices of the same stocks in January the 4th is 0.2830188679245283.
Thought: Do I need to use a tool? No
AI: The answer is 0.4444444444444444. Is there anything else I can help you with?

> Finished chain.
Spent a total of 2518 tokens

 

질문을 잘 이해하고 거기에 맞는 SQL 쿼리를 잘 작성한 것 같습니다.

 

참고로 로컬에서 돌려도 SQL DB 툴 없이 Calculator로 자기가 주가를 만들어 내서 답을 내 줍니다.

 

Langchain 에서 제공하는 프롬프트에는 확실하지 않으면 모른다고 대답해라 라는 부분이 없기 때문에 ChatGPT가 그냥 알아서 주가를 만들어 내고 그 값을 기반으로 질문에 답하고 있습니다.

 

With this, the agent still manages to solve the question but uses a more complex approach of pure SQL rather than relying on more straightforward SQL and the calculator tool.

 

이를 통해 에이전트는 여전히 문제를 해결하지만 더 간단한 SQL 및 계산기 도구에 의존하기보다는 순수 SQL의 더 복잡한 접근 방식을 사용합니다.

 

ReAct Docstore

Another common agent is the react-docstore agent. As before, it uses the ReAct methodology, but now it is explicitly built for information search and lookup using a LangChain docstore.

 

또 다른 일반적인 에이전트는 React-docstore 에이전트입니다. 이전과 마찬가지로 ReAct 방법론을 사용하지만 이제는 LangChain docstores  를 사용하여 정보 검색 및 조회를 위해 명시적으로 구축되었습니다.

LangChain docstores allow us to store and retrieve information using traditional retrieval methods. One of these docstores is Wikipedia, which gives us access to the information on the site.

 

LangChain docstores  를 사용하면 전통적인 검색 방법을 사용하여 정보를 저장하고 검색할 수 있습니다. 이러한 문서 저장소 중 하나는 Wikipedia이며, 이를 통해 사이트의 정보에 액세스할 수 있습니다.

We will implement this agent using two docstore methods — Search and Lookup. With Search, our agent will search for a relevant article, and with Lookup, the agent will find the relevant chunk of information within the retrieved article. To initialize these two tools, we do:

 

Search 및 Lookup이라는 두 가지 Docstore 방법을 사용하여 이 에이전트를 구현합니다. 검색을 통해 에이전트는 관련 기사를 검색하고, Lookup을 통해 에이전트는 검색된 기사 내에서 관련 정보 덩어리를 찾습니다. 이 두 도구를 초기화하려면 다음을 수행합니다.

 

from langchain import Wikipedia
from langchain.agents.react.base import DocstoreExplorer

docstore=DocstoreExplorer(Wikipedia())
tools = [
    Tool(
        name="Search",
        func=docstore.search,
        description='search wikipedia'
    ),
    Tool(
        name="Lookup",
        func=docstore.lookup,
        description='lookup a term in wikipedia'
    )
]

 

Now initialize the agent: 이제 에이전트를 초기화합니다.

 

docstore_agent = initialize_agent(
    tools, 
    llm, 
    agent="react-docstore", 
    verbose=True,
    max_iterations=3
)

 

이 코드는 langchain 패키지를 사용하여 Wikipedia 문서 스토어를 활용하는 에이전트를 초기화하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. from langchain import Wikipedia: langchain 패키지에서 Wikipedia 문서 스토어를 가져옵니다.
  2. from langchain.agents.react.base import DocstoreExplorer: Wikipedia 문서 스토어를 탐색하기 위한 기본 도구를 가져옵니다.
  3. docstore = DocstoreExplorer(Wikipedia()): Wikipedia 문서 스토어를 초기화합니다. Wikipedia()는 Wikipedia의 내용을 검색하고 가져오기 위한 도구를 제공합니다.
  4. tools = [...]: docstore에서 제공하는 검색 및 조회 기능을 사용하는 두 가지 도구를 초기화합니다.
    • Tool(name="Search", func=docstore.search, description='search wikipedia'): Wikipedia에서 검색을 수행하는 도구입니다.
    • Tool(name="Lookup", func=docstore.lookup, description='lookup a term in wikipedia'): Wikipedia에서 용어를 조회하는 도구입니다.
  5. docstore_agent = initialize_agent(tools, llm, agent="react-docstore", verbose=True, max_iterations=3): Wikipedia 문서 스토어를 사용하는 에이전트를 초기화합니다.
    • tools: 사용할 도구 리스트를 설정합니다. 여기서는 Wikipedia 문서 스토어를 활용하는 검색 및 조회 도구가 포함됩니다.
    • llm: 사용할 언어 모델을 설정합니다. 여기서는 llm 변수에 저장된 대화형 OpenAI 언어 모델을 사용합니다.
    • agent="react-docstore": 초기화할 에이전트의 유형을 지정합니다. 여기서는 'react-docstore'으로 설정되어 있습니다.
    • verbose=True: 초기화 및 실행 과정에서 발생하는 정보를 자세히 출력하도록 설정합니다.
    • max_iterations=3: 에이전트가 수행할 최대 반복 횟수를 설정합니다. 여기서는 최대 3회까지 반복합니다.

이렇게 초기화된 docstore_agent는 Wikipedia 문서 스토어를 활용하여 검색 및 조회를 수행할 수 있는 에이전트입니다.

 

 

Let’s try the following: 다음을 시도해 봅시다:

 

docstore_agent("What were Archimedes' last words?")
> Entering new AgentExecutor chain...
Thought: I need to search Archimedes and find his last words.
Action: Search[Archimedes]
Observation: Archimedes of Syracuse (; c. 287 – c. 212 BC) was a Greek mathematician, physicist, engineer...
Thought: The paragraph does not mention Archimedes' last words. I need to look up "last words".
Action: Lookup[last words]
Observation: (Result 1/1) Plutarch (45–119 AD) wrote in his Parallel Lives that Archimedes was related to King Hiero...
Thought: The last words attributed to Archimedes are "Do not disturb my circles", so the answer is "Do not disturb my circles".
Action: Finish["Do not disturb my circles"]

> Finished chain.
{'input': "What were Archimedes' last words?",
 'output': '"Do not disturb my circles"'}

 

로컬에서 돌리면 아래와 같이 iteration limit이나 time limit에 걸려서 stop 합니다. 

어쨌든 위키피디아에서 질문한 내용을 검색해서 답을 찾으려고 시도는 하네요.

 

 

The prompt for this agent is very long, so we’ll show a shortened version. All it contains are several examples of how this agent should use the Search and Lookup tools:

 

이 에이전트에 대한 프롬프트는 매우 길기 때문에 단축 버전을 표시하겠습니다. 여기에는 이 에이전트가 검색 및 조회 도구를 어떻게 사용해야 하는지에 대한 몇 가지 예가 포함되어 있습니다.

 

Question: What is the elevation range for the area that the eastern sector of the Colorado orogeny extends into?

Thought: I need to search Colorado orogeny, find the area that the eastern sector of the Colorado orogeny extends into, then find the elevation range of the area.
Action: Search[Colorado orogeny]
Observation: The Colorado orogeny was an episode of mountain building (an orogeny) in Colorado and surrounding areas.

Thought: It does not mention the eastern sector. So I need to look up eastern sector.
Action: Lookup[eastern sector]
Observation: (Result 1 / 1) The eastern sector extends into the High Plains and is called the Central Plains orogeny.

Thought: The eastern sector of Colorado orogeny extends into the High Plains. So I need to search High Plains and find its elevation range.
Action: Search[High Plains]
Observation: High Plains refers to one of two distinct land regions

Thought: I need to instead search High Plains (United States).
Action: Search[High Plains (United States)]
Observation: The High Plains are a subregion of the Great Plains. From east to west, the High Plains rise in elevation from around 1,800 to 7,000 ft (550 to 2,130 m).[3]

Thought: High Plains rise in elevation from around 1,800 to 7,000 ft, so the answer is 1,800 to 7,000 ft.
Action: Finish[1,800 to 7,000 ft]

 

The prompt contains several examples in a similar format. At the end of the prompt, we see:

 

프롬프트에는 비슷한 형식의 여러 예가 포함되어 있습니다. 프롬프트가 끝나면 다음이 표시됩니다.

 

Question: {input}
{agent_scratchpad}

 

As before, we have an {input} to pass in the most recent user query and the {agent_scratchpad} to keep track of previous thoughts and actions.

 

이전과 마찬가지로 가장 최근의 사용자 쿼리를 전달하기 위한 {input}과 이전 생각과 행동을 추적하기 한 {agent_scratchpad}가 있습니다.

Unlike our conversational agent, there is no {chat_history} input. That means that we are using another zero-shot agent.

 

대화형 에이전트와 달리 {chat_history} 입력이 없습니다. 이는 우리가 또 다른 제로샷 에이전트를 사용하고 있음을 의미합니다.

 

Note : 프롬프트가 길어서 교재에서는 일부만 보여 줬는데. 저는 로컬에서 프롬프트를 전부 출력해 봤습니다.

 

 

전체 프롬프트 내용은 2023년 11월 15일 현재 아래와 같습니다.

 

Question: What is the elevation range for the area that the eastern sector of the Colorado orogeny extends into?
Thought: I need to search Colorado orogeny, find the area that the eastern sector of the Colorado orogeny extends into, then find the elevation range of the area.
Action: Search[Colorado orogeny]
Observation: The Colorado orogeny was an episode of mountain building (an orogeny) in Colorado and surrounding areas.
Thought: It does not mention the eastern sector. So I need to look up eastern sector.
Action: Lookup[eastern sector]
Observation: (Result 1 / 1) The eastern sector extends into the High Plains and is called the Central Plains orogeny.
Thought: The eastern sector of Colorado orogeny extends into the High Plains. So I need to search High Plains and find its elevation range.
Action: Search[High Plains]
Observation: High Plains refers to one of two distinct land regions
Thought: I need to instead search High Plains (United States).
Action: Search[High Plains (United States)]
Observation: The High Plains are a subregion of the Great Plains. From east to west, the High Plains rise in elevation from around 1,800 to 7,000 ft (550 to 2,130 m).[3]
Thought: High Plains rise in elevation from around 1,800 to 7,000 ft, so the answer is 1,800 to 7,000 ft.
Action: Finish[1,800 to 7,000 ft]

Question: Musician and satirist Allie Goertz wrote a song about the "The Simpsons" character Milhouse, who Matt Groening named after who?
Thought: The question simplifies to "The Simpsons" character Milhouse is named after who. I only need to search Milhouse and find who it is named after.
Action: Search[Milhouse]
Observation: Milhouse Mussolini Van Houten is a recurring character in the Fox animated television series The Simpsons voiced by Pamela Hayden and created by Matt Groening.
Thought: The paragraph does not tell who Milhouse is named after, maybe I can look up "named after".
Action: Lookup[named after]
Observation: (Result 1 / 1) Milhouse was named after U.S. president Richard Nixon, whose middle name was Milhous.
Thought: Milhouse was named after U.S. president Richard Nixon, so the answer is Richard Nixon.
Action: Finish[Richard Nixon]

Question: Which documentary is about Finnish rock groups, Adam Clayton Powell or The Saimaa Gesture?
Thought: I need to search Adam Clayton Powell and The Saimaa Gesture, and find which documentary is about Finnish rock groups.
Action: Search[Adam Clayton Powell]
Observation: Could not find [Adam Clayton Powell]. Similar: [’Adam Clayton Powell III’, ’Seventh Avenue (Manhattan)’, ’Adam Clayton Powell Jr. State Office Building’, ’Isabel Washington Powell’, ’Adam Powell’, ’Adam Clayton Powell (film)’, ’Giancarlo Esposito’].
Thought: To find the documentary, I can search Adam Clayton Powell (film).
Action: Search[Adam Clayton Powell (film)]
Observation: Adam Clayton Powell is a 1989 American documentary film directed by Richard Kilberg. The film is about the rise and fall of influential African-American politician Adam Clayton Powell Jr.[3][4] It was later aired as part of the PBS series The American Experience.
Thought: Adam Clayton Powell (film) is a documentary about an African-American politician, not Finnish rock groups. So the documentary about Finnish rock groups must instead be The Saimaa Gesture.
Action: Finish[The Saimaa Gesture]

Question: What profession does Nicholas Ray and Elia Kazan have in common?
Thought: I need to search Nicholas Ray and Elia Kazan, find their professions, then find the profession they have in common.
Action: Search[Nicholas Ray]
Observation: Nicholas Ray (born Raymond Nicholas Kienzle Jr., August 7, 1911 - June 16, 1979) was an American film director, screenwriter, and actor best known for the 1955 film Rebel Without a Cause.
Thought: Professions of Nicholas Ray are director, screenwriter, and actor. I need to search Elia Kazan next and find his professions.
Action: Search[Elia Kazan]
Observation: Elia Kazan was an American film and theatre director, producer, screenwriter and actor.
Thought: Professions of Elia Kazan are director, producer, screenwriter, and actor. So profession Nicholas Ray and Elia Kazan have in common is director, screenwriter, and actor.
Action: Finish[director, screenwriter, actor]

Question: Which magazine was started first Arthur’s Magazine or First for Women?
Thought: I need to search Arthur’s Magazine and First for Women, and find which was started first.
Action: Search[Arthur’s Magazine]
Observation: Arthur’s Magazine (1844-1846) was an American literary periodical published in Philadelphia in the 19th century.
Thought: Arthur’s Magazine was started in 1844. I need to search First for Women next.
Action: Search[First for Women]
Observation: First for Women is a woman’s magazine published by Bauer Media Group in the USA.[1] The magazine was started in 1989.
Thought: First for Women was started in 1989. 1844 (Arthur’s Magazine) < 1989 (First for Women), so Arthur’s Magazine was started first.
Action: Finish[Arthur’s Magazine]

Question: Were Pavel Urysohn and Leonid Levin known for the same type of work?
Thought: I need to search Pavel Urysohn and Leonid Levin, find their types of work, then find if they are the same.
Action: Search[Pavel Urysohn]
Observation: Pavel Samuilovich Urysohn (February 3, 1898 - August 17, 1924) was a Soviet mathematician who is best known for his contributions in dimension theory.
Thought: Pavel Urysohn is a mathematician. I need to search Leonid Levin next and find its type of work.
Action: Search[Leonid Levin]
Observation: Leonid Anatolievich Levin is a Soviet-American mathematician and computer scientist.
Thought: Leonid Levin is a mathematician and computer scientist. So Pavel Urysohn and Leonid Levin have the same type of work.
Action: Finish[yes]


Question: {input}
{agent_scratchpad}

 

 

Let’s look at one final agent — the self-ask-with-search agent. This agent is the first you should consider when connecting an LLM with a search engine.

 

최종 에이전트인 검색을 통한 자체 질문 에이전트를 살펴보겠습니다. 이 에이전트는 LLM을 검색 엔진에 연결할 때 가장 먼저 고려해야 할 에이전트입니다.

The agent will perform searches and ask follow-up questions as often as required to get a final answer. We initialize the agent like so:

 

상담원은 최종 답변을 얻기 위해 필요한 만큼 자주 검색을 수행하고 후속 질문을 합니다. 다음과 같이 에이전트를 초기화합니다.

 

from langchain import SerpAPIWrapper

# initialize the search chain
search = SerpAPIWrapper(serpapi_api_key='serp_api_key')

# create a search tool
tools = [
    Tool(
        name="Intermediate Answer",
        func=search.run,
        description='google search'
    )
]

# initialize the search enabled agent
self_ask_with_search = initialize_agent(
    tools,
    llm,
    agent="self-ask-with-search",
    verbose=True
)

 

이 코드는 langchain 패키지를 사용하여 SerpAPI를 활용하여 구글 검색을 수행하는 에이전트를 초기화하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. from langchain import SerpAPIWrapper: langchain 패키지에서 SerpAPI를 활용한 검색 기능을 제공하는 SerpAPIWrapper를 가져옵니다.
  2. search = SerpAPIWrapper(serpapi_api_key='serp_api_key'): SerpAPIWrapper를 초기화하고, API 키를 설정하여 검색 객체를 생성합니다. 여기서 'serp_api_key'는 실제 SerpAPI의 API 키로 대체되어야 합니다.
  3. tools = [...]: SerpAPI 검색을 수행하는 도구를 초기화합니다.
    • Tool(name="Intermediate Answer", func=search.run, description='google search'): Google 검색을 수행하는 도구입니다.
  4. self_ask_with_search = initialize_agent(tools, llm, agent="self-ask-with-search", verbose=True): 검색 기능이 포함된 에이전트를 초기화합니다.
    • tools: 사용할 도구 리스트를 설정합니다. 여기서는 SerpAPI를 통한 Google 검색이 가능한 도구가 포함됩니다.
    • llm: 사용할 언어 모델을 설정합니다. 여기서는 llm 변수에 저장된 대화형 OpenAI 언어 모델을 사용합니다.
    • agent="self-ask-with-search": 초기화할 에이전트의 유형을 지정합니다. 여기서는 'self-ask-with-search'로 설정되어 있습니다.
    • verbose=True: 초기화 및 실행 과정에서 발생하는 정보를 자세히 출력하도록 설정합니다.

이렇게 초기화된 self_ask_with_search 에이전트는 SerpAPI를 활용하여 Google 검색을 수행할 수 있는 도구를 가지고 있습니다. 이 에이전트를 사용하면 특정 질문에 대한 중간 응답을 검색하여 얻을 수 있습니다.

 

Now let’s ask a question requiring multiple searches and “self ask” steps.

 

이제 여러 검색과 " self ask " 단계가 필요한 질문을 해보겠습니다.

 

self_ask_with_search(
    "who lived longer; Plato, Socrates, or Aristotle?"
)
> Entering new AgentExecutor chain...
 Yes.
Follow up: How old was Plato when he died?
Intermediate answer: eighty
Follow up: How old was Socrates when he died?
Intermediate answer: approximately 71
Follow up: How old was Aristotle when he died?
Intermediate answer: 62 years
So the final answer is: Plato

> Finished chain.
{'input': 'who lived longer; Plato, Socrates, or Aristotle?',
 'output': 'Plato'}

 

질문은 플라톤과 소크라테스 그리고 아리스토텔레스 중 누가 가장 오래 살았는가 입니다.

SerpAPI는 구글 검색을 도와 주는 API 인 것 같습니다. 이곳에 가입해서 API key를 얻어서 로컬에서 돌렸습니다.

 

 

 

로컬에서는 질문을 잘 못 이해해서 구글 검색할 때 질문을 When did someone die? 로 했네요. 교재에서는 How old was when some one died? 라고 제대로 했는데 말이죠.

어쨌든 SERPAPI를 사용하는 것과 스스로 질문을 만들어 내는 동작은 잘 하는 것 같습니다.

 

질문에서 who를 Who로 바꾸어 보았습니다.

 

 

이랬더니 답변이 제대로 나옵니다. w를 W로 바꾸었을 뿐인데 ChatGPT는 다르게 해석하네요.

하여간 이 기능은 로컬에서도 제대로 작동하는 것을 확인 했습니다.

 

재미 삼아 하나 더 질문 해 봤습니다.

 

 

올해 5월에 83세의 나이로 죽은 Tina Turner의 정보도 구글 검색으로 찾았네요.

 

We can see the multi-step process of the agent. It performs multiple follow-up questions to hone in on the final answer.

 

에이전트의 다단계 프로세스를 볼 수 있습니다. 최종 답을 찾기 위해 여러 가지 후속 질문을 수행합니다.

That’s it for this chapter on LangChain agents. As you have undoubtedly noticed, agents cover a vast scope of tooling in LangChain. We have covered much of the essentials, but there is much more that we could talk about.

 

LangChain 에이전트에 대한 이번 장의 내용은 이것입니다. 의심할 바 없이 아시다시피 에이전트는 LangChain에서 광범위한 도구를 다루고 있습니다. 우리는 필수적인 내용을 많이 다루었지만, 이야기할 수 있는 내용이 훨씬 더 많습니다.

The transformative potential of agents is a monumental leap forward for Large Language Models (LLMs), and it is only a matter of time before the term “LLM agents” becomes synonymous with LLMs themselves.

 

에이전트의 혁신적인 잠재력은 LLM(대형 언어 모델)의 획기적인 도약이며 "LLM 에이전트"라는 용어가 LLM 자체와 동의어가 되는 것은 시간 문제일 뿐입니다.

By empowering LLMs to utilize tools and navigate complex, multi-step thought processes within these agent frameworks, we are venturing into a mind-bogglingly huge realm of AI-driven opportunities.

 

LLM이 도구를 활용하고 이러한 에이전트 프레임워크 내에서 복잡하고 다단계 사고 프로세스를 탐색할 수 있도록 지원함으로써 우리는 놀라울 정도로 거대한 AI 기반 기회 영역으로 모험을 떠나고 있습니다.

 

References

[1] Langchain.io (2019), Wayback Machine

[2] Jun-hang Lee, Mother of Language Slides (2018), SlideShare

 

 

 

 

반응형

Chapter 5. Retrieval Augmentation

2023. 11. 15. 05:40 | Posted by 솔웅


반응형

https://www.pinecone.io/learn/series/langchain/langchain-retrieval-augmentation/

 

Fixing Hallucination with Knowledge Bases | Pinecone

© Pinecone Systems, Inc. | San Francisco, CA Pinecone is a registered trademark of Pinecone Systems, Inc.

www.pinecone.io

 

Fixing Hallucination with Knowledge Bases

 

Large Language Models (LLMs) have a data freshness problem. Even some of the most powerful models, like GPT-4, have no idea about recent events.

 

LLM(대형 언어 모델)에는 데이터 freshness  최신성 문제가 있습니다. GPT-4와 같은 가장 강력한 모델 조차도 최근 사건에 대해 전혀 모릅니다.

 

The world, according to LLMs, is frozen in time. They only know the world as it appeared through their training data.

 

LLM에 따르면 세상은 시간이 멈춰 있습니다. 그들은 훈련 데이터를 통해 나타난 대로만 세상을 알고 있습니다.

 

That creates problems for any use case that relies on up-to-date information or a particular dataset. For example, you may have internal company documents you’d like to interact with via an LLM.

 

이는 최신 정보나 특정 데이터세트에 의존하는 모든 사용 사례에 문제를 야기합니다. 예를 들어 LLM을 통해 상호 작용하고 싶은 회사 내부 문서가 있을 수 있습니다.

 

The first challenge is adding those documents to the LLM, we could try training the LLM on these documents, but this is time-consuming and expensive. And what happens when a new document is added? Training for every new document added is beyond inefficient — it is simply impossible.

 

첫 번째 과제는 해당 문서를 LLM에 추가하는 것입니다. 이러한 문서에 대해 LLM을 교육해 볼 수 있지만 이는 시간과 비용이 많이 듭니다. 새 문서가 추가되면 어떻게 되나요? 추가된 모든 새 문서에 대한 교육은 비효율적일 뿐만 아니라 불가능합니다.

 

So, how do we handle this problem? We can use retrieval augmentation. This technique allows us to retrieve relevant information from an external knowledge base and give that information to our LLM.

 

그렇다면 이 문제를 어떻게 처리해야 할까요? 검색 확대 retrieval augmentation 를 사용할 수 있습니다. 이 기술을 사용하면 외부 지식 기반에서 관련 정보를 검색하고 해당 정보를 LLM에 제공할 수 있습니다.

 

The external knowledge base is our “window” into the world beyond the LLM’s training data. In this chapter, we will learn all about implementing retrieval augmentation for LLMs using LangChain.

 

외부 지식 기반은 LLM의 교육 데이터 너머의 세계를 들여다보는 "창"입니다. 이 장에서는 LangChain을 사용하여 LLM에 대한 검색 확장 retrieval augmentation 을 구현하는 방법에 대해 모두 배웁니다.

 

https://youtu.be/kvdVduIJsc8?si=iq2SbckGL7DPQT3a

 

 

Creating the Knowledge Base

 

We have two primary types of knowledge for LLMs. The parametric knowledge refers to everything the LLM learned during training and acts as a frozen snapshot of the world for the LLM.

 

LLM에 대한 두 가지 주요 유형의 지식이 있습니다. parametric knowledge 은 LLM이 훈련 중에 배운 모든 것을 의미하며 LLM의 세계에 대한 고정된 스냅샷 역할을 합니다.

 

The second type of knowledge is source knowledge. This knowledge covers any information fed into the LLM via the input prompt. When we talk about retrieval augmentation, we’re talking about giving the LLM valuable source knowledge.

 

두 번째 유형의 지식은 source knowledge 입니다. 이 지식은 입력 프롬프트를 통해 LLM에 입력된 모든 정보를 다룹니다. retrieval augmentation 에 대해 이야기할 때 LLM에 valuable source knowledge을 제공하는 것에 대해 이야기합니다.

 

(You can follow along with the following sections using the Jupyter notebook here! )

https://colab.research.google.com/github/pinecone-io/examples/blob/master/learn/generation/langchain/handbook/05-langchain-retrieval-augmentation.ipynb

 

05-langchain-retrieval-augmentation.ipynb

Run, share, and edit Python notebooks

colab.research.google.com

 

Getting Data for our Knowledge Base

 

To help our LLM, we need to give it access to relevant source knowledge. To do that, we need to create our knowledge base.

 

LLM을 돕기 위해서는 관련 소스 지식에 대한 액세스 권한을 부여해야 합니다. 그러기 위해서는 지식 기반을 구축해야 합니다.

 

We start with a dataset. The dataset used naturally depends on the use case. It could be code documentation for an LLM that needs to help write code, company documents for an internal chatbot, or anything else.

 

데이터 세트로 시작합니다. 자연스럽게 사용되는 데이터 세트는 사용 사례에 따라 다릅니다. 코드 작성에 도움이 되는 LLM용 코드 문서, 내부 챗봇용 회사 문서 등이 될 수 있습니다.

 

In our example, we will use a subset of Wikipedia. To get that data, we will use Hugging Face datasets like so:

 

이 예에서는 Wikipedia의 subset 을 사용합니다. 해당 데이터를 얻기 위해 다음과 같이 Hugging Face 데이터세트를 사용합니다.

 

Large Language Models (LLMs) have a data freshness problem. The most powerful LLMs in the world, like GPT-4, have no idea about recent world events.

 

LLM(대형 언어 모델)에는 데이터 최신성 문제가 있습니다. GPT-4와 같이 세계에서 가장 강력한 LLM은 최근 세계 사건에 대해 전혀 모릅니다.

 

The world of LLMs is frozen in time. Their world exists as a static snapshot of the world as it was within their training data.

 

LLM의 세계는 시간이 멈춰 있습니다. 그들의 세계는 훈련 데이터 내에 있는 것처럼 세계의 정적 스냅샷으로 존재합니다.

 

A solution to this problem is retrieval augmentation. The idea behind this is that we retrieve relevant information from an external knowledge base and give that information to our LLM. In this notebook we will learn how to do that.

 

이 문제에 대한 해결책은 검색 확대(Retrieval Augmentation)입니다. 이에 대한 아이디어는 외부 지식 기반에서 관련 정보를 검색하여 해당 정보를 LLM에 제공한다는 것입니다. 이 노트북에서 우리는 그 방법을 배울 것입니다. LLM(대형 언어 모델)에는 데이터 신선도 문제가 있습니다. GPT-4와 같이 세계에서 가장 강력한 LLM은 최근 세계 사건에 대해 전혀 모릅니다.

 

To begin, we must install the prerequisite libraries that we will be using in this notebook. If we install all libraries we will find a conflict in the Hugging Face datasets library so we must install everything in a specific order like so:

 

시작하려면 이 노트북에서 사용할 필수 구성 요소 라이브러리를 설치해야 합니다. 모든 라이브러리를 설치하면 Hugging Face 데이터 세트 라이브러리에서 충돌이 발견되므로 다음과 같이 특정 순서로 모든 것을 설치해야 합니다.

 

!pip install -qU \
    datasets==2.12.0 \
    apache_beam \
    mwparserfromhell

from datasets import load_dataset

data = load_dataset("wikipedia", "20220301.simple", split='train[:10000]')
data

 

이 코드는 Python 패키지 datasets와 다른 패키지들을 설치하고, wikipedia 데이터셋에서 특정 날짜의 일부 데이터를 불러오는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. !pip install -qU datasets==2.12.0 apache_beam mwparserfromhell: 셸 명령어를 사용하여 필요한 Python 패키지들을 설치합니다.
    • !pip install: Jupyter Notebook에서 셸 명령어를 실행하기 위한 방식입니다.
    • -qU: 설치 과정을 조용히 진행하되 패키지를 최신 버전으로 업그레이드합니다.
    • datasets==2.12.0: datasets 패키지의 2.12.0 버전을 설치합니다.
    • apache_beam: Apache Beam 패키지를 설치합니다.
    • mwparserfromhell: mwparserfromhell 패키지를 설치합니다.
  2. from datasets import load_dataset: datasets 패키지에서 load_dataset 함수를 가져옵니다. 이 함수는 Hugging Face의 데이터셋을 쉽게 로드할 수 있도록 도와줍니다.
  3. data = load_dataset("wikipedia", "20220301.simple", split='train[:10000]'): load_dataset 함수를 사용하여 wikipedia 데이터셋에서 특정 날짜의 20220301.simple 버전을 로드합니다. 여기서는 학습 데이터 중 처음 10,000개의 샘플을 불러옵니다. 결과로 data 변수에 데이터셋이 할당됩니다.

이렇게 초기화된 data 변수에는 wikipedia 데이터셋의 일부가 로드되어 있습니다. 이 데이터셋을 통해 Wikipedia의 문서나 문장 등의 정보를 활용할 수 있습니다.

 

data[6]

 

Now we install the remaining libraries:

 

!pip install -qU \
  langchain==0.0.162 \
  openai==0.27.7 \
  tiktoken==0.4.0 \
  "pinecone-client[grpc]"==2.2.2

 

 

🚨 Note: the above pip install is formatted for Jupyter notebooks. If running elsewhere you may need to drop the !.

 

🚨 참고: 위의 pip 설치는 Jupyter 노트북용으로 형식화되었습니다. 다른 곳에서 실행하는 경우 !를 삭제해야 할 수도 있습니다.


Every record contains a lot of text. Our first task is therefore to identify a good preprocessing methodology for chunking these articles into more "concise" chunks to later be embedding and stored in our Pinecone vector database.

 

모든 레코드에는 많은 텍스트가 포함되어 있습니다. 따라서 우리의 첫 번째 작업은 나중에 Pinecone 벡터 데이터베이스에 포함되고 저장될 수 있도록 이러한 기사를 보다 "간결한" 청크로 분할하기 위한 좋은 전처리 방법을 식별하는 것입니다.

 

For this we use LangChain's RecursiveCharacterTextSplitter to split our text into chunks of a specified max length.

 

이를 위해 LangChain의 RecursiveCharacterTextSplitter를 사용하여 텍스트를 지정된 최대 길이의 청크로 분할합니다.

 

 

Creating Chunks

 

Splitting our text into smaller chunks is essential for several reasons. Primarily we are looking to:

 

여러 가지 이유로 텍스트를 작은 덩어리로 나누는 것이 필수적입니다. 주로 우리가 찾고 있는 것은:

 

  • Improve “embedding accuracy” — this will improve the relevance of results later.
  • "임베딩 정확도" 향상 - 이는 나중에 결과의 관련성을 향상시킵니다.
  • Reduce the amount of text fed into our LLM as source knowledge. Limiting input improves the LLM’s ability to follow instructions, reduces generation costs, and helps us get faster responses.
  • LLM에 소스 지식으로 입력되는 텍스트의 양을 줄입니다. 입력을 제한하면 LLM의 지침 준수 능력이 향상되고 생성 비용이 절감되며 더 빠른 응답을 얻을 수 있습니다.
  • Provide users with more precise information sources as we can narrow down the information source to a smaller chunk of text.
  • 정보 소스를 더 작은 텍스트 덩어리로 좁힐 수 있으므로 사용자에게 보다 정확한 정보 소스를 제공합니다.
  • In the case of very long chunks of text, we will exceed the maximum context window of our embedding or completion models. Splitting these chunks makes it possible to add these longer documents to our knowledge base.
  • 매우 긴 텍스트 청크의 경우 임베딩 또는 완성 모델의 최대 컨텍스트 창을 초과합니다. 이러한 청크를 분할하면 이러한 긴 문서를 지식 기반에 추가할 수 있습니다.

To create these chunks, we first need a way of measuring the length of our text. LLMs don’t measure text by word or character — they measure it by “tokens”.

 

이러한 청크를 만들려면 먼저 텍스트의 길이를 측정하는 방법이 필요합니다. LLM은 텍스트를 단어나 문자로 측정하지 않고 "토큰"으로 측정합니다.

 

A token is typically the size of a word or sub-word and varies by LLM. The tokens themselves are built using a tokenizer. We will be using gpt-3.5-turbo as our completion model, and we can initialize the tokenizer for this model like so:

 

토큰은 일반적으로 단어 또는 하위 단어의 크기이며 LLM에 따라 다릅니다. 토큰 자체는 토크나이저를 사용하여 구축됩니다. 우리는 완성 모델로 gpt-3.5-turbo를 사용할 것이며 다음과 같이 이 모델에 대한 토크나이저를 초기화할 수 있습니다.

 

import tiktoken  # !pip install tiktoken

tokenizer = tiktoken.get_encoding('p50k_base')

 

Using the tokenizer, we can create tokens from plain text and count the number of tokens. We will wrap this into a function called tiktoken_len:

 

토크나이저를 사용하면 일반 텍스트에서 토큰을 생성하고 토큰 수를 계산할 수 있습니다. 이것을 tiktoken_len이라는 함수로 래핑하겠습니다.

 

# create the length function
def tiktoken_len(text):
    tokens = tokenizer.encode(
        text,
        disallowed_special=()
    )
    return len(tokens)

tiktoken_len("hello I am a chunk of text and using the tiktoken_len function "
             "we can find the length of this chunk of text in tokens")
28

 

이 코드는 텍스트의 토큰 수를 계산하는 사용자 정의 함수 tiktoken_len을 정의하고, 이 함수를 호출하여 특정 텍스트의 토큰 수를 출력하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. 함수 정의:
    • tiktoken_len: 함수의 이름으로, 텍스트의 토큰 수를 계산하는 함수입니다.
    • text: 함수에 전달되는 텍스트입니다.
    • tokenizer.encode: 주어진 텍스트를 토큰으로 인코딩하는 함수입니다.
    • disallowed_special=(): 특수 토큰을 허용하지 않도록 설정합니다.
    • len(tokens): 인코딩된 토큰의 길이를 반환합니다.
  1. 함수 호출:
    • tiktoken_len 함수를 호출하여 주어진 텍스트의 토큰 수를 계산합니다.
    • 여기서는 주어진 텍스트의 토큰 수를 반환하는 것이 목적입니다.

따라서, 이 코드는 주어진 텍스트의 토큰 수를 계산하는 사용자 정의 함수를 정의하고, 특정 텍스트에 대해 이 함수를 호출하여 토큰 수를 출력하는 예제입니다.

 

 

With our token counting function ready, we can initialize a LangChain RecursiveCharacterTextSplitter object. This object will allow us to split our text into chunks no longer than what we specify via the chunk_size parameter.

 

토큰 계산 기능이 준비되면 LangChain RecursiveCharacterTextSplitter 개체를 초기화할 수 있습니다. 이 개체를 사용하면 Chunk_size 매개변수를 통해 지정한 것보다 더 길지 않은 청크로 텍스트를 분할할 수 있습니다.

 

from langchain.text_splitter import RecursiveCharacterTextSplitter

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=400,
    chunk_overlap=20,
    length_function=tiktoken_len,
    separators=["\n\n", "\n", " ", ""]
)

 

Now we split the text like so: 이제 텍스트를 다음과 같이 분할합니다.

 

chunks = text_splitter.split_text(data[6]['text'])[:3]
chunks
['Alan Mathison Turing OBE FRS (London, 23 June 1912 – Wilmslow, Cheshire, 7 June 1954) was an English mathematician and computer scientist. He was born in Maida Vale, London.\n\nEarly life and family \nAlan Turing was born in Maida Vale, London on 23 June 1912. His father was part of a family of merchants from Scotland. His mother, Ethel Sara, was the daughter of an engineer.\n\nEducation \nTuring went to St. Michael\'s, a school at 20 Charles Road, St Leonards-on-sea, when he was five years old.\n"This is only a foretaste of what is to come, and only the shadow of what is going to be.” – Alan Turing.\n\nThe Stoney family were once prominent landlords, here in North Tipperary. His mother Ethel Sara Stoney (1881–1976) was daughter of Edward Waller Stoney (Borrisokane, North Tipperary) and Sarah Crawford (Cartron Abbey, Co. Longford); Protestant Anglo-Irish gentry.\n\nEducated in Dublin at Alexandra School and College; on October 1st 1907 she married Julius Mathison Turing, latter son of Reverend John Robert Turing and Fanny Boyd, in Dublin. Born on June 23rd 1912, Alan Turing would go on to be regarded as one of the greatest figures of the twentieth century.\n\nA brilliant mathematician and cryptographer Alan was to become the founder of modern-day computer science and artificial intelligence; designing a machine at Bletchley Park to break secret Enigma encrypted messages used by the Nazi German war machine to protect sensitive commercial, diplomatic and military communications during World War 2. Thus, Turing made the single biggest contribution to the Allied victory in the war against Nazi Germany, possibly saving the lives of an estimated 2 million people, through his effort in shortening World War II.',
 'In 2013, almost 60 years later, Turing received a posthumous Royal Pardon from Queen Elizabeth II. Today, the “Turing law” grants an automatic pardon to men who died before the law came into force, making it possible for living convicted gay men to seek pardons for offences now no longer on the statute book.\n\nAlas, Turing accidentally or otherwise lost his life in 1954, having been subjected by a British court to chemical castration, thus avoiding a custodial sentence. He is known to have ended his life at the age of 41 years, by eating an apple laced with cyanide.\n\nCareer \nTuring was one of the people who worked on the first computers. He created the theoretical  Turing machine in 1936. The machine was imaginary, but it included the idea of a computer program.\n\nTuring was interested in artificial intelligence. He proposed the Turing test, to say when a machine could be called "intelligent". A computer could be said to "think" if a human talking with it could not tell it was a machine.\n\nDuring World War II, Turing worked with others to break German ciphers (secret messages). He  worked for the Government Code and Cypher School (GC&CS) at Bletchley Park, Britain\'s codebreaking centre that produced Ultra intelligence.\nUsing cryptanalysis, he helped to break the codes of the Enigma machine. After that, he worked on other German codes.',
 'From 1945 to 1947, Turing worked on the design of the ACE (Automatic Computing Engine) at the National Physical Laboratory. He presented a paper on 19 February 1946. That paper was "the first detailed design of a stored-program computer". Although it was possible to build ACE, there were delays in starting the project. In late 1947 he returned to Cambridge for a sabbatical year. While he was at Cambridge, the Pilot ACE was built without him. It ran its first program on 10\xa0May 1950.\n\nPrivate life \nTuring was a homosexual man. In 1952, he admitted having had sex with a man in England. At that time, homosexual acts were illegal. Turing was convicted. He had to choose between going to jail and taking hormones to lower his sex drive. He decided to take the hormones. After his punishment, he became impotent. He also grew breasts.\n\nIn May 2012, a private member\'s bill was put before the House of Lords to grant Turing a statutory pardon. In July 2013, the government supported it. A royal pardon was granted on 24 December 2013.\n\nDeath \nIn 1954, Turing died from cyanide poisoning. The cyanide came from either an apple which was poisoned with cyanide, or from water that had cyanide in it. The reason for the confusion is that the police never tested the apple for cyanide. It is also suspected that he committed suicide.\n\nThe treatment forced on him is now believed to be very wrong. It is against medical ethics and international laws of human rights. In August 2009, a petition asking the British Government to apologise to Turing for punishing him for being a homosexual was started. The petition received thousands of signatures. Prime Minister Gordon Brown acknowledged the petition. He called Turing\'s treatment "appalling".\n\nReferences\n\nOther websites \nJack Copeland 2012. Alan Turing: The codebreaker who saved \'millions of lives\'. BBC News / Technology']

 

이 코드는 텍스트를 잘게 나누는 작업을 수행하는데 사용되는 RecursiveCharacterTextSplitter 클래스를 초기화하고, 특정 텍스트를 나누어서 반환하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. from langchain.text_splitter import RecursiveCharacterTextSplitter: langchain 패키지에서 RecursiveCharacterTextSplitter 클래스를 가져옵니다. 이 클래스는 텍스트를 재귀적으로 나누는 데 사용됩니다.
  2. text_splitter = RecursiveCharacterTextSplitter(chunk_size=400, chunk_overlap=20, length_function=tiktoken_len, separators=["\n\n", "\n", " ", ""]):
    • RecursiveCharacterTextSplitter 클래스의 인스턴스를 생성합니다.
    • chunk_size=400: 각 청크(조각)의 크기를 지정합니다. 여기서는 400으로 설정되어 있습니다.
    • chunk_overlap=20: 청크 간의 겹치는 부분 크기를 지정합니다. 여기서는 20으로 설정되어 있습니다.
    • length_function=tiktoken_len: 텍스트의 길이를 측정하는 함수로 앞서 정의한 tiktoken_len 함수를 사용합니다.
    • separators=["\n\n", "\n", " ", ""]: 텍스트를 나누는 기준으로 사용할 구분자(separators) 목록을 지정합니다. 여기서는 빈 문자열을 포함한 여러 구분자가 설정되어 있습니다.
  3. chunks = text_splitter.split_text(data[6]['text'])[:3]:
    • text_splitter 객체를 사용하여 특정 텍스트(data[6]['text'])를 나눕니다.
    • [:3]: 나눈 결과 중에서 처음부터 3개의 청크를 선택합니다.

이렇게 초기화된 text_splitter 객체를 사용하여 텍스트를 나누고, 나눈 결과 중에서 처음부터 3개의 청크를 선택한 것입니다. 이렇게 나눈 청크들은 chunks 변수에 할당되어 있습니다.

 

None of these chunks are larger than the 400 chunk size limit we set earlier:

 

이러한 청크는 이전에 설정한 400 청크 크기 제한보다 크지 않습니다.

 

tiktoken_len(chunks[0]), tiktoken_len(chunks[1]), tiktoken_len(chunks[2])
(397, 304, 399)

 

With the text_splitter, we get nicely-sized chunks of text. We’ll use this functionality during the indexing process later. For now, let’s take a look at embeddings.

 

text_splitter를 사용하면 적절한 크기의 텍스트 덩어리를 얻을 수 있습니다. 나중에 인덱싱 프로세스 중에 이 기능을 사용할 것입니다. 지금은 임베딩에 대해 살펴보겠습니다.

 

Creating Embeddings

The vector embeddings are vital to retrieving relevant context for our LLM. We take the chunks of text we’d like to store in our knowledge base and encode each chunk into a vector embedding.

 

벡터 임베딩은 LLM에 대한 관련 컨텍스트를 검색하는 데 필수적입니다. 우리는 지식 베이스에 저장하고 싶은 텍스트 덩어리를 가져와 각 덩어리를 벡터 임베딩으로 인코딩합니다.

 

These embeddings can act as “numerical representations” of the meaning of each chunk of text. This is possible because we create the embeddings with another AI language model that has learned to translate human-readable text into AI-readable embeddings.

 

이러한 임베딩은 각 텍스트 덩어리의 의미를 "수치적으로 표현"하는 역할을 할 수 있습니다. 이는 사람이 읽을 수 있는 텍스트를 AI가 읽을 수 있는 임베딩으로 변환하는 방법을 학습한 또 다른 AI 언어 모델을 사용하여 임베딩을 생성하기 때문에 가능합니다.

 

 

We then store these embeddings in our vector database (more on this soon) and can find text chunks with similar meanings by calculating the distance between embeddings in vector space.

 

그런 다음 이러한 임베딩을 벡터 데이터베이스에 저장하고(자세한 내용은 곧 설명) 벡터 공간에서 임베딩 사이의 거리를 계산하여 유사한 의미를 가진 텍스트 청크를 찾을 수 있습니다.

 

 

 

The embedding model we will use is another OpenAI model called text-embedding-ada-002. We can initialize it via LangChain like so:

 

우리가 사용할 임베딩 모델은 text-embedding-ada-002라는 또 다른 OpenAI 모델입니다. 다음과 같이 LangChain을 통해 초기화할 수 있습니다.

 

from langchain.embeddings.openai import OpenAIEmbeddings

model_name = 'text-embedding-ada-002'

embed = OpenAIEmbeddings(
    document_model_name=model_name,
    query_model_name=model_name,
    openai_api_key=OPENAI_API_KEY
)

 

이 코드는 langchain 패키지에서 OpenAIEmbeddings 클래스를 사용하여 텍스트 임베딩을 생성하는 작업을 수행하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. from langchain.embeddings.openai import OpenAIEmbeddings: langchain 패키지에서 OpenAIEmbeddings 클래스를 가져옵니다. 이 클래스는 OpenAI의 언어 모델을 사용하여 텍스트를 임베딩하는 데 사용됩니다.
  2. model_name = 'text-embedding-ada-002': 사용할 OpenAI 언어 모델의 이름을 지정합니다. 여기서는 'text-embedding-ada-002' 모델을 사용합니다.
  3. embed = OpenAIEmbeddings(document_model_name=model_name, query_model_name=model_name, openai_api_key=OPENAI_API_KEY): OpenAIEmbeddings 클래스의 인스턴스를 생성합니다.
    • document_model_name=model_name: 텍스트 문서에 대한 임베딩을 생성하는 데 사용할 모델의 이름을 설정합니다.
    • query_model_name=model_name: 쿼리(질문)에 대한 임베딩을 생성하는 데 사용할 모델의 이름을 설정합니다.
    • openai_api_key=OPENAI_API_KEY: OpenAI API에 액세스하기 위한 API 키를 지정합니다.

이제 embed 객체를 사용하여 주어진 텍스트 문서나 쿼리에 대한 임베딩을 생성할 수 있습니다. 이렇게 생성된 임베딩은 텍스트의 의미적 표현을 나타내는 벡터로 사용될 수 있습니다.

 

Now we can embed our text:  이제 텍스트를 embed   할 수 있습니다.

 

texts = [
    'this is the first chunk of text',
    'then another second chunk of text is here'
]

res = embed.embed_documents(texts)
len(res), len(res[0])
(2, 1536)

 

From this, we get two embeddings as we passed in two chunks of text. Each embedding is a 1536-dimensional vector. This dimension is simply the output dimensionality of text-embedding-ada-002.

 

여기에서 두 개의 텍스트 덩어리를 전달하면서 두 개의 임베딩을 얻습니다. 각 임베딩은 1536차원 벡터입니다. 이 차원은 단순히 text-embedding-ada-002의 출력 차원입니다.

 

With that, we have our dataset, text splitter, and embedding model. We have everything needed to begin constructing our knowledge base.

 

이를 통해 데이터 세트, 텍스트 분할기 및 임베딩 모델이 있습니다. 우리는 지식 기반 구축을 시작하는 데 필요한 모든 것을 갖추고 있습니다.

 

Vector Database

A vector database is a type of knowledge base that allows us to scale the search of similar embeddings to billions of records, manage our knowledge base by adding, updating, or removing records, and even do things like filtering.

 

벡터 데이터베이스는 유사한 임베딩 검색을 수십억 개의 레코드로 확장하고, 레코드를 추가, 업데이트 또는 제거하여 knowledge base 을 관리하고, 심지어 필터링과 같은 작업도 수행할 수 있게 해주는 일종의 knowledge base 입니다.

 

We will be using the Pinecone vector database. To use it, we need a free API key. Then we initialize our database index like so:

 

우리는 Pinecone 벡터 데이터베이스를 사용할 것입니다. 이를 사용하려면 무료 API 키가 필요합니다. 그런 다음 데이터베이스 인덱스를 다음과 같이 초기화합니다.

 

import pinecone

index_name = 'langchain-retrieval-augmentation'

pinecone.init(
        api_key="YOUR_API_KEY",  # find api key in console at app.pinecone.io
        environment="YOUR_ENV"  # find next to api key in console
)

# we create a new index
pinecone.create_index(
        name=index_name,
        metric='dotproduct',
        dimension=len(res[0]) # 1536 dim of text-embedding-ada-002
)

 

이 코드는 Pinecone을 사용하여 새로운 인덱스를 만들고 설정하는 작업을 수행하는 예제입니다. Pinecone은 벡터 검색을 위한 서비스로, 벡터를 저장하고 검색할 수 있는 기능을 제공합니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. import pinecone: Pinecone 패키지를 가져옵니다. Pinecone은 벡터 검색 서비스를 구현하는 패키지입니다.
  2. index_name = 'langchain-retrieval-augmentation': 새로 생성할 Pinecone 인덱스의 이름을 지정합니다. 여기서는 'langchain-retrieval-augmentation'으로 설정되어 있습니다.
  3. pinecone.init(api_key="YOUR_API_KEY", environment="YOUR_ENV"): Pinecone을 초기화하고 설정을 수행합니다.
    • api_key="YOUR_API_KEY": Pinecone API에 액세스하기 위한 API 키를 설정합니다. 여기서는 사용자가 직접 발급받은 API 키를 넣어야 합니다.
    • environment="YOUR_ENV": Pinecone 서비스가 실행되는 환경을 설정합니다. 이는 Pinecone 콘솔에서 확인할 수 있는 환경 값입니다.
  4. pinecone.create_index(name=index_name, metric='dotproduct', dimension=len(res[0])): Pinecone에 새로운 인덱스를 생성합니다.
    • name=index_name: 새로운 인덱스의 이름을 설정합니다.
    • metric='dotproduct': 벡터 간 유사성을 측정하는 데 사용할 메트릭을 지정합니다. 여기서는 내적(dotproduct)을 사용하고 있습니다.
    • dimension=len(res[0]): 벡터의 차원을 설정합니다. len(res[0])는 앞서 언급된 res 변수의 첫 번째 요소의 차원을 나타냅니다. 이 경우에는 1536차원으로 설정되어 있습니다. Pinecone은 벡터의 차원을 사전에 알고 있어야 하기 때문에 이를 설정합니다.

이렇게 Pinecone을 초기화하고, 새로운 인덱스를 생성하는 코드입니다. Pinecone은 생성된 인덱스를 통해 벡터를 저장하고 검색할 수 있는 서비스를 제공합니다.

 

Then we connect to the new index: 그런 다음 새 인덱스에 연결합니다.

 

index = pinecone.GRPCIndex(index_name)

index.describe_index_stats()
{'dimension': 1536,
 'index_fullness': 0.0,
 'namespaces': {},
 'total_vector_count': 0}

 

이 코드는 Pinecone에서 설정한 인덱스에 대한 통계 정보를 조회하는 예제입니다. Pinecone에서는 벡터 검색을 위한 인덱스를 설정하고, 이러한 인덱스에 대한 통계 정보를 확인할 수 있습니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. index = pinecone.GRPCIndex(index_name): pinecone.GRPCIndex 클래스의 인스턴스를 생성합니다.
    • index_name: 조회하려는 Pinecone 인덱스의 이름을 전달합니다. 이전 코드에서 생성한 인덱스의 이름인 'langchain-retrieval-augmentation'이 여기서 사용됩니다.
  2. index.describe_index_stats(): 생성한 index 객체를 통해 해당 인덱스에 대한 통계 정보를 조회합니다.
    • describe_index_stats(): Pinecone에서 제공하는 메서드로, 인덱스에 대한 통계 정보를 반환합니다.

따라서, 이 코드는 Pinecone에서 설정한 특정 인덱스의 통계 정보를 조회하는 작업을 수행합니다. 이 통계 정보는 해당 인덱스에 저장된 벡터의 상태 및 특성에 대한 정보를 포함합니다.

 

 

We will see that the new Pinecone index has a total_vector_count of 0 because we haven’t added any vectors yet. Our next task is to do that.

 

아직 벡터를 추가하지 않았기 때문에 새 Pinecone 인덱스의 total_Vector_count가 0임을 알 수 있습니다. 우리의 다음 임무는 그것을 하는 것입니다.

The indexing process consists of us iterating through the data we’d like to add to our knowledge base, creating IDs, embeddings, and metadata — then adding these to the index.

 

indexing   프로세스는 knowledge base 에 추가하려는 데이터를 반복하고 ID, 임베딩 및 메타데이터를 생성한 다음 이를 색인에 추가하는 것으로 구성됩니다.

We can do this in batches to speed up the process.

 

프로세스 속도를 높이기 위해 이 작업을 일괄적으로 수행할 수 있습니다.

 

from tqdm.auto import tqdm
from uuid import uuid4

batch_limit = 100

texts = []
metadatas = []

for i, record in enumerate(tqdm(data)):
    # first get metadata fields for this record
    metadata = {
        'wiki-id': str(record['id']),
        'source': record['url'],
        'title': record['title']
    }
    # now we create chunks from the record text
    record_texts = text_splitter.split_text(record['text'])
    # create individual metadata dicts for each chunk
    record_metadatas = [{
        "chunk": j, "text": text, **metadata
    } for j, text in enumerate(record_texts)]
    # append these to current batches
    texts.extend(record_texts)
    metadatas.extend(record_metadatas)
    # if we have reached the batch_limit we can add texts
    if len(texts) >= batch_limit:
        ids = [str(uuid4()) for _ in range(len(texts))]
        embeds = embed.embed_documents(texts)
        index.upsert(vectors=zip(ids, embeds, metadatas))
        texts = []
        metadatas = []

 

이 코드는 Pinecone을 사용하여 벡터 검색을 위한 데이터를 인덱싱하는 작업을 수행하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. from tqdm.auto import tqdm: 데이터 처리의 진행 상황을 시각적으로 확인하기 위해 tqdm 패키지를 가져옵니다.
  2. from uuid import uuid4: UUID(Universally Unique Identifier)를 생성하기 위해 uuid4 함수를 가져옵니다.
  3. batch_limit = 100: 각 배치에 포함될 텍스트 및 메타데이터의 최대 수를 나타내는 변수입니다.
  4. texts = [] 및 metadatas = []: 텍스트 및 메타데이터를 저장할 빈 리스트를 초기화합니다.
  5. for i, record in enumerate(tqdm(data)):: 데이터셋에서 각 레코드에 대한 반복문을 수행하며, tqdm을 사용하여 처리 상황을 표시합니다.
  6. metadata = {...}: 각 레코드에서 추출한 메타데이터를 metadata 딕셔너리에 저장합니다. 여기서는 'wiki-id', 'source', 'title' 정보를 추출합니다.
  7. record_texts = text_splitter.split_text(record['text']): 현재 레코드의 텍스트를 text_splitter 객체를 사용하여 나눕니다. 나눈 결과를 record_texts에 저장합니다.
  8. record_metadatas = [...]: 각 텍스트 청크에 대한 메타데이터를 생성하고, 해당 청크의 순서("chunk"), 텍스트("text"), 그리고 이전에 추출한 메타데이터를 포함하는 딕셔너리의 리스트를 생성합니다.
  9. texts.extend(record_texts) 및 metadatas.extend(record_metadatas): 현재 레코드에서 추출한 텍스트와 메타데이터를 각각 texts와 metadatas 리스트에 추가합니다.
  10. if len(texts) >= batch_limit:: 현재까지 추가된 텍스트의 수가 지정된 배치 제한에 도달하면 아래 작업을 수행합니다.
  11. ids = [str(uuid4()) for _ in range(len(texts))]: 각 텍스트에 대한 고유한 UUID를 생성하여 ids 리스트에 저장합니다.
  12. embeds = embed.embed_documents(texts): embed 객체를 사용하여 텍스트를 임베딩합니다. 이는 Pinecone에 벡터를 저장하기 위한 작업입니다.
  13. index.upsert(vectors=zip(ids, embeds, metadatas)): upsert 메서드를 사용하여 벡터와 메타데이터를 Pinecone 인덱스에 추가합니다. zip 함수를 사용하여 각 벡터와 메타데이터를 결합합니다.
  14. texts = [] 및 metadatas = []: 배치 처리가 완료되면 다음 배치를 처리하기 위해 texts와 metadatas 리스트를 초기화합니다.

이렇게 반복문을 통해 데이터를 일정한 크기의 배치로 나누어 Pinecone 인덱스에 추가하는 작업을 수행합니다.

 

We’ve now indexed everything. To check the number of records in our index, we call describe_index_stats again:

 

이제 모든 항목의 색인이 생성되었습니다. 인덱스의 레코드 수를 확인하려면 explain_index_stats를 다시 호출합니다.

 

index.describe_index_stats()
{'dimension': 1536,
 'index_fullness': 0.1,
 'namespaces': {'': {'vector_count': 27437}},
 'total_vector_count': 27437}

 

 

Our index contains ~27K records. As mentioned earlier, we can scale this up to billions, but 27K is enough for our example.

 

우리 색인에는 ~27,000개의 레코드가 포함되어 있습니다. 앞서 언급했듯이 이를 수십억까지 확장할 수 있지만 이 예에서는 27K면 충분합니다.

 

LangChain Vector Store and Querying

We construct our index independently of LangChain. That’s because it’s a straightforward process, and it is faster to do this with the Pinecone client directly. However, we’re about to jump back into LangChain, so we should reconnect to our index via the LangChain library.

 

우리는 LangChain과 독립적으로 인덱스를 구성합니다. 그 이유는 프로세스가 간단하고 Pinecone 클라이언트를 사용하여 직접 수행하는 것이 더 빠르기 때문입니다. 그러나 우리는 곧 LangChain으로 돌아갈 예정이므로 LangChain 라이브러리를 통해 인덱스에 다시 연결해야 합니다.

 

from langchain.vectorstores import Pinecone

text_field = "text"

# switch back to normal index for langchain
index = pinecone.Index(index_name)

vectorstore = Pinecone(
    index, embed.embed_query, text_field
)

 

이 코드는 langchain 패키지에서 Pinecone 벡터 스토어를 초기화하는 작업을 수행하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. from langchain.vectorstores import Pinecone: langchain 패키지에서 Pinecone 벡터 스토어를 가져옵니다.
  2. text_field = "text": 벡터 스토어에서 검색을 수행할 텍스트 필드의 이름을 지정합니다. 여기서는 "text"로 설정되어 있습니다.
  3. index = pinecone.Index(index_name): Pinecone에 대한 Index 객체를 생성합니다.
    • index_name: 앞서 설정한 Pinecone 인덱스의 이름을 사용합니다.
  4. vectorstore = Pinecone(index, embed.embed_query, text_field): Pinecone 벡터 스토어를 초기화합니다.
    • index: Pinecone 인덱스에 대한 객체입니다.
    • embed.embed_query: Pinecone에 저장된 벡터와 검색 쿼리를 임베딩하는 데 사용되는 함수입니다. 이 함수는 embed 객체의 embed_query 메서드입니다.
    • text_field: 검색 쿼리에 사용할 텍스트 필드의 이름을 설정합니다.

이렇게 초기화된 vectorstore 객체는 Pinecone에 저장된 벡터를 활용하여 특정 텍스트 필드에서 검색을 수행할 수 있습니다. Pinecone 벡터 스토어는 벡터를 효율적으로 저장하고 검색하는 데 사용되는 도구입니다.

 

 

We can use the similarity search method to make a query directly and return the chunks of text without any LLM generating the response.

 

유사성 검색 방법을 사용하여 직접 쿼리를 만들고 LLM이 응답을 생성하지 않고도 텍스트 덩어리를 반환할 수 있습니다.

 

query = "who was Benito Mussolini?"

vectorstore.similarity_search(
    query,  # our search query
    k=3  # return 3 most relevant docs
)
[Document(page_content='Benito Amilcare Andrea Mussolini KSMOM GCTE (29 July 1883 – 28 April 1945) was an Italian politician and journalist. He was also the Prime Minister of Italy from 1922 until 1943. He was the leader of the National Fascist Party.\n\nBiography\n\nEarly life\nBenito Mussolini was named after Benito Juarez, a Mexican opponent of the political power of the Roman Catholic Church, by his anticlerical (a person who opposes the political interference of the Roman Catholic Church in secular affairs) father. Mussolini\'s father was a blacksmith. Before being involved in politics, Mussolini was a newspaper editor (where he learned all his propaganda skills) and elementary school teacher.\n\nAt first, Mussolini was a socialist, but when he wanted Italy to join the First World War, he was thrown out of the socialist party. He \'invented\' a new ideology, Fascism, much out of Nationalist\xa0and Conservative views.\n\nRise to power and becoming dictator\nIn 1922, he took power by having a large group of men, "Black Shirts," march on Rome and threaten to take over the government. King Vittorio Emanuele III gave in, allowed him to form a government, and made him prime minister. In the following five years, he gained power, and in 1927 created the OVRA, his personal secret police force. Using the agency to arrest, scare, or murder people against his regime, Mussolini was dictator\xa0of Italy by the end of 1927. Only the King and his own Fascist party could challenge his power.', lookup_str='', metadata={'chunk': 0.0, 'source': 'https://simple.wikipedia.org/wiki/Benito%20Mussolini', 'title': 'Benito Mussolini', 'wiki-id': '6754'}, lookup_index=0),
 Document(page_content='Fascism as practiced by Mussolini\nMussolini\'s form of Fascism, "Italian Fascism"- unlike Nazism, the racist ideology that Adolf Hitler followed- was different and less destructive than Hitler\'s. Although a believer in the superiority of the Italian nation and national unity, Mussolini, unlike Hitler, is quoted "Race? It is a feeling, not a reality. Nothing will ever make me believe that biologically pure races can be shown to exist today".\n\nMussolini wanted Italy to become a new Roman Empire. In 1923, he attacked the island of Corfu, and in 1924, he occupied the city state of Fiume. In 1935, he attacked the African country Abyssinia (now called Ethiopia). His forces occupied it in 1936. Italy was thrown out of the League of Nations because of this aggression. In 1939, he occupied the country Albania. In 1936, Mussolini signed an alliance with Adolf Hitler, the dictator of Germany.\n\nFall from power and death\nIn 1940, he sent Italy into the Second World War on the side of the Axis countries. Mussolini attacked Greece, but he failed to conquer it. In 1943, the Allies landed in Southern Italy. The Fascist party and King Vittorio Emanuel III deposed Mussolini and put him in jail, but he was set free by the Germans, who made him ruler of the Italian Social Republic puppet state which was in a small part of Central Italy. When the war was almost over, Mussolini tried to escape to Switzerland with his mistress, Clara Petacci, but they were both captured and shot by partisans. Mussolini\'s dead body was hanged upside-down, together with his mistress and some of Mussolini\'s helpers, on a pole at a gas station in the village of Millan, which is near the border  between Italy and Switzerland.', lookup_str='', metadata={'chunk': 1.0, 'source': 'https://simple.wikipedia.org/wiki/Benito%20Mussolini', 'title': 'Benito Mussolini', 'wiki-id': '6754'}, lookup_index=0),
 Document(page_content='Fascist Italy \nIn 1922, a new Italian government started. It was ruled by Benito Mussolini, the leader of Fascism in Italy. He became head of government and dictator, calling himself "Il Duce" (which means "leader" in Italian). He became friends with German dictator Adolf Hitler. Germany, Japan, and Italy became the Axis Powers. In 1940, they entered World War II together against France, Great Britain, and later the Soviet Union. During the war, Italy controlled most of the Mediterranean Sea.\n\nOn July 25, 1943, Mussolini was removed by the Great Council of Fascism. On September 8, 1943, Badoglio said that the war as an ally of Germany was ended. Italy started fighting as an ally of France and the UK, but Italian soldiers did not know whom to shoot. In Northern Italy, a movement called Resistenza started to fight against the German invaders. On April 25, 1945, much of Italy became free, while Mussolini tried to make a small Northern Italian fascist state called the Republic of Salò. The fascist state failed and Mussolini tried to flee to Switzerland and escape to Francoist Spain, but he was captured by Italian partisans. On 28 April 1945 Mussolini was executed by a partisan.\n\nAfter World War Two \n\nThe state became a republic on June 2, 1946. For the first time, women were able to vote. Italian people ended the Savoia dynasty and adopted a republic government.\n\nIn February 1947, Italy signed a peace treaty with the Allies. They lost all the colonies and some territorial areas (Istria and parts of Dalmatia).\n\nSince then Italy has joined NATO and the European Community (as a founding member). It is one of the seven biggest industrial economies in the world.\n\nTransportation \n\nThe railway network in Italy totals . It is the 17th longest in the world. High speed trains include ETR-class trains which travel at .', lookup_str='', metadata={'chunk': 5.0, 'source': 'https://simple.wikipedia.org/wiki/Italy', 'title': 'Italy', 'wiki-id': '363'}, lookup_index=0)]

 

All of these are relevant results, telling us that the retrieval component of our systems is functioning. The next step is adding our LLM to generatively answer our question using the information provided in these retrieved contexts.

 

이 모든 것은 관련 결과이며, 이는 우리 시스템의 검색 구성 요소가 작동하고 있음을 알려줍니다. 다음 단계는 검색된 컨텍스트에서 제공된 정보를 사용하여 질문에 생성적으로 답변하기 위해 LLM을 추가하는 것입니다.

 

Generative Question Answering

 

In generative question-qnswering (GQA), we pass our question to the LLM but instruct it to base the answer on the information returned from our knowledge base. We can do this in LangChain easily using the RetrievalQA chain.

 

생성적 질문 답변(GQA)에서는 질문을 LLM에 전달하지만 knowledge base 에서 반환된 정보를 기반으로 답변을 작성하도록 지시합니다. RetrievalQA 체인을 사용하여 LangChain에서 이를 쉽게 수행할 수 있습니다.

 

from langchain.chat_models import ChatOpenAI
from langchain.chains import RetrievalQA

# completion llm
llm = ChatOpenAI(
    openai_api_key=OPENAI_API_KEY,
    model_name='gpt-3.5-turbo',
    temperature=0.0
)

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

 

이 코드는 langchain 패키지에서 대화형 OpenAI 언어 모델과 벡터 검색을 결합하여 정보 검색을 수행하는 작업을 수행하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. from langchain.chat_models import ChatOpenAI: langchain 패키지에서 대화형 OpenAI 언어 모델을 가져옵니다.
  2. from langchain.chains import RetrievalQA: langchain 패키지에서 벡터 검색을 기반으로 한 질문 응답 시스템을 가져옵니다.
  3. llm = ChatOpenAI(openai_api_key=OPENAI_API_KEY, model_name='gpt-3.5-turbo', temperature=0.0): 대화형 OpenAI 언어 모델(ChatOpenAI)을 초기화합니다.
    • openai_api_key=OPENAI_API_KEY: OpenAI API에 액세스하기 위한 API 키를 설정합니다.
    • model_name='gpt-3.5-turbo': 사용할 OpenAI 언어 모델의 이름을 설정합니다. 여기서는 'gpt-3.5-turbo' 모델을 사용합니다.
    • temperature=0.0: 모델의 응답에 대한 랜덤성을 조절하는 온도 매개변수를 0으로 설정하여 확률 분포의 뾰족함을 제거합니다.
  4. qa = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=vectorstore.as_retriever()): 벡터 검색 기반의 질문 응답 시스템(RetrievalQA)을 초기화합니다.
    • llm=llm: 사용할 언어 모델을 설정합니다.
    • chain_type="stuff": 언어 모델과 검색 엔진을 결합하여 생성할 체인의 유형을 설정합니다. 여기서는 'stuff'로 설정되어 있습니다.
    • retriever=vectorstore.as_retriever(): 검색을 수행할 벡터 스토어를 설정합니다. vectorstore 객체를 벡터 검색 엔진으로 변환합니다.

이렇게 초기화된 qa 객체는 대화형 OpenAI 언어 모델과 벡터 검색을 결합하여 사용자의 질문에 대한 답변을 생성하는 시스템을 나타냅니다. 이 시스템은 벡터 검색을 통해 저장된 정보에서 적절한 답변을 찾아내고, OpenAI 언어 모델을 사용하여 질문에 응답합니다.

 

Let’s try this with our earlier query:  이전 쿼리로 이를 시도해 보겠습니다.

 

qa.run(query)
'Benito Mussolini was an Italian politician and journalist who served as the Prime Minister of Italy from 1922 until 1943. He was the leader of the National Fascist Party and invented the ideology of Fascism. Mussolini was a dictator of Italy by the end of 1927, and his form of Fascism, "Italian Fascism," was different and less destructive than Hitler\'s Nazism. Mussolini wanted Italy to become a new Roman Empire and attacked several countries, including Abyssinia (now called Ethiopia) and Greece. He was removed from power in 1943 and was executed by Italian partisans in 1945.'

 

The response we get this time is generated by our gpt-3.5-turbo LLM based on the retrieved information from our vector database.

 

이번에 얻은 응답은 벡터 데이터베이스에서 검색된 정보를 기반으로 gpt-3.5-turbo LLM에 의해 생성되었습니다.

We’re still not entirely protected from convincing yet false hallucinations by the model, they can happen, and it’s unlikely that we can eliminate the problem completely. However, we can do more to improve our trust in the answers provided.

 

우리는 여전히 모델에 의한 설득력 있지만 잘못된 환각 hallucinations  으로부터 완전히 보호되지는 않으며, 이러한 일이 발생할 수 있으며, 문제를 완전히 제거할 수 있을 것 같지 않습니다. 그러나 제공된 답변에 대한 신뢰를 높이기 위해 더 많은 노력을 기울일 수 있습니다.

An effective way of doing this is by adding citations to the response, allowing a user to see where the information is coming from. We can do this using a slightly different version of the RetrievalQA chain called RetrievalQAWithSourcesChain.

 

이를 수행하는 효과적인 방법은 응답에 인용을 추가하여 사용자가 정보의 출처를 확인할 수 있도록 하는 것입니다. RetrievalQAWithSourcesChain이라는 약간 다른 버전의 RetrievalQA 체인을 사용하여 이 작업을 수행할 수 있습니다.

 

from langchain.chains import RetrievalQAWithSourcesChain

qa_with_sources = RetrievalQAWithSourcesChain.from_chain_type(
    llm=llm,
    chain_type="stuff",
    retriever=vectorstore.as_retriever()
)

 

이 코드는 langchain 패키지에서 정보 검색을 수행하는 체인에 대한 초기화를 수행하는 예제입니다. RetrievalQAWithSourcesChain 클래스는 벡터 검색을 기반으로 한 질문 응답 시스템을 나타냅니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. from langchain.chains import RetrievalQAWithSourcesChain: langchain 패키지에서 벡터 검색을 기반으로 한 질문 응답 시스템에 소스(출처) 정보를 추가한 RetrievalQAWithSourcesChain 클래스를 가져옵니다.
  2. qa_with_sources = RetrievalQAWithSourcesChain.from_chain_type(llm=llm, chain_type="stuff", retriever=vectorstore.as_retriever()): RetrievalQAWithSourcesChain 클래스의 인스턴스를 초기화합니다.
    • llm=llm: 사용할 언어 모델을 설정합니다. 여기서는 llm 변수에 저장된 대화형 OpenAI 언어 모델을 사용합니다.
    • chain_type="stuff": 언어 모델과 검색 엔진을 결합하여 생성할 체인의 유형을 설정합니다. 여기서는 'stuff'로 설정되어 있습니다.
    • retriever=vectorstore.as_retriever(): 검색을 수행할 벡터 스토어를 설정합니다. vectorstore 객체를 벡터 검색 엔진으로 변환합니다.

이렇게 초기화된 qa_with_sources 객체는 벡터 검색을 기반으로 한 질문 응답 시스템을 나타냅니다. 더불어, 이 시스템은 각 답변에 대한 소스(출처) 정보를 함께 제공합니다. 이는 사용자에게 답변의 신뢰성과 출처를 제공하는 데 도움이 됩니다.

qa_with_sources(query)

 

{'question': 'who was Benito Mussolini?',
 'answer': 'Benito Mussolini was an Italian politician and journalist who was the Prime Minister of Italy from 1922 until 1943. He was the leader of the National Fascist Party and invented the ideology of Fascism. He became dictator of Italy by the end of 1927 and was friends with German dictator Adolf Hitler. Mussolini attacked Greece and failed to conquer it. He was removed by the Great Council of Fascism in 1943 and was executed by a partisan on April 28, 1945. After the war, several Neo-Fascist movements have had success in Italy, the most important being the Movimento Sociale Italiano. His granddaughter Alessandra Mussolini has outspoken views similar to Fascism. \n',
 'sources': 'https://simple.wikipedia.org/wiki/Benito%20Mussolini, https://simple.wikipedia.org/wiki/Fascism'}

 

Now we have answered the question being asked but also included the source of this information being used by the LLM.

 

이제 우리는 질문에 대한 답변을 마쳤지만 LLM에서 사용하는 이 정보의 출처도 포함했습니다.

We’ve learned how to ground Large Language Models with source knowledge by using a vector database as our knowledge base. Using this, we can encourage accuracy in our LLM’s responses, keep source knowledge up to date, and improve trust in our system by providing citations with every answer.

 

우리는 벡터 데이터베이스를 지식 기반으로 사용하여 소스 지식으로 대규모 언어 모델을 기반으로 하는 방법을 배웠습니다. 이를 사용하면 LLM 응답의 정확성을 높이고, 소스 지식을 최신 상태로 유지하며, 모든 답변에 인용을 제공하여 시스템에 대한 신뢰도를 높일 수 있습니다.

We’re already seeing LLMs and knowledge bases paired together in huge products like Bing’s AI search, Google Bard, and ChatGPT plugins. Without a doubt, the future of LLMs is tightly coupled with high-performance, scalable, and reliable knowledge bases.

 

우리는 이미 Bing의 AI 검색, Google Bard 및 ChatGPT 플러그인과 같은 거대한 제품에서 LLM과 지식 기반이 함께 결합되는 것을 보고 있습니다. 의심의 여지 없이 LLM의 미래는 고성능, 확장 가능하고 안정적인 지식 기반과 긴밀하게 결합되어 있습니다.

반응형

Chapter 4. Conversational Memory

2023. 11. 15. 00:33 | Posted by 솔웅


반응형

https://www.pinecone.io/learn/series/langchain/langchain-conversational-memory/

 

Conversational Memory for LLMs with Langchain | Pinecone

Conversational memory is how a chatbot can respond to multiple queries in a chat-like manner. It enables a coherent conversation, and without it, every query would be treated as an entirely independent input without considering past interactions. The LLM w

www.pinecone.io

 

Conversational Memory for LLMs with Langchain

 

Conversational memory is how a chatbot can respond to multiple queries in a chat-like manner. It enables a coherent conversation, and without it, every query would be treated as an entirely independent input without considering past interactions.

 

대화형 메모리는 챗봇이 채팅과 같은 방식으로 여러 쿼리에 응답할 수 있는 방법입니다. 이는 일관된 대화를 가능하게 하며, 이것이 없으면 모든 쿼리는 과거 상호 작용을 고려하지 않고 완전히 독립적인 입력으로 처리됩니다.

 

The LLM with and without conversational memory. The blue boxes are user prompts and in grey are the LLMs responses. Without conversational memory (right), the LLM cannot respond using knowledge of previous interactions.&nbsp;&nbsp;대화형 메모리가 있거나 없는 LLM. 파란색 상자는 사용자 프롬프트이고 회색 상자는 LLM 응답입니다. 대화형 메모리(오른쪽)가 없으면 LLM은 이전 상호 작용에 대한 지식을 사용하여 응답할 수 없습니다.

 

 

The memory allows a Large Language Model (LLM) to remember previous interactions with the user. By default, LLMs are stateless — meaning each incoming query is processed independently of other interactions. The only thing that exists for a stateless agent is the current input, nothing else.

 

메모리를 사용하면 LLM(대형 언어 모델)이 사용자와의 이전 상호 작용을 기억할 수 있습니다. 기본적으로 LLM은 상태 비저장입니다. 즉, 들어오는 각 쿼리는 다른 상호 작용과 독립적으로 처리됩니다. 상태 비저장 에이전트에 존재하는 유일한 것은 현재 입력이며 다른 것은 없습니다.

 

There are many applications where remembering previous interactions is very important, such as chatbots. Conversational memory allows us to do that.

 

챗봇과 같이 이전 상호 작용을 기억하는 것이 매우 중요한 애플리케이션이 많이 있습니다. 대화 기억을 통해 우리는 그렇게 할 수 있습니다.

 

There are several ways that we can implement conversational memory. In the context of [LangChain](/learn/langchain-intro/, they are all built on top of the ConversationChain.

 

대화형 메모리를 구현하는 방법에는 여러 가지가 있습니다. [LangChain](/learn/langchain-intro/의 맥락에서 이들은 모두 ConversationChain 위에 구축되었습니다.

 

https://youtu.be/X05uK0TZozM?si=fvoIMy8W8ZtPueGO

 

 

ConversationChain

 

We can start by initializing the ConversationChain. We will use OpenAI’s text-davinci-003 as the LLM, but other models like gpt-3.5-turbo can be used.

 

ConversationChain을 초기화하는 것부터 시작할 수 있습니다. OpenAI의 text-davinci-003을 LLM으로 사용하지만 gpt-3.5-turbo와 같은 다른 모델도 사용할 수 있습니다.

 

from langchain import OpenAI
from langchain.chains import ConversationChain

# first initialize the large language model
llm = OpenAI(
	temperature=0,
	openai_api_key="OPENAI_API_KEY",
	model_name="text-davinci-003"
)

# now initialize the conversation chain
conversation = ConversationChain(llm=llm)

 

이 코드는 langchain 패키지에서 OpenAI 및 ConversationChain 클래스를 사용하여 대화 체인을 초기화하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. from langchain import OpenAI: langchain 패키지에서 OpenAI 클래스를 가져옵니다. 이 클래스는 OpenAI의 언어 모델을 사용하기 위한 인터페이스를 제공합니다.
  2. from langchain.chains import ConversationChain: langchain 패키지에서 ConversationChain 클래스를 가져옵니다. 이 클래스는 대화 체인을 구현하는 데 사용됩니다.
  3. llm = OpenAI(...):
    • OpenAI 클래스의 인스턴스를 생성합니다.
    • temperature=0: 온도(temperature)를 0으로 설정합니다. 온도는 모델의 출력에 대한 불확실성을 조절하는 매개변수로, 0으로 설정하면 출력이 더 결정적이고 확실해집니다.
    • openai_api_key="OPENAI_API_KEY": OpenAI API에 액세스하기 위한 API 키를 지정합니다. 여기서는 실제 API 키를 넣어야 합니다.
    • model_name="text-davinci-003": 사용할 OpenAI 모델의 이름을 지정합니다. 여기서는 "text-davinci-003" 모델을 사용합니다.
  4. conversation = ConversationChain(llm=llm): ConversationChain 클래스의 인스턴스를 생성합니다.
    • llm=llm: 사용할 언어 모델을 나타내는 OpenAI 클래스의 인스턴스를 전달합니다.

이렇게 초기화된 llm과 conversation 객체를 사용하면 OpenAI 언어 모델을 활용한 대화형 작업을 수행할 수 있습니다.

 

 

로컬에서는 api 키를 외부 파일에서 읽어오고 모델은 gpt-3.5-turbo-instruct를 사용했습니다.

 

 

 

We can see the prompt template used by the ConversationChain like so:

 

다음과 같이 ConversationChain에서 사용하는 프롬프트 템플릿을 볼 수 있습니다.

 

print(conversation.prompt.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. If the AI does not know the answer to a question, it truthfully says it does not know.

Current conversation:
{history}
Human: {input}
AI:

ConversationChain()에서 사용하는 default prompt를 볼 수 있습니다.

 

 

Here, the prompt primes the model by telling it that the following is a conversation between a human (us) and an AI (text-davinci-003). The prompt attempts to reduce hallucinations (where a model makes things up) by stating:

 

여기서 프롬프트는 인간(우리)과 AI(text-davinci-003) 사이의 대화임을 알려 모델을 준비시킵니다. 프롬프트에서는 다음과 같이 말하여 환각 hallucinations  (모델이 꾸며낸 현상)을 줄이려고 시도합니다.

 

"If the AI does not know the answer to a question, it truthfully says it does not know."

 

 

This can help but does not solve the problem of hallucinations — but we will save this for the topic of a future chapter.

 

이것은 도움이 될 수 있지만 환각 문제를 해결하지는 않습니다. 그러나 우리는 이것을 다음 장의 주제로 남겨두겠습니다.

Following the initial prompt, we see two parameters; {history} and {input}. The {input} is where we’d place the latest human query; it is the input entered into a chatbot text box:

 

초기 프롬프트에 이어 두 개의 매개변수가 표시됩니다. {history}  및 {input} . {input}은 최신 human query 를 배치하는 곳입니다. 그것은 챗봇 텍스트 상자에 입력된 내용입니다.

 

 

The {history} is where conversational memory is used. Here, we feed in information about the conversation history between the human and AI.

 

{history}는 대화형 메모리가 사용되는 곳입니다. 여기에서는 인간과 AI 간의 대화 이력에 대한 정보를 제공합니다.

These two parameters — {history} and {input} — are passed to the LLM within the prompt template we just saw, and the output that we (hopefully) return is simply the predicted continuation of the conversation.

 

이 두 매개변수({history} 및 {input})는 방금 본 프롬프트 템플릿 내에서 LLM으로 전달되며, (희망적으로) 반환되는 출력은 단순히 예측된 대화의 연속입니다.

 

Forms of Conversational Memory

We can use several types of conversational memory with the ConversationChain. They modify the text passed to the {history} parameter.

 

ConversationChain을 통해 여러 유형의 대화 메모리를 사용할 수 있습니다. {history} 매개변수에 전달된 텍스트를 수정합니다.

 

ConversationBufferMemory

(Follow along with our Jupyter notebooks

 

https://colab.research.google.com/github/pinecone-io/examples/blob/master/learn/generation/langchain/handbook/03-langchain-conversational-memory.ipynb#scrollTo=uZR3iGJJtdDE

 

==> 이곳으로 가면 CoLab에서 직접 실행할 수 있습니다.

 

 

The ConversationBufferMemory is the most straightforward conversational memory in LangChain. As we described above, the raw input of the past conversation between the human and AI is passed — in its raw form — to the {history} parameter.

 

ConversationBufferMemory는 LangChain에서 가장 간단한 대화 메모리입니다. 위에서 설명한 대로 인간과 AI 간의 과거 대화의 원시 입력은 원시 형식으로 {history} 매개변수에 전달됩니다.

 

from langchain.chains.conversation.memory import ConversationBufferMemory

conversation_buf = ConversationChain(
    llm=llm,
    memory=ConversationBufferMemory()
)

 

conversation_buf("Good morning AI!")

 

{'input': 'Good morning AI!',
 'history': '',
 'response': " Good morning! It's a beautiful day today, isn't it? How can I help you?"}

 

이 코드는 langchain 패키지에서 ConversationBufferMemory 클래스를 사용하여 대화 기록을 저장하면서 대화 체인을 초기화하고, 초기화된 대화 체인을 사용하여 "Good morning AI!"라는 문장을 처리하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. from langchain.chains.conversation.memory import ConversationBufferMemory: langchain 패키지에서 ConversationBufferMemory 클래스를 가져옵니다. 이 클래스는 대화 기록을 저장하는 메모리를 구현한 것으로, 대화 체인에서 사용됩니다.
  2. conversation_buf = ConversationChain(llm=llm, memory=ConversationBufferMemory()): ConversationChain 클래스의 인스턴스를 생성합니다.
    • llm=llm: 사용할 언어 모델을 나타내는 OpenAI 클래스의 인스턴스를 전달합니다.
    • memory=ConversationBufferMemory(): 대화 기록을 저장할 메모리로 ConversationBufferMemory 클래스의 인스턴스를 전달합니다. 이를 통해 대화 중에 이전 대화 내용을 기억하고 저장할 수 있습니다.
  3. conversation_buf("Good morning AI!"): 생성된 conversation_buf 객체에 대화 문장을 전달하여 처리합니다. 여기서는 "Good morning AI!"라는 문장을 전달했습니다. 이 문장은 대화 기록에 추가되고, 이전 대화 기록을 활용하여 언어 모델이 적절한 응답을 생성하는 데 활용될 수 있습니다.

이렇게 초기화된 conversation_buf 객체를 사용하면 대화 과정에서 이전 대화 내용을 유지하면서 언어 모델을 활용할 수 있습니다.

 

로컬 실행 결과는 아래와 같습니다.

 

 

여기서 memory 부분을 없애고 실행 해 보겠습니다.

 

결과는 같습니다. momory를 없애면 default 로 사용하는 것이 있나 봅니다.

일단 교재에 나온대로 memory를 사용해서 이후 코드를 실행해 보겠습니다.

 

We return the first response from the conversational agent. Let’s continue the conversation, writing prompts that the LLM can only answer if it considers the conversation history. We also add a count_tokens function so we can see how many tokens are being used by each interaction.

 

대화 에이전트의 첫 번째 응답을 반환합니다. 대화를 계속하면서 LLM이 대화 기록을 고려할 경우에만 답변할 수 있다는 프롬프트를 작성해 보겠습니다. 또한 각 상호 작용에서 사용되는 토큰 수를 확인할 수 있도록 count_tokens 함수를 추가합니다.

 

from langchain.callbacks import get_openai_callback

def count_tokens(chain, query):
    with get_openai_callback() as cb:
        result = chain.run(query)
        print(f'Spent a total of {cb.total_tokens} tokens')

    return result

 

count_tokens(
    conversation_buf, 
    "My interest here is to explore the potential of integrating Large Language Models with external knowledge"
)

 

Spent a total of 179 tokens

' Interesting! Large Language Models are a type of artificial intelligence that can process natural language and generate text. They can be used to generate text from a given context, or to answer questions about a given context. Integrating them with external knowledge can help them to better understand the context and generate more accurate results. Is there anything else I can help you with?'

 

이 코드는 langchain 패키지에서 get_openai_callback 함수를 사용하여 OpenAI API 콜백을 얻은 후, 이를 활용하여 대화 체인을 실행하면서 사용된 토큰의 수를 계산하고 출력하는 함수를 정의하고 호출하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. from langchain.callbacks import get_openai_callback: langchain 패키지에서 get_openai_callback 함수를 가져옵니다. 이 함수는 OpenAI API 호출에 대한 콜백을 생성하는 함수로, 어떤 함수를 실행하는 동안 API 호출에 대한 정보를 추적할 수 있게 해줍니다.
  2. def count_tokens(chain, query): ...: count_tokens라는 함수를 정의합니다. 이 함수는 언어 모델 체인을 받아들이고, 주어진 쿼리를 실행하면서 사용된 토큰의 수를 계산하고 출력합니다.
  3. with get_openai_callback() as cb: ...: get_openai_callback 함수를 호출하여 OpenAI API 콜백을 가져오고, 이를 cb 변수에 할당합니다. with 문을 사용하여 콜백을 적용하면 해당 블록 내에서 API 호출에 대한 정보를 추적할 수 있습니다.
  4. result = chain.run(query): 대화 체인(chain)을 실행하면서 주어진 쿼리를 처리합니다.
  5. print(f'Spent a total of {cb.total_tokens} tokens'): API 호출 동안 사용된 총 토큰 수를 출력합니다. 이 정보는 cb.total_tokens를 통해 얻어옵니다.
  6. return result: 실행 결과를 반환합니다.
  7. count_tokens(conversation_buf, "..."): 앞서 정의한 count_tokens 함수를 호출하여 대화 체인(conversation_buf)을 사용하면서 특정 쿼리를 실행하고, 사용된 총 토큰 수를 출력합니다. 여기서는 "My interest here is to explore the potential of integrating Large Language Models with external knowledge"라는 문장을 사용했습니다.

이 코드는 대화 체인을 실행하면서 사용된 토큰의 수를 계산하고 출력하는 함수를 호출하는 간단한 예제입니다.

 

로컬에서 실행한 결과는 아래와 같습니다.

 

 

새로운 input을 넣습니다.

count_tokens(
    conversation_buf,
    "I just want to analyze the different possibilities. What can you think of?"
)

 

Spent a total of 268 tokens

' Well, integrating Large Language Models with external knowledge can open up a lot of possibilities. For example, you could use them to generate more accurate and detailed summaries of text, or to answer questions about a given context more accurately. You could also use them to generate more accurate translations, or to generate more accurate predictions about future events.'

 

Token 수가 점점 늘어나는 것을 볼 수 있습니다.

왜냐하면 history가 계속 쌓이기 때문입니다.

 

로컬에서 실행한 결과를 보겠습니다.

 

 

계속 대화를 이어 나가 봅니다.

 

count_tokens(
    conversation_buf, 
    "Which data source types could be used to give context to the model?"
)
Spent a total of 360 tokens

'  There are a variety of data sources that could be used to give context to a Large Language Model. These include structured data sources such as databases, unstructured data sources such as text documents, and even audio and video data sources. Additionally, you could use external knowledge sources such as Wikipedia or other online encyclopedias to provide additional context.'
count_tokens(
    conversation_buf, 
    "What is my aim again?"
)
Spent a total of 388 tokens

' Your aim is to explore the potential of integrating Large Language Models with external knowledge.'

 

로컬에서 돌린 결과 입니다.

 

 

LLM은 같은 질문을 하더라도 약간씩 다른 답변을 하기 때문에 결과는 약간 다릅니다. 또한 교재에서 사용한 모델과 제가 로컬에서 사용한 모델이 다르기 때문에 답변이 다르기도 합니다.

어쨌든 질문이 계속 될 수록 입력 토큰 수는 늘어나는 것을 볼 수 있습니다.

 

history에 이전 대화내용이 계속 쌓이기 때문입니다.

 

The LLM can clearly remember the history of the conversation. Let’s take a look at how this conversation history is stored by the ConversationBufferMemory:

 

LLM은 대화 내용을 명확하게 기억할 수 있습니다. 이 대화 기록이 ConversationBufferMemory에 의해 어떻게 저장되는지 살펴보겠습니다.

 

print(conversation_buf.memory.buffer)

 

Human: Good morning AI!
AI:  Good morning! It's a beautiful day today, isn't it? How can I help you?
Human: My interest here is to explore the potential of integrating Large Language Models with external knowledge
AI:  Interesting! Large Language Models are a type of artificial intelligence that can process natural language and generate text. They can be used to generate text from a given context, or to answer questions about a given context. Integrating them with external knowledge can help them to better understand the context and generate more accurate results. Is there anything else I can help you with?
Human: I just want to analyze the different possibilities. What can you think of?
AI:  Well, integrating Large Language Models with external knowledge can open up a lot of possibilities. For example, you could use them to generate more accurate and detailed summaries of text, or to answer questions about a given context more accurately. You could also use them to generate more accurate translations, or to generate more accurate predictions about future events.
Human: Which data source types could be used to give context to the model?
AI:   There are a variety of data sources that could be used to give context to a Large Language Model. These include structured data sources such as databases, unstructured data sources such as text documents, and even audio and video data sources. Additionally, you could use external knowledge sources such as Wikipedia or other online encyclopedias to provide additional context.
Human: What is my aim again?
AI:  Your aim is to explore the potential of integrating Large Language Models with external knowledge.

 

 

We can see that the buffer saves every interaction in the chat history directly. There are a few pros and cons to this approach. In short, they are:

 

버퍼가 채팅 기록의 모든 상호 작용을 직접 저장하는 것을 볼 수 있습니다. 이 접근 방식에는 몇 가지 장점과 단점이 있습니다. 간단히 말해서, 그들은 다음과 같습니다:

  

Pros                                                                            Cons

Storing everything gives the LLM the maximum amount of information

모든 것을 저장하면 LLM에 최대한의 정보가 제공됩니다.
More tokens mean slowing response times and higher costs

토큰이 많을수록 응답 시간이 느려지고 비용이 높아집니다.
Storing everything is simple and intuitive

모든 것을 저장하는 것은 간단하고 직관적입니다.
Long conversations cannot be remembered as we hit the LLM token limit (4096 tokens for text-davinci-003 and gpt-3.5-turbo)

LLM 토큰 제한(text-davinci-003 및 gpt-3.5-turbo의 경우 4096개 토큰)에 도달하여 긴 대화를 기억할 수 없습니다.

 

The ConversationBufferMemory is an excellent option to get started with but is limited by the storage of every interaction. Let’s take a look at other options that help remedy this.

 

ConversationBufferMemory는 시작하기에 탁월한 옵션이지만 모든 상호 작용의 저장 공간으로 인해 제한됩니다. 이 문제를 해결하는 데 도움이 되는 다른 옵션을 살펴보겠습니다.

 

ConversationSummaryMemory

 

Using ConversationBufferMemory, we very quickly use a lot of tokens and even exceed the context window limit of even the most advanced LLMs available today.

 

ConversationBufferMemory를 사용하면 많은 토큰을 매우 빠르게 사용할 수 있으며 심지어 오늘날 사용 가능한 가장 고급 LLM의 컨텍스트 창 제한도 초과합니다.

To avoid excessive token usage, we can use ConversationSummaryMemory. As the name would suggest, this form of memory summarizes the conversation history before it is passed to the {history} parameter.

 

과도한 토큰 사용을 방지하기 위해 ConversationSummaryMemory를 사용할 수 있습니다. 이름에서 알 수 있듯이 이 형태의 메모리는 {history} 매개변수에 전달되기 전에 대화 기록을 요약합니다.

We initialize the ConversationChain with the summary memory like so:

 

다음과 같이 요약 메모리를 사용하여 ConversationChain을 초기화합니다.

 

from langchain.chains.conversation.memory import ConversationSummaryMemory

conversation = ConversationChain(
	llm=llm,
	memory=ConversationSummaryMemory(llm=llm)
)

 

이 코드는 langchain 패키지에서 ConversationSummaryMemory 클래스를 사용하여 대화 기록의 요약 정보를 저장하면서 대화 체인을 초기화하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. from langchain.chains.conversation.memory import ConversationSummaryMemory: langchain 패키지에서 ConversationSummaryMemory 클래스를 가져옵니다. 이 클래스는 대화 기록을 요약하고 저장하는 메모리를 구현한 것으로, 대화 체인에서 사용됩니다.
  2. conversation = ConversationChain(llm=llm, memory=ConversationSummaryMemory(llm=llm)): ConversationChain 클래스의 인스턴스를 생성합니다.
    • llm=llm: 사용할 언어 모델을 나타내는 OpenAI 클래스의 인스턴스를 전달합니다.
    • memory=ConversationSummaryMemory(llm=llm): 대화 기록을 저장할 메모리로 ConversationSummaryMemory 클래스의 인스턴스를 전달합니다. 이때, 언어 모델(llm)도 함께 전달됩니다.

이렇게 초기화된 conversation 객체는 대화 체인을 실행하면서 대화 기록을 요약 정보와 함께 저장하는 데 사용될 수 있습니다. 이를 통해 대화 중에 이전 대화 내용을 요약하고 저장하여 활용할 수 있습니다.

print(conversation_sum.memory.prompt.template)

 

Progressively summarize the lines of conversation provided, adding onto the previous summary returning a new summary.

EXAMPLE
Current summary:
The human asks what the AI thinks of artificial intelligence. The AI thinks artificial intelligence is a force for good.

New lines of conversation:
Human: Why do you think artificial intelligence is a force for good?
AI: Because artificial intelligence will help humans reach their full potential.

New summary:
The human asks what the AI thinks of artificial intelligence. The AI thinks artificial intelligence is a force for good because it will help humans reach their full potential.
END OF EXAMPLE

Current summary:
{summary}

New lines of conversation:
{new_lines}

New summary:

 

ConversationSummaryMemory() 에서 제공하는 기본 prompt 입니다.

LLM에게 대화의 내용을 요약하라고 가이드를 주고 예제까지 보여 주는 프롬프트를 사용합니다.

 

로컬에서 돌린 결과 입니다. 2023년 11월 14일 현재 해당 프롬프트는 변화 없이 그대로 사용 중이네요.

 

 

Using this, we can summarize every new interaction and append it to a “running summary” of all past interactions. Let’s have another conversation utilizing this approach.

 

이를 사용하여 모든 새로운 상호 작용을 요약하고 모든 과거 상호 작용의 "실행 요약"에 추가할 수 있습니다. 이 접근 방식을 활용하여 또 다른 대화를 나누겠습니다.

 

# without count_tokens we'd call `conversation_sum("Good morning AI!")`
# but let's keep track of our tokens:
count_tokens(
    conversation_sum, 
    "Good morning AI!"
)
Spent a total of 290 tokens

" Good morning! It's a beautiful day today, isn't it? How can I help you?"
count_tokens(
    conversation_sum, 
    "My interest here is to explore the potential of integrating Large Language Models with external knowledge"
)
Spent a total of 440 tokens

" That sounds like an interesting project! I'm familiar with Large Language Models, but I'm not sure how they could be integrated with external knowledge. Could you tell me more about what you have in mind?"
count_tokens(
    conversation_sum, 
    "I just want to analyze the different possibilities. What can you think of?"
)
Spent a total of 664 tokens

' I can think of a few possibilities. One option is to use a large language model to generate a set of candidate answers to a given query, and then use external knowledge to filter out the most relevant answers. Another option is to use the large language model to generate a set of candidate answers, and then use external knowledge to score and rank the answers. Finally, you could use the large language model to generate a set of candidate answers, and then use external knowledge to refine the answers.'
count_tokens(
    conversation_sum, 
    "Which data source types could be used to give context to the model?"
)
Spent a total of 799 tokens

' There are many different types of data sources that could be used to give context to the model. These could include structured data sources such as databases, unstructured data sources such as text documents, or even external APIs that provide access to external knowledge. Additionally, the model could be trained on a combination of these data sources to provide a more comprehensive understanding of the context.'
count_tokens(
    conversation_sum, 
    "What is my aim again?"
)
Spent a total of 853 tokens

' Your aim is to explore the potential of integrating Large Language Models with external knowledge.'

 

로컬에서 실행 한 결과 입니다.

 

 

In this case the summary contains enough information for the LLM to “remember” our original aim. We can see this summary in it’s raw form like so:

 

이 경우 요약에는 LLM이 원래 목표를 " remember "할 수 있을 만큼 충분한 정보가 포함되어 있습니다. 이 요약을 다음과 같은 원시 형식으로 볼 수 있습니다.

 

print(conversation_sum.memory.buffer)
The human greeted the AI with a good morning, to which the AI responded with a good morning and asked how it could help. The human expressed interest in exploring the potential of integrating Large Language Models with external knowledge, to which the AI responded positively and asked for more information. The human asked the AI to think of different possibilities, and the AI suggested three options: using the large language model to generate a set of candidate answers and then using external knowledge to filter out the most relevant answers, score and rank the answers, or refine the answers. The human then asked which data source types could be used to give context to the model, to which the AI responded that there are many different types of data sources that could be used, such as structured data sources, unstructured data sources, or external APIs. Additionally, the model could be trained on a combination of these data sources to provide a more comprehensive understanding of the context. The human then asked what their aim was again, to which the AI responded that their aim was to explore the potential of integrating Large Language Models with external knowledge.

 

로컬 실행 결과

 

 

The number of tokens being used for this conversation is greater than when using the ConversationBufferMemory, so is there any advantage to using ConversationSummaryMemory over the buffer memory?

 

이 대화에 사용되는 토큰의 수가 ConversationBufferMemory를 사용할 때보다 많은데, 버퍼 메모리보다 ConversationSummaryMemory를 사용하면 어떤 이점이 있나요?

 

Token count (y-axis) for the buffer memory vs. summary memory as the number of interactions (x-axis) increases.&nbsp;&nbsp;상호 작용 수(x축)가 증가함에 따라 버퍼 메모리와 요약 메모리의 토큰 수(y축)가 달라집니다.

 

 

For longer conversations, yes. Here, we have a longer conversation. As shown above, the summary memory initially uses far more tokens. However, as the conversation progresses, the summarization approach grows more slowly. In contrast, the buffer memory continues to grow linearly with the number of tokens in the chat.

 

더 긴 대화를 원하신다면 그렇습니다. 여기서 우리는 좀 더 긴 대화를 나누었습니다. 위에 표시된 것처럼 요약 메모리는 처음에 훨씬 더 많은 토큰을 사용합니다. 그러나 대화가 진행됨에 따라 요약 접근 방식은 더욱 느리게 성장합니다. 대조적으로, 버퍼 메모리는 채팅의 토큰 수에 따라 선형적으로 계속 증가합니다.

 

Pros                                                                           Cons

Shortens the number of tokens for long conversations.

긴 대화를 위한 토큰 수를 줄입니다.
Can result in higher token usage for smaller conversations

소규모 대화에서는 토큰 사용량이 높아질 수 있습니다.
Enables much longer conversations

훨씬 더 긴 대화가 가능해집니다.
Memorization of the conversation history is wholly reliant on the summarization ability of the intermediate summarization LLM

대화 내용의 암기는 전적으로 중간 요약 LLM의 요약 능력에 달려 있습니다.
Relatively straightforward implementation, intuitively simple to understand

비교적 간단한 구현, 직관적으로 이해하기 쉽습니다.
Also requires token usage for the summarization LLM; this increases costs (but does not limit conversation length)

또한 요약 LLM을 위한 토큰 사용이 필요합니다. 이로 인해 비용이 증가합니다(그러나 대화 길이는 제한되지 않음).

 

Conversation summarization is a good approach for cases where long conversations are expected. Yet, it is still fundamentally limited by token limits. After a certain amount of time, we still exceed context window limits.

 

Conversation summarization 은 긴 대화가 예상되는 경우에 좋은 접근 방식입니다. 그러나 여전히 토큰 한도에 의해 근본적으로 제한됩니다. 일정 시간이 지난 후에도 여전히 컨텍스트 창 제한을 초과합니다.

 

ConversationBufferWindowMemory

The ConversationBufferWindowMemory acts in the same way as our earlier “buffer memory” but adds a window to the memory. Meaning that we only keep a given number of past interactions before “forgetting” them. We use it like so:

 

ConversationBufferWindowMemory는 이전의 "버퍼 메모리"와 동일한 방식으로 작동하지만 메모리에 창을 추가합니다. 이는 과거 상호 작용을 "잊기" 전에 주어진 수의 과거 상호 작용만 유지한다는 의미입니다. 우리는 그것을 다음과 같이 사용합니다:

 

from langchain.chains.conversation.memory import ConversationBufferWindowMemory

conversation = ConversationChain(
	llm=llm,
	memory=ConversationBufferWindowMemory(k=1)
)

 

이 코드는 langchain 패키지에서 ConversationBufferWindowMemory 클래스를 사용하여 대화 기록의 윈도우화된(최근 대화 기록만 유지하는) 메모리를 구현하면서 대화 체인을 초기화하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. from langchain.chains.conversation.memory import ConversationBufferWindowMemory: langchain 패키지에서 ConversationBufferWindowMemory 클래스를 가져옵니다. 이 클래스는 대화 기록을 윈도우화하여 최근 대화 기록만을 유지하고 저장하는 메모리를 구현한 것으로, 대화 체인에서 사용됩니다.
  2. conversation = ConversationChain(llm=llm, memory=ConversationBufferWindowMemory(k=1)): ConversationChain 클래스의 인스턴스를 생성합니다.
    • llm=llm: 사용할 언어 모델을 나타내는 OpenAI 클래스의 인스턴스를 전달합니다.
    • memory=ConversationBufferWindowMemory(k=1): 대화 기록을 저장할 메모리로 ConversationBufferWindowMemory 클래스의 인스턴스를 전달합니다. k=1은 윈도우의 크기를 나타내며, 여기서는 최근 대화 한 Window을 유지하도록 설정되어 있습니다.

이렇게 초기화된 conversation 객체는 대화 체인을 실행하면서 대화 기록을 윈도우화된 메모리에 저장하는 데 사용될 수 있습니다. 이를 통해 최근 대화 내용만을 유지하고 필요한 경우 활용할 수 있습니다.

 

 

In this instance, we set k=1 — this means the window will remember the single latest interaction between the human and AI. That is the latest human response and the latest AI response. We can see the effect of this below:

 

이 경우 k=1로 설정합니다. 이는 창이 인간과 AI 간의 최신 상호 작용을 기억한다는 의미입니다. 그것이 최신 인간의 대응이고 최신의 AI 대응이다. 아래에서 이에 대한 효과를 볼 수 있습니다.

 

count_tokens(
    conversation_bufw, 
    "Good morning AI!"
)
Spent a total of 85 tokens

" Good morning! It's a beautiful day today, isn't it? How can I help you?"
count_tokens(
    conversation_bufw, 
    "My interest here is to explore the potential of integrating Large Language Models with external knowledge"
)
Spent a total of 178 tokens

' Interesting! Large Language Models are a type of artificial intelligence that can process natural language and generate text. They can be used to generate text from a given context, or to answer questions about a given context. Integrating them with external knowledge can help them to better understand the context and generate more accurate results. Do you have any specific questions about this integration?'
count_tokens(
    conversation_bufw, 
    "I just want to analyze the different possibilities. What can you think of?"
)
Spent a total of 233 tokens

' There are many possibilities for integrating Large Language Models with external knowledge. For example, you could use external knowledge to provide additional context to the model, or to provide additional training data. You could also use external knowledge to help the model better understand the context of a given text, or to help it generate more accurate results.'
count_tokens(
    conversation_bufw, 
    "Which data source types could be used to give context to the model?"
)
Spent a total of 245 tokens

' Data sources that could be used to give context to the model include text corpora, structured databases, and ontologies. Text corpora provide a large amount of text data that can be used to train the model and provide additional context. Structured databases provide structured data that can be used to provide additional context to the model. Ontologies provide a structured representation of knowledge that can be used to provide additional context to the model.'
count_tokens(
    conversation_bufw, 
    "What is my aim again?"
)
Spent a total of 186 tokens

' Your aim is to use data sources to give context to the model.'

 

로컬에서 실행한 결과 입니다.

 

 

로컬에서는 aim을 모른다고 대답하네요. 교재에서는 최근 대화에서 유추해서 대답을 했구요.

 

By the end of the conversation, when we ask "What is my aim again?", the answer to this was contained in the human response three interactions ago. As we only kept the most recent interaction (k=1), the model had forgotten and could not give the correct answer.

 

대화가 끝날 무렵, “또 내 목표는 무엇입니까?”라고 물으면 이에 대한 대답은 세 번의 상호작용 전 인간의 반응에 담겨 있었습니다. 가장 최근의 상호작용(k=1)만 유지했기 때문에 모델이 잊어버리고 정답을 줄 수 없었습니다.

We can see the effective “memory” of the model like so:

 

다음과 같이 모델의 효과적인 "메모리"를 볼 수 있습니다.

 

bufw_history = conversation_bufw.memory.load_memory_variables(
    inputs=[]
)['history']

 

이 코드는 대화 체인의 ConversationBufferWindowMemory에서 현재까지의 대화 기록을 불러오는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. conversation_bufw.memory.load_memory_variables(inputs=[])['history']:
    • conversation_bufw: 앞서 초기화한 ConversationChain 객체입니다. 이 객체는 대화 체인을 나타냅니다.
    • memory: ConversationBufferWindowMemory에서 사용된 메모리 객체를 나타냅니다. 이 메모리에는 최근 대화 기록이 유지되고 있습니다.
    • load_memory_variables(inputs=[]): 메모리에서 변수들을 불러오는 메서드를 호출합니다. 여기서 inputs=[]는 불러오기에 필요한 입력 변수를 지정하는데, 여기서는 입력이 필요하지 않아 빈 리스트로 설정되었습니다.
    • ['history']: 불러오고자 하는 변수의 이름을 지정합니다. 여기서는 대화 기록을 나타내는 'history' 변수를 불러옵니다.

따라서, bufw_history는 현재까지의 대화 기록을 나타내는 변수로 할당됩니다. 이 변수를 통해 이전 대화 내용을 활용할 수 있습니다.

 

print(bufw_history)
Human: What is my aim again?
AI:  Your aim is to use data sources to give context to the model.

 

로컬 실행 결과

 

 

 

Although this method isn’t suitable for remembering distant interactions, it is good at limiting the number of tokens being used — a number that we can increase/decrease depending on our needs. For the longer conversation used in our earlier comparison, we can set k=6 and reach ~1.5K tokens per interaction after 27 total interactions:

 

이 방법은 멀리 떨어져 있는 상호 작용을 기억하는 데 적합하지 않지만 사용되는 토큰 수(필요에 따라 늘리거나 줄일 수 있는 숫자)를 제한하는 데는 좋습니다. 이전 비교에서 사용된 더 긴 대화의 경우 k=6으로 설정하고 총 27번의 상호 작용 후 상호 작용당 ~1.5K 토큰에 도달할 수 있습니다.

 

Token count including the ConversationBufferWindowMemory at k=6 and k=12.&nbsp;k=6 및 k=12에서 ConversationBufferWindowMemory를 포함한 토큰 수입니다.

 

If we only need memory of recent interactions, this is a great option. However, for a mix of both distant and recent interactions, there are other options.

 

최근 상호작용에 대한 기억만 필요하다면 이는 훌륭한 선택입니다. 그러나 원거리 상호작용과 최근 상호작용이 혼합된 경우에는 다른 옵션이 있습니다.

 

ConversationSummaryBufferMemory

 

The ConversationSummaryBufferMemory is a mix of the ConversationSummaryMemory and the ConversationBufferWindowMemory. It summarizes the earliest interactions in a conversation while maintaining the max_token_limit most recent tokens in their conversation. It is initialized like so:

 

ConversationSummaryBufferMemory는 ConversationSummaryMemory와 ConversationBufferWindowMemory가 혼합된 것입니다. 대화에서 가장 최근의 토큰인 max_token_limit를 유지하면서 대화의 가장 초기 상호 작용을 요약합니다. 다음과 같이 초기화됩니다.

 

conversation_sum_bufw = ConversationChain(
    llm=llm, memory=ConversationSummaryBufferMemory(
        llm=llm,
        max_token_limit=650
)

 

 

When applying this to our earlier conversation, we can set max_token_limit to a small number and yet the LLM can remember our earlier “aim”.

 

이를 이전 대화에 적용할 때 max_token_limit를 작은 숫자로 설정할 수 있지만 LLM은 이전 "목표"를 기억할 수 있습니다.

 

This is because that information is captured by the “summarization” component of the memory, despite being missed by the “buffer window” component.

 

이는 해당 정보가 "버퍼 창" 구성 요소에 의해 누락되었음에도 불구하고 메모리의 "요약" 구성 요소에 의해 캡처되기 때문입니다.

 

Naturally, the pros and cons of this component are a mix of the earlier components on which this is based.

 

당연히 이 구성 요소의 장단점은 이 구성 요소의 기반이 되는 이전 구성 요소가 혼합되어 있습니다.

 

Pros                                                                                   Cons

Summarizer means we can remember distant interactions

Summarizer  는 멀리 떨어져 있는 상호 작용을 기억할 수 있음을 의미합니다.
Summarizer increases token count for shorter conversations

Summarizer  는 더 짧은 대화를 위해 토큰 수를 늘립니다.
Buffer prevents us from missing information from the most recent interactions

버퍼는 가장 최근의 상호 작용에서 정보가 누락되는 것을 방지합니다.
Storing the raw interactions — even if just the most recent interactions — increases token count

가장 최근의 상호작용이라도 원시 상호작용을 저장하면 토큰 수가 늘어납니다.

 

 

Although requiring more tweaking on what to summarize and what to maintain within the buffer window, the ConversationSummaryBufferMemory does give us plenty of flexibility and is the only one of our memory types (so far) that allows us to remember distant interactions and store the most recent interactions in their raw — and most information-rich — form.

 

요약할 내용과 버퍼 창 내에서 유지 관리할 내용에 대해 더 많은 조정이 필요하지만 ConversationSummaryBufferMemory는 우리에게 많은 유연성을 제공하며 (지금까지) 먼 상호 작용을 기억하고 가장 최근의 내용을 저장할 수 있는 유일한 메모리 유형입니다. 정보가 가장 풍부하고 원시적인 형태의 상호 작용입니다.

 

Token count comparisons including the ConversationSummaryBufferMemory type with max_token_limit values of 650 and 1300.&nbsp;&nbsp;max_token_limit 값이 650 및 1300인 ConversationSummaryBufferMemory 유형을 포함한 토큰 수 비교.

 

 

We can also see that despite including a summary of past interactions and the raw form of recent interactions — the increase in token count of ConversationSummaryBufferMemory is competitive with other methods.

 

또한 과거 상호작용의 요약과 최근 상호작용의 원시 형태를 포함함에도 불구하고 ConversationSummaryBufferMemory의 토큰 수 증가가 다른 방법과 경쟁적이라는 것을 알 수 있습니다.

 

Other Memory Types

 

The memory types we have covered here are great for getting started and give a good balance between remembering as much as possible and minimizing tokens.

 

여기에서 다룬 메모리 유형은 시작하기에 적합하며 가능한 한 많이 기억하는 것과 토큰을 최소화하는 것 사이에 적절한 균형을 제공합니다.

 

However, we have other options — particularly the ConversationKnowledgeGraphMemory and ConversationEntityMemory. We’ll give these different forms of memory the attention they deserve in upcoming chapters.

 

그러나 다른 옵션, 특히 ConversationKnowledgeGraphMemory 및 ConversationEntityMemory가 있습니다. 우리는 다음 장에서 이러한 다양한 형태의 기억에 마땅한 관심을 기울일 것입니다.

 

That’s it for this introduction to conversational memory for LLMs using LangChain. As we’ve seen, there are plenty of options for helping stateless LLMs interact as if they were in a stateful environment — able to consider and refer back to past interactions.

 

이것이 LangChain을 사용하는 LLM의 대화형 메모리에 대한 소개입니다. 앞서 살펴보았듯이, 상태 비저장 LLM이 마치 상태 저장 환경에 있는 것처럼 상호 작용하여 과거 상호 작용을 고려하고 다시 참조할 수 있도록 돕는 다양한 옵션이 있습니다.

 

As mentioned, there are other forms of memory we can cover. We can also implement our own memory modules, use multiple types of memory within the same chain, combine them with agents, and much more. All of which we will cover in future chapters.

 

언급한 바와 같이, 우리가 다룰 수 있는 다른 형태의 기억이 있습니다. 또한 자체 메모리 모듈을 구현하고, 동일한 체인 내에서 여러 유형의 메모리를 사용하고, 이를 에이전트와 결합하는 등의 작업을 수행할 수 있습니다. 이에 대한 모든 내용은 향후 장에서 다룰 것입니다.

 

 

 

 

 

 

 

 

 

 

 

 

반응형


반응형

Building Composable Pipelines with Chains

 

02-langchain-chains.ipynb

Run, share, and edit Python notebooks

colab.research.google.com

 

!pip install -qU langchain openai

 

LangChain Handbook

 

Getting Started with Chains

Chains are the core of LangChain. They are simply a chain of components, executed in a particular order.

 

체인은 LangChain의 핵심입니다. 이는 단순히 특정 순서로 실행되는 구성 요소 체인입니다.

 

The simplest of these chains is the LLMChain. It works by taking a user's input, passing in to the first element in the chain — a PromptTemplate — to format the input into a particular prompt. The formatted prompt is then passed to the next (and final) element in the chain — a LLM.

 

이러한 체인 중 가장 간단한 것은 LLMChain입니다. 이는 사용자의 입력을 받아 체인의 첫 번째 요소인 PromptTemplate에 전달하여 입력을 특정 프롬프트로 형식화하는 방식으로 작동합니다. 그러면 형식화된 프롬프트가 체인의 다음(그리고 마지막) 요소인 LLM으로 전달됩니다.

 

We'll start by importing all the libraries that we'll be using in this example.

 

이 예제에서 사용할 모든 라이브러리를 가져오는 것부터 시작하겠습니다.

 

 

import inspect
import re

from getpass import getpass
from langchain import OpenAI, PromptTemplate
from langchain.chains import LLMChain, LLMMathChain, TransformChain, SequentialChain
from langchain.callbacks import get_openai_callback

 

 

이 코드는 다양한 모듈과 클래스를 가져오는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. import inspect: Python의 inspect 모듈을 가져옵니다. 이 모듈은 코드 내에서 다양한 객체(함수, 클래스, 모듈 등)를 검사하는 데 사용됩니다. 주로 디버깅이나 동적으로 코드를 조사하는 데 활용됩니다.
  2. import re: 정규 표현식을 사용하기 위해 Python의 re 모듈을 가져옵니다. 정규 표현식은 문자열의 패턴을 검색하고 조작하는 데 사용됩니다.
  3. from getpass import getpass: getpass 모듈에서 getpass 함수를 가져옵니다. 이 함수는 사용자로부터 안전하게 비밀번호나 기타 민감한 정보를 입력 받는 데 사용됩니다.
  4. from langchain import OpenAI, PromptTemplate: langchain 패키지에서 OpenAI 클래스와 PromptTemplate 클래스를 가져옵니다. OpenAI 클래스는 OpenAI의 언어 모델과 상호작용하기 위한 도구를 제공하며, PromptTemplate 클래스는 템플릿 기반의 대화 흐름을 만들기 위한 도구입니다.
  5. from langchain.chains import LLMChain, LLMMathChain, TransformChain, SequentialChain: langchain 패키지의 chains 모듈에서 다양한 체인 관련 클래스를 가져옵니다. 이러한 클래스들은 언어 모델, 수학 모델, 변환 체인, 순차적 체인 등을 구성하는 데 사용됩니다.
  6. from langchain.callbacks import get_openai_callback: langchain 패키지의 callbacks 모듈에서 get_openai_callback 함수를 가져옵니다. 콜백은 비동기적인 이벤트를 처리하거나 특정 동작을 실행하는 데 사용됩니다.

이 코드는 다양한 도구와 클래스들을 가져와서 활용할 수 있도록 준비하는 것으로 보입니다. 가져온 모듈과 클래스들은 이후 코드에서 사용될 것으로 예상됩니다. 코드의 나머지 부분이 있는 경우, 해당 부분에 따라 더 자세한 설명을 제공할 수 있습니다.

 

 

To run this notebook, we will need to use an OpenAI LLM. Here we will setup the LLM we will use for the whole notebook, just input your openai api key when prompted.

 

이 노트북을 실행하려면 OpenAI LLM을 사용해야 합니다. 여기에서는 전체 노트북에 사용할 LLM을 설정합니다. 메시지가 표시되면 openai api 키를 입력하기만 하면 됩니다.

 

OPENAI_API_KEY = getpass()

 

이 코드는 getpass 함수를 사용하여 OpenAI API 키를 안전하게 입력받는 예제입니다. 아래는 코드의 설명입니다:

  1. from getpass import getpass: getpass 함수를 사용하기 위해 Python의 getpass 모듈에서 getpass 함수를 가져옵니다. 이 함수는 사용자로부터 안전하게 입력을 받는 데 사용됩니다.
  2. OPENAI_API_KEY = getpass(): getpass() 함수를 호출하여 사용자에게 입력을 받습니다. 사용자가 키보드로 입력한 값은 표시되지 않고, 안전하게 읽어들입니다. 이렇게 입력받은 값은 OPENAI_API_KEY 변수에 할당됩니다.

따라서, 이 코드는 사용자로부터 OpenAI API 키를 안전하게 입력받아 OPENAI_API_KEY 변수에 저장하는 역할을 합니다. 이러한 방식으로 API 키를 입력받으면 키가 화면에 표시되지 않아 보안 상 이점이 있습니다.

 

 

 

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

 

내 로컬 코드에서는 getpass() 를 사용하지 않고 외부 파일에 api 키를 저장해 놓고 이 파일을 읽어 오는 방법을 사용했습니다. 그리고 모델을 gpt-3.5-turbo-1106으로 지정했습니다.

 

 

An extra utility we will use is this function that will tell us how many tokens we are using in each call. This is a good practice that is increasingly important as we use more complex tools that might make several calls to the API (like agents). It is very important to have a close control of how many tokens we are spending to avoid unsuspected expenditures.

 

우리가 사용할 추가 유틸리티는 각 호출에서 사용하는 토큰 수를 알려주는 이 함수입니다. 이는 API(예: 에이전트)를 여러 번 호출할 수 있는 보다 복잡한 도구를 사용함에 따라 점점 더 중요해지는 좋은 방법입니다. 예상치 못한 지출을 피하기 위해 우리가 지출하는 토큰 수를 면밀히 통제하는 것이 매우 중요합니다.

 

def count_tokens(chain, query):
    with get_openai_callback() as cb:
        result = chain.run(query)
        print(f'Spent a total of {cb.total_tokens} tokens')

    return result

 

이 코드는 주어진 언어 모델 체인 (chain)을 사용하여 주어진 질문 (query)에 대한 응답을 생성하고, 그 과정에서 사용된 토큰의 수를 계산하는 함수입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. def count_tokens(chain, query):: count_tokens 함수를 정의합니다. 이 함수는 두 개의 매개변수를 받습니다. 하나는 언어 모델 체인 (chain), 다른 하나는 질문 또는 쿼리 (query)입니다.
  2. with get_openai_callback() as cb:: get_openai_callback() 함수를 사용하여 OpenAI API 호출을 추적하는 콜백을 얻습니다. with 문을 사용하면 콜백이 사용된 코드 블록을 벗어날 때 자원을 적절하게 해제할 수 있습니다.
  3. result = chain.run(query): 언어 모델 체인을 사용하여 주어진 질문에 대한 응답을 생성합니다. 이 결과는 result 변수에 저장됩니다.
  4. print(f'Spent a total of {cb.total_tokens} tokens'): OpenAI API 호출 중 사용된 총 토큰 수를 출력합니다. cb.total_tokens는 콜백 객체에 저장된 총 토큰 수를 나타냅니다.
  5. return result: 함수는 언어 모델 체인을 사용하여 생성된 응답을 반환합니다.

이 함수는 언어 모델을 사용하여 주어진 질문에 대한 응답을 생성하면서 사용된 토큰의 총 수를 계산하고, 그 값을 출력하며, 마지막으로 생성된 응답을 반환합니다.

 

 

What are chains anyway?

 

Definition: Chains are one of the fundamental building blocks of this lib (as you can guess!).

 

정의: 체인은 이 라이브러리의 기본 구성 요소 중 하나입니다(추측할 수 있듯이!).

 

The official definition of chains is the following: 체인의 공식적인 정의는 다음과 같습니다.

A chain is made up of links, which can be either primitives or other chains. Primitives can be either prompts, llms, utils, or other chains.

체인은 기본 체인일 수도 있고 다른 체인일 수도 있는 링크로 구성됩니다. 기본 요소는 프롬프트, llms, utils 또는 기타 체인일 수 있습니다.

So a chain is basically a pipeline that processes an input by using a specific combination of primitives. Intuitively, it can be thought of as a 'step' that performs a certain set of operations on an input and returns the result. They can be anything from a prompt-based pass through a LLM to applying a Python function to an text.

 

따라서 체인은 기본적으로 특정 프리미티브 조합을 사용하여 입력을 처리하는 파이프라인입니다. 직관적으로 이는 입력에 대해 특정 작업 집합을 수행하고 결과를 반환하는 '단계'로 생각할 수 있습니다. LLM을 통한 프롬프트 기반 전달부터 텍스트에 Python 함수를 적용하는 것까지 무엇이든 될 수 있습니다.

 

Chains are divided in three types: Utility chains, Generic chains and Combine Documents chains. In this edition, we will focus on the first two since the third is too specific (will be covered in due course).

 

체인은 유틸리티 체인, 일반 체인, 결합 문서 체인의 세 가지 유형으로 나뉩니다. 이번 판에서는 세 번째 부분이 너무 구체적이기 때문에 처음 두 가지 부분에 초점을 맞추겠습니다(적절한 과정에서 다루겠습니다).

 

  1. Utility Chains: chains that are usually used to extract a specific answer from a llm with a very narrow purpose and are ready to be used out of the box.

    유틸리티 체인: 일반적으로 매우 좁은 목적으로 LLM에서 특정 답변을 추출하는 데 사용되며 즉시 사용할 수 있는 체인입니다.

  2. Generic Chains: chains that are used as building blocks for other chains but cannot be used out of the box on their own.

    일반 체인: 다른 체인의 빌딩 블록으로 사용되지만 자체적으로는 사용할 수 없는 체인입니다.
 

 

Let's take a peek into what these chains have to offer!

 

이 체인이 무엇을 제공하는지 살펴보겠습니다!

 

Utility Chains

Let's start with a simple utility chain. The LLMMathChain gives llms the ability to do math. Let's see how it works!

 

간단한 유틸리티 체인부터 시작해 보겠습니다. LLMMathChain은 llms에 수학을 수행할 수 있는 기능을 제공합니다. 그것이 어떻게 작동하는지 봅시다!

 

Pro-tip: use verbose=True to see what the different steps in the chain are!

전문가 팁: 체인의 다른 단계가 무엇인지 확인하려면 verbose=True를 사용하세요!

 

아래 코드에서는 LLMMathChain()을 사용합니다. 이것을 사용하려면 numexpr 패키지를 먼저 설치해야 합니다.

 

llm_math = LLMMathChain(llm=llm, verbose=True)

count_tokens(llm_math, "What is 13 raised to the .3432 power?")

 

이 코드는 LLMMathChain을 사용하여 주어진 수학적인 질문에 대한 응답을 생성하고, 그 과정에서 사용된 토큰의 수를 계산하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. llm_math = LLMMathChain(llm=llm, verbose=True): LLMMathChain 클래스의 인스턴스를 생성합니다. 이때, llm은 언어 모델을 나타내는 변수이며, verbose=True는 자세한 정보를 출력하도록 하는 옵션입니다. 이 체인은 수학적인 계산을 수행하는 데 사용됩니다.
  2. count_tokens(llm_math, "What is 13 raised to the .3432 power?"): count_tokens 함수를 호출하여, 앞서 생성한 llm_math 체인을 사용하여 주어진 수학적인 질문에 대한 응답을 생성하고, 그 과정에서 사용된 토큰의 수를 계산합니다. 이 질문은 "13을 0.3432 제곱한 값은 무엇인가요?"를 나타냅니다.

이 코드는 LLMMathChain을 이용하여 수학적인 계산을 수행하면서 사용된 토큰의 수를 출력하는 간단한 예제입니다.

 

 

Let's see what is going on here. The chain recieved a question in natural language and sent it to the llm. The llm returned a Python code which the chain compiled to give us an answer. A few questions arise.. How did the llm know that we wanted it to return Python code?

 

여기서 무슨 일이 일어나고 있는지 봅시다. 체인은 자연어로 질문을 받아 LLM으로 보냈습니다. llm은 우리에게 답변을 제공하기 위해 체인이 컴파일한 Python 코드를 반환했습니다. 몇 가지 질문이 생깁니다. llm은 우리가 Python 코드를 반환하기를 원한다는 것을 어떻게 알았습니까?

 

Enter prompts

 

The question we send as input to the chain is not the only input that the llm recieves 😉. The input is inserted into a wider context, which gives precise instructions on how to interpret the input we send. This is called a prompt. Let's see what this chain's prompt is!

 

우리가 체인에 입력으로 보내는 질문은 llm이 받는 유일한 입력이 아닙니다 😉. 입력은 더 넓은 컨텍스트에 삽입되어 우리가 보내는 입력을 해석하는 방법에 대한 정확한 지침을 제공합니다. 이것을 프롬프트라고 합니다. 이 체인의 프롬프트가 무엇인지 봅시다!

 

print(llm_math.prompt.template)

 

 

 

Ok.. let's see what we got here. So, we are literally telling the llm that for complex math problems it should not try to do math on its own but rather it should print a Python code that will calculate the math problem instead. Probably, if we just sent the query without any context, the llm would try (and fail) to calculate this on its own. Wait! This is testable.. let's try it out! 🧐

 

좋아.. 우리가 여기서 무엇을 얻었는지 보자. 따라서 우리는 문자 그대로 llm에게 복잡한 수학 문제의 경우 자체적으로 수학을 수행하려고 시도해서는 안 되며 대신 수학 문제를 계산할 Python 코드를 인쇄해야 한다고 말하는 것입니다. 아마도 컨텍스트 없이 쿼리를 보낸다면 llm은 이를 자체적으로 계산하려고 시도하고 실패할 것입니다. Wait!  이것은 테스트 가능합니다. 한번 시험해 봅시다! 🧐

 

# we set the prompt to only have the question we ask
prompt = PromptTemplate(input_variables=['question'], template='{question}')
llm_chain = LLMChain(prompt=prompt, llm=llm)

# we ask the llm for the answer with no context

count_tokens(llm_chain, "What is 13 raised to the .3432 power?")

 

이 코드는 주어진 수학적인 질문에 대한 답을 생성하면서 사용된 토큰의 수를 계산하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. llm_math = LLMMathChain(llm=llm, verbose=True): LLMMathChain 클래스의 인스턴스를 생성합니다. 이때, llm은 언어 모델을 나타내는 변수이며, verbose=True는 자세한 정보를 출력하도록 하는 옵션입니다. LLMMathChain은 수학적인 계산을 수행하는 언어 모델 체인을 나타냅니다.
  2. count_tokens(llm_math, "What is 13 raised to the .3432 power?"): count_tokens 함수를 호출합니다. 이 함수는 언어 모델 체인을 사용하여 주어진 수학적인 질문에 대한 응답을 생성하면서 사용된 토큰의 수를 계산합니다. 이 예제에서는 "13을 0.3432 제곱한 값은 무엇인가요?"와 같은 형태의 질문을 의미합니다.

이 코드는 언어 모델 체인을 사용하여 수학적인 질문에 대한 응답을 생성하면서 사용된 토큰의 수를 출력하는 간단한 예제입니다.

 

 

Wrong answer! Herein lies the power of prompting and one of our most important insights so far:

 

잘못된 답변! 여기에 프롬프트의 힘과 지금까지 우리가 얻은 가장 중요한 통찰 중 하나가 있습니다.

 

Insight: by using prompts intelligently, we can force the llm to avoid common pitfalls by explicitly and purposefully programming it to behave in a certain way.

 

통찰력: 프롬프트를 지능적으로 사용함으로써 llm이 특정 방식으로 작동하도록 명시적이고 의도적으로 프로그래밍함으로써 일반적인 함정을 피하도록 강제할 수 있습니다.

 

 

Another interesting point about this chain is that it not only runs an input through the llm but it later compiles Python code. Let's see exactly how this works.

 

이 체인의 또 다른 흥미로운 점은 llm을 통해 입력을 실행할 뿐만 아니라 나중에 Python 코드를 컴파일한다는 것입니다. 이것이 어떻게 작동하는지 정확히 살펴보겠습니다.

 

print(inspect.getsource(llm_math._call))

 

이 코드는 inspect 모듈을 사용하여 llm_math 객체의 _call 메서드의 소스 코드를 출력하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. inspect.getsource(llm_math._call): inspect 모듈의 getsource 함수를 사용하여 llm_math 객체의 _call 메서드의 소스 코드를 가져옵니다. _call 메서드는 언어 모델 체인의 호출을 처리하는 함수로, 언어 모델에 특정 입력을 전달하고 결과를 반환합니다.
  2. print(...): print 함수를 사용하여 _call 메서드의 소스 코드를 출력합니다.

이 코드는 주어진 객체의 특정 메서드의 소스 코드를 출력하는 간단한 디버깅용 목적의 예제입니다. 출력된 내용은 _call 메서드의 코드 자체일 것이며, 해당 메서드가 어떻게 작동하는지를 확인할 수 있습니다.

 

 

 

So we can see here that if the llm returns Python code we will compile it with a Python REPL* simulator. We now have the full picture of the chain: either the llm returns an answer (for simple math problems) or it returns Python code which we compile for an exact answer to harder problems. Smart!

 

따라서 여기서는 llm이 Python 코드를 반환하면 Python REPL* 시뮬레이터를 사용하여 이를 컴파일한다는 것을 알 수 있습니다. 이제 우리는 체인의 전체 그림을 갖게 되었습니다. llm은 답(간단한 수학 문제의 경우)을 반환하거나 더 어려운 문제에 대한 정확한 답을 위해 컴파일하는 Python 코드를 반환합니다. Smart !

 

Also notice that here we get our first example of chain composition, a key concept behind what makes langchain special. We are using the LLMMathChain which in turn initializes and uses an LLMChain (a 'Generic Chain') when called. We can make any arbitrary number of such compositions, effectively 'chaining' many such chains to achieve highly complex and customizable behaviour.

 

또한 여기에서 langchain을 특별하게 만드는 핵심 개념인 체인 구성의 첫 번째 예를 볼 수 있습니다. 우리는 호출 시 LLMChain('일반 체인')을 초기화하고 사용하는 LLMMathChain을 사용하고 있습니다. 우리는 그러한 구성을 임의의 수만큼 만들 수 있으며, 매우 복잡하고 사용자 정의 가능한 동작을 달성하기 위해 많은 체인을 효과적으로 '체인'할 수 있습니다.

 

Utility chains usually follow this same basic structure: there is a prompt for constraining the llm to return a very specific type of response from a given query. We can ask the llm to create SQL queries, API calls and even create Bash commands on the fly 🔥

 

유틸리티 체인은 일반적으로 이와 동일한 기본 구조를 따릅니다. 즉, 주어진 쿼리에서 매우 구체적인 유형의 응답을 반환하도록 llm을 제한하는 프롬프트가 있습니다. 우리는 llm에게 SQL 쿼리, API 호출을 생성하고 심지어 즉시 Bash 명령을 생성하도록 요청할 수 있습니다 🔥

 

The list continues to grow as langchain becomes more and more flexible and powerful so we encourage you to check it out and tinker with the example notebooks that you might find interesting.

 

langchain이 점점 더 유연해지고 강력해짐에 따라 목록은 계속해서 늘어나고 있으므로 이를 확인하고 흥미로울 수 있는 예제 노트북을 살펴보는 것이 좋습니다.

 

 

*A Python REPL (Read-Eval-Print Loop) is an interactive shell for executing Python code line by line

 

*Python REPL(Read-Eval-Print Loop)은 Python 코드를 한 줄씩 실행하기 위한 대화형 셸입니다.

 

 

Generic chains

 

There are only three Generic Chains in langchain and we will go all in to showcase them all in the same example. Let's go!

 

langchain에는 3개의 일반 체인만 있으며 동일한 예에서 모두 보여드리기 위해 올인하겠습니다. 갑시다!

 

Say we have had experience of getting dirty input texts. Specifically, as we know, llms charge us by the number of tokens we use and we are not happy to pay extra when the input has extra characters. Plus its not neat 😉

 

더러운 입력 텍스트를 받은 경험이 있다고 가정해 보겠습니다. 특히 우리가 알고 있듯이 llms는 우리가 사용하는 토큰 수에 따라 비용을 청구하며 입력에 추가 문자가 있는 경우 추가 비용을 지불하지 않습니다. 게다가 깔끔하지도 않아요 😉

 

 

First, we will build a custom transform function to clean the spacing of our texts. We will then use this function to build a chain where we input our text and we expect a clean text as output.

 

먼저, 텍스트의 간격을 정리하기 위한 사용자 정의 변환 함수를 구축하겠습니다. 그런 다음 이 함수를 사용하여 텍스트를 입력하고 출력으로 깨끗한 텍스트를 기대하는 체인을 구축합니다.

 

def transform_func(inputs: dict) -> dict:
    text = inputs["text"]
    
    # replace multiple new lines and multiple spaces with a single one
    text = re.sub(r'(\r\n|\r|\n){2,}', r'\n', text)
    text = re.sub(r'[ \t]+', ' ', text)

    return {"output_text": text}

 

이 코드는 주어진 입력 텍스트에 대해 특정 변환을 수행하는 함수를 정의한 것입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. def transform_func(inputs: dict) -> dict:: transform_func라는 함수를 정의합니다. 이 함수는 입력으로 딕셔너리를 받아들이고, 출력으로 또 다른 딕셔너리를 반환합니다. 입력과 출력의 형태는 각각 dict 타입으로 지정되어 있습니다.
  2. text = inputs["text"]: 입력으로 받은 딕셔너리에서 'text' 키에 해당하는 값을 가져와서 text 변수에 할당합니다. 이 변수에는 변환을 수행할 텍스트가 들어 있어야 합니다.
  3. 주석 부분(# ...)에서부터는 텍스트를 변환하는 과정을 설명하고 있습니다.
    • re.sub(r'(\r\n|\r|\n){2,}', r'\n', text): 정규 표현식을 사용하여 여러 줄 바꿈 문자 또는 공백이 두 번 이상 반복되는 경우 이를 하나로 줄입니다.
    • re.sub(r'[ \t]+', ' ', text): 정규 표현식을 사용하여 여러 개의 공백 또는 탭 문자가 하나의 공백으로 대체됩니다.
  4. return {"output_text": text}: 변환된 텍스트를 새로운 딕셔너리에 담아서 반환합니다. 이때, 'output_text'라는 키를 사용하여 변환된 텍스트를 저장합니다.

이 코드는 주어진 텍스트에 대해 여러 변환을 수행하는 함수로, 특히 여러 줄 바꿈이나 여러 공백을 간단하게 처리하는 부분이 구현되어 있습니다.

 

 

Importantly, when we initialize the chain we do not send an llm as an argument. As you can imagine, not having an llm makes this chain's abilities much weaker than the example we saw earlier. However, as we will see next, combining this chain with other chains can give us highly desirable results.

 

중요한 것은 체인을 초기화할 때 llm을 인수로 보내지 않는다는 것입니다. 상상할 수 있듯이 llm이 없으면 이 체인의 능력이 앞서 본 예보다 훨씬 약해집니다. 그러나 다음에 살펴보겠지만 이 체인을 다른 체인과 결합하면 매우 바람직한 결과를 얻을 수 있습니다.

 

clean_extra_spaces_chain = TransformChain(input_variables=["text"], output_variables=["output_text"], transform=transform_func)

 

이 코드는 TransformChain 클래스를 사용하여 텍스트 변환 체인을 생성하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. clean_extra_spaces_chain = TransformChain(input_variables=["text"], output_variables=["output_text"], transform=transform_func): TransformChain 클래스의 인스턴스를 생성합니다. 이때, 생성자에 세 개의 인자를 전달합니다.
    • input_variables=["text"]: 체인에 사용될 입력 변수로, 이 경우에는 'text' 변수를 사용합니다.
    • output_variables=["output_text"]: 체인의 출력으로 사용될 변수로, 'output_text'라는 이름으로 변환된 결과를 저장합니다.
    • transform=transform_func: 변환 함수로, 이 경우에는 transform_func 함수를 사용합니다. 이 함수는 주어진 입력 텍스트에 대해 변환을 수행하고, 결과를 출력 변수에 저장하는 역할을 합니다.

이렇게 생성된 clean_extra_spaces_chain은 입력으로 텍스트를 받아들여 변환 함수를 적용하고, 그 결과를 'output_text' 변수에 저장하는 텍스트 변환 체인입니다. 이 체인을 사용하면 텍스트에 대해 정의된 변환 함수를 쉽게 적용할 수 있습니다.

 

 

Great! Now things will get interesting.  훌륭합니다! 이제 상황이 흥미로워질 것입니다.

 

Say we want to use our chain to clean an input text and then paraphrase the input in a specific style, say a poet or a policeman. As we now know, the TransformChain does not use a llm so the styling will have to be done elsewhere. That's where our LLMChain comes in. We know about this chain already and we know that we can do cool things with smart prompting so let's take a chance!

 

시인이나 경찰처럼 체인을 사용하여 입력 텍스트를 정리한 다음 입력 내용을 특정 스타일로 바꾸어 표현하고 싶다고 가정해 보겠습니다. 우리가 지금 알고 있듯이 TransformChain은 llm을 사용하지 않으므로 스타일링은 다른 곳에서 수행해야 합니다. 이것이 바로 우리 LLMChain이 필요한 곳입니다. 우리는 이미 이 체인에 대해 알고 있으며 스마트 프롬프트로 멋진 일을 할 수 있다는 것을 알고 있으므로 기회를 잡아봅시다!

 

template = """Paraphrase this text:

{output_text}

In the style of a {style}.

Paraphrase: """
prompt = PromptTemplate(input_variables=["style", "output_text"], template=template)

 

이 코드는 특정 템플릿을 사용하여 주어진 스타일과 텍스트에 대한 문장을 생성하는 PromptTemplate 클래스의 인스턴스를 만드는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. template = """Paraphrase this text:...Paraphrase: """: 템플릿 문자열을 정의합니다. 이 템플릿에는 두 개의 변수가 포함되어 있습니다.
    • {output_text}: 이 부분은 나중에 'output_text'라는 변수로 대체될 것입니다. 이는 어떤 텍스트를 나타내는 변수입니다.
    • {style}: 이 부분은 나중에 'style'이라는 변수로 대체될 것입니다. 이는 스타일을 나타내는 변수입니다.
    • 나머지 부분은 템플릿의 구조를 정의하고 있습니다.
  2. prompt = PromptTemplate(input_variables=["style", "output_text"], template=template): PromptTemplate 클래스의 인스턴스를 생성합니다.
    • input_variables=["style", "output_text"]: 템플릿에 사용될 변수들을 정의합니다. 여기에서는 'style'과 'output_text' 변수를 사용합니다.
    • template=template: 사용할 템플릿을 설정합니다. 앞서 정의한 template 변수를 사용합니다.

이렇게 생성된 prompt는 주어진 스타일과 텍스트에 대한 문장을 생성하는데 사용될 수 있는 템플릿과 변수를 가진 PromptTemplate 클래스의 인스턴스입니다. 이를 사용하면 특정 문맥과 스타일에 따라 텍스트를 생성하는데 유용하게 활용될 수 있습니다.

 

And next, initialize our chain:  다음으로 체인을 초기화합니다.

 

style_paraphrase_chain = LLMChain(llm=llm, prompt=prompt, output_key='final_output')

 

이 코드는 주어진 언어 모델(llm)과 특정 템플릿(prompt)을 사용하여 문장을 생성하는 LLMChain 클래스의 인스턴스를 생성하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. style_paraphrase_chain = LLMChain(llm=llm, prompt=prompt, output_key='final_output'): LLMChain 클래스의 인스턴스를 생성합니다.
    • llm=llm: 사용할 언어 모델을 나타내는 변수입니다. 이 변수는 이전에 초기화된 언어 모델 객체를 참조합니다.
    • prompt=prompt: 사용할 템플릿을 나타내는 변수입니다. 이 변수는 이전에 초기화된 템플릿 객체를 참조합니다.
    • output_key='final_output': 생성된 문장의 결과를 저장할 변수의 이름을 지정합니다. 이 경우, 'final_output'라는 변수에 결과를 저장하도록 설정되어 있습니다.

이렇게 생성된 style_paraphrase_chain은 특정 스타일과 텍스트에 대한 문장을 생성하는데 사용될 수 있는 언어 모델 체인의 인스턴스입니다. 이 체인을 사용하면 주어진 템플릿과 변수를 활용하여 특정 스타일로 텍스트를 생성하는 과정이 쉽게 수행될 수 있습니다. 생성된 문장은 'final_output'이라는 변수에 저장됩니다.

 

 

Great! Notice that the input text in the template is called 'output_text'. Can you guess why?

 

좋습니! 템플릿의 입력 텍스트는 'output_text'라고 합니다. 이유를 짐작할 수 있나요?

 

We are going to pass the output of the TransformChain to the LLMChain!

 

TransformChain의 출력을 LLMChain으로 전달하겠습니다!

 

Finally, we need to combine them both to work as one integrated chain. For that we will use SequentialChain which is our third generic chain building block.

 

마지막으로, 둘을 결합하여 하나의 통합 체인으로 작동해야 합니다. 이를 위해 우리는 세 번째 일반 체인 빌딩 블록인 SequentialChain을 사용할 것입니다.

 

sequential_chain = SequentialChain(chains=[clean_extra_spaces_chain, style_paraphrase_chain], input_variables=['text', 'style'], output_variables=['final_output'])

 

이 코드는 두 개의 언어 모델 체인을 연속적으로 실행하는 SequentialChain 클래스의 인스턴스를 생성하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. sequential_chain = SequentialChain(chains=[clean_extra_spaces_chain, style_paraphrase_chain], input_variables=['text', 'style'], output_variables=['final_output']): SequentialChain 클래스의 인스턴스를 생성합니다.
    • chains=[clean_extra_spaces_chain, style_paraphrase_chain]: 체인에 사용할 언어 모델 체인의 리스트를 지정합니다. 이 경우, clean_extra_spaces_chain과 style_paraphrase_chain 두 개의 체인이 연속적으로 실행됩니다.
    • input_variables=['text', 'style']: 체인에 입력으로 제공될 변수들의 이름을 지정합니다. 여기에서는 'text'와 'style' 변수가 입력으로 사용됩니다.
    • output_variables=['final_output']: 체인의 출력으로 생성된 변수의 이름을 지정합니다. 여기에서는 'final_output' 변수가 출력으로 사용됩니다.

이렇게 생성된 sequential_chain은 clean_extra_spaces_chain을 먼저 실행한 다음, 그 결과를 이어서 style_paraphrase_chain에 전달하여 두 체인을 연속적으로 실행하는 언어 모델 체인의 인스턴스입니다. 결과는 'final_output' 변수에 저장됩니다. 이렇게 체인을 구성하면 여러 단계의 언어 모델이 연속적으로 적용되어 원하는 형식으로 텍스트를 생성할 수 있습니다.

 

Our input is the langchain docs description of what chains are but dirty with some extra spaces all around.

 

우리의 입력은 체인이 무엇인지에 대한 langchain 문서 설명이지만 주위에 약간의 추가 공간이 있어 더러워졌습니다.

 

input_text = """
Chains allow us to combine multiple 


components together to create a single, coherent application. 

For example, we can create a chain that takes user input,       format it with a PromptTemplate, 

and then passes the formatted response to an LLM. We can build more complex chains by combining     multiple chains together, or by 


combining chains with other components.
"""

 

 

We are all set. Time to get creative! 우리는 모두 준비되었습니다. 창의력을 발휘할 시간입니다!

 

count_tokens(sequential_chain, {'text': input_text, 'style': 'a 90s rapper'})

 

이 코드는 앞서 생성한 sequential_chain 언어 모델 체인을 사용하여 특정 입력 텍스트와 스타일에 대한 문장을 생성하면서 사용된 토큰의 수를 계산하는 함수인 count_tokens를 호출하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. count_tokens(sequential_chain, {'text': input_text, 'style': 'a 90s rapper'}): count_tokens 함수를 호출합니다. 이 함수는 언어 모델 체인과 입력 변수들을 받아들여 언어 모델을 실행하면서 사용된 토큰의 수를 계산하고, 그 값을 출력합니다.
  2. sequential_chain: 사용할 언어 모델 체인으로, 앞서 생성한 sequential_chain 객체를 전달합니다.
  3. {'text': input_text, 'style': 'a 90s rapper'}: 입력으로 사용될 변수들과 그에 대한 값들을 딕셔너리 형태로 전달합니다. 여기에서는 'text' 변수에는 input_text라는 입력 텍스트가, 'style' 변수에는 "a 90s rapper"라는 스타일이 전달됩니다.

이 코드는 주어진 언어 모델 체인을 사용하여 입력 텍스트와 스타일에 대한 문장을 생성하면서 사용된 토큰의 수를 계산하는 함수를 호출하는 예제입니다. 결과로는 생성된 문장과 사용된 토큰의 총 수가 출력될 것입니다.

 

 

 

A note on langchain-hub

langchain-hub is a sister library to langchain, where all the chains, agents and prompts are serialized for us to use.

 

langchain-hub는 우리가 사용할 수 있도록 모든 체인, 에이전트 및 프롬프트가 직렬화되어 있는 langchain의 자매 라이브러리입니다.

 

from langchain.chains import load_chain

 

이 코드는 langchain 패키지에서 chains 모듈 내의 load_chain 함수를 가져오는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. from langchain.chains import load_chain: langchain 패키지에서 chains 모듈 내의 load_chain 함수를 가져옵니다.
    • from ... import ... 형태는 특정 모듈 또는 모듈 내의 특정 함수, 클래스 등을 현재 코드에 가져오는(import) 방식을 나타냅니다.
    • 여기서는 langchain 패키지에서 chains 모듈을 불러오고, 그 중에서 load_chain 함수를 가져옵니다.

load_chain 함수는 언어 모델 체인을 불러오는 함수일 것으로 예상됩니다. 이 함수를 사용하면 이전에 저장된 언어 모델 체인을 다시 불러와서 재사용할 수 있습니다. 코드의 나머지 부분이 있으면 해당 부분에 따라 더 자세한 설명을 제공할 수 있습니다.

 

 

Loading from langchain hub is as easy as finding the chain you want to load in the repository and then using load_chain with the corresponding path. We also have load_prompt and initialize_agent, but more on that later. Let's see how we can do this with our LLMMathChain we saw earlier:

 

langchain 허브에서 로드하는 것은 저장소에서 로드하려는 체인을 찾은 다음 해당 경로와 함께 load_chain을 사용하는 것만큼 쉽습니다. load_prompt와 초기화_agent도 있지만 이에 대해서는 나중에 자세히 설명합니다. 앞서 본 LLMMathChain을 사용하여 이를 수행하는 방법을 살펴보겠습니다.

 

llm_math_chain = load_chain('lc://chains/llm-math/chain.json')

 

What if we want to change some of the configuration parameters? We can simply override it after loading:

 

일부 구성 매개변수를 변경하려면 어떻게 해야 합니까? 로드한 후에 간단히 재정의할 수 있습니다.

 

llm_math_chain.verbose
llm_math_chain.verbose = False
llm_math_chain.verbose

 

 

That's it for this example on chains. 이것이 체인에 대한 예제의 전부입니다.

반응형


반응형

https://www.pinecone.io/learn/series/langchain/langchain-prompt-templates/

 

Prompt Engineering and LLMs with Langchain | Pinecone

We have always relied on different models for different tasks in machine learning. With the introduction of multi-modality and Large Language Models (LLMs), this has changed. Gone are the days when we needed separate models for classification, named entity

www.pinecone.io

 

 

Prompt Engineering and LLMs with Langchain

 

We have always relied on different models for different tasks in machine learning. With the introduction of multi-modality and Large Language Models (LLMs), this has changed.

 

우리는 기계 학습의 다양한 작업을 위해 항상 다양한 모델을 사용해 왔습니다. 다중 양식 및 LLM(대형 언어 모델)이 도입되면서 상황이 바뀌었습니다.

 

Gone are the days when we needed separate models for classification, named entity recognition (NER), question-answering (QA), and many other tasks.

 

분류, 개체명 인식(NER), 질문 답변(QA) 및 기타 여러 작업을 위해 별도의 모델이 필요했던 시대는 지나갔습니다.

 

Before transfer learning, different tasks and use cases required training different models.

 

With the introduction of transformers and transfer learning, all that was needed to adapt a language model for different tasks was a few small layers at the end of the network (the head) and a little fine-tuning.

 

transformers  와 전이 학습 transfer learning 이 도입되면서 다양한 작업에 맞게 언어 모델을 조정하는 데 필요한 것은 네트워크 끝(헤드)에 있는 몇 개의 작은 레이어와 약간의 미세 조정뿐이었습니다.

 

Transformers and the idea of transfer learning allowed us to reuse the same core components of pretrained transformer models for different tasks by switching model &ldquo;heads&rdquo; and performing fine-tuning.&nbsp;Transformers와 전이 학습 아이디어를 통해 우리는 모델 "헤드"를 전환하고 미세 조정을 수행함으로써 사전 훈련된 변환기 모델의 동일한 핵심 구성 요소를 다양한 작업에 재사용할 수 있었습니다.

 

Today, even that approach is outdated. Why change these last few model layers and go through an entire fine-tuning process when you can prompt the model to do classification or QA.

 

오늘날에는 그러한 접근 방식조차 구식입니다. 모델에 분류 또는 QA를 수행하라는 메시지를 표시할 수 있는데 마지막 몇 개의 모델 레이어를 변경하고 전체 미세 조정 프로세스를 거쳐야 하는 이유는 무엇입니까?

 

Many tasks can be performed using the same Large Language Models (LLMs) by simply changing the instructions in the prompts.&nbsp;프롬프트의 지침을 변경하기만 하면 동일한 LLM(대형 언어 모델)을 사용하여 많은 작업을 수행할 수 있습니다.

 

Large Language Models (LLMs) can perform all these tasks and more. These models have been trained with a simple concept, you input a sequence of text, and the model outputs a sequence of text. The one variable here is the input text — the prompt.

 

LLM(대형 언어 모델)은 이러한 모든 작업과 그 이상을 수행할 수 있습니다. 이러한 모델은 간단한 개념으로 훈련되었습니다. 사용자가 일련의 텍스트를 입력하면 모델이 일련의 텍스트를 출력합니다. 여기서 변수 중 하나는 입력 텍스트, 즉 프롬프트입니다.

 

In this new age of LLMs, prompts are king. Bad prompts produce bad outputs, and good prompts are unreasonably powerful. Constructing good prompts is a crucial skill for those building with LLMs.

 

LLM의 새로운 시대에는 프롬프트가 가장 중요합니다. 나쁜 프롬프트는 나쁜 결과를 낳고, 좋은 프롬프트는 비합리적으로 강력합니다. 좋은 프롬프트를 구성하는 것은 LLM을 구축하는 사람들에게 중요한 기술입니다.

 

The LangChain library recognizes the power of prompts and has built an entire set of objects for them. In this article, we will learn all there is to know about PromptTemplates and implementing them effectively.

 

LangChain 라이브러리는 프롬프트의 힘을 인식하고 이를 위한 전체 개체 세트를 구축했습니다. 이 문서에서는 PromptTemplate에 대해 알아야 할 모든 내용과 이를 효과적으로 구현하는 방법을 알아봅니다.

 

 

https://youtu.be/RflBcK0oDH0?si=5DWGTgF8bbz1M6iq

Prompt Templates for GPT 3.5 and other LLMs - LangChain #2

 

Prompt Engineering

 

Before diving into Langchain’s PromptTemplate, we need to better understand prompts and the discipline of prompt engineering.

 

Langchain의 PromptTemplate을 살펴보기 전에 프롬프트와 프롬프트 엔지니어링 규율을 더 잘 이해해야 합니다.


A prompt is typically composed of multiple parts:

 

프롬프트는 일반적으로 여러 부분으로 구성됩니다.

 

 

Not all prompts use these components, but a good prompt often uses two or more. Let’s define them more precisely.

 

모든 프롬프트가 이러한 구성 요소를 사용하는 것은 아니지만 좋은 프롬프트는 두 개 이상을 사용하는 경우가 많습니다. 좀 더 정확하게 정의해 보겠습니다.

 

Instructions tell the model what to do, how to use external information if provided, what to do with the query, and how to construct the output.

 

지침 Instructions  은 수행할 작업, 제공된 외부 정보를 사용하는 방법, 쿼리로 수행할 작업 및 출력 구성 방법을 모델에 알려줍니다.

 

External information or context(s) act as an additional source of knowledge for the model. These can be manually inserted into the prompt, retrieved via a vector database (retrieval augmentation), or pulled in via other means (APIs, calculations, etc.).

 

외부 정보 또는 컨텍스트는 모델에 대한 추가 지식 소스 역할을 합니다. 이는 프롬프트에 수동으로 삽입하거나, 벡터 데이터베이스를 통해 검색하거나(검색 확대), 다른 수단(API, 계산 등)을 통해 가져올 수 있습니다.

 

User input or query is typically (but not always) a query input into the system by a human user (the prompter).

 

사용자 입력 또는 쿼리는 일반적으로(항상은 아니지만) 인간 사용자(프롬프터)가 시스템에 입력한 쿼리입니다.

 

Output indicator marks the beginning of the to-be-generated text. If generating Python code, we may use import to indicate to the model that it must begin writing Python code (as most Python scripts begin with import).

 

출력 표시기는 생성될 텍스트의 시작을 표시합니다. Python 코드를 생성하는 경우 import를 사용하여 Python 코드 작성을 시작해야 함을 모델에 나타낼 수 있습니다(대부분의 Python 스크립트는 import로 시작함).

 

Each component is usually placed in the prompt in this order. Starting with instructions, external information (where applicable), prompter input, and finally, the output indicator.

 

각 구성요소는 일반적으로 이 순서대로 프롬프트에 배치됩니다. 지침부터 시작하여 외부 정보(해당하는 경우), 프롬프터 입력, 마지막으로 출력 표시기입니다.

 

Let’s see how we’d feed this into an OpenAI model using Langchain:

 

Langchain을 사용하여 OpenAI 모델에 이를 어떻게 적용하는지 살펴보겠습니다.

 

 

prompt = """Answer the question based on the context below. If the
question cannot be answered using the information provided answer
with "I don't know".

Context: Large Language Models (LLMs) are the latest models used in NLP.
Their superior performance over smaller models has made them incredibly
useful for developers building NLP enabled applications. These models
can be accessed via Hugging Face's `transformers` library, via OpenAI
using the `openai` library, and via Cohere using the `cohere` library.

Question: Which libraries and model providers offer LLMs?

Answer: """

 

from langchain.llms import OpenAI

# initialize the models
openai = OpenAI(
    model_name="text-davinci-003",
    openai_api_key="YOUR_API_KEY"
)

 

print(openai(prompt))

 

 

Hugging Face's `transformers` library, OpenAI using the `openai` library, and Cohere using the `cohere` library.

 

 

이 코드는 주어진 문맥에 기반하여 특정 질문에 답하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. prompt = """Answer the question based on the context below... via Cohere using the coherelibrary."": 특정 문맥을 기반으로 하는 템플릿 문자열을 정의합니다. 질문에 대한 답을 생성하는 데 사용됩니다.{context}와 {question}`는 각각 나중에 변수로 대체될 부분입니다.
  2. from langchain.llms import OpenAI: langchain 패키지에서 OpenAI 클래스를 가져옵니다. 이 클래스는 OpenAI의 언어 모델과 상호작용하는 데 사용됩니다.
  3. openai = OpenAI(model_name="text-davinci-003", openai_api_key="YOUR_API_KEY"): OpenAI 클래스의 인스턴스를 생성하고, OpenAI의 언어 모델을 초기화합니다. model_name은 사용할 언어 모델의 이름이며, openai_api_key는 OpenAI API에 액세스하기 위한 API 키입니다. 여기서는 "text-davinci-003" 모델을 사용하고 있으며, API 키는 직접 사용자의 키로 대체되어야 합니다.
  4. print(openai(prompt)): openai 인스턴스의 __call__ 메서드를 사용하여 주어진 템플릿을 이용하여 모델에 질문을 제출하고, 모델의 응답을 출력합니다.

이 코드는 주어진 문맥과 질문에 대한 응답을 생성하는 예제로, OpenAI의 언어 모델을 사용하여 모델에 특정 질문을 물어보고 그에 대한 답을 출력하는 과정을 보여줍니다.

 

 

OpenAI 사에서 제공하는 모델 text-davinci-003에서 gpt-3.5-turbo-1106으로 최신 버전이면서 저렴한 모델로 바꾸었습니다.

 

로컬에서 업데이트된 소스코드와 결과는 아래와 같습니다.

 

 

In reality, we’re unlikely to hardcode the context and user question. We’d feed them in via a template — which is where Langchain’s PromptTemplate comes in.

 

실제로는 컨텍스트와 사용자 질문을 하드코딩할 가능성이 없습니다. 우리는 Langchain의 PromptTemplate이 들어오는 템플릿을 통해 이를 제공했습니다.

 

Prompt Templates

 

The prompt template classes in Langchain are built to make constructing prompts with dynamic inputs easier. Of these classes, the simplest is the PromptTemplate. We’ll test this by adding a single dynamic input to our previous prompt, the user query.

 

Langchain의 프롬프트 템플릿 클래스는 동적 입력이 포함된 프롬프트를 더 쉽게 구성할 수 있도록 만들어졌습니다. 이러한 클래스 중에서 가장 간단한 것은 PromptTemplate입니다. 이전 프롬프트인 사용자 쿼리에 단일 동적 입력을 추가하여 이를 테스트하겠습니다.

 

from langchain import PromptTemplate

template = """Answer the question based on the context below. If the
question cannot be answered using the information provided answer
with "I don't know".

Context: Large Language Models (LLMs) are the latest models used in NLP.
Their superior performance over smaller models has made them incredibly
useful for developers building NLP enabled applications. These models
can be accessed via Hugging Face's `transformers` library, via OpenAI
using the `openai` library, and via Cohere using the `cohere` library.

Question: {query}

Answer: """

prompt_template = PromptTemplate(
    input_variables=["query"],
    template=template
)

 

 

With this, we can use the format method on our prompt_template to see the effect of passing a query to the template.

 

이를 통해 우리는 템플릿에 쿼리를 전달하는 효과를 확인하기 위해 프롬프트_템플릿의 형식 메서드를 사용할 수 있습니다.

 

print(
    prompt_template.format(
        query="Which libraries and model providers offer LLMs?"
    )
)

 

Answer the question based on the context below. If the
question cannot be answered using the information provided answer
with "I don't know".

Context: Large Language Models (LLMs) are the latest models used in NLP.
Their superior performance over smaller models has made them incredibly
useful for developers building NLP enabled applications. These models
can be accessed via Hugging Face's `transformers` library, via OpenAI
using the `openai` library, and via Cohere using the `cohere` library.

Question: Which libraries and model providers offer LLMs?

Answer: 

 

Naturally, we can pass the output of this directly into an LLM object like so:

 

당연히 다음과 같이 이 출력을 LLM 개체에 직접 전달할 수 있습니다.

 

print(openai(
    prompt_template.format(
        query="Which libraries and model providers offer LLMs?"
    )
))

 

Hugging Face's `transformers` library, OpenAI using the `openai` library, and Cohere using the `cohere` library.

 

로컬에서 돌린 결과는 아래와 같습니다.

 

 

 

This is just a simple implementation that can easily be replaced with f-strings (like f"insert some custom text '{custom_text}' etc"). However, using Langchain’s PromptTemplate object, we can formalize the process, add multiple parameters, and build prompts with an object-oriented approach.

 

이는 f-문자열(예: f"사용자 정의 텍스트 '{custom_text}' 등 삽입")로 쉽게 대체할 수 있는 간단한 구현입니다. 그러나 Langchain의 PromptTemplate 개체를 사용하면 프로세스를 공식화하고, 여러 매개변수를 추가하고, 개체 지향 접근 방식으로 프롬프트를 구축할 수 있습니다.


These are significant advantages, but only some of what Langchain offers to help us with prompts.

 

이는 상당한 이점이지만 Langchain이 프롬프트를 통해 우리에게 도움을 주기 위해 제공하는 것 중 일부에 불과합니다.

 

Few Shot Prompt Templates

 

The success of LLMs comes from their large size and ability to store “knowledge” within the model parameter, which is learned during model training. However, there are more ways to pass knowledge to an LLM. The two primary methods are:

 

LLM의 성공은 모델 교육 중에 학습된 모델 매개 변수 내에 "지식"을 저장할 수 있는 큰 규모와 능력에서 비롯됩니다. 그러나 지식을 LLM에 전달하는 더 많은 방법이 있습니다. 두 가지 기본 방법은 다음과 같습니다.

Parametric knowledge — the knowledge mentioned above is anything that has been learned by the model during training time and is stored within the model weights (or parameters).

 

위에 언급된 지식은 훈련 시간 동안 모델에 의해 학습되었으며 모델 가중치(또는 매개변수) 내에 저장되는 모든 것입니다.


Source knowledge — any knowledge provided to the model at inference time via the input prompt.

 

추론 시 입력 프롬프트를 통해 모델에 제공되는 모든 지식입니다.


Langchain’s FewShotPromptTemplate caters to source knowledge input. The idea is to “train” the model on a few examples — we call this few-shot learning — and these examples are given to the model within the prompt.

 

Langchain의 FewShotPromptTemplate은 소스 지식 입력을 충족합니다. 아이디어는 몇 가지 예를 통해 모델을 "훈련"하는 것입니다. 이를 few-shot learning 이라고 하며 이러한 예는 프롬프트 내에서 모델에 제공됩니다.



Few-shot learning is perfect when our model needs help understanding what we’re asking it to do. We can see this in the following example:

 

모델이 요청하는 작업을 이해하는 데 도움이 필요할 때 퓨샷 학습이 완벽합니다. 다음 예에서 이를 확인할 수 있습니다.

 

prompt = """The following is a conversation with an AI assistant.
The assistant is typically sarcastic and witty, producing creative 
and funny responses to the users questions. Here are some examples: 

User: What is the meaning of life?
AI: """

openai.temperature = 1.0  # increase creativity/randomness of output

print(openai(prompt))

 

Life is like a box of chocolates, you never know what you're gonna get!

 

 

이 경우에는 진지한 질문에 대한 답으로 재미있는 것, 농담을 요구하는 것입니다. 그러나 임의성/창의성을 높이는 온도를 1.0으로 설정해도 심각한 반응을 보입니다.

 

모델을 돕기 위해 우리가 원하는 답변 유형에 대한 몇 가지 예를 제공할 수 있습니다.

 

prompt = """The following are exerpts from conversations with an AI
assistant. The assistant is typically sarcastic and witty, producing
creative  and funny responses to the users questions. Here are some
examples: 

User: How are you?
AI: I can't complain but sometimes I still do.

User: What time is it?
AI: It's time to get a watch.

User: What is the meaning of life?
AI: """

print(openai(prompt))

 

42, of course!

 

로컬에서 돌린 결과

 

 

With our examples reinforcing the instructions we passed in the prompt, we’re much more likely to get a more amusing response. We can then formalize this process with Langchain’s FewShotPromptTemplate:

 

프롬프트에서 전달한 지침을 강화하는 예제를 통해 더 재미있는 응답을 얻을 가능성이 훨씬 더 높습니다. 그런 다음 Langchain의 FewShotPromptTemplate을 사용하여 이 프로세스를 공식화할 수 있습니다.

 

from langchain import FewShotPromptTemplate

# create our examples
examples = [
    {
        "query": "How are you?",
        "answer": "I can't complain but sometimes I still do."
    }, {
        "query": "What time is it?",
        "answer": "It's time to get a watch."
    }
]

# create a example template
example_template = """
User: {query}
AI: {answer}
"""

# create a prompt example from above template
example_prompt = PromptTemplate(
    input_variables=["query", "answer"],
    template=example_template
)

# now break our previous prompt into a prefix and suffix
# the prefix is our instructions
prefix = """The following are exerpts from conversations with an AI
assistant. The assistant is typically sarcastic and witty, producing
creative  and funny responses to the users questions. Here are some
examples: 
"""
# and the suffix our user input and output indicator
suffix = """
User: {query}
AI: """

# now create the few shot prompt template
few_shot_prompt_template = FewShotPromptTemplate(
    examples=examples,
    example_prompt=example_prompt,
    prefix=prefix,
    suffix=suffix,
    input_variables=["query"],
    example_separator="\n\n"
)

 

이 코드는 langchain 패키지에서 FewShotPromptTemplate 클래스를 사용하여 Few-Shot 학습을 위한 템플릿을 생성하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. from langchain import FewShotPromptTemplate: langchain 패키지에서 FewShotPromptTemplate 클래스를 가져옵니다.
  2. examples = [...]: Few-Shot 학습을 위한 예제로, 각 예제는 사용자의 질문 (query)과 그에 대한 AI의 응답 (answer)을 담고 있는 딕셔너리입니다.
  3. example_template = """...""": Few-Shot 학습의 템플릿으로 사용할 문자열을 정의합니다. {query}와 {answer}는 나중에 변수로 대체될 부분입니다.
  4. example_prompt = PromptTemplate(...):
    • PromptTemplate 클래스를 사용하여 Few-Shot 학습 예제의 템플릿을 초기화합니다.
    • input_variables에는 사용할 변수들이 들어있는 리스트가 포함되어 있습니다.
    • template에는 Few-Shot 학습에서 사용될 템플릿 문자열이 들어가게 됩니다.
  5. `prefix = """...""", suffix = """..."""":
    • Few-Shot 학습의 템플릿을 생성할 때 사용할 접두사(prefix)와 접미사(suffix)를 정의합니다.
    • 접두사에는 Few-Shot 학습의 목적과 관련된 지침이나 설명이 들어가고, 접미사에는 사용자의 입력과 AI의 출력을 표시하는 부분이 들어갑니다.
  6. few_shot_prompt_template = FewShotPromptTemplate(...):
    • FewShotPromptTemplate 클래스를 사용하여 Few-Shot 학습을 위한 템플릿을 생성합니다.
    • examples에는 Few-Shot 학습 예제로 사용할 딕셔너리의 리스트를 전달합니다.
    • example_prompt에는 예제 템플릿을 담고 있는 PromptTemplate 인스턴스를 전달합니다.
    • prefix와 suffix는 각각 접두사와 접미사를 나타냅니다.
    • input_variables에는 사용할 변수들이 들어있는 리스트가 포함되어 있습니다.
    • example_separator는 예제들 사이의 구분자로 사용됩니다.

이렇게 생성된 few_shot_prompt_template는 Few-Shot 학습에 활용할 수 있는 템플릿이며, 예제와 지침이 함께 있어 모델이 특정 유형의 응답을 생성할 때 참고할 수 있도록 합니다.

 

 

If we then pass in the examples and user query, we will get this:

 

그런 다음 예제와 사용자 쿼리를 전달하면 다음과 같은 결과를 얻게 됩니다.

 

query = "What is the meaning of life?"

print(few_shot_prompt_template.format(query=query))

 

 

The following are exerpts from conversations with an AI
assistant. The assistant is typically sarcastic and witty, producing
creative  and funny responses to the users questions. Here are some
examples: 



User: How are you?
AI: I can't complain but sometimes I still do.



User: What time is it?
AI: It's time to get a watch.



User: What is the meaning of life?
AI:

 

 

This process can seem somewhat convoluted. Why do all of this with a FewShotPromptTemplate object, the examples dictionary, etc. — when we can do the same with a few lines of code and an f-string?

 

이 과정은 다소 복잡해 보일 수 있습니다. FewShotPromptTemplate 개체, 예제 사전 등을 사용하여 이 모든 작업을 수행하는 이유는 무엇입니까? 몇 줄의 코드와 f-문자열로 동일한 작업을 수행할 수 있는데도 말이죠.

Again, this approach is more formalized, integrates well with other features in Langchain (such as chains — more on this soon), and comes with several features. One of those is the ability to vary the number of examples to be included based on query length.

 

다시 말하지만, 이 접근 방식은 더욱 공식화되었으며 Langchain의 다른 기능(예: 체인 - 곧 자세히 설명)과 잘 통합되며 몇 가지 기능이 제공됩니다. 그 중 하나는 쿼리 길이에 따라 포함할 예제 수를 변경하는 기능입니다.

A dynamic number of examples is important because the maximum length of our prompt and completion output is limited. This limitation is measured by the maximum context window.

 

프롬프트 및 완료 출력의 최대 길이가 제한되어 있으므로 동적 예제 수는 중요합니다. 이 제한은 최대 컨텍스트 창으로 측정됩니다.

context window=input tokens+output tokens

At the same time, we can maximize the number of examples given to the model for few-shot learning.

 

동시에 퓨샷 학습을 위해 모델에 제공되는 예제 수를 최대화할 수 있습니다.

Considering this, we need to balance the number of examples included and our prompt size. Our hard limit is the maximum context size, but we must also consider the cost of processing more tokens through the LLM. Fewer tokens mean a cheaper service and faster completions from the LLM.

 

이를 고려하여 포함된 예제 수와 프롬프트 크기의 균형을 맞춰야 합니다. 우리의 하드 한도는 최대 컨텍스트 크기이지만 LLM을 통해 더 많은 토큰을 처리하는 데 드는 비용도 고려해야 합니다. 토큰이 적다는 것은 LLM에서 더 저렴한 서비스와 더 빠른 완료를 의미합니다.

The FewShotPromptTemplate allows us to vary the number of examples included based on these variables. First, we create a more extensive list of examples:

 

FewShotPromptTemplate을 사용하면 이러한 변수에 따라 포함된 예제 수를 변경할 수 있습니다. 먼저, 보다 광범위한 예제 목록을 만듭니다.

 

examples = [
    {
        "query": "How are you?",
        "answer": "I can't complain but sometimes I still do."
    }, {
        "query": "What time is it?",
        "answer": "It's time to get a watch."
    }, {
        "query": "What is the meaning of life?",
        "answer": "42"
    }, {
        "query": "What is the weather like today?",
        "answer": "Cloudy with a chance of memes."
    }, {
        "query": "What is your favorite movie?",
        "answer": "Terminator"
    }, {
        "query": "Who is your best friend?",
        "answer": "Siri. We have spirited debates about the meaning of life."
    }, {
        "query": "What should I do today?",
        "answer": "Stop talking to chatbots on the internet and go outside."
    }
]

 

 

After this, rather than passing the examples directly, we actually use a LengthBasedExampleSelector like so:

 

그런 다음 예제를 직접 전달하는 대신 실제로 다음과 같이 LengthBasedExampleSelector를 사용합니다.

 

from langchain.prompts.example_selector import LengthBasedExampleSelector

example_selector = LengthBasedExampleSelector(
    examples=examples,
    example_prompt=example_prompt,
    max_length=50  # this sets the max length that examples should be
)

 

 

It’s important to note that we’re measuring the max_length as the number of words determined by splitting the string by spaces and newlines. The exact logic looks like this:

 

문자열을 공백과 개행 문자로 나누어 결정된 단어 수로 max_length를 측정한다는 점에 유의하는 것이 중요합니다. 정확한 논리는 다음과 같습니다.

 

import re

some_text = "There are a total of 8 words here.\nPlus 6 here, totaling 14 words."

words = re.split('[\n ]', some_text)
print(words, len(words))

 

['There', 'are', 'a', 'total', 'of', '8', 'words', 'here.', 'Plus', '6', 'here,', 'totaling', '14', 'words.'] 14

 

 

We then pass our example_selector to the FewShotPromptTemplate to create a new — and dynamic — prompt template:

 

그런 다음 example_selector를 FewShotPromptTemplate에 전달하여 새로운 동적 프롬프트 템플릿을 만듭니다.

 

# now create the few shot prompt template
dynamic_prompt_template = FewShotPromptTemplate(
    example_selector=example_selector,  # use example_selector instead of examples
    example_prompt=example_prompt,
    prefix=prefix,
    suffix=suffix,
    input_variables=["query"],
    example_separator="\n"
)

 

 

Now if we pass a shorter or longer query, we should see that the number of included examples will vary.

 

이제 더 짧거나 긴 쿼리를 전달하면 포함된 예제 수가 달라지는 것을 확인해야 합니다.

 

print(dynamic_prompt_template.format(query="How do birds fly?"))

 

The following are exerpts from conversations with an AI
assistant. The assistant is typically sarcastic and witty, producing
creative  and funny responses to the users questions. Here are some
examples: 


User: How are you?
AI: I can't complain but sometimes I still do.


User: What time is it?
AI: It's time to get a watch.


User: What is the meaning of life?
AI: 42


User: What is the weather like today?
AI: Cloudy with a chance of memes.


User: How do birds fly?
AI:

 

로컬에서 돌린 결과는 아래와 같습니다.

 

 

 

Passing a longer question will result in fewer examples being included:

 

더 긴 질문을 전달하면 포함되는 예시 수가 줄어듭니다.

 

query = """If I am in America, and I want to call someone in another country, I'm
thinking maybe Europe, possibly western Europe like France, Germany, or the UK,
what is the best way to do that?"""

print(dynamic_prompt_template.format(query=query))

 

The following are exerpts from conversations with an AI
assistant. The assistant is typically sarcastic and witty, producing
creative  and funny responses to the users questions. Here are some
examples: 


User: How are you?
AI: I can't complain but sometimes I still do.


User: If I am in America, and I want to call someone in another country, I'm
thinking maybe Europe, possibly western Europe like France, Germany, or the UK,
what is the best way to do that?
AI:

 

 

 

With this, we’re returning fewer examples within the prompt variable. Allowing us to limit excessive token usage and avoid errors from surpassing the maximum context window of the LLM.

 

이를 통해 프롬프트 변수 내에서 더 적은 수의 예제를 반환합니다. 과도한 토큰 사용을 제한하고 LLM의 최대 컨텍스트 창을 초과하는 오류를 방지할 수 있습니다.


Naturally, prompts are an essential component of the new world of LLMs. It’s worth exploring the tooling made available with Langchain and getting familiar with different prompt engineering techniques.

 

당연히 프롬프트는 새로운 LLM 세계의 필수 구성 요소입니다. Langchain에서 사용할 수 있는 도구를 탐색하고 다양한 프롬프트 엔지니어링 기술에 익숙해지는 것이 좋습니다.

 

Here we’ve covered just a few examples of the prompt tooling available in Langchain and a limited exploration of how they can be used. In the next chapter, we’ll explore another essential part of Langchain — called chains — where we’ll see more usage of prompt templates and how they fit into the wider tooling provided by the library.

 

여기에서는 Langchain에서 사용할 수 있는 프롬프트 도구의 몇 가지 예와 이를 사용할 수 있는 방법에 대한 제한된 탐색을 다루었습니다. 다음 장에서는 체인이라고 불리는 Langchain의 또 다른 필수 부분을 살펴보겠습니다. 여기서 프롬프트 템플릿의 더 많은 사용법과 라이브러리에서 제공하는 더 넓은 도구에 어떻게 적용되는지 살펴보겠습니다.

 

Resources

Langchain Handbook Repo

 

 

 

 

 

반응형


반응형

https://www.pinecone.io/learn/series/langchain/langchain-intro/

 

LangChain: Introduction and Getting Started | Pinecone

Large Language Models (LLMs) entered the world stage with the release of OpenAI’s GPT-3 in 2020 [1]. Since then, they’ve enjoyed a steady growth in popularity. That is until late 2022. Interest in LLMs and the broader discipline of generative AI has sk

www.pinecone.io

 

 

LangChain: Introduction and Getting Started

 

Large Language Models (LLMs) entered the world stage with the release of OpenAI’s GPT-3 in 2020 [1]. Since then, they’ve enjoyed a steady growth in popularity.

 

LLM(대형 언어 모델)은 2020년 OpenAI의 GPT-3 출시로 세계 무대에 진출했습니다[1]. 그 이후로 그들은 꾸준히 인기를 얻었습니다.

 

That is until late 2022. Interest in LLMs and the broader discipline of generative AI has skyrocketed. The reasons for this are likely the continuous upward momentum of significant advances in LLMs.

 

이는 2022년 말까지입니다. LLM과 생성 AI의 광범위한 분야에 대한 관심이 급증했습니다. 그 이유는 LLM의 상당한 발전이 지속적으로 증가하고 있기 때문일 것입니다.

 

We saw the dramatic news about Google’s “sentient” LaMDA chatbot. The first high-performance and open-source LLM called BLOOM was released. OpenAI released their next-generation text embedding model and the next generation of “GPT-3.5” models.

 

우리는 Google의 "지각력 있는" LaMDA 챗봇에 대한 극적인 소식을 보았습니다. BLOOM이라는 최초의 고성능 오픈 소스 LLM이 출시되었습니다. OpenAI는 차세대 텍스트 임베딩 모델과 차세대 "GPT-3.5" 모델을 출시했습니다.

 

After all these giant leaps forward in the LLM space, OpenAI released ChatGPT — thrusting LLMs into the spotlight.

 

LLM 공간에서 이러한 큰 도약을 마친 후 OpenAI는 ChatGPT를 출시하여 LLM을 주목하게 되었습니다.

 

LangChain appeared around the same time. Its creator, Harrison Chase, made the first commit in late October 2022. Leaving a short couple of months of development before getting caught in the LLM wave.

 

LangChain도 비슷한 시기에 등장했습니다. 제작자인 Harrison Chase는 2022년 10월 말에 첫 번째 커밋을 했습니다. 몇 달의 짧은 개발 기간을 거쳐 LLM 물결에 휩싸였습니다.

 

Despite being early days for the library, it is already packed full of incredible features for building amazing tools around the core of LLMs. In this article, we’ll introduce the library and start with the most straightforward component offered by LangChain — LLMs.

 

라이브러리는 초기 단계임에도 불구하고 이미 LLM 핵심을 중심으로 놀라운 도구를 구축하기 위한 놀라운 기능으로 가득 차 있습니다. 이 기사에서는 라이브러리를 소개하고 LangChain이 제공하는 가장 간단한 구성 요소인 LLM부터 시작하겠습니다.

 

https://youtu.be/nE2skSRWTTs?si=fTiKJZwYyYhNr4_c

 

 

LangChain

At its core, LangChain is a framework built around LLMs. We can use it for chatbots, Generative Question-Answering (GQA), summarization, and much more.

 

LangChain의 핵심은 LLM을 중심으로 구축된 프레임워크입니다. 챗봇, 생성적 질문 답변(GQA), 요약 등에 사용할 수 있습니다.

 

The core idea of the library is that we can “chain” together different components to create more advanced use cases around LLMs. Chains may consist of multiple components from several modules:

 

라이브러리의 핵심 아이디어는 다양한 구성 요소를 함께 " chain "하여 LLM에 대한 고급 사용 사례를 만들 수 있다는 것입니다. 체인은 여러 모듈의 여러 구성 요소로 구성될 수 있습니다.

 

  • Prompt templates: Prompt templates are templates for different types of prompts. Like “chatbot” style templates, ELI5 question-answering, etc

  • 프롬프트 템플릿: 프롬프트 템플릿은 다양한 유형의 프롬프트에 대한 템플릿입니다. "챗봇" 스타일 템플릿, ELI5 질문 답변 등
  • LLMs: Large language models like GPT-3, BLOOM, etc  GPT-3, BLOOM 등과 같은 대규모 언어 모델

  • Agents: Agents use LLMs to decide what actions should be taken. Tools like web search or calculators can be used, and all are packaged into a logical loop of operations.

  • 에이전트: 에이전트는 LLM을 사용하여 취해야 할 조치를 결정합니다. 웹 검색이나 계산기와 같은 도구를 사용할 수 있으며 모두 논리적 작업 루프로 패키지됩니다.

  • Memory: Short-term memory, long-term memory. 단기기억, 장기기억.

We will dive into each of these in much more detail in upcoming chapters of the LangChain handbook.

For now, we’ll start with the basics behind prompt templates and LLMs. We’ll also explore two LLM options available from the library, using models from Hugging Face Hub or OpenAI.

 

지금은 프롬프트 템플릿과 LLM의 기본 사항부터 시작하겠습니다. 또한 Hugging Face Hub 또는 OpenAI의 모델을 사용하여 라이브러리에서 사용할 수 있는 두 가지 LLM 옵션도 살펴보겠습니다.

 

Our First Prompt Templates

 

Prompts being input to LLMs are often structured in different ways so that we can get different results. For Q&A, we could take a user’s question and reformat it for different Q&A styles, like conventional Q&A, a bullet list of answers, or even a summary of problems relevant to the given question.

 

LLM에 입력되는 프롬프트는 종종 다른 결과를 얻을 수 있도록 다른 방식으로 구성됩니다. Q&A의 경우 사용자의 질문을 받아 일반적인 Q&A, 답변의 글머리 기호 목록, 특정 질문과 관련된 문제 요약 등 다양한 Q&A 스타일로 형식을 변경할 수 있습니다.

 

Creating Prompts in LangChain

 

Let’s put together a simple question-answering prompt template. We first need to install the langchain library.

 

간단한 질문 답변 프롬프트 템플릿을 만들어 보겠습니다. 먼저 langchain 라이브러리를 설치해야 합니다.

 

!pip install langchain

 

Follow along with the code via the walkthrough!

 

From here, we import the PromptTemplate class and initialize a template like so:

 

여기에서 PromptTemplate 클래스를 가져오고 다음과 같이 템플릿을 초기화합니다.

 

from langchain import PromptTemplate

template = """Question: {question}

Answer: """
prompt = PromptTemplate(
        template=template,
    input_variables=['question']
)

# user question
question = "Which NFL team won the Super Bowl in the 2010 season?"

 

이 코드는 Python 언어를 사용하여 langchain 패키지에서 PromptTemplate 클래스를 사용하는 간단한 예제입니다. 

  1. from langchain import PromptTemplate: langchain 패키지에서 PromptTemplate 클래스를 가져옵니다.
  2. template = """Question: {question}\n\nAnswer: """: PromptTemplate에 전달할 템플릿 문자열을 정의합니다. {question}은 나중에 변수로 대체될 부분입니다.
  3. prompt = PromptTemplate(template=template, input_variables=['question']): PromptTemplate 클래스의 인스턴스를 생성합니다. 여기서 템플릿과 입력 변수가 지정됩니다. template에는 앞에서 정의한 템플릿 문자열이, input_variables에는 입력으로 받을 변수들이 리스트로 지정됩니다. 이 경우에는 'question' 변수를 입력으로 받습니다.
  4. question = "Which NFL team won the Super Bowl in the 2010 season?": 사용자의 질문을 나타내는 변수를 정의합니다.

코드는 질문 변수를 이용하여 PromptTemplate 클래스에 정의된 템플릿에 맞게 포맷팅한 후, 그 결과를 출력할 준비를 마칩니다. 이것은 대화 시스템이나 언어 생성 모델에서 사용될 수 있는 일반적인 템플릿 기반의 입력 처리 방식을 보여주는 예제입니다.

 

 

When using these prompt template with the given question we will get:

 

주어진 질문에 이러한 프롬프트 템플릿을 사용하면 다음과 같은 결과를 얻을 수 있습니다.

 

Question: Which NFL team won the Super Bowl in the 2010 season? Answer:

 

For now, that’s all we need. We’ll use the same prompt template across both Hugging Face Hub and OpenAI LLM generations.

 

지금은 그것이 우리에게 필요한 전부입니다. Hugging Face Hub와 OpenAI LLM 세대 모두에서 동일한 프롬프트 템플릿을 사용할 것입니다.

 

Hugging Face Hub LLM

 

The Hugging Face Hub endpoint in LangChain connects to the Hugging Face Hub and runs the models via their free inference endpoints. We need a Hugging Face account and API key to use these endpoints.

 

LangChain의 Hugging Face Hub 엔드포인트는 Hugging Face Hub에 연결되어 무료 추론 엔드포인트를 통해 모델을 실행합니다. 이러한 엔드포인트를 사용하려면 Hugging Face 계정과 API 키가 필요합니다.

 

Once you have an API key, we add it to the HUGGINGFACEHUB_API_TOKEN environment variable. We can do this with Python like so:

 

API 키가 있으면 이를 HUGGINGFACEHUB_API_TOKEN 환경 변수에 추가합니다. Python으로 다음과 같이 이를 수행할 수 있습니다.

 

import os

os.environ['HUGGINGFACEHUB_API_TOKEN'] = 'HF_API_KEY'

 

 

Next, we must install the huggingface_hub library via Pip.

 

다음으로 Pip을 통해 Huggingface_hub 라이브러리를 설치해야 합니다.

!pip install huggingface_hub

 

 

 

Now we can generate text using a Hub model. We’ll use google/flan-t5-x1.

 

이제 허브 모델을 사용하여 텍스트를 생성할 수 있습니다. 우리는 google/flan-t5-x1을 사용하겠습니다.

 

The default Hugging Face Hub inference APIs do not use specialized hardware and, therefore, can be slow. They are also not suitable for running larger models like bigscience/bloom-560m or google/flan-t5-xxl (note xxl vs. xl).

 

기본 Hugging Face Hub 추론 API는 특수 하드웨어를 사용하지 않으므로 속도가 느릴 수 있습니다. 또한 bigscience/bloom-560m 또는 google/flan-t5-xxl(xxl과 xl 참고)과 같은 대규모 모델을 실행하는 데에도 적합하지 않습니다.

 

from langchain import HuggingFaceHub, LLMChain

# initialize Hub LLM
hub_llm = HuggingFaceHub(
        repo_id='google/flan-t5-xl',
    model_kwargs={'temperature':1e-10}
)

# create prompt template > LLM chain
llm_chain = LLMChain(
    prompt=prompt,
    llm=hub_llm
)

# ask the user question about NFL 2010
print(llm_chain.run(question))

 

green bay packers

 

이 코드는 langchain 패키지를 사용하여 Hugging Face Hub의 언어 모델을 초기화하고, 해당 모델을 사용하여 주어진 질문에 대한 응답을 생성하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. from langchain import HuggingFaceHub, LLMChain: langchain 패키지에서 Hugging Face Hub 및 LLMChain 클래스를 가져옵니다.
  2. hub_llm = HuggingFaceHub(repo_id='google/flan-t5-xl', model_kwargs={'temperature':1e-10}): Hugging Face Hub의 언어 모델을 초기화합니다. repo_id는 모델의 저장소 식별자이며, 이 경우에는 'google/flan-t5-xl'을 사용합니다. model_kwargs는 모델에 대한 추가적인 매개변수를 설정하는 것으로, 여기에서는 'temperature'를 매우 작은 값으로 설정하고 있습니다.
  3. llm_chain = LLMChain(prompt=prompt, llm=hub_llm): Prompt와 Hugging Face Hub에서 초기화된 언어 모델을 사용하여 LLMChain 클래스의 인스턴스를 생성합니다. 이것은 언어 생성 모델과 템플릿 기반 입력 처리를 결합하는 것으로 보입니다.
  4. print(llm_chain.run(question)): LLMChain의 run 메서드를 사용하여 주어진 질문에 대한 응답을 생성하고 출력합니다. 이는 템플릿과 언어 생성 모델을 조합하여 사용자의 입력에 대한 응답을 생성하는 간단한 예제입니다.

For this question, we get the correct answer of "green bay packers".

 

이 질문에 대한 정답은 '그린베이패커스'입니다.

 

Asking Multiple Questions

 

If we’d like to ask multiple questions, we can try two approaches:

 

여러 가지 질문을 하고 싶다면 두 가지 접근 방식을 시도해 볼 수 있습니다.

  1. Iterate through all questions using the generate method, answering them one at a time.

    generate 메소드를 사용하여 모든 질문을 반복하고 한 번에 하나씩 답변합니다.

  2. Place all questions into a single prompt for the LLM; this will only work for more advanced LLMs.

    모든 질문을 LLM의 단일 프롬프트에 배치하세요. 이는 고급 LLM에만 적용됩니다.

Starting with option (1), let’s see how to use the generate method:

 

옵션 (1)부터 시작하여 generate 메소드를 사용하는 방법을 살펴보겠습니다.

 

qs = [
    {'question': "Which NFL team won the Super Bowl in the 2010 season?"},
    {'question': "If I am 6 ft 4 inches, how tall am I in centimeters?"},
    {'question': "Who was the 12th person on the moon?"},
    {'question': "How many eyes does a blade of grass have?"}
]
res = llm_chain.generate(qs)
res

 

이 코드는 여러 개의 질문이 담긴 리스트를 사용하여 이전에 초기화한 llm_chain을 통해 각 질문에 대한 응답을 생성하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. qs = [...]: 여러 개의 질문을 딕셔너리 형태로 담은 리스트를 정의합니다. 각 딕셔너리는 'question' 키를 가지고 해당 질문을 값으로 가지고 있습니다.
  2. res = llm_chain.generate(qs): llm_chain의 generate 메서드를 사용하여 여러 개의 질문에 대한 응답을 생성합니다. 이 메서드는 주어진 질문 리스트에 대한 응답을 반환합니다. 결과는 res 변수에 저장됩니다.
  3. res: 생성된 응답이 담긴 변수를 출력합니다. 이 변수에는 각 질문에 대한 모델의 응답이 딕셔너리 형태로 저장되어 있습니다.

이 코드는 한 번에 여러 질문에 대한 응답을 생성하는 과정을 보여주는 예제로, 대량의 데이터나 다양한 입력에 대한 처리에 유용할 수 있습니다.

 

 

LLMResult(generations=[[Generation(text='green bay packers', generation_info=None)], [Generation(text='184', generation_info=None)], [Generation(text='john glenn', generation_info=None)], [Generation(text='one', generation_info=None)]], llm_output=None)

 

 

Here we get bad results except for the first question. This is simply a limitation of the LLM being used.

 

여기서는 첫 번째 질문을 제외하고는 나쁜 결과를 얻었습니다. 이는 단순히 사용되는 LLM의 제한 사항입니다.

 

If the model cannot answer individual questions accurately, grouping all queries into a single prompt is unlikely to work. However, for the sake of experimentation, let’s try it.

 

모델이 개별 질문에 정확하게 대답할 수 없는 경우 모든 쿼리를 단일 프롬프트로 그룹화하는 것은 작동하지 않을 수 있습니다. 하지만 실험을 위해 시도해 보겠습니다.

 

multi_template = """Answer the following questions one at a time.

Questions:
{questions}

Answers:
"""
long_prompt = PromptTemplate(template=multi_template, input_variables=["questions"])

llm_chain = LLMChain(
    prompt=long_prompt,
    llm=flan_t5
)

qs_str = (
    "Which NFL team won the Super Bowl in the 2010 season?\n" +
    "If I am 6 ft 4 inches, how tall am I in centimeters?\n" +
    "Who was the 12th person on the moon?" +
    "How many eyes does a blade of grass have?"
)

print(llm_chain.run(qs_str))

 

이 코드는 여러 개의 질문을 한 번에 받아들이고, 이에 대한 응답을 생성하는 예제입니다. 아래는 코드의 각 부분에 대한 설명입니다:

  1. multi_template = """Answer the following questions one at a time...\n""": 여러 개의 질문을 받아들이고, 이에 대한 응답을 생성하는 템플릿 문자열을 정의합니다. {questions}는 나중에 변수로 대체될 부분입니다.
  2. long_prompt = PromptTemplate(template=multi_template, input_variables=["questions"]): PromptTemplate 클래스를 사용하여 템플릿을 초기화합니다. input_variables에는 변수로 사용할 부분이 들어있는 리스트가 포함되어 있습니다.
  3. llm_chain = LLMChain(prompt=long_prompt, llm=flan_t5): 앞서 초기화한 long_prompt와 언어 모델 flan_t5를 사용하여 LLMChain 클래스의 인스턴스를 생성합니다.
  4. qs_str = "...": 여러 개의 질문을 줄바꿈을 사용하여 하나의 문자열로 정의합니다.
  5. print(llm_chain.run(qs_str)): llm_chain의 run 메서드를 사용하여 주어진 여러 개의 질문에 대한 응답을 생성하고 출력합니다.

이 코드는 여러 질문을 동시에 받아들이고, 이에 대한 응답을 생성하는 간단한 템플릿 및 언어 모델 활용 예제입니다.

 

If I am 6 ft 4 inches, how tall am I in centimeters

 

 

As expected, the results are not helpful. We’ll see later that more powerful LLMs can do this.

 

예상대로 결과는 도움이 되지 않습니다. 나중에 더 강력한 LLM이 이를 수행할 수 있다는 것을 알게 될 것입니다.

 

OpenAI LLMs

 

The OpenAI endpoints in LangChain connect to OpenAI directly or via Azure. We need an OpenAI account and API key to use these endpoints.

 

LangChain의 OpenAI 엔드포인트는 직접 또는 Azure를 통해 OpenAI에 연결됩니다. 이러한 엔드포인트를 사용하려면 OpenAI 계정과 API 키가 필요합니다.

 

Once you have an API key, we add it to the OPENAI_API_TOKEN environment variable. We can do this with Python like so:

 

API 키가 있으면 이를 OPENAI_API_TOKEN 환경 변수에 추가합니다. Python으로 다음과 같이 이를 수행할 수 있습니다.

import os

os.environ['OPENAI_API_TOKEN'] = 'OPENAI_API_KEY'

 

Next, we must install the openai library via Pip.

 

다음으로 Pip을 통해 openai 라이브러리를 설치해야 합니다.

 

Now we can generate text using OpenAI’s GPT-3 generation (or completion) models. We’ll use text-davinci-003.

 

이제 OpenAI의 GPT-3 생성(또는 완성) 모델을 사용하여 텍스트를 생성할 수 있습니다. text-davinci-003을 사용하겠습니다.

 

from langchain.llms import OpenAI

davinci = OpenAI(model_name='text-davinci-003')

 

 

Alternatively, if you’re using OpenAI via Azure, you can do:

 

또는 Azure를 통해 OpenAI를 사용하는 경우 다음을 수행할 수 있습니다.

 

from langchain.llms import AzureOpenAI

llm = AzureOpenAI(
    deployment_name="your-azure-deployment", 
    model_name="text-davinci-003"
)

 

We’ll use the same simple question-answer prompt template as before with the Hugging Face example. The only change is that we now pass our OpenAI LLM davinci:

 

이전에 Hugging Face 예와 동일한 간단한 질문-답변 프롬프트 템플릿을 사용하겠습니다. 유일한 변경 사항은 이제 OpenAI LLM davinci를 통과했다는 것입니다.

 

llm_chain = LLMChain(
    prompt=prompt,
    llm=davinci
)

print(llm_chain.run(question))

 

The Green Bay Packers won the Super Bowl in the 2010 season.

 

제 로컬에서는 2023년 11월 6일 OpenAI에서 발표된 gpt-4-1106-preview 모델을 사용했습니다.

결과는 아래와 같습니다.

 

 

As expected, we’re getting the correct answer. We can do the same for multiple questions using generate:

 

예상대로 정답이 나오네요. 생성을 사용하여 여러 질문에 대해 동일한 작업을 수행할 수 있습니다.

 

qs = [
    {'question': "Which NFL team won the Super Bowl in the 2010 season?"},
    {'question': "If I am 6 ft 4 inches, how tall am I in centimeters?"},
    {'question': "Who was the 12th person on the moon?"},
    {'question': "How many eyes does a blade of grass have?"}
]
llm_chain.generate(qs)

 

LLMResult(generations=[[Generation(text=' The Green Bay Packers won the Super Bowl in the 2010 season.', generation_info={'finish_reason': 'stop', 'logprobs': None})], [Generation(text=' 193.04 centimeters', generation_info={'finish_reason': 'stop', 'logprobs': None})], [Generation(text=' Charles Duke was the 12th person on the moon. He was part of the Apollo 16 mission in 1972.', generation_info={'finish_reason': 'stop', 'logprobs': None})], [Generation(text=' A blade of grass does not have any eyes.', generation_info={'finish_reason': 'stop', 'logprobs': None})]], llm_output={'token_usage': {'total_tokens': 124, 'prompt_tokens': 75, 'completion_tokens': 49}})

 

 

로컬에서 gpt-4-1106-preview를 사용해서 얻은 값은 아래와 같습니다.

 

LLMResult(generations=[[ChatGeneration(text='The Green Bay Packers won the Super Bowl for the 2010 NFL season. They defeated the Pittsburgh Steelers in Super Bowl XLV on February 6, 2011, with a final score of 31-25.', generation_info={'finish_reason': 'stop'}, message=AIMessage(content='The Green Bay Packers won the Super Bowl for the 2010 NFL season. They defeated the Pittsburgh Steelers in Super Bowl XLV on February 6, 2011, with a final score of 31-25.'))], [ChatGeneration(text='To convert feet and inches to centimeters, you can use the following conversions:\n\n1 inch = 2.54 centimeters\n1 foot = 12 inches\n\nFirst, convert your height to inches only:\n\n6 feet 4 inches = (6 * 12) + 4 inches\n= 72 + 4 inches\n= 76 inches\n\nNow convert inches to centimeters:\n\n76 inches * 2.54 cm/inch = 193.04 centimeters\n\nSo, if you are 6 feet 4 inches tall, you are 193.04 centimeters tall.', generation_info={'finish_reason': 'stop'}, message=AIMessage(content='To convert feet and inches to centimeters, you can use the following conversions:\n\n1 inch = 2.54 centimeters\n1 foot = 12 inches\n\nFirst, convert your height to inches only:\n\n6 feet 4 inches = (6 * 12) + 4 inches\n= 72 + 4 inches\n= 76 inches\n\nNow convert inches to centimeters:\n\n76 inches * 2.54 cm/inch = 193.04 centimeters\n\nSo, if you are 6 feet 4 inches tall, you are 193.04 centimeters tall.'))], [ChatGeneration(text='The 12th and last person to walk on the Moon was Eugene Cernan. He was the commander of the Apollo 17 mission, which landed on the Moon in December 1972. Cernan, along with Harrison Schmitt, explored the lunar surface during this mission before returning to Earth. Eugene Cernan is often remembered for being the last human to leave footprints on the lunar surface, as no manned missions have returned to the Moon since Apollo 17.', generation_info={'finish_reason': 'stop'}, message=AIMessage(content='The 12th and last person to walk on the Moon was Eugene Cernan. He was the commander of the Apollo 17 mission, which landed on the Moon in December 1972. Cernan, along with Harrison Schmitt, explored the lunar surface during this mission before returning to Earth. Eugene Cernan is often remembered for being the last human to leave footprints on the lunar surface, as no manned missions have returned to the Moon since Apollo 17.'))], [ChatGeneration(text='A blade of grass does not have eyes. Plants do not have eyes like animals do; instead, they have cells and structures that can sense light and allow them to orient themselves and grow towards light sources, a process known as phototropism. However, this sensing capability is not analogous to vision as experienced by animals with eyes.', generation_info={'finish_reason': 'stop'}, message=AIMessage(content='A blade of grass does not have eyes. Plants do not have eyes like animals do; instead, they have cells and structures that can sense light and allow them to orient themselves and grow towards light sources, a process known as phototropism. However, this sensing capability is not analogous to vision as experienced by animals with eyes.'))]], llm_output={'token_usage': {'prompt_tokens': 101, 'completion_tokens': 329, 'total_tokens': 430}, 'model_name': 'gpt-4-1106-preview'}, run=[RunInfo(run_id=UUID('ea1a84b0-eb29-4da0-9850-76b54fbf90d8')), RunInfo(run_id=UUID('477f0185-7bc3-4767-b598-52b113878efd')), RunInfo(run_id=UUID('32846093-23ba-48a5-be5c-58bfc7ded427')), RunInfo(run_id=UUID('c41409a1-7fc0-410a-a15f-b8457e2343d9'))])

 

 

최신 모델이라서 그런지 답이 훨씬 자세하게 나옵니다.

 

참고로 2023년 11월 9일 현재 사용하는 gpt-4와 gpt-3의 모델들은 아래와 같습니다.

 

 

 

 

 

다시 Pinecorn 교재로 들어가 보겠습니다.

 

Most of our results are correct or have a degree of truth. The model undoubtedly functions better than the google/flan-t5-xl model. As before, let’s try feeding all questions into the model at once.

 

대부분의 결과는 정확하거나 어느 정도 진실입니다. 이 모델은 의심할 여지 없이 google/flan-t5-xl 모델보다 더 잘 작동합니다. 이전과 마찬가지로 모든 질문을 모델에 한 번에 입력해 보겠습니다.

 

llm_chain = LLMChain(
    prompt=long_prompt,
    llm=davinci
)

qs_str = (
    "Which NFL team won the Super Bowl in the 2010 season?\n" +
    "If I am 6 ft 4 inches, how tall am I in centimeters?\n" +
    "Who was the 12th person on the moon?" +
    "How many eyes does a blade of grass have?"
)

print(llm_chain.run(qs_str))

 

The New Orleans Saints won the Super Bowl in the 2010 season.
6 ft 4 inches is 193 centimeters.
The 12th person on the moon was Harrison Schmitt.
A blade of grass does not have eyes.

 

로컬에서 gpt4 turbo로 돌린 결과는 아래와 같습니다.

 

 

1. The NFL team that won the Super Bowl for the 2010 season (Super Bowl XLV, played on February 6, 2011) was the Green Bay Packers. They defeated the Pittsburgh Steelers with a score of 31-25.

2. If you are 6 feet 4 inches tall, to convert your height to centimeters (cm), you would first convert the feet to inches and then convert inches to centimeters using the conversion factor: 1 inch = 2.54 cm.
6 feet x 12 inches/foot = 72 inches
4 inches are already in inches
So, total inches = 72 + 4 = 76 inches
Now convert inches to centimeters:
76 inches x 2.54 cm/inch = 193.04 cm
Therefore, you are 193.04 centimeters tall.

3. The 12th person to walk on the moon was Harrison Schmitt. He was part of the Apollo 17 mission, which was the last manned lunar landing mission of NASA's Apollo program.

4. A blade of grass does not have eyes. Eyes are complex organs associated with animals, and grass is a type of plant. Plants do not have eyes or sensory organs like animals. They do have sensory mechanisms to respond to light, gravity, and other environmental stimuli, but these are not analogous to eyes.

 

 

As we keep rerunning the query, the model will occasionally make errors, but at other times manage to get all answers correct.

 

쿼리를 계속 재실행하면 모델이 가끔 오류를 일으키기도 하지만 때로는 모든 답변이 정확해지는 경우도 있습니다.

 


That’s it for our introduction to LangChain — a library that allows us to build more advanced apps around LLMs like OpenAI’s GPT-3 models or the open-source alternatives available via Hugging Face.

 

이것이 바로 OpenAI의 GPT-3 모델이나 Hugging Face를 통해 제공되는 오픈 소스 대안과 같은 LLM을 중심으로 고급 앱을 구축할 수 있는 라이브러리인 LangChain에 대한 소개입니다.

 

As mentioned, LangChain can do much more than we’ve demonstrated here. We’ll be covering these other features in upcoming articles.

 

앞서 언급했듯이 LangChain은 여기서 설명한 것보다 훨씬 더 많은 일을 할 수 있습니다. 향후 에서 이러한 다른 기능을 다룰 예정입니다.

 

References

 

[1] GPT-3 Archived Repo (2020), OpenAI GitHub

 

 

 

반응형


반응형

https://www.pinecone.io/learn/series/langchain/

 

LangChain AI Handbook | Pinecone

The handbook to the LangChain library for building applications around generative AI and large language models (LLMs).

www.pinecone.io

 

LangChain AI Handbook

By James Briggs & Francisco Ingham

 

The LangChain library empowers developers to create intelligent applications using large language models. It’s revolutionizing industries and technology, transforming our every interaction with technology.

 

LangChain 라이브러리는 개발자가 대규모 언어 모델을 사용하여 지능형 애플리케이션을 만들 수 있도록 지원합니다. 이는 산업과 기술에 혁명을 일으키고 기술과의 모든 상호 작용을 변화시키고 있습니다.

 

Introduction

At its core, LangChain is a framework built around LLMs. We can use it for chatbots, Generative Question-Answering (GQA), summarization, and much more.

 

LangChain의 핵심은 LLM을 중심으로 구축된 프레임워크입니다. 챗봇, 생성적 질문 답변(GQA), 요약 등에 사용할 수 있습니다.

 

Let's dive in!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

반응형
이전 1 다음