LangChain Agent의 핵심 아이디어는 LLM을 두뇌로 활용하여 자동으로 생각하고 결정을 내리며 여러 동작을 실행하여 최종적으로 목표 작업을 완수하는 것입니다.

팁: 개발적인 관점에서는 사전에 다양한 API를 개발하고, 그런 다음 에이전트에게 작업을 부여하여 LLM이 작업을 완료하기 위해 어떤 API를 호출할지 분석하도록 하는 것을 의미합니다.

LangChain Agent가 해결하려는 문제를 더 잘 이해하기 위해 예를 들어보겠습니다.

예를 들어:

만일 "도커를 제품 배포 솔루션으로 사용할 수 있는지"를 조사하려면, 먼저 Baidu에서 "도커 소개"를 검색하고 검색 결과를 찾은 후 "도커 배포의 장단점"을 더 검색하고 그 결과를 찾아가는 등의 과정을 거칠 것입니다.

LangChain Agent는 이러한 과정을 시뮬레이션하려고 합니다. 저는 일련의 도구(예: Baidu 검색, URL 콘텐츠 추출 도구 등)를 미리 패키징하고, 그런 다음 에이전트에게 "도커를 제품 배포 솔루션으로 사용할 수 있는지"라는 목표 작업을 부여합니다. 에이전트는 그런 다음 LLM을 부르는 프롬프트를 구성합니다. 이 작업을 완수하기 위해 다음 단계는 무슨 동작을 실행해야 하는지(즉, 어떤 도구를 호출해야 하는지)를 궁금해합니다. 인공지능은 호출해야 하는 도구를 반환하고, 코드는 이 도구를 실행하고, 그 결과를 다시 인공지능에게 돌려주며, 다음으로 실행해야 할 도구를 물어본다. 이 과정을 반복함으로써 앞서 언급한 작업을 완료할 수 있습니다.

팁: GPT 모델의 출시로 LLM이 뇌로 작동하여 다양한 개발된 API를 부를 수 있게 되어 폭발적인 능력을 지닌 것입니다. 이 기능은 현재 실험 단계에 있으며 LLM을 반복적으로 부르므로 토큰을 많이 소비합니다. 몇만 개의 토큰이 분 단위로 소요될 수 있습니다. 비용을 절약하려면 먼저 LLM에게 간단한 논리적 판단 작업을 수행하도록 하는 것이 좋습니다.

핵심 개념

이제 관련 구성 요소 및 개념을 소개하겠습니다.

에이전트

에이전트는 우리를 대신하여 의사결정을 내리는 우리의 보조자로 이해할 수 있습니다. LangChain Agent의 밑바닥 구현에서, LLM을 통해 다음 동작(또는 API 호출)이 결정됩니다. ReAct 모드는 인공지능 의사결정 프로세스를 설명합니다. 관심 있는 사람들은 더 알아볼 수 있습니다.

LangChain은 다양한 시나리오를 위해 여러 유형의 에이전트를 제공합니다.

도구

도구는 LLM의 능력을 확장하기 위해 사전에 패키지된 여러 기능적 API로 이해하는 것이 좋습니다. LLM은 특정 API를 호출하여 작업을 완료하기로 결정합니다.

툴킷

툴킷은 종종 LLM에게 하나 이상의 도구를 제공하여 작업 완료 시 LLM에게 더 많은 옵션을 제공합니다.

에이전트 실행기

프록시 실행기는 LLM이 선택한 도구(API)를 실행하는 역할을 담당합니다. 이 실행 기능의 유사 코드는 다음과 같습니다.

next_action = agent.get_action(...)
while next_action != AgentFinish:
    observation = run(next_action)
    next_action = agent.get_action(..., next_action, observation)
return next_action

실행 프로세스는 복잡하지 않지만, 실행기는 주로 다양한 세부 문제를 처리합니다. 이는 주로 다음을 포함합니다:

  1. 에이전트가 존재하지 않는 도구를 선택하는 상황 처리
  2. 도구 오류 상황 처리
  3. 에이전트가 도구 호출로 해결할 수 없는 출력을 생성하는 상황 처리
  4. 디버깅 문제.

빠른 시작

이 섹션에서는 LangChain의 Agent의 기본 사용법을 소개합니다.

1. LLM 불러오기

먼저, 우리가 에이전트를 제어하기 위해 사용할 언어 모델(LLM)을 불러와봅시다.

from langchain_openai import ChatOpenAI

llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)

2. 도구 정의하기

그런 다음, 에이전트가 호출할 일부 도구를 정의합니다. 우리는 입력 단어의 길이를 계산하는 매우 간단한 Python 함수를 작성해볼 것입니다.

from langchain.agents import tool

@tool
def get_word_length(word: str) -> int:
    """단어의 길이를 반환합니다."""
    return len(word)

get_word_length.invoke("abc")

