LLM에게 외부 도구 사용 능력을 부여하고, 도구 실행 결과를 반영하여 응답하는 LangChain Tool Calling 전반을 정리함. 흐름 중심 + 언제/왜 쓰는지 중심으로 리마인드용 구성.
| 용어 | 설명 |
|---|---|
| LangChain | LLM 기반 파이프라인 구성용 프레임워크. 도구, DB, 메모리 등 확장 가능 |
| Tool | LLM이 호출 가능한 외부 기능 (예: 웹 검색, 계산기 등) |
| ToolMessage | 도구 실행 결과를 LLM에게 전달하는 메시지 객체 |
| Agent | 질문 의도 파악 → 도구 선택 → 실행 → 응답까지 자율적으로 수행하는 시스템 |
사용자 질문
↓
LLM 판단 → 도구 호출 필요(tool_calls)
↓
도구 실행 → ToolMessage 생성
↓
ToolMessage 포함하여 LLM 재호출
↓
최종 응답 생성
from langchain_core.tools import tool
@tool
def search_web(query: str) -> str:
# 웹 검색 수행 후 결과 요약 리턴
...
.name, .args_schema, .description 자동 생성됨from langchain_core.messages import ToolMessage
ToolMessage(
content="검색 결과 내용",
tool_call_id="1", # ai_msg.tool_calls 의 ID와 일치해야 함
name="search_web"
)
prompt = ChatPromptTemplate([
("system", "You are a helpful AI assistant..."),
("human", "{user_input}"),
("placeholder", "{messages}"),
])
llm = ChatOpenAI(model="gpt-4o-mini")
llm_with_tools = llm.bind_tools([web_search])
llm_chain = prompt | llm_with_tools
@chain
def web_search_chain(user_input: str, config: RunnableConfig):
ai_msg = llm_chain.invoke({"user_input": user_input}, config=config)
tool_msgs = web_search.batch(ai_msg.tool_calls, config=config)
return llm_chain.invoke({"user_input": user_input, "messages": [ai_msg, *tool_msgs]}, config=config)
examples = [
HumanMessage("트러플 리조또 특징 알려줘"),
AIMessage(tool_calls=[...]),
ToolMessage("블랙트러플 + 파르메산...", tool_call_id="1"),
AIMessage(tool_calls=[...]), # search_wine
ToolMessage("샤르도네 어울림", tool_call_id="2")
]
prompt = ChatPromptTemplate.from_messages([
("system", "도구별 용도를 구분해서 사용하세요"),
*examples,
("human", "{query}"),
])
summary_chain = ... # LCEL 체인 구성
wiki_summary = summary_chain.as_tool(
name="wiki_summary",
description="Summarize wiki result",
args_schema=WikiSummarySchema
)
prompt = ChatPromptTemplate.from_messages([
("system", "You are an assistant..."),
MessagesPlaceholder("chat_history"),
("human", "{input}"),
MessagesPlaceholder("agent_scratchpad")
])
agent = create_tool_calling_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools)
executor.invoke({"input": "샴페인 추천해줘"})
def answer_invoke(message, history):
... # chat_history 구성
return agent_executor.invoke({"input": message, "chat_history": chat_history})['output']
gr.ChatInterface(fn=answer_invoke, examples=["추천해줘"]).launch()
@tool → 도구 등록
llm.invoke → tool_call 생성
ToolMessage → 도구 결과 포장
llm 재호출 → 최종 응답 생성
AgentExecutor → 자동화된 흐름 실행
📌 "도구를 쓸지 판단하고, 실제 호출하고, 결과를 반영하는 전체 흐름"을 정확히 아는 게 핵심임.
📌 흐름이 익숙해지면 Gradio / FastAPI / Agent 등 다양한 방식으로 확장 가능.
참조: AI 에이전트로 구현하는 RAG 시스템(w.LangGraph) / 판다스 스튜디오 / 인프런