LangGraph 공식문서를 번역한 내용입니다. 필요한 경우 부연 설명을 추가하였고 이해하기 쉽게 예제를 일부 변경하였습니다. 문제가 되면 삭제하겠습니다.
https://langchain-ai.github.io/langgraph/how-tos/pass-run-time-values-to-tools/
가끔은 도구 호출 LLM이 도구 함수의 일부 인수만 채우고, 나머지 인수는 런타임에서 제공하도록 하고 싶을 수 있다. LangChain 스타일의 도구를 사용하는 경우, 이를 처리하는 쉬운 방법은 함수 파라미터에 InjectedArg
를 주석으로 추가하는 것이다. 이 주석은 해당 파라미터를 LLM에 표시되지 않도록 한다.
LangGraph 애플리케이션에서는 런타임에서 그래프 상태나 공유 메모리(스토어)를 도구에 전달하고 싶을 수 있다. 이러한 상태 유지 도구는 도구의 출력이 과거 에이전트 단계에 의해 영향을 받을 때 유용하다(예: 서브 에이전트를 도구로 사용하고 메시지 히스토리를 서브 에이전트로 전달하고자 할 때) 또는 도구의 입력이 과거 에이전트 단계의 맥락을 바탕으로 검증이 필요할 때 유용하다.
이 가이드에서는 LangGraph의 사전 구축된 ToolNode
를 사용하여 이를 어떻게 처리할 수 있는지 보여준다.
아래 예제에서의 핵심 기술은 파라미터를 "주입됨(injected)"으로 주석 처리하는 것이다. 이는 해당 파라미터가 프로그램에 의해 주입되며 LLM에 의해 보이거나 채워지지 않아야 함을 의미한다. 다음 코드를 참고하자.
from typing import Annotated
from langchain_core.runnables import RunnableConfig
from langchain_core.tools import InjectedToolArg
from langgraph.store.base import BaseStore
from langgraph.prebuilt import InjectedState, InjectedStore
async def my_tool(
some_arg: str,
another_arg: float,
config: RunnableConfig,
store: Annotated[BaseStore, InjectedStore],
state: Annotated[State, InjectedState],
messages: Annotated[list, InjectedState("messages")]
):
"""Call my_tool to have an impact on the real world.
Args:
some_arg: a very important argument
another_arg: another argument the LLM will provide
"""
print(some_arg, another_arg, config, store, state, messages)
return "... some response"
준비
우선, 필요한 패키지를 설치하자.
pip install langgraph langchain-openai
그래프 상태를 도구로 전달
먼저, 도구에 그래프 상태에 접근할 수 있도록 하는 방법을 살펴보자. 그래프 상태를 정의해야 한다.
from typing import List
from dotenv import load_dotenv
from langgraph.prebuilt.chat_agent_executor import AgentState
load_dotenv()
class State(AgentState):
docs: List[str]
도구 정의
도구가 그래프 상태를 입력으로 받도록 하되, 모델이 도구 호출 시 이 입력을 생성하려고 하지 않도록 해야 한다. 이를 위해 InjectedState
주석을 사용하여 인수를 필수 그래프 상태(또는 그래프 상태의 일부 필드)로 표시할 수 있다. 이렇게 표시된 인수는 모델에 의해 생성되지 않는다. ToolNode
를 사용할 때, 그래프 상태는 자동으로 관련 도구와 인수에 전달된다.
이 예제에서는 Documents
를 반환하는 도구와 주장을 뒷받침하는 Documents
를 실제로 인용하는 또 다른 도구를 만든다.
from typing_extensions import Annotated
from langchain_core.tools import tool
from langgraph.prebuilt import InjectedState
@tool
def get_context(question: str, state: Annotated[dict, InjectedState]):
"""Get relevant context for answering the question."""
return "\n\n".join(doc for doc in state["docs"])
이 도구들의 입력 스키마를 살펴보면, 여전히 상태가 나열되어 있음을 볼 수 있다.
result = get_context.get_input_schema().schema()
print(result)
{'description': 'Get relevant context for answering the question.', 'properties': {'question': {'title': 'Question', 'type': 'string'}, 'state': {'title': 'State', 'type': 'object'}}, 'required': ['question', 'state'], 'title': 'get_context', 'type': 'object'}
하지만 도구 호출 스키마를 살펴보면, 이는 도구 호출을 위해 모델에 전달되는 것이며, 상태는 제거된 것을 볼 수 있다.
result = get_context.tool_call_schema.schema()
print(result)
{'description': 'Get relevant context for answering the question.', 'properties': {'question': {'title': 'Question', 'type': 'string'}}, 'required': ['question'], 'title': 'get_context', 'type': 'object'}
그래프 정의
이 예제에서는 사전 구축된 ReAct 에이전트를 사용할 것이다. 먼저 모델과 도구 호출 노드(ToolNode
)를 정의해야 한다.
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import ToolNode, create_react_agent
from langgraph.checkpoint.memory import MemorySaver
model = ChatOpenAI(model="gpt-4o-mini", temperature=0)
tools = [get_context]
tool_node = ToolNode(tools)
checkpointer = MemorySaver()
graph = create_react_agent(model, tools, state_schema=State, checkpointer=checkpointer)
사용
docs = [
"FooBar company just raised 1 Billion dollars!",
"FooBar company was founded in 2019",
]
inputs = {
"messages": [{"type": "user", "content": "what's the latest news about FooBar"}],
"docs": docs,
}
config = {"configurable": {"thread_id": "1"}}
for chunk in graph.stream(inputs, config, stream_mode="values"):
chunk["messages"][-1].pretty_print()
================================ Human Message =================================
what's the latest news about FooBar
================================== Ai Message ==================================
Tool Calls:
get_context (call_nyDliKkkk9cB1K4jrGfwquIO)
Call ID: call_nyDliKkkk9cB1K4jrGfwquIO
Args:
question: latest news about FooBar
================================= Tool Message =================================
Name: get_context
FooBar company just raised 1 Billion dollars!
FooBar company was founded in 2019
================================== Ai Message ==================================
The latest news about FooBar is that the company has just raised $1 billion in funding. FooBar was founded in 2019.
그래프에 공유 메모리 (저장소) 전달
여러 대화나 사용자 간에 공유되는 메모리에 도구가 접근하는 경우도 있다. 이를 위해서는 다른 주석인 InjectedStore
를 사용하여 LangGraph의 Store를 도구에 전달하면 된다.
예제를 수정하여 문서를 인메모리 스토어에 저장하고 get_context
도구를 사용해 이를 검색한다. 또한, 문서가 사용자 ID에 따라 접근 가능하도록 만들어 일부 문서는 특정 사용자에게만 표시되도록 한다. 도구는 그런 다음 config
에 제공된 user_id
를 사용해 올바른 문서 집합을 검색한다.
from typing import Annotated, Tuple, List
from dotenv import load_dotenv
from langchain_core.documents import Document
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
from langgraph.checkpoint.memory import MemorySaver
from langgraph.store.memory import InMemoryStore
load_dotenv()
doc_store = InMemoryStore()
namespace = ("documents", "1") # user ID
doc_store.put(
namespace, "doc_0", {"doc": "FooBar company just raised 1 Billion dollars!"}
)
namespace = ("documents", "2") # user ID
doc_store.put(namespace, "doc_1", {"doc": "FooBar company was founded in 2019"})
도구 정의
from langgraph.store.base import BaseStore
from langchain_core.runnables import RunnableConfig
from langgraph.prebuilt import InjectedStore, ToolNode, create_react_agent
@tool
def get_context(
question: str,
config: RunnableConfig,
store: Annotated[BaseStore, InjectedStore()],
) -> Tuple[str, List[Document]]:
"""Get relevant context for answering the question."""
user_id = config.get("configurable", {}).get("user_id")
docs = [item.value["doc"] for item in store.search(("documents", user_id))]
return "\n\n".join(doc for doc in docs)
또한, 도구 호출 모델이 get_context
도구의 store
인수를 무시하는지 확인할 수 있다.
result = get_context.tool_call_schema.schema()
print(result)
{'description': 'Get relevant context for answering the question.', 'properties': {'question': {'title': 'Question', 'type': 'string'}}, 'required': ['question'], 'title': 'get_context', 'type': 'object'}
그래프 정의
ReAct Agent를 업데이트 하자.
model = ChatOpenAI(model="gpt-4o-mini", temperature=0)
tools = [get_context]
tool_node = ToolNode(tools)
checkpointer = MemorySaver()
graph = create_react_agent(model, tools, checkpointer=checkpointer, store=doc_store)
사용해보자
user_id를 가진 그래프를 실행해보자.
messages = [{"type": "user", "content": "what's the latest news about FooBar"}]
config = {"configurable": {"thread_id": "1", "user_id": "1"}}
for chunk in graph.stream({"messages": messages}, config, stream_mode="values"):
chunk["messages"][-1].pretty_print()
================================ Human Message =================================
what's the latest news about FooBar
================================== Ai Message ==================================
Tool Calls:
get_context (call_OR2ErJC4DYixKFjYcf1yT6eK)
Call ID: call_OR2ErJC4DYixKFjYcf1yT6eK
Args:
question: latest news about FooBar
================================= Tool Message =================================
Name: get_context
FooBar company just raised 1 Billion dollars!
================================== Ai Message ==================================
The latest news about FooBar is that the company has just raised 1 billion dollars!
도구가 스토어에서 정보를 조회할 때 "1" 사용자에 대해 올바른 문서만 검색한 것을 볼 수 있다. 이제 다른 사용자에 대해서도 다시 시도해 보자.
messages = [{"type": "user", "content": "what's the latest news about FooBar"}]
config = {"configurable": {"thread_id": "2", "user_id": "2"}}
for chunk in graph.stream({"messages": messages}, config, stream_mode="values"):
chunk["messages"][-1].pretty_print()
================================ Human Message =================================
what's the latest news about FooBar
================================== Ai Message ==================================
Tool Calls:
get_context (call_0zaDzU72feDfFgjr6ToWvDzB)
Call ID: call_0zaDzU72feDfFgjr6ToWvDzB
Args:
question: latest news about FooBar
================================= Tool Message =================================
Name: get_context
FooBar company was founded in 2019
================================== Ai Message ==================================
The latest information I have is that FooBar was founded in 2019. For the most current news, I recommend checking reliable news sources or the company's official website.
이번에는 도구가 다른 문서를 가져온 것을 볼 수 있다.