에이전트(Agent) - 개념 (1)

2026. 6. 22. 05:21AI/LLM

1. 에이전트 (Agent)

 

LLM에게 스스로 어떤 행동을 취할 지 동적으로 결정할 수 있도록 만들어내는 것.

 

이 말은 지금까지는 동적으로 결정하지 않았다는 의미가 되는데,

 

우리가 직접 쿼리나 Document를 LLM에게 넘겼기 때문에 답변을 받은 것이다.

 

그러나 이제부터는 동적으로 결정을 할 것이다. LLM이 매 단계마다 결정을 해야되고

 

그것을 동적으로 선택하고, 실행할지 말지를 에이전트가 직접 결정한다.

 

중요한건 목표달성까지 CoT(추론)하여 반복해서 실행을 한다는 점이다.

 

랭체인(LangChain)에서는 에이전트를 동일한 인터페이스로 사용할 수 있게끔 제공한다.

 

주로 복잡하고 여러 단계를 거치는 작업들을 에이전트로 만든다.

 

1) 구성 요소

 

2) ReAct 패턴 : 추론과 행동의 반복

 

계속 추론을 하고, 그 추론의 결과로 행동과 관찰을 반복한다.

 

이 패턴의 사이클은 아래와 같다.

실제 과정의 예시는 아래와 같다.

 

3) Tool

 

Tool은 에이전트가 외부와 상호작용하기 위해 호출 할 수 있는 함수로써

 

에이전트 사용을 위해 Tool을 지정해줘야 하는데,

 

Tool 사용법으로는 직접 정의하거나 내장 툴을 사용하는 방법이 있다.

 

직접 정의 시 우리가 작성해줘야 하는 구조는 아래와 같다. (그 중 description이 제일 중요!)

1. DuckDuckGoSearchRun

 

먼저 Search Tool 내장 툴의 예시에 대해 알아본다.

 

예를 들어 LLM에 다음과 같이 query를 날릴 경우, LLM은 제대로 답변하지 못한다.

response = qwen_llm.invoke("2026년 대한민국 대통령은 누구인가요?")
print(response.content)

 

그러므로, Web Search 하라는 툴을 사용하여 Agent에게 시킬 것이다.

 

여기서는 DuckDuckGoSearchRun를 이용한다.

# Search Tool : DuckDuckGoSearch (무료)

tools = DuckDuckGoSearchRun()

agent = create_agent(
    model= qwen_llm,
    tools= [tools],
    system_prompt='You are a helpful assistant'
)

result = agent.invoke(
    {
        "messages": [
            {
                "role": "user",
                "content": "2026년 대한민국 대통령은 누구인가요?"
            }
        ]
    }
)
def print_result(result):
    for i, msg in enumerate(result['messages']):
        print(f"\n===== {i} =====")
        print(type(msg).__name__)

        if hasattr(msg, "content"):
            print(msg.content)

        if hasattr(msg, "tool_calls"):
            print(msg.tool_calls)

print_result(result)

 

여기서 툴을 사용할지 말지는 LLM이 결정하고, 자유롭게 LLM이 답변한 것이다.

 

2. GoogleWebSearch

 

GoogleWebSearch는 회원가입과 API_KEY가 필요하다

 

https://serper.dev/

 

Serper - The World's Fastest and Cheapest Google Search API

Our default rate limit for the Ultimate credits is 300 queries per second. This allows you to perform approximately 15,000 to 18,000 searches in 1 minute. If your specific use case requires a higher concurrency we are able to change this limit.

serper.dev

load_dotenv()

SERPER_API_KEY= os.getenv("SERPER_API_KEY")
from langchain_community.utilities import GoogleSerperAPIWrapper
from langchain_core.tools import Tool

search = GoogleSerperAPIWrapper()

search_tool = Tool(
    name="google_search",
    func=search.run,
    description="""
    Google 검색으로 최신 정보를 찾습니다.
    뉴스, 트렌드, 최신 기술 동향 검색에 사용하세요.
    """
)

agent = create_agent(
    model=qwen_llm,
    tools= [search_tool],
    system_prompt="You are a helpful assistant"
)

result = agent.invoke(
    {
        "messages": [
            {
                "role": "user",
                "content": "2026년 대한민국 대통령은 누구인가요?"
            }
        ]
    }
)

print_result(result)

 

3. Database Agent

 

먼저 다음과 같은 데이터베이스를 생성한다.

import sqlite3

conn = sqlite3.connect("./db/company.db")

