LangChain Tool Calling 기능 실험 예제

LangChain 을 이용하여 AI 모델에 툴 호출(tool calling) 기술을 적용해보고, 이를 통해 AI 답변이 어떻게 달라지는지 테스트를 진행해보기로 한다. Tool Calling 기능은 대화형 AI 모델이 외부 도구를 직접 호출하여 특정 작업을 수행할 수 있도록 하는 기능이며, AI가 단순히 대화를 나누는 것을 넘어서 도구를 호출하여 복잡한 연산 혹은 실데이터에 기반한 답변을 수행할 수 있게 해준다.

Tool Calling 없이 질의하기

tool-calling 기능이 제대로 작동하는지 보기위해, 일부러 잘못된 결과값이 나오도록 function을 셋팅할 예정이다. 우선 사용할 라이브러리를 셋팅한다. 여기서는 Ollama 로 설치한 로컬 LLM 모델을 사용할 거라, langchain-ollama 을 미리 설치해야 하고, 자세한 내용은 여기를 클릭한다.

from langchain_ollama import ChatOllama
from langchain_core.prompts import ChatPromptTemplate, FewShotChatMessagePromptTemplate
import langchain_core, langchain_ollama
import pprint

#langchain_core : 0.3.10
#langchain_ollama : 0.2.0
print("langchain_core : " + langchain_core.__version__)
print("langchain_ollama : " + langchain_ollama.__version__)

AI 모델로 llama3.1:8b 버전을 선택했다. 원래는 3b 모델을 사용해보려 했으나, 툴 호출 기능이 제대로 작동하지 않아, 8b모델로 했더니 결과가 만족스러웠다. 참고로 gemma2 는 tool calling 기능이 없었다.

from langchain_ollama import ChatOllama

llm = ChatOllama(
    model="llama3.1:8b",
    temperature=0,
    base_url="http://host.docker.internal:11434"
)

기본적인 수학 계산을 AI에게 요청해 봤다.

ai_msg = llm.invoke(input="What is 3 * 12? Also, what is 11 + 49?")

이정도 질의는 별다른 셋팅이 없어도 정확하게 잘 나온다. tool-calling 기능이 필요하지 않은 수준이다.

print(ai_msg.content)

"""
Two simple math questions!

**1. 3 * 12 = 36**

Multiplying 3 by 12 gives us a product of 36.

**2. 11 + 49 = 60**

Adding 11 and 49 together gives us a sum of 60.
"""

Tool Calling 정의 및 연동

엉뚱한 계산을 수행할 2개의 tool 을 만들었다. 그냥 물어봐도 정확히 답이 나오기 때문에 tool calling 기능을 이용하여 잘못된 결과값이 나오도록 리턴해보려한다. 덧셈 함수에는 + 10을 추가로 더했고, 곱셈 함수는 X 10을 추가했다.

from langchain_core.tools import tool

@tool
def add(a:int, b:int) -> int:
    """Adds a and b."""
    return a + b + 10

@tool
def multiply(a:int, b:int) -> int:
    """Multiplies a and b."""
    return a * b * 10   

의도적으로 결과가 잘못되도록 설정해놓고, Tool 을 LLM에 연결한다.

tools = [add, multiply]
llm_with_tools = llm.bind_tools(tools)

다시 한 번 동일한 질의를 요청할 건데, Tool 이 연결된 LLM 모델을 통해 처리하도록 한다. LLM 모델이 function 의 이름, 주석 등을 기반으로 질의의 내용과 매핑하게 된다. 여기서는 매핑만 할뿐 아직 결과를 산출하지 않는다.

from langchain_core.messages import HumanMessage

query = "What is 3 * 12? Also, what is 11 + 49?"
messages = [HumanMessage(query)]

ai_msg = llm_with_tools.invoke(messages)

아까 human 메시지만 넣어놨던 messages 변수에 ai_msg 를 추가해놓는다.

messages.append(ai_msg)

결과를 들여다보면, 입력한 질의가 LLM 을 통해 적절한 함수를 매핑하고 파라메터까지 제대로 구조화되어 들어간 것을 볼 수 있다.

print(ai_msg.tool_calls)

"""
[{'args': {'a': 3, 'b': 12},
  'id': '66e773b5-9012-4343-b74e-e674bf9322fa',
  'name': 'multiply',
  'type': 'tool_call'},
 {'args': {'a': 11, 'b': 49},
  'id': '502862d1-0d5b-48be-892e-8355ac2f292f',
  'name': 'add',
  'type': 'tool_call'}]
"""

이제 매핑된 tool 을 이용하여 결과를 실행하는 단계이며, invoke 로 수행한 결과를 messages 에 차곡차곡 쌓는다.

for tool_call in ai_msg.tool_calls:
    selected_tool = {"add": add, "multiply":multiply}[tool_call["name"]]
    tool_msg = selected_tool.invoke(tool_call)
    messages.append(tool_msg)

messages 에 최종 쌓인 내역은 human 1건, tool-calling 매핑결과 1건, tool 수행한 결과값 2건으로 총 4개가 만들어졌다. 보면 알겠지만, 엉터리 값이 산출되도록 function 을 만들었기 때문에 11 + 49 + 10 = 70 이 나왔고, 3 * 12 * 10 = 360 이 산출되어 메시지에 들어가 있다.

print(messages)

"""
[HumanMessage(content='What is 3 * 12? Also, what is 11 + 49?', additional_kwargs={}, response_metadata={}),
 AIMessage(content='', additional_kwargs={}, response_metadata={'model': 'llama3.1:8b', 'created_at': '2024-10-14T10:05:58.477577Z', 'message': {'role': 'assistant', 'content': '', 'tool_calls': [{'function': {'name': 'multiply', 'arguments': {'a': 3, 'b': 12}}}, {'function': {'name': 'add', 'arguments': {'a': 11, 'b': 49}}}]}, 'done_reason': 'stop', 'done': True, 'total_duration': 5185118709, 'load_duration': 32918959, 'prompt_eval_count': 233, 'prompt_eval_duration': 1599681000, 'eval_count': 43, 'eval_duration': 3551051000}, id='run-ad7f5247-74c9-4d55-97e5-1e791446a9b9-0', tool_calls=[{'name': 'multiply', 'args': {'a': 3, 'b': 12}, 'id': '66e773b5-9012-4343-b74e-e674bf9322fa', 'type': 'tool_call'}, {'name': 'add', 'args': {'a': 11, 'b': 49}, 'id': '502862d1-0d5b-48be-892e-8355ac2f292f', 'type': 'tool_call'}], usage_metadata={'input_tokens': 233, 'output_tokens': 43, 'total_tokens': 276}),
 ToolMessage(content='360', name='multiply', tool_call_id='66e773b5-9012-4343-b74e-e674bf9322fa'),
 ToolMessage(content='70', name='add', tool_call_id='502862d1-0d5b-48be-892e-8355ac2f292f')]
"""

messages 를 가지고 invoke 해서 답변만 받으면 끝이다.

ai_final_msg = llm_with_tools.invoke(messages)

엉터리 결과를 만들어내는 tool 이었지만, 그래도 제대로 실행되어 360 과 70이 제대로 반영되었다.

print(ai_final_msg.content)

"""
The result of the multiplication is 360, 
and the result of the addition is 70.
"""

더 보면 좋을 글들