Langchain과 Langgraph의 Agent구현의 차이점.
안녕하세요. Teus입니다.
이번 포스팅은 현재 LLM Agent을 구현하기위해서
가장 많이 쓰이는 Langchain과 Langgraph의 create_react_agent
함수의 차이점에 대해서 간략하게 알아봅니다.
0. LLM Agent
Agent는 보통은 특수한 영역 또는 기능에 특화된 일꾼 입니다.
근데 여기에 이게 Tools
를 추가해서
유저의 요청에 따라, 상황에 맞는 Tool를 사용해서 기존에 학습하지 못했던 기능을 가능케 합니다.
이를위해서 ReAct(Reasoning and Action) Agent를 사용하게 됩니다.
이때 기존 포스팅을 통해서, Scratch부터 Agent를 만들어 봤지만
실제 Production에서는 Langchain이나 Langgraph를 사용하게 됩니다.
1. Langchain의 create_react_agent
Langchain에서는 create_react_agent
함수를 통해서 Agent를 만들 수 있고
prompt를 통해서 Agent가 어떤 Chain Of Thought 를 진행할지 기술하게 됩니다.
#홈페이지 Example
from langchain import hub
from langchain_community.llms import OpenAI
from langchain.agents import AgentExecutor, create_react_agent
from langchain_core.prompts import PromptTemplate
template = '''Answer the following questions as best you can. You have access to the following tools:
{tools}
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 [{tool_names}]
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 = PromptTemplate.from_template(template)
model = OpenAI()
tools = ...
agent = create_react_agent(model, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools)
agent_executor.invoke({"input": "hi"})
# Use with chat history
from langchain_core.messages import AIMessage, HumanMessage
agent_executor.invoke(
{
"input": "what's my name?",
# Notice that chat_history is a string
# since this prompt is aimed at LLMs, not chat models
"chat_history": "Human: My name is Bob\nAI: Hello Bob!",
}
)
그래서 사용자의 prompt작성 수준에 따라서 Agent의 Tools Call능력이나, Reasoning능력이 달라지게 됩니다.
하지만 API문서에서 보면, 아래와 같은 문구를 확인할 수가 있습니다.
Warning
This implementation is based on the foundational ReAct paper but is older and not
well-suited for production applications. For a more robust and feature-rich
implementation, we recommend using the create_react_agent function from the LangGraph
library. See the [reference doc](https://langchain-
ai.github.io/langgraph/reference/prebuilt/#langgraph.prebuilt.chat_agent_executor.crea
te_react_agent) for more information.
Langchain Agent는 Production 용도에는 적합하지 않다는 내용이네요.
Langchain의 경우 prompt를 이용해서 Agent가 Tool을 사용하게 유도합니다.
이 덕분에 Tool Call이 학습되지 않은 모델에서도 Tool을 사용할 수 있다는 장점이 있습니다.
2. Langgraph의 create_react_agent
Langgraph의 경우 동일한 이름의 create_react_agent
함수를 제공합니다.
대신 차이점을 보이는게
Langgraph의 공식 문서에서는
prompt입력에 대한 가이드 라던가, 복잡한 prompt를 입력하라고 안내해주지 않습니다.
#공식예제
#https://langchain-ai.github.io/langgraph/agents/agents/#2-create-an-agent
from langgraph.prebuilt import create_react_agent
def get_weather(city: str) -> str:
"""Get weather for a given city."""
return f"It's always sunny in {city}!"
agent = create_react_agent(
model="anthropic:claude-3-7-sonnet-latest",
tools=[get_weather],
prompt="You are a helpful assistant"
)
# Run the agent
agent.invoke(
{"messages": [{"role": "user", "content": "what is the weather in sf"}]}
)
3. 둘이 뭔 차이에요?
비고 | Langchain | Langgraph |
---|---|---|
prompt | Reasoing과정 직접 작성 | 단순한 Agent의 역할부여 |
ReAct의구현 | prompt로 정의+Langchain내부 구현 | Langgraph로 구현 |
Function call | prompt를 이용해서 LLM이 Tool을 사용하게 유도 | LLM 학습 당시 구현한 Tool Call API사용 |
Tool 사용 가능 모델 | 모든 모델 | Tool Call이 가능한 Model |
두 라이브러리의 실제 vLLM 또는 OpenAI로 보내지는 prompt를 확인하기 위해서 아래 코드를 사용합니다.
#langchain
import logging
import requests
from langchain.agents import (
AgentExecutor,
create_react_agent,
)
from langchain_core.tools import Tool
from langchain_openai import ChatOpenAI
import logging
# Set up logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
#logging을 통해서 debbuging진행
class LoggingChatOpenAI(ChatOpenAI):
def _call(self, prompt: str, stop=None) -> str:
logger.debug(f"Prompt: {prompt}")
logger.debug(f"Stop: {stop}")
response = super()._call(prompt, stop)
logger.debug(f"Response: {response}")
return response
# Use the LoggingChatOpenAI class instead of ChatOpenAI
model = LoggingChatOpenAI(
model="MyModel",
temperature=0,
max_tokens=None,
timeout=None,
max_retries=2,
api_key="mytoken",
base_url="http://myvllmservice/v1",
)
# Define a very simple tool function that returns the current time
def get_weather(city: str) -> str:
"""Get weather for a given city."""
return f"It's always sunny in {city}!"
# List of tools available to the agent
tools = [
Tool(
name="Weather", # Name of the tool
func=get_weather, # Function that the tool will execute
# Description of the tool
description=get_weather.__doc__,
),
]
# Initialize a ChatOpenAI model
from langchain_core.prompts import PromptTemplate
template = '''Answer the following questions as best you can. You have access to the following tools:
{tools}
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 [{tool_names}]
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 = PromptTemplate.from_template(template)
# Create the ReAct agent using the create_react_agent function
agent = create_react_agent(
llm=model,
tools=tools,
prompt=prompt,
stop_sequence=True,
)
# Create an agent executor from the agent and tools
agent_executor = AgentExecutor.from_agent_and_tools(
agent=agent,
tools=tools,
verbose=True,
)
# Run the agent with a test query
response = agent_executor.invoke({"input": "tell me what time is it now"})
# Print the response from the agent
print("response:", response)
#langgraph
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from langchain_mcp_adapters.tools import load_mcp_tools
from langgraph.prebuilt import create_react_agent
from langchain_openai import ChatOpenAI
import asyncio
import logging
# Set up logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
class LoggingChatOpenAI(ChatOpenAI):
def _call(self, prompt: str, stop=None) -> str:
logger.debug(f"Prompt: {prompt}")
logger.debug(f"Stop: {stop}")
response = super()._call(prompt, stop)
logger.debug(f"Response: {response}")
return response
def get_weather(city: str) -> str:
"""Get weather for a given city."""
return f"It's always sunny in {city}!"
# Use the LoggingChatOpenAI class instead of ChatOpenAI
model = LoggingChatOpenAI(
model="MyModel",
temperature=0,
max_tokens=None,
timeout=None,
max_retries=2,
api_key="mytoken",
base_url="http://myvllmservice/v1",
)
# MCP Server 설정(이경우 Client.py파일과 Server.py파일이 같은 위치에 있음)
server_params = StdioServerParameters(
command="python",
# Make sure to update to the full absolute path to your math_server.py file
args=["weather_server_stdio.py"],
)
from langchain_core.prompts import PromptTemplate
template = '''Answer the following questions as best you can. You have access to the following tools:
{tools}
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 [{tool_names}]
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}'''
# MCP Python SDK를 활용해서 MCP Server를 사용함
async def run_agent():
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
# Initialize the connection
await session.initialize()
# MCP Server에 있는 Tool리스트를 가지고 옵니다.
tools = await load_mcp_tools(session)
# Tools와 Model을 사용해서 Agent 생성
agent = create_react_agent(
model = model,
tools = tools,
prompt = template
)
# Agent한테 Query를 진행해, Agent가 알아서 tools을 사용하게 만듭니다.
agent_response = await agent.ainvoke({"messages": "what's time is it now?. i'm live in seoul city. find about city seoul"})
return agent_response
# Run the async function
if __name__ == "__main__":
result = asyncio.run(run_agent())
print(result)
#weather_server_stdio.py
from mcp.server.fastmcp import FastMCP
import datetime
mcp = FastMCP("Weather")
@mcp.tool()
async def get_weather(location: str) -> str:
"""Get weather for location."""
return "It's always sunny in New York"
if __name__ == "__main__":
mcp.run(transport="stdio")
이제 코드를 실행시키고, 디버깅 로그를 확인해봅니다.
#langchain을 사용한 경우
DEBUG:openai._base_client:Request options: {'method': 'post', 'url':
'/chat/completions', 'files': None, 'idempotency_key': 'stainless-python-retry-
75c082d1-3005-4aa5-9924-8b88f7afb4b3', 'json_data': {'messages': [{'content': 'Answer
the following questions as best you can. You have access to the following
tools:\n\nTime(city: str) -> str - Get weather for a given city.\n\nUse the following
format:\n\nQuestion: the input question you must answer\nThought: you should always
think about what to do\nAction: the action to take, should be one of [Time]\nAction
Input: the input to the action\nObservation: the result of the action\n... (this
Thought/Action/Action Input/Observation can repeat N times)\nThought: I now know the
final answer\nFinal Answer: the final answer to the original input
question\n\nBegin!\n\nQuestion: how about weather in seoul?\nThought:', 'role':
'user'}], 'model': 'Gemma-3-27b-it', 'stop': ['\nObservation'], 'stream': True,
'temperature': 0.0}}
prompt만 따로 빼서보면
Answer the following questions as best you can. You have access to the following tools:
Time(city: str) -> str - Get weather for a given city.
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 [Time]
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\nFinal Answer: the final answer to the original input question
Begin!
Question: how about weather in seoul?
Thought:'
requests를 보내기 전에 이미 prompt부분이 Agent정의때 사용된 {input}, {tools}, {tools_names}, {agent_scratchppad}
가 이미 포맷팅이 된 것을 확인할 수 있습니다.
실제로 requests를 보내기 이전에 prompt 포맷팅이 진행되는 것음
langchain의 create_react_agent 소스코드에서도 확인할 수가 있습니다.
#https://python.langchain.com/api_reference/_modules/langchain/agents/react/agent.html#create_react_agent
def create_react_agent(
llm: BaseLanguageModel,
tools: Sequence[BaseTool],
prompt: BasePromptTemplate,
output_parser: Optional[AgentOutputParser] = None,
tools_renderer: ToolsRenderer = render_text_description,
*,
stop_sequence: Union[bool, list[str]] = True,
) -> Runnable:
missing_vars = {"tools", "tool_names", "agent_scratchpad"}.difference(
prompt.input_variables + list(prompt.partial_variables)
)
if missing_vars:
raise ValueError(f"Prompt missing required variables: {missing_vars}")
prompt = prompt.partial(
tools=tools_renderer(list(tools)),
tool_names=", ".join([t.name for t in tools]),
)
...
반면 Langgraph를 확인해보면
DEBUG:openai._base_client:Request options: {'method': 'post', 'url':
'/chat/completions', 'files': None, 'idempotency_key': 'stainless-python-retry-
2ce447ef-a0e2-434c-94a2-5577d9ffcbb7', 'json_data': {'messages': [{'content': 'Answer
the following questions as best you can. You have access to the following
tools:\n\n{tools}\n\nUse the following format:\n\nQuestion: the input question you
must answer\nThought: you should always think about what to do\nAction: the action to
take, should be one of [{tool_names}]\nAction Input: the input to the
action\nObservation: the result of the action\n... (this Thought/Action/Action
Input/Observation can repeat N times)\nThought: I now know the final answer\nFinal
Answer: the final answer to the original input question\n\nBegin!\n\nQuestion:
{input}\nThought:{agent_scratchpad}', 'role': 'system'}, {'content': "what's time is
it now?. i'm live in seoul city. find about city seoul", 'role': 'user'}], 'model':
'Gemma-3-27b-it', 'stream': False, 'temperature': 0.0, 'tools': [{'type': 'function',
'function': {'name': 'get_current_time', 'description': 'useful tools when you need
to know current time', 'parameters': {'properties': {'city': {'type': 'string'}},
'required': ['city'], 'type': 'object'}}}]}}
Langgraph는 prompt가 포맷팅 되지 않고 그대로 전달되게 됩니다.
여기서 Langchain과의 큰 차이점은 requests의 parameter로 tools가 전달되게 됩니다.
'tools': [
{'type': 'function',
'function': {
'name': 'get_current_time',
'description': 'useful tools when you need to know current time',
'parameters': {
'properties': {
'city': {'type': 'string'}
}, 'required': ['city'],
'type': 'object'
}
}
}
]
4. 정리
Langchain : Client Side에서 prompt를 처리함
Langgraph : LLM Service Side에서 prompt를 처리함
외부 환경에서 상용 서비스(ChatGPT, Gemini, Claude)를 이용할 경우
langgraph로 tool call API를 이용할 수가 있습니다.
그렇기 때문에 이런경우는 정상적으로 Tool을 사용하는 Agent를 구현할 수 있습니다.
특히나 요즘 MCP가 대세인데, MCP를 사용하기 위해서는 Langgraph를 이용하는 것이 좋습니다,
하지만 vLLM등을 이용하는 Private LLM환경에서는
Tool Call이 허용되지 않는 모델이나, vLLM 설정에 따라 Tool Call이 불가능 할 수 있습니다.
이런 경우에 Agent를 구현하기 위해서 Langchain을 사용한 Agent구현을 고려해 볼 수 있습니다.