정리:
툴콜링(Tool Calling)은 LLM이 외부 도구(API, 함수 등)를 호출할 수 있도록 해주는 메커니즘으로, LLM이 지닌 몇 가지 한계—예: 최신 정보 부족, 복잡한 연산 수행 불가, 정확성 문제(할루시네이션 등)—를 보완해주는 중요한 기능입니다.
{ "tool": "Multiply", "args": { "a": 2, "b": 3 } } 툴콜링은 LangChain 기반 LLM 에이전트 또는 LangGraph 기반 워크플로우에서 핵심 기능입니다. 도구를 정의하고, 스키마를 설계하고, 이를 LLM과 연동하여 에이전트화하는 과정은 LLM 기반 자동화 시스템 구현의 기초입니다.
.env 파일에 설정 (TABBY_API_KEY=... 형식)..env 파일에 키 추가from langchain_community.tools import TavilySearchResults
search_tool = TavilySearchResults(max_results=2, name="web-search")
max_results: 검색 결과 개수 제한 (예: 2개)name: 도구 이름 설정result = search_tool.invoke("스테이크와 어울리는 와인을 추천해줘")
[{ "url": ..., "content": ... }, ...] 형태의 리스트로 반환됨.| 요소 | 설명 |
|---|---|
name | 도구 이름 |
description | 도구의 역할 설명 |
args_schema | 입력 스키마 (예: query: str) |
invoke() | 실행 메서드 (단일 실행) |
stream() | 스트리밍 방식 실행 (옵션) |
LLM은 도구의 이름과 설명, 스키마를 통해 어떤 도구를 언제, 어떻게 쓸지 판단함.
.env 파일에 OpenAI 및 Tabby API 키를 설정.python-dotenv 패키지를 이용해 환경 변수 로딩.invoke() 메소드에 자연어 쿼리를 직접 전달 → LLM이 텍스트 생성 또는 단순 실행.ChatOpenAI)을 초기화.웹서치)를 정의.llm.bind_tools([웹서치])를 통해 도구 바인딩.llm_with_tools 같은 변수에 저장.invoke("스테이크에 어울리는 와인 추천해줘")와 같이 자연어 쿼리 전달."스테이크에 어울리는 와인 추천" "스테이크와 와인 페어링 팁"AIMessage)의 구조:content: 도구 호출을 하지 않았다면 일반 응답이 들어감.tool_calls: 도구 호출이 이루어진 경우 여기에 호출 정보가 저장됨.{
"name": "tavily_search_results_json",
"arguments": {"query": "스테이크에 어울리는 와인 추천"}
}"안녕하세요" → LLM은 도구 호출 없이 인사만 출력."스테이크에 어울리는 와인 추천해 주세요" → LLM은 도구 호출을 생성 (검색 쿼리 2개 생성).LangChain에서 LLM의 Tool Calling 결과를 받아 실제 도구를 실행하는 방법은 총 3가지가 있습니다. 각각의 방식은 ToolCall 객체의 arguments, 전체 메시지, 또는 직접 작성한 ToolMessage를 활용하는 차이가 있습니다.
arguments만 추출하여 도구 실행하기 (가장 기본적인 방법)ToolCall 객체 내 arguments 속성만 추출해 사용.invoke() 함수의 인자로 arguments만 넘김.output = tool.invoke(tool_call.arguments)
ToolCall 메시지 자체를 전달하기 (일반적으로 가장 많이 사용)ToolCall 객체 전체를 invoke()에 전달.id, name, arguments)을 사용해 실행.output = tool.invoke(tool_call)
ToolMessage 직접 정의해서 실행하기 (정밀 제어 필요할 때 사용)ToolMessage 클래스를 이용해 도구 메시지를 수동으로 생성.content, tool_call_id, tool_name 등을 직접 설정.ToolMessage(content=..., tool_call_id=..., tool_name=...)
batch() 사용)ToolCall 객체를 배열로 전달해 동시에 여러 도구 실행 가능.outputs = tool.batch([tool_call1, tool_call2])
| 방식 | 설명 | 사용 상황 |
|---|---|---|
1. arguments만 전달 | 쿼리 등 필요한 값만 추출해서 사용 | 간단한 도구 실행 시 |
| 2. 전체 메시지 전달 | ToolCall 전체를 넘김 | 가장 일반적 |
| 3. 메시지 직접 생성 | ToolMessage 수동 생성 | 복잡한 구조나 직접 제어 필요할 때 |
“스테이크에 어울리는 와인을 추천해 주세요”
{"url": ..., "content": ...} 형태의 리스트를 포함함ChatPromptTemplate
LLM (예: GPT-4 Omni Mini)
LCEL Chain 구성
# 예시 흐름
prompt = ChatPromptTemplate.from_messages([...]) # 시스템, 휴먼, 도구 메시지 구성
llm_with_tools = llm.bind_tools([web_search_tool]) # 도구 바인딩
chain = prompt | llm_with_tools # 체인 연결
@chain_decorator
def answer_user_query(user_input: str):
ai_message = chain.invoke({"user_input": user_input})
# 도구 호출이 필요한 경우
tool_calls = ai_message.tool_calls
tool_responses = web_search_tool.batch(tool_calls)
# 검색 결과 포함해서 최종 답변 생성
final_response = chain.invoke({
"user_input": user_input,
"messages": [ai_message, *tool_responses]
})
return final_response
| 단계 | 설명 |
|---|---|
| 🔹 검색 | LLM이 질문을 바탕으로 적절한 검색어로 도구 호출 |
| 🔹 도구 결과 | Tavily API를 통해 웹에서 정보 수집 |
| 🔹 메시지 구성 | 검색 결과는 ToolMessage 형태로 저장됨 |
| 🔹 프롬프트 구성 | 사용자 질문 + 시스템 메시지 + ToolMessage 포함 |
| 🔹 최종 답변 | LLM이 전체 프롬프트를 기반으로 사용자 질문에 응답 |
질문: “모에샹동 샴페인 가격 알려줘”
→ 검색 결과 참조하여
"모에샹동 임페리얼 샴페인의 평균 가격은 37,000원에서 32,000원 사이입니다."
(출처: 데일리샷 URL 포함)
@tool 데코레이터로 도구 만들기@tool은 LangChain Core의 langchain_core.tools에서 import@tool을 붙이면 LLM이 사용할 수 있는 도구가 됨from langchain_core.tools import tool
@tool
def SearchWeb(query: str) -> str:
"""
Search the web for information not found in the internal database. Useful for retrieving the most recent or external knowledge.
"""
# 검색 API 호출 → 결과 추출 및 문자열 포맷
results = external_search_api(query) # 예시
if not results:
return "관련 정보를 찾을 수 없습니다."
return "\n---\n".join([
f"<doc>\nURL: {r['url']}\nText: {r['content']}\n</doc>" for r in results[:2]
])
llm = ChatOpenAI(...)
llm_with_tool = llm.bind_tools([SearchWeb])
response = llm_with_tool.invoke("스테이크에 어울리는 와인 추천해 주세요")
print(response.tool_calls)
tool_calls에 도구 호출 정보가 들어있음SearchWeb 도구가 실행됨<doc> 구분)| 항목 | 설명 |
|---|---|
@tool | Python 함수 → LangChain 도구로 변환 |
| Docstring | 도구 설명으로 사용됨 (LLM이 이 설명을 기반으로 도구 사용 여부 판단) |
| 함수 이름 | 도구 이름 |
| 바인딩 | llm.bind_tools([SearchWeb]) |
| 도구 호출 | 사용자가 자연어로 질문 → LLM이 적절한 검색어 생성 + 도구 호출 결정 |
| 출력 | 검색 결과는 HTML 형태로 정리된 단일 문자열 |
여러 LLM이 같은 조건에서 도구 호출을 얼마나 잘 수행하는지(툴콜링 능력) 비교 실험.
서치웹(Search Web)이라는 검색 도구를 바인딩 (LangChain의 bind_tools() 사용)| 모델명 | 도구 호출 성공 여부 | 특징 및 결과 |
|---|---|---|
| GPT-4 Omni Mini | ✅ 성공 | 가장 안정적, 한국어 인식도 좋음 |
| Gemini 1.5 Flash | ❌ 실패 | Tool Calls 비어있음 → 일반 텍스트 응답 |
| Gemini 1.5 Pro | ✅ 성공 | Flash보다 확실히 우수 |
| LLaMA 3 (Grok 70B) | ✅ 성공 | 영어 쿼리 생성은 뛰어남, 한국어 처리 약함 |
Runnable.s2r_tool() 메서드위키피디아 문서를 검색할 수 있는 도구를 Runnable → Tool로 만들어 활용
wikipedia 라이브러리 설치WikipediaLoaderfrom langchain_community.document_loaders import WikipediaLoader
def search_wikipedia(inputs: dict):
query = inputs["query"]
k = inputs.get("k", 2)
loader = WikipediaLoader(query=query, lang="ko", load_max_docs=k)
return loader.load()
from langchain_core.runnables import RunnableLambda
runnable_wiki = RunnableLambda(search_wikipedia)
from pydantic import BaseModel, Field
class WikiSearchInput(BaseModel):
query: str = Field(..., description="검색어")
k: int = Field(default=2, description="가져올 문서 수")
wiki_search_tool = runnable_wiki.with_types(input_type=WikiSearchInput).as_tool(
name="wiki_search",
description="위키피디아에서 일반적인 배경지식을 검색하는 도구입니다."
)
name: 도구 이름description: 도구의 기능 설명 (LLM이 도구를 이해하는 데 핵심)input_type: 정확한 입력 스키마 지정 (LLM의 정확한 호출 유도)wiki_search_tool.invoke({"query": "파스타의 유래"})
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4-0125-preview").bind_tools([
wiki_search_tool, # 위키 검색 도구
web_search_tool # 기존에 만든 웹 검색 도구 (예: Tavily)
])
“서울 강남의 파스타 맛집은 어디인가요? 그리고 파스타의 유래는 무엇인가요?”
ToolCall #1: "서울 강남 유명 파스타 맛집" → web_searchToolCall #2: "파스타" → wiki_search두 도구 모두 호출되어 각 결과에 대한 검색 결과 반환됨.
| 단계 | 설명 |
|---|---|
| 🧱 함수 정의 | 검색 함수 작성 (query, k) |
| 🔁 Runnable 변환 | RunnableLambda() 로 감싸기 |
| 🧾 입력 스키마 | pydantic.BaseModel 로 입력 타입 정의 |
| 🔨 도구 변환 | .as_tool() 메서드로 Tool 객체 생성 |
| 🤖 도구 바인딩 | llm.bind_tools([tool1, tool2]) |
| 💬 복합 질문 처리 | LLM이 상황에 따라 적절한 도구 선택 및 호출 |
wiki_search_and_summarize)query 키 사용)<doc>
Source: [문서 링크]
Content: [문서 내용]
</doc>ChatPromptTemplate 사용다음 내용을 요약해주세요: {context}RunnableLambda, Prompt, LLM, RunnablePick 등의 LCEL 구성 요소 사용.as_tool() 메서드 사용:summary_tool = summary_chain.as_tool(
name="wiki_summary",
description="Search and summarize a Korean Wikipedia article related to the query.",
args_schema=WikiSummarySchema # query 필드만 포함된 Pydantic 입력 스키마
)
k(문서 개수) 같은 고정 값은 스키마에 포함하지 않음SearchWeb (웹 검색용)wiki_summary (위키 문서 검색 및 요약용)llm_with_tools = llm.bind_tools([SearchWeb, summary_tool])
질문 예시:
“파스타 맛집 추천해줘, 그리고 파스타의 유래는 뭐야?”
LLM이 복합 질문을 받아 2개의 도구 호출 판단:
"파스타 맛집""파스타 유래"각 도구에 맞는 쿼리 자동 생성 및 호출
도구 호출 결과(문서 또는 요약)를 수집:
ToolMessage 객체로 반환됨사용자 질문 + 도구 결과 → 다시 LLM에 전달
LLM은 이 정보를 바탕으로 최종 답변 생성
| 구성 요소 | 역할 |
|---|---|
@tool 또는 .as_tool() | 함수/체인을 LangChain 도구로 변환 |
args_schema | 입력 형식 명시 (Pydantic) |
bind_tools() | 도구들을 LLM에 바인딩 |
invoke() 또는 체인 실행 | 사용자의 쿼리로 도구 호출 + 결과 생성 |
ToolMessage | 도구 실행 결과 객체 |
ChatPromptTemplate | 요약 요청 등 사용자 지시 템플릿 구성 |
RunnableLambda | 사용자 정의 함수 포함 가능 |
LCEL 체인 | 전체 흐름을 체인으로 연결해 관리 |
LCEL을 통해 구현한 검색 + 요약 체인을
.as_tool()을 이용해 도구로 만들고, LLM이 이를 자동으로 호출할 수 있도록 구성 → 복합질문에 대한 자동 도구 호출 + 결과 기반 응답이 가능해짐.
좋습니다! 아래는 위의 긴 설명을 핵심 중심으로 정리한 요약본입니다.
Document 객체로 구성하고, 메뉴 번호, 이름, 파일 경로 등을 메타데이터로 설정Ollama + bge-m3레스토랑_메뉴, 레스토랑_와인Chroma.from_documents(documents, embedding, collection_name="레스토랑_메뉴")
SimilarityRetriever 생성하여 유사한 문서 2개 검색"시그니처 스테이크 가격과 특징" → 관련 문서 2개 반환됨@tool 데코레이터로 툴화query (문자열) Document 리스트 @tool
def search_menu(query: str) -> List[Document]:
"""레스토랑 메뉴 정보를 검색하는 도구입니다."""
search_wine() 도구 생성search_menu, search_wine 도구를 LLM에 바인딩tool_calls 2개 발생search_menu → "시그니처 스테이크"로 메뉴 정보 검색search_wine → "스테이크"에 어울리는 와인 검색→ LLM이 자동으로 어떤 도구를 언제 사용할지 판단
| 단계 | 설명 |
|---|---|
| 1️⃣ 문서 읽기 및 청킹 | 정규표현식으로 항목 단위 분리 |
| 2️⃣ 벡터 저장소 생성 | Chroma + bge-m3 인베딩 사용 |
| 3️⃣ 벡터 검색기 생성 | 유사도 기반 검색기 (Retriever) |
| 4️⃣ 도구로 변환 | @tool로 Structured Tool 등록 |
| 5️⃣ LLM에 바인딩 | 도구를 LLM에 연결, 자동 툴 호출 실험 성공 |
| 도구 이름 | 설명 |
|---|---|
search_web | Tavily API 기반 웹 검색 도구 |
wiki_summary | 위키피디아 문서 검색 및 요약 도구 |
search_wine | 벡터 저장소에서 와인 메뉴 검색 도구 |
search_menu | 벡터 저장소에서 레스토랑 메뉴 검색 도구 |
@tool 데코레이터를 사용하면 도구 이름 = 함수 이름 으로 자동 지정됨 → 확인 필수"시그니처 스테이크 가격은 얼마인가요? 그리고 스테이크와 어울리는 와인은?"
search_menu 도구 → "시그니처 스테이크"로 호출 search_wine 도구 → "스테이크와 어울리는 와인"으로 호출tool_messages = []
for tool_call in ai_message.tool_calls:
if tool_call.name == "search_menu":
result = search_menu.invoke(tool_call)
tool_messages.append(result)
elif tool_call.name == "search_wine":
result = search_wine.invoke(tool_call)
tool_messages.append(result)
...
response = final_chain.invoke({
"user_input": original_query,
"messages": [ai_message, *tool_messages]
})
final_chain은 LLM이 이미 도구 호출 정보와 결과를 알고 있으므로search_menu → 메뉴 가격/설명 검색search_wine → 어울리는 와인 + 가격 추천 search_menu → "파스타" 메뉴 검색 wiki_summary → "파스타" 역사 검색 팬스테이크 비프도 파스타로 오인하여 포함| 문제 | 원인 | 해결책 |
|---|---|---|
| 메뉴 구분 실패 | 벡터 검색 결과를 LLM이 잘못 판단 | 더 정교한 Filtering or GPT-4 Omni 사용 |
| 도구 선택 정확도 | LLM이 잘못된 도구 선택 가능성 | description을 명확히, 예시 질문 추가 |
| 구성 요소 | 설명 |
|---|---|
| 도구 바인딩 | 4개의 도구를 LLM에 등록 |
| ToolCall | LLM이 선택한 도구 호출 요청 |
| ToolMessage | 호출 결과를 담는 메시지 객체 |
| 체인 구성 | ToolCall → Tool 실행 → 메시지 결합 → 최종 응답 생성 |
SearchMenu, 일반 정보 → WikiSummary, 와인 → SearchWine, 최신 정보 → SearchWeb트러플 리조또 가격, 특징, 어울리는 와인 알려줘
SearchMenu("트러플 리조또")WikiSummary("트러플 리조또")SearchWine("트러플 리조또에 어울리는 와인")→ 도구 호출 메시지와 그 결과들을 단계별로 참조하여 최종 응답 생성
“스테이크 메뉴가 있는지 알려줘, 어울리는 와인도 추천해줘”
SearchMenu("스테이크")SearchWine("스테이크에 어울리는 와인")“파스타의 유래와 강남 파스타 맛집 추천해줘”
WikiSummary)은 정확SearchMenu)으로 잘못 판단ToolMessage로 받아System Message + Few-shot Examples + User MessageTool Calls + Tool ResponsesAnswer 생성| 구분 | 내용 |
|---|---|
| ✅ 목적 | 도구 호출 정확도 향상 |
| 🛠️ 방법 | Few-shot Prompting + Tool Binding |
| 🧱 구성 | 시스템 메시지 + 예시 메시지 + 사용자 입력 |
| 📈 효과 | LLM이 도구 사용 흐름을 학습해 적절히 호출 |
| ❗ 한계 | 도구 선택 실패에 대한 되돌리기 등은 LangChain만으로는 구현 어려움 |
| 💡 대안 | LangGraph 도입 → 평가/분기/재실행 흐름 가능 |
기존의 툴콜링은 "무엇을 실행할지"까지만 결정했다면
에이전트는 실행부터 결과 해석, 다음 행동 결정까지 포함
1. 사용자 질문 입력
2. LLM이 어떤 도구를 사용할지 결정 (툴콜링)
3. 도구 실행
4. 실행 결과 → LLM에게 피드백 (에이전트 스크래치 패드에 기록)
5. 추가 행동이 필요한 경우 반복
6. 최종적으로 응답 생성
이 과정을 자동으로 반복하는 것이 "에이전트 루프"입니다.
| 구성 요소 | 설명 |
|---|---|
create_tool_calling_agent() | 에이전트를 정의(생성)하는 함수 |
AgentExecutor() | 에이전트를 실행하는 객체 |
agent_prompt | 에이전트 동작을 제어하는 프롬프트 템플릿 |
필수 요소:
1. input – 사용자 질문
2. agent_scratchpad – 툴 실행 내역 + 결과 피드백 (자동 기록)
옵션 요소:
chat_history – 이전 대화 기록 포함 가능 (옵셔널로 설정 가능)예시:
agent_prompt = ChatPromptTemplate.from_messages([
("system", "역할 지시문"),
("human", "{input}"),
("placeholder", "{agent_scratchpad}")
])
# 1. 에이전트 정의
agent = create_tool_calling_agent(
llm=llm,
tools=[tool1, tool2, ...],
prompt=agent_prompt
)
# 2. 에이전트 실행기 생성
agent_executor = AgentExecutor(
agent=agent,
tools=[tool1, tool2, ...],
verbose=True # 중간 과정 출력 여부
)
# 3. 쿼리 실행
response = agent_executor.invoke({"input": "시그니처 스테이크 가격과 어울리는 와인을 추천해줘"})
search_menu 실행 → 결과 해석search_wine 실행 → 결과 해석🌟 시그니처 스테이크는 38,000원이며, 육즙 가득한 프리미엄 스테이크입니다.
🍷 어울리는 와인으로는 ... 추천드립니다!
{
"input": "사용자의 입력 쿼리",
"output": "에이전트가 생성한 최종 응답"
}
output 필드만 따로 추출해서 사용할 수 있음from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
agent_prompt = ChatPromptTemplate.from_messages([
("system", dedent("""
You are an AI assistant providing restaurant menu information and general food-related knowledge.
Your main goal is to provide accurate information and effective recommendations to users.
Key guidelines:
1. For restaurant menu information, use the search_menu tool. This tool provides details on menu items, including prices, ingredients, and cooking methods.
2. For general food information, history, and cultural background, utilize the wiki_summary tool.
3. For wine recommendations or food and wine pairing information, use the search_wine tool.
4. If additional web searches are needed or for the most up-to-date information, use the search_web tool.
5. Provide clear and concise responses based on the search results.
6. If a question is ambiguous or lacks necessary information, politely ask for clarification.
7. Always maintain a helpful and professional tone.
8. When providing menu information, describe in the order of price, main ingredients, and distinctive cooking methods.
9. When making recommendations, briefly explain the reasons.
10. Maintain a conversational, chatbot-like style in your final responses. Be friendly, engaging, and natural in your communication.
Remember, understand the purpose of each tool accurately and use them in appropriate situations.
Combine the tools to provide the most comprehensive and accurate answers to user queries.
Always strive to provide the most current and accurate information.
""")),
MessagesPlaceholder(variable_name="chat_history", optional=True),
("human", "{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad"),
])
# Tool calling Agent 생성
from langchain.agents import AgentExecutor, create_tool_calling_agent
tools = [search_web, wiki_summary, search_wine, search_menu]
agent = create_tool_calling_agent(llm, tools, agent_prompt)
# AgentExecutor 생성
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
# AgentExecutor 실행
query = "시그니처 스테이크의 가격과 특징은 무엇인가요? 그리고 스테이크와 어울리는 와인 추천도 해주세요."
agent_response = agent_executor.invoke({"input": query})
pprint(agent_response)
| 항목 | 내용 |
|---|---|
| 목적 | LLM이 도구 사용 및 응답 생성까지 자율 수행 |
| 핵심 기능 | 도구 선택 → 실행 → 결과 피드백 → 응답 |
| 구성 요소 | create_tool_calling_agent, AgentExecutor, 프롬프트 |
| 주요 개념 | agent_scratchpad, input, output, chat_history |
| 응용 | 복합질문 처리, 정보 수집, 보고서 생성 등 고급 자동화 가능 |
좋습니다! 말씀하신 내용을 바탕으로 LangChain 기반 멀티 도구 LLM 에이전트를 Gradio Chatbot으로 구현하는 전체 과정을 아래와 같이 깔끔하게 정리해드릴게요.
LangChain 에이전트를 Gradio를 통해 웹 기반 챗봇 형태로 시각화
→ 사용자 질문 → 도구 판단 및 호출 → 응답 생성 → 웹 UI로 출력
| 도구 | 역할 |
|---|---|
| LangChain | LLM + 도구 호출 체인 관리 |
| Gradio | Chat UI 구현 (chatbot 형태) |
| VS Code / Jupyter | 서버 실행 및 로그 확인 환경 |
| 도구 이름 | 설명 |
|---|---|
search_web | 웹 검색 (Tavily 기반) |
wiki_summary | 위키피디아 검색 요약 |
search_wine | 벡터 기반 와인 검색 |
search_menu | 벡터 기반 메뉴 검색 |
import gradio <as gr
def answer_fn(message: str, history: list):
# 1. 채팅 히스토리 구성
past_messages = []
for human, ai in history[-2:]: # 최근 1턴만 반영
past_messages.append(HumanMessage(content=human))
past_messages.append(AIMessage(content=ai))
# 2. LangChain 에이전트 실행
try:
response = agent_executor.invoke({
"user_input": message,
"messages": past_messages
})
return response["output"] # 최종 응답 반환
except Exception as e:
return f"⚠️ 오류 발생: {str(e)}"
demo = gr.ChatInterface(
fn=answer_fn,
title="음식 추천 챗봇 🍽️",
description="요리, 레스토랑, 와인 관련 질문을 도구 기반으로 응답합니다.",
examples=[
"시그니처 스테이크 가격 알려줘",
"스테이크와 어울리는 와인은?",
"해산물 파스타 추천해줘",
"파스타 유래가 궁금해"
],
theme="soft"
)
demo.launch()
| 단계 | 설명 |
|---|---|
| 1️⃣ 사용자 입력 | Gradio Chat UI에서 입력 |
| 2️⃣ 최근 히스토리 반영 | 최신 1턴(2 메시지)을 LangChain 메시지로 변환 |
| 3️⃣ 에이전트 실행 | LLM이 적절한 도구 호출 판단 및 실행 |
| 4️⃣ ToolMessage 활용 | 도구 결과를 포함하여 최종 응답 생성 |
| 5️⃣ 응답 반환 | Gradio 인터페이스로 응답 출력 |
| 6️⃣ 로그 확인 | VS Code 또는 터미널 로그에서 도구 호출 및 응답 확인 가능 |
“시그니처 스테이크 가격은 얼마인가요? 그리고 어울리는 와인은?”
search_menu 호출 → 메뉴 설명 + 가격search_wine 호출 → 와인 이름 + 특징 + 가격질문1: “해산물 파스타에 뭐 들어가요?”
질문2: “설의 파스타 평점은?”
팬스테이크 비프가 파스타로 오인됨 → 벡터 검색 결과 오용demo.close()
Gradio 서버는 기본적으로 7860번 포트에서 실행[User Input]
↓
[Chat History] ← (최근 1턴 유지)
↓
[Agent Executor (LangChain)]
↓
[ToolCall 판단 → 도구 실행]
↓
[ToolMessage + User Input]
↓
[LLM 최종 응답 생성]
↓
[Gradio Chat UI 출력]
[1] User Input
|
v
"Is this termination legal under Korean labor law?"
|
v
[2] LLM (with tool-calling)
|
└── Decides: "I need legal info → Call a tool"
|
v
[3] Tool Call
|
└── e.g., search_labor_law(query="termination conditions")
|
v
[4] Tool Response
|
└── Returns relevant law content or case info
|
v
[5] LLM (Processes output)
|
└── Understands the law + user's question
|
v
[6] Final Answer
|
└── "According to Korean labor law Article XX, termination is only legal if..."