참고: 함수 주석은 매우 중요합니다. 이러한 주석들은 LLM이 호출함으로써 어떤 문제를 해결할 수 있는지 알려줍니다. get_word_length 함수는 LLM에게 단어의 길이를 계산할 수 있다고 알려주는 것입니다.

3

일련의 도구를 정의합니다.

tools = [get_word_length]

3. 프롬프트 생성

이제 프롬프트를 생성해 봅시다. OpenAI Function Calling은 도구 사용에 최적화되어 있으므로 추론이나 출력 형식에 대한 자세한 지침은 거의 필요하지 않습니다. 우리에게는 inputagent_scratchpad라는 두 가지 입력 변수만 있습니다. input은 사용자의 입력 질문을 나타내고, agent_scratchpad는 모델을 실행할 때 프롬프트 템플릿에 삽입될 에이전트의 호출 지침을 나타냅니다.

from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "당신은 매우 강력한 어시스턴트이지만 현재 상황을 이해하지 못합니다.",
        ),
        ("user", "{input}"),
        MessagesPlaceholder(variable_name="agent_scratchpad"),
    ]
)

4. 도구를 LLM에 바인딩

에이전트가 어떤 도구를 사용할 수 있는지를 알기 위해서는 어떻게 해야 할까요?

이는 OpenAI 도구 호출 기능에 의존합니다 (많은 모델이 유사한 기능을 지원합니다). 우리는 모델에 정의된 도구 호출 형식만 알려주면 됩니다.

llm_with_tools = llm.bind_tools(tools)

5. 에이전트 생성

이전 내용을 통합했으므로 이제 프록시 프로그램을 생성할 수 있습니다. 이전 실용적인 유틸리티 함수 두 가지를 가져옵니다. 하나는 중간 단계 (프록시 동작, 도구 출력)을 모델에 전송할 수 있는 입력 메시지로 형식화하는 함수이고, 다른 하나는 출력 메시지를 프록시 동작/프록시 종료로 변환하는 함수입니다.

from langchain.agents.format_scratchpad.openai_tools import format_to_openai_tool_messages
from langchain.agents.output_parsers.openai_tools import OpenAIToolsAgentOutputParser

agent = (
    {
        "input": lambda x: x["input"],
        "agent_scratchpad": lambda x: format_to_openai_tool_messages(x["intermediate_steps"]),
    }
    | prompt
    | llm_with_tools
    | OpenAIToolsAgentOutputParser()
)

에이전트 실행기 정의

from langchain.agents import AgentExecutor

agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

다음은 예제를 통해 프록시 프로그램 작동을 시연해 봅시다.

list(agent_executor.stream({"input": "단어 'edcua'에는 몇 글자가 있나요?"}))

에이전트 출력 로그 예시

> 새로운 에이전트 실행 체인 진입...

호출: 매개변수 'word': 'educa'로 'get_word_length' 실행 중

단어 "educa"에는 5개의 글자가 있습니다.

> 실행 체인 완료.

이 예를 통해 프록시 프로그램의 전체 과정을 시연했습니다.

에이전트에 메모리 기능 추가

에이전트에 이전 대화 내용을 기억하도록 하려면 실제로 매우 간단합니다. 우리는 단순히 AI에 의해 반환된 내용을 프롬프트에 삽입하고 함께 AI에 제출하기만 하면 됩니다.

프롬프트 템플릿 수정

다음은 대화 기록 템플릿 변수를 포함하도록 프롬프트 템플릿을 수정합니다.

from langchain.prompts import MessagesPlaceholder

MEMORY_KEY = "chat_history"
prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "당신은 훌륭한 어시스턴트이지만 단어의 길이를 계산하는 데는 능숙하지 않습니다.",
        ),
        MessagesPlaceholder(variable_name=MEMORY_KEY),
        ("user", "{input}"),
        MessagesPlaceholder(variable_name="agent_scratchpad"),
    ]
)

에이전트 프로세스 정의 수정

프롬프트 템플릿에 대화 기록 데이터를 제공하도록 에이전트 프로세스 정의를 수정합니다. 이를 위해 chat_history 매개변수 처리를 추가합니다.

agent = (
    {
        "input": lambda x: x["input"],
        "agent_scratchpad": lambda x: format_to_openai_tool_messages(
            x["intermediate_steps"]
        ),
        "chat_history": lambda x: x["chat_history"],
    }
    | prompt
    | llm_with_tools
    | OpenAIToolsAgentOutputParser()
)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
chat_history = []

input1 = "단어 'educa'에는 몇 글자가 있나요?"
result = agent_executor.invoke({"input": input1, "chat_history": chat_history})
chat_history.extend(
    [
        HumanMessage(content=input1),
        AIMessage(content=result["output"]),
    ]
)
agent_executor.invoke({"input": "이 단어는 실제로 존재합니까?", "chat_history": chat_history})