cursor = conn.cursor()

cursor.execute("""
CREATE TABLE employee(id integer primary key autoincrement, name text, salary integer)
""")

cursor.execute("""
INSERT INTO employee(name, salary) VALUES('kim', 4000),('Lee',5000),('Park', 6000)
""")

conn.commit()
conn.close()

 

SQLDatabase 라이브러리를 통해 DB를 조회한다.

from langchain_community.utilities import SQLDatabase

db = SQLDatabase.from_uri("sqlite:///db/company.db")
print(db.run('select * from employee'))

 

이제 SQLDatabaseToolkit 이용해 데이터베이스 에이전트를 만들어 본다

from langchain_community.agent_toolkits import SQLDatabaseToolkit

toolkit = SQLDatabaseToolkit(db=db,llm=openai_llm)
tools = toolkit.get_tools()

agent = create_agent(
    model=openai_llm,
    tools=tools,
    system_prompt = "당신은 한국어로 답하는 데이터베이스 어시스턴트 입니다. 중국어로 절대 답하지 마세요."
)

result = agent.invoke(
    {
        "messages": [
            {
                "role": "user",
                "content": "직원의 평균 급여는 얼마인가요?"
            }
        ]
    }
)

print_result(result)

 

4. Calculator Agent

 

직접 제공을 해주고 있지만 요즘 추세는 calculator를 직접 커스텀해서 구현하는 편이다.

from langchain_classic.chains import LLMMathChain

llm_math = LLMMathChain.from_llm(llm=openai_llm, verbose=True)

calc_tool = Tool(name="calculator", func=llm_math.run, description="수학 계산이 필요할 때 사용합니다. 시장 규모 계산, 성장률 계산, 단위 변환 등에 사용하세요.")

calc_tool.invoke("1200억 달러를 원화로 환산 (1달러 = 1530원)")

 

5. Custom Tools

 

커스텀 툴을 설정하는데 description 작성이 중요하다.

 

기능 설명, 언제 사용해야 하는지, 입력값 형식, 출력값 형식, 필수인지 선택인지 명시를 해주는 것이 좋다.

 

커스텀 툴 작성 방법에는, @tool 데코레이터를 사용하는 방식을 많이 사용하고 있다.

 

@tool decorator

from langchain_core.tools import tool

# 함수구현
@tool
def calculator(expression):
    """
    수식을 계산합니다.
    사칙 연산에 사용하세요.
    """
    return str(eval(expression))
calculator.invoke("123 * 567")

 

이제 에이전트를 만들어 연결해본다.

agent = create_agent(model=openai_llm, tools=[calculator])

result = agent.invoke({
    "messages": [
        {
            "role": "user",
            "content": "123 곱하기 789의 결과를 알려주세요."
        }
    ]
})

print_result(result)

 

두번째로, BaseModel을 써서 틀을 지정하고 만드는 경우도 있다.

 

아래 예시를 살펴보자.

from pydantic import BaseModel, Field

class ExchangeRateInput(BaseModel):
    from_currency: str = Field(description="변환할 통화 코드 (예 : USD, JPY, KRW)")
    to_currency: str = Field(description="변환될 통화 코드 (예: USD, JPY, KRW)")
    amount: float = Field(default=1.0, description="변환할 금액 (기본값 : 1)")

@tool(args_schema=ExchangeRateInput)
def get_exchange_rate(from_currency, to_currency, amount):
    """
    실시간 환율을 조회하고, 금액을 변환합니다 
    USD, EUR, JPY, KRW 통화간 환율 계산에 사용하세요.
    해외 결제, 환전, 가격 비교 질문에 사용합니다
    """
    mock_rates = {
        ('USD', 'KRW') : 1540.0,
        ('EUR', 'KRW') : 1440.0,
        ('JPY', 'KRW') : 9.2,
        ('KRW', 'USD') : 0.00064
    }

    rate = mock_rates.get((from_currency, to_currency))

    if not rate:
        return f"{from_currency} -> {to_currency} 환율 정보 없음"

    converted = amount * rate
    return f"{amount} {from_currency} = {converted:.2f} {to_currency} (환율 : {rate})"

 

위는 @tool에 description과 args_schema를 설정한 예시이다.

agent = create_agent(model=openai_llm, tools=[get_exchange_rate])

result = agent.invoke({
    'messages': [
        {
            "role": "user",
            "content": "1달러 한국 돈으로 환전하면 얼마인가요?"
        }
    ]
})

