기본 에이전트 만들기
LangChain으로 기본 에이전트를 만들어봅니다. 간단한 도구부터 시작해서 점점 기능을 추가합니다.
가장 간단한 에이전트
from langchain_ollama import OllamaLLMfrom langchain.agents import create_react_agent, AgentExecutorfrom langchain.tools import Toolfrom langchain.prompts import PromptTemplate# 1. LLM 준비llm = OllamaLLM(model="llama4")# 2. 도구 정의def greet(name: str) -> str: """이름으로 인사합니다.""" return f"안녕하세요, {name}님!"tools = [ Tool( name="greet", func=greet, description="이름을 받아 인사말을 생성합니다. 입력: 이름" )]# 3. 프롬프트PROMPT = """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 answerThought: you should always think about what to doAction: the action to take, should be one of [{tool_names}]Action Input: the input to the actionObservation: the result of the action... (this Thought/Action/Action Input/Observation can repeat N times)Thought: I now know the final answerFinal Answer: the final answer to the original input questionBegin!Question: {input}Thought: {agent_scratchpad}"""prompt = PromptTemplate.from_template(PROMPT)# 4. 에이전트 생성agent = create_react_agent(llm, tools, prompt)# 5. 실행기executor = AgentExecutor( agent=agent, tools=tools, verbose=True)# 6. 실행result = executor.invoke({"input": "철수에게 인사해줘"})print(result["output"])
여러 도구 조합
from langchain_ollama import OllamaLLMfrom langchain.agents import create_react_agent, AgentExecutorfrom langchain.tools import Toolfrom langchain.prompts import PromptTemplate# 도구들def add(a_b: str) -> str: """두 숫자를 더합니다. 입력 형식: '숫자1, 숫자2'""" try: a, b = map(float, a_b.split(",")) return str(a + b) except: return "올바른 형식: '숫자1, 숫자2'"def multiply(a_b: str) -> str: """두 숫자를 곱합니다. 입력 형식: '숫자1, 숫자2'""" try: a, b = map(float, a_b.split(",")) return str(a * b) except: return "올바른 형식: '숫자1, 숫자2'"def get_length(text: str) -> str: """텍스트의 길이를 반환합니다.""" return str(len(text))tools = [ Tool(name="add", func=add, description="두 숫자 덧셈. 입력: '숫자1, 숫자2'"), Tool(name="multiply", func=multiply, description="두 숫자 곱셈. 입력: '숫자1, 숫자2'"), Tool(name="length", func=get_length, description="텍스트 길이 계산. 입력: 텍스트")]# 에이전트 생성llm = OllamaLLM(model="llama4")prompt = PromptTemplate.from_template(PROMPT) # 위와 동일agent = create_react_agent(llm, tools, prompt)executor = AgentExecutor(agent=agent, tools=tools, verbose=True)# 복합 질문result = executor.invoke({ "input": "'Hello World'의 글자 수에 10을 곱하면?"})print(result["output"])
실행 과정
> Entering new AgentExecutor chain...
Thought: 먼저 'Hello World'의 글자 수를 구해야 한다.
Action: length
Action Input: Hello World
Observation: 11
Thought: 글자 수는 11이다. 이제 10을 곱한다.
Action: multiply
Action Input: 11, 10
Observation: 110.0
Thought: 계산 완료.
Final Answer: 'Hello World'의 글자 수는 11이고, 10을 곱하면 110입니다.
> Finished chain.
@tool 데코레이터
더 간편하게 도구를 정의합니다.
from langchain.tools import tool@tooldef search_web(query: str) -> str: """웹에서 정보를 검색합니다.""" # 실제로는 검색 API 호출 return f"'{query}'에 대한 검색 결과입니다."@tooldef get_current_time() -> str: """현재 시간을 반환합니다.""" from datetime import datetime return datetime.now().strftime("%Y-%m-%d %H:%M:%S")@tooldef calculate(expression: str) -> str: """수학 표현식을 계산합니다. 예: '2 + 3 * 4'""" try: result = eval(expression) return str(result) except Exception as e: return f"계산 오류: {e}"# 도구 목록tools = [search_web, get_current_time, calculate]# 에이전트 생성agent = create_react_agent(llm, tools, prompt)executor = AgentExecutor(agent=agent, tools=tools, verbose=True)# 실행result = executor.invoke({"input": "현재 시간이 몇 시야?"})
도구 설명의 중요성
도구의 description이 에이전트의 도구 선택에 결정적입니다.
# 나쁜 예@tooldef func1(x: str) -> str: """함수1""" # 설명이 없어 언제 쓸지 모름 return x# 좋은 예@tooldef calculate_tax(income: str) -> str: """ 연소득을 입력받아 예상 세금을 계산합니다. 입력: 연소득 (원 단위, 숫자만) 출력: 예상 세금 (원) 예: '50000000' -> '5000000' """ income_val = float(income) tax = income_val * 0.1 # 단순화 return str(int(tax))
AgentExecutor 옵션
executor = AgentExecutor( agent=agent, tools=tools, # 출력 옵션 verbose=True, # 과정 출력 return_intermediate_steps=True, # 중간 단계 반환 # 실행 제어 max_iterations=10, # 최대 반복 횟수 max_execution_time=60, # 최대 실행 시간 (초) early_stopping_method="generate", # 조기 종료 방식 # 오류 처리 handle_parsing_errors=True, # 파싱 오류 처리)# 중간 단계 확인result = executor.invoke({"input": "10 + 20을 계산해줘"})print("최종 답변:", result["output"])print("중간 단계:", result.get("intermediate_steps", []))
오류 처리
executor = AgentExecutor( agent=agent, tools=tools, verbose=True, handle_parsing_errors=True # 파싱 오류 자동 처리)# 또는 커스텀 오류 처리def handle_error(error): return f"오류 발생: {error}. 다시 시도하세요."executor = AgentExecutor( agent=agent, tools=tools, handle_parsing_errors=handle_error)
콜백으로 모니터링
from langchain.callbacks.base import BaseCallbackHandlerclass AgentMonitor(BaseCallbackHandler): """에이전트 모니터링""" def on_agent_action(self, action, **kwargs): print(f"🔧 도구 사용: {action.tool}") print(f" 입력: {action.tool_input}") def on_agent_finish(self, finish, **kwargs): print(f"✅ 완료: {finish.return_values}") def on_tool_start(self, tool, input_str, **kwargs): print(f"⚙️ 도구 시작: {tool}") def on_tool_end(self, output, **kwargs): print(f"📤 도구 결과: {output[:100]}...")# 콜백 적용executor = AgentExecutor( agent=agent, tools=tools, callbacks=[AgentMonitor()], verbose=False # 기본 출력 끄기)result = executor.invoke({"input": "현재 시간 알려줘"})
전체 예제
from langchain_ollama import OllamaLLMfrom langchain.agents import create_react_agent, AgentExecutorfrom langchain.tools import toolfrom langchain.prompts import PromptTemplate# 도구 정의@tooldef search(query: str) -> str: """인터넷에서 정보를 검색합니다.""" return f"'{query}' 검색 결과: Python은 프로그래밍 언어입니다."@tooldef calculator(expression: str) -> str: """수학 계산을 수행합니다. 예: '2 + 3'""" try: return str(eval(expression)) except: return "계산 오류"@tooldef translator(text: str) -> str: """영어를 한국어로 번역합니다.""" # 실제로는 번역 API 사용 translations = { "hello": "안녕하세요", "world": "세계", "python": "파이썬" } lower_text = text.lower() return translations.get(lower_text, f"'{text}'의 번역")# 프롬프트PROMPT = """Answer the following questions as best you can. Use Korean for your final answer.You have access to the following tools:{tools}Use the following format:Question: the input question you must answerThought: you should always think about what to doAction: the action to take, should be one of [{tool_names}]Action Input: the input to the actionObservation: the result of the action... (this Thought/Action/Action Input/Observation can repeat N times)Thought: I now know the final answerFinal Answer: the final answer to the original input question (in Korean)Begin!Question: {input}Thought: {agent_scratchpad}"""prompt = PromptTemplate.from_template(PROMPT)# 에이전트 생성llm = OllamaLLM(model="llama4")tools = [search, calculator, translator]agent = create_react_agent(llm, tools, prompt)executor = AgentExecutor( agent=agent, tools=tools, verbose=True, max_iterations=5, handle_parsing_errors=True)# 테스트questions = [ "100 나누기 4는?", "Python이 뭐야?", "Hello를 한국어로 번역해줘"]for q in questions: print(f"\n질문: {q}") result = executor.invoke({"input": q}) print(f"답변: {result['output']}") print("-" * 50)
정리
| 구성 요소 | 역할 |
|---|---|
| LLM | 추론 엔진 |
| Tool | 외부 기능 실행 |
| Prompt | 에이전트 행동 지침 |
| AgentExecutor | 실행 및 관리 |
다음 단계
기본 에이전트를 만들었습니다. 다음 장에서는 LangChain 내장 도구를 활용합니다.