ReAct Agent는 Reasoning과 Acting을 결합한 가장 일반적인 에이전트 형태
에이전트는 행동-관찰-추론 단계를 순환하며 작업을 수행
도구 호출(act)과 결과 분석(observe)을 통해 다음 행동을 결정(reason)하는 체계적인 프로세스를 가짐
ReAct 도구는 명확한 입출력 인터페이스를 통해 정의
각 도구는 특정 기능을 수행하는 독립적인 컴포넌트로 구현
도구의 입력과 출력 형식을 명확히 정의하여 에이전트와의 상호작용을 보장
# RAG 체인 생성 (메타데이터를 포함해서 답변 생성)
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough, RunnableParallel
from langchain_openai import ChatOpenAI
# 질문 템플릿
template = """Answer the question based only on the following context.
Do not use any external information or knowledge.
If the answer is not in the context, answer "I don't know".
Use the same language as the question.
[Context]
{context}
[Question]
{question}
[Answer]
"""
# 프롬프트 생성
prompt = ChatPromptTemplate.from_template(template)
# LLM 모델 생성
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
# 문서 포맷터 함수
def format_docs_with_metadata(docs):
formatted_docs = []
for doc in docs:
content = doc.page_content
metadata = doc.metadata
source = metadata.get('source', '출처 없음')
formatted_docs.append(f"내용: {content}\n출처: {source}")
return "\n\n".join(formatted_docs)
# RAG 체인 생성 (메타데이터 포함)
def create_rag_chain_with_metadata(vectorstore, top_k=2):
"""벡터 저장소에서 문서를 검색하여 메타데이터를 포함한 답변을 생성하는 RAG 체인 생성"""
# 벡터 저장소에서 문서를 검색
retriever = vectorstore.as_retriever(search_kwargs={'k': top_k})
chain = RunnablePassthrough.assign(
context=lambda x: format_docs_with_metadata(retriever.invoke(x["question"]))
) | RunnableParallel(
context=lambda x: x["context"],
answer=prompt | llm | StrOutputParser()
)
return chain
# 한국어 RAG 체인 생성
rag_chain_korean = create_rag_chain_with_metadata(db_korean, top_k=4)
# 한국어 RAG 체인 실행
response = rag_chain_korean.invoke({"question": "테슬라 창업자는 누구인가요?"})
pprint(response)
- 출력
{'answer': '테슬라 창업자는 Martin Eberhard와 Marc Tarpenning입니다.',
'context': '내용: ### Roadster (2005–2009)\n'
'\n'
'Elon Musk는 주류 차량으로 확장하기 전에 프리미엄 스포츠카로 시작하는 전략에 초점을 맞춰 적극적인 역할을 '
'수행했습니다. 후속 자금 조달에는 Valor Equity Partners (2006)와 Sergey Brin, '
'Larry Page, Jeff Skoll과 같은 기업가의 투자가 포함되었습니다.\n'
...
}
# 영어 RAG 체인 생성
rag_chain_english = create_rag_chain_with_metadata(db_english, top_k=4)
# 영어 RAG 체인 실행
response = rag_chain_english.invoke({"question": "Who is the founder of Tesla?"})
pprint(response)
- 출력
{'answer': 'Tesla was founded by Martin Eberhard and Marc Tarpenning.',
'context': '내용: Tesla, Inc. is an American multinational automotive and clean '
'energy company. It designs, manufactures, and sells electric '
'vehicles (BEVs), stationary
...
}
# 한국어 RAG 도구 생성 (한국어 문서 벡터 저장소 사용)
rag_tool_korean = rag_chain_korean.as_tool(
name="rag_korean_db",
description="한국어 질문에 대한 리비안, 테슬라 관련 문서를 벡터 저장소에서 검색하고, 그 결과와 함께 답변을 생성합니다."
)
print(f"Tool 이름: {rag_tool_korean.name}")
print(f"Tool 설명: {rag_tool_korean.description}")
print(f"Tool 입력 파라미터: ")
pprint(rag_tool_korean.args)
- 출력
Tool 이름: rag_korean_db
Tool 설명: 한국어 질문에 대한 리비안, 테슬라 관련 문서를 벡터 저장소에서 검색하고, 그 결과와 함께 답변을 생성합니다.
Tool 입력 파라미터:
{'question': {'title': 'Question', 'type': 'string'}}
# 영어 RAG 도구 생성 (영어 문서 벡터 저장소 사용)
rag_tool_english = rag_chain_english.as_tool(
name="rag_english_db",
description="Retrieve and generate answers from the vector store for English questions related to Rivian and Tesla."
)
print(f"Tool 이름: {rag_tool_english.name}")
print(f"Tool 설명: {rag_tool_english.description}")
print(f"Tool 입력 파라미터: ")
pprint(rag_tool_english.args)
- 출력
Tool 이름: rag_english_db
Tool 설명: Retrieve and generate answers from the vector store for English questions related to Rivian and Tesla.
Tool 입력 파라미터:
{'question': {'title': 'Question', 'type': 'string'}}
bind_tools 메서드로 LLM에 도구들을 연결하여 사용 가능하게 함
도구 호출 결과는 ToolCall 객체를 통해 체계적으로 확인할 수 있음
from langchain_openai import ChatOpenAI
# 도구 목록
tools = [rag_tool_korean, rag_tool_english]
# LLM 모델
llm = ChatOpenAI(model="gpt-4o-mini")
# 모델에 도구를 바인딩 (추가)
llm_with_tools = llm.bind_tools(tools=tools)
# 도구 사용하기
query = "테슬라 창업자는 누구인가요?"
response = llm_with_tools.invoke(query)
pprint(response)
- 출력
AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_VbUQseK0pJhXvDJGxcKpQ5Au', 'function': {'arguments': '{"question":"테슬라 창업자"}', 'name': 'rag_korean_db'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': ... })
# ToolCall 객체 확인
response.tool_calls
- 출력
[{'name': 'rag_korean_db',
'args': {'question': '테슬라 창업자'},
'id': 'call_VbUQseK0pJhXvDJGxcKpQ5Au',
'type': 'tool_call'}]
# 영어 도구에 대한 질문
query_en = "Who is the founder of Tesla?"
response_en = llm_with_tools.invoke(query_en)
pprint(response_en)
- 출력
AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_vZ6O4dIGNtF5REN2kvHzWkw4', 'function': {'arguments': '{"question":"Who is the founder of Tesla?"}', 'name': 'rag_english_db'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': ... })
# ToolCall 객체 확인
response_en.tool_calls
- 출력
[{'name': 'rag_english_db',
'args': {'question': 'Who is the founder of Tesla?'},
'id': 'call_vZ6O4dIGNtF5REN2kvHzWkw4',
'type': 'tool_call'}]
# 도구와 관련 없는 질문 테스트
query_test = "오늘 날씨는 어떤가요?"
response_test = llm_with_tools.invoke(query_test)
pprint(response_test)
- 출력
AIMessage(content='죄송하지만, 현재 날씨 정보를 제공할 수 없습니다. 하지만 날씨 관련 정보를 원하시면, 지역이나 특정 날짜에 대해 검색해보실 수 있는 웹사이트를 이용해 보시는 것을 추천드립니다.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': ... })
도구 호출 함수는 AIMessage의 tool_calls를 실행하고 결과를 반환하는 헬퍼 함수로 구현
tool_map을 통해 각 도구별 호출을 처리하며 invoke 메소드로 실행
최종 체인은 llm_with_tools와 call_tools를 파이프라인으로 연결하여 구성
# 도구 맵 생성
tool_map = {
"rag_korean_db": rag_tool_korean,
"rag_english_db": rag_tool_english
}
# 도구 맵을 사용하여 도구 이름을 도구 객체로 변환 (도구 이름을 키로 사용)
tool_map["rag_korean_db"].invoke({"question": "테슬라 창업자는 누구인가요?"})
- 출력
{'context': "내용: ### Roadster (2005–2009)\n\nElon Musk는 주류 차량으로 확장하기 전에 프리미엄 스포츠카로 시작하는 전략에 초점을 맞춰 적극적인 역할을 수행했습니다. ... \n출처: data\\테슬라_KR.md",
'answer': '테슬라 창업자는 Martin Eberhard와 Marc Tarpenning입니다.'}
# 도구 맵을 사용하여 도구 이름을 도구 객체로 변환 (영어)
tool_map["rag_english_db"].invoke({"question": "Who is the founder of Tesla?"})
- 출력
{'context': '내용: Tesla, Inc. is an American multinational automotive and clean energy company. It designs, manufactures, and sells electric vehicles (BEVs) ... \n출처: data\\Tesla_EN.md',
'answer': 'Tesla was founded by Martin Eberhard and Marc Tarpenning.'}
from langchain_core.messages import AIMessage
from langchain_core.runnables import Runnable
# 도구 호출 함수 정의
def call_tools(msg: AIMessage) -> Runnable:
"""
tool calling helper 함수: AIMessage에 있는 tool_calls를 실행하고 결과를 반환
"""
tool_calls = msg.tool_calls.copy()
for tool_call in tool_calls:
tool_call["output"] = tool_map[tool_call["name"]].invoke(tool_call["args"])
return tool_calls
# 도구 호출 함수를 사용하여 도구 호출 실행
print("ToolCall 객체: ")
pprint(response.tool_calls[0])
print("-"*150)
tool_calls = call_tools(response) # 도구 호출 실행 (AIMessage 객체를 입력으로 사용)
pprint(tool_calls)
- 출력
ToolCall 객체:
{'args': {'question': '테슬라 창업자'},
'id': 'call_VbUQseK0pJhXvDJGxcKpQ5Au',
'name': 'rag_korean_db',
'output': {'answer': 'Martin Eberhard와 Marc Tarpenning입니다.',
'context': '내용: ### Roadster (2005–2009)\n'
'\n'
'Elon Musk는 ...'
'출처: data\\테슬라_KR.md'},
'type': 'tool_call'}
------------------------------------------------------------------------------------------------------------------------------------------------------
[{'args': {'question': '테슬라 창업자'},
'id': 'call_VbUQseK0pJhXvDJGxcKpQ5Au',
'name': 'rag_korean_db',
'output': {'answer': 'Martin Eberhard와 Marc Tarpenning이 테슬라를 창립했습니다.',
'context': '내용: ### Roadster (2005–2009)\n'
'\n'
'Elon Musk는 ...'
'출처: data\\테슬라_KR.md'},
'type': 'tool_call'}]
# 도구 호출 체인 생성
search_tool_chain = llm_with_tools | call_tools
# 도구 호출 실행 (한국어 쿼리)
query = "테슬라 창업자는 누구인가요?"
search_response = search_tool_chain.invoke(query)
pprint(search_response)
- 출력
[{'args': {'question': '테슬라 창업자는 누구인가요?'},
'id': 'call_jd6CWOP7p9p8qQld94SxrKIl',
'name': 'rag_korean_db',
'output': {'answer': '테슬라 창업자는 Martin Eberhard와 Marc Tarpenning입니다.',
'context': '내용: ### Roadster (2005–2009)\n'
'\n'
'Elon Musk는 ...'
'출처: data\\테슬라_KR.md'},
'type': 'tool_call'}]
# 도구 호출 실행 (영어 쿼리)
query = "Who is the founder of Tesla?"
search_response = search_tool_chain.invoke(query)
pprint(search_response)
- 출력
[{'args': {'question': 'Who is the founder of Tesla?'},
'id': 'call_MLipsR2MBs7aTLzu3tSunZNE',
'name': 'rag_english_db',
'output': {'answer': 'Tesla was founded by Martin Eberhard and Marc '
'Tarpenning.',
'context': '내용: Tesla, Inc. is an American multinational '
...
'출처: data\\Tesla_EN.md'},
'type': 'tool_call'}]
# 도구 호출 실행 (한국어 쿼리) - 도구와 관련 없는 질문
query = "오늘 날씨는 어떤가요?"
search_response = search_tool_chain.invoke(query)
pprint(search_response)
- 출력
[]
AgentExecutor는 LangChain의 기본 에이전트 실행 시스템
에이전트의 계획-실행-관찰 사이클을 자동으로 관리
에이전트의 행동을 모니터링하고 결과를 반환
에이전트는 LLM과 도구를 통합하여 복잡한 작업을 수행하는 시스템
프롬프트 템플릿을 기반으로 사용자 요청을 해석하고 적절한 도구 선택
도구 실행 결과를 분석하여 최종 응답을 생성하는 워크플로우 구현
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_openai import ChatOpenAI
from langchain.agents import create_tool_calling_agent
# 프롬프트 템플릿 정의
prompt = ChatPromptTemplate.from_messages([
("system", "당신은 사용자의 요청을 처리하는 AI Assistant입니다."),
("user", "{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad")
])
# LLM 모델 생성
llm = ChatOpenAI(model="gpt-4o-mini",temperature=0)
# 도구 목록 생성
tools = [rag_tool_korean, rag_tool_english]
# 에이전트 생성 (도구 호출)
agent = create_tool_calling_agent(llm, tools, prompt)
AgentExecutor는 에이전트의 작업 흐름을 관리하고 결과를 처리하는 컴포넌트
사용자 입력부터 최종 출력까지의 전체 프로세스를 조율하고 제어
에러 처리, 로깅, 결과 포맷팅 등 시스템 운영에 필요한 기능 제공
from langchain.agents import AgentExecutor
# 에이전트 실행기 생성
agent_executor = AgentExecutor(
agent=agent, # 도구 호출 에이전트
tools=tools, # 도구 목록
verbose=True, # 상세 로그 출력
)
# 에이전트 실행 (한국어 쿼리)
response = agent_executor.invoke(
{"input": "테슬라 창업자는 누구인가요?"}
)
- 출력
> Entering new AgentExecutor chain...
Invoking: `rag_korean_db` with `{'question': '테슬라 창업자는 누구인가요?'}`
{'context': "내용: ### Roadster (2005–2009)\n\nElon Musk는 ... \n출처: data\\테슬라_KR.md", 'answer': '테슬라 창업자는 Martin Eberhard와 Marc Tarpenning입니다.'}테슬라 창업자는 Martin Eberhard와 Marc Tarpenning입니다. 이들은 2003년 7월 1일에 테슬라 모터스를 설립하였으며, 각각 CEO와 CFO를 역임했습니다. 이후 Elon Musk가 2004년에 투자하여 회장 겸 최대 주주가 되었습니다.
> Finished chain.
# 에이전트 실행 결과 출력
pprint(response)
- 출력
{'input': '테슬라 창업자는 누구인가요?',
'output': '테슬라 창업자는 Martin Eberhard와 Marc Tarpenning입니다. 이들은 2003년 7월 1일에 테슬라 '
'모터스를 설립하였으며, 각각 CEO와 CFO를 역임했습니다. 이후 Elon Musk가 2004년에 투자하여 회장 겸 '
'최대 주주가 되었습니다.'}
return_intermediate_steps=True
# 에이전트 실행기 생성 (중간 단계 반환)
agent_executor = AgentExecutor(
agent=agent, # 도구 호출 에이전트
tools=tools, # 도구 목록
return_intermediate_steps=True # 중간 단계 반환 (기본값 False)
)
# 에이전트 실행 (영어 쿼리)
response = agent_executor.invoke(
{"input": "Who is the founder of Tesla?"}
)
# 에이전트 실행 결과 출력
pprint(response)
- 출력
{'input': 'Who is the founder of Tesla?',
'intermediate_steps': [(ToolAgentAction(tool='rag_english_db', tool_input={'question': 'Who is the founder of Tesla?'}, log="\nInvoking: `rag_english_db` with `{'question': 'Who is the founder of Tesla?'}`\n\n\n", message_log=[AIMessageChunk(content='', additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_9im7hyRiUjkxMfpTpg30G8QM', 'function': {'arguments': '{"question":"Who is the founder of Tesla?"}', 'name': 'rag_english_db'}, 'type': 'function'}]}, response_metadata={'finish_reason': 'tool_calls', 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_86d0290411'}, id='run-5fa46d19-3a86-4d43-a25f-cd390c5a5f1b', tool_calls=[{'name': 'rag_english_db', 'args': {'question': 'Who is the founder of Tesla?'}, 'id': 'call_9im7hyRiUjkxMfpTpg30G8QM', 'type': 'tool_call'}], tool_call_chunks=[{'name': 'rag_english_db', 'args': '{"question":"Who is the founder of Tesla?"}', 'id': 'call_9im7hyRiUjkxMfpTpg30G8QM', 'index': 0, 'type': 'tool_call_chunk'}])], tool_call_id='call_9im7hyRiUjkxMfpTpg30G8QM'),
{'answer': 'Tesla was founded by Martin Eberhard and '
'Marc Tarpenning.',
'context': '내용: Tesla, Inc. is an American '
... '출처: data\\Tesla_EN.md'})],
'output': 'Tesla was founded by Martin Eberhard and Marc Tarpenning in July '
'2003. Elon Musk joined the company shortly after, leading its '
'initial funding and becoming chairman.'}
# 에이전트 실행 (한국어 쿼리) - 도구와 관련 없는 질문
response = agent_executor.invoke(
{"input": "오늘 날씨는 어떤가요?"}
)
# 에이전트 실행 결과 출력
pprint(response)
- 출력
{'input': '오늘 날씨는 어떤가요?',
'intermediate_steps': [],
'output': '죄송하지만, 현재 날씨 정보를 제공할 수 없습니다. 하지만 다른 질문이나 도움이 필요하신 부분이 있다면 말씀해 주세요!'}