print_result(result)

 

StructuredTool

 

여러 입력을 받을 수 있는 Tool 이지만 자주 사용하지는 않는다.

from langchain_core.tools import StructuredTool

def multiply(a: int, b: int) -> int:
    """
    두 숫자를 곱합니다
    """
    return a * b

multi_tool = StructuredTool.from_function(func=multiply)

agent = create_agent(model=openai_llm, tools=[multi_tool])

result = agent.invoke({
    'messages': [
        {
            "role": "user",
            "content": "12 곱하기 34는?"
        }
    ]
})

print_result(result)

 

def calculate_bmi(height: float, weight: float) -> str:
    """
    height(cm)와 weight(kg)를 입력받아 BMI 계산
    """
    bmi = weight / ((height / 100) ** 2)
    return f"BMI = {bmi:.2f}"
 
bmi_tool = StructuredTool.from_function(calculate_bmi)

agent = create_agent(
    model=openai_llm,
    tools=[bmi_tool]
)

result = agent.invoke({
    'messages': [
        {
            "role": "user",
            "content": "내 키는 180cm이고, 몸무게는 78kg이야. 내 BMI에 대해 알려주고, 분석해줘."
        }
    ]
})

print_result(result)

 

BaseTool

 

Tool을 클래스 형태로 직접 구현하는 방식으로,

 

로직이 복잡하거나 상태를 가지거나 API 연동이 길어질 때 사용할 수 있다.

from langchain_core.tools import BaseTool

class WeatherInput(BaseModel):
    city: str = Field(description="날씨를 조회할 도시명")

class WeatherTool(BaseTool):
    name: str = "weather"
    description: str = "특정 도시의 날짜를 조회합니다."
    args_schema:type[BaseModel] = WeatherInput

    def _run(self, city:str):

        # 임시 데이터 -> 나중에 날씨 API로 바뀌어야 함
        weather_data = {
            "서울": "맑음, 30도",
            "부산": "맑음, 32도",
            "제주": "비, 20도"
        }

        return weather_data.get(city, f"{city} 날씨 정보 없음")

weather_tool = WeatherTool()

agent = create_agent(
    model=openai_llm,
    tools=[weather_tool]
)

result = agent.invoke({
    'messages': [
        {
            "role": "user",
            "content": "서울의 날씨 알려줘."
        }
    ]
})

print_result(result)

 

Retriever Tool

 

RAG 시스템 만든 것을 Tool로 등록할 수 있다.

# retriever Tool

from langchain_core.documents import Document
from langchain_chroma import Chroma
from langchain_classic.tools.retriever import create_retriever_tool

docs = [
    Document(page_content="연차는 입사 1년 미만은 월 1개 발생합니다."),
    Document(page_content="병가는 연 30일까지 사용 가능합니다."),
    Document(page_content="퇴직금은 1년 이상 근무 시 지급됩니다.")
]

vectorstore = Chroma.from_documents(docs, embedding=ollama_embedding)

retriever = vectorstore.as_retriever()

retriever_tool = create_retriever_tool(retriever, name="company_policy", description="회사 인사 규정을 검색합니다.")

agent = create_agent(model=openai_llm, tools=[retriever_tool])

result = agent.invoke(
    {
        "messages": [
            {
                "role": "user",
                "content": "연차는 어떻게 발생하나요?"
            }
        ]
    }
)

print_result(result)

 

4) LangSmith

 

일일히 트레이스 하지 않고, 도구를 사용하여 자동으로 에이전트의 실행을 시각화하고

 

비용, 지연시간, 오류율을 추적한다.

 

https://smith.langchain.com/

 

LangSmith

View in LangSmith

smith.langchain.com

!pip install -U langchain langchain_openai
LANGSMITH_TRACING_V2=true
LANGSMITH_ENDPOINT=https://api.smith.langchain.com
LANGSMITH_API_KEY=<langsmith-api-key>
LANGSMITH_PROJECT=<"project-name">
from langchain.agents import create_agent
load_dotenv()


def get_weather(city: str) -> str:
    """Get weather for a given city."""
    return f"It's always sunny in {city}!"


agent = create_agent(
    model=openai_llm,
    tools=[get_weather],
    system_prompt="You are a helpful assistant",
)

# Run the agent
agent.invoke(
    {"messages": [{"role": "user", "content": "What is the weather in San Francisco?"}]}
)

 

이렇게 하면 아래와 같이 자동으로 트레이싱 해준다.