15. LLM ์—์ด์ „ํŠธ (LLM Agents)

15. LLM ์—์ด์ „ํŠธ (LLM Agents)

ํ•™์Šต ๋ชฉํ‘œ

  • ์—์ด์ „ํŠธ ๊ฐœ๋…๊ณผ ์•„ํ‚คํ…์ฒ˜ ์ดํ•ด
  • ReAct ํŒจํ„ด ๊ตฌํ˜„
  • ๋„๊ตฌ ์‚ฌ์šฉ (Tool Use) ๊ธฐ๋ฒ•
  • LangChain Agent ํ™œ์šฉ
  • ์ž์œจ ์—์ด์ „ํŠธ ์‹œ์Šคํ…œ (AutoGPT ๋“ฑ)

1. LLM ์—์ด์ „ํŠธ ๊ฐœ์š”

์—์ด์ „ํŠธ๋ž€?

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                      LLM ์—์ด์ „ํŠธ                            โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                              โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                                            โ”‚
โ”‚  โ”‚    LLM      โ”‚  โ—€โ”€โ”€ ๋‘๋‡Œ (์˜์‚ฌ๊ฒฐ์ •)                       โ”‚
โ”‚  โ”‚  (Brain)    โ”‚                                            โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                            โ”‚
โ”‚         โ”‚                                                    โ”‚
โ”‚         โ–ผ                                                    โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                                            โ”‚
โ”‚  โ”‚   Planning  โ”‚  โ—€โ”€โ”€ ๊ณ„ํš ์ˆ˜๋ฆฝ                             โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                            โ”‚
โ”‚         โ”‚                                                    โ”‚
โ”‚         โ–ผ                                                    โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                         โ”‚
โ”‚  โ”‚    Tools    โ”‚    โ”‚   Memory    โ”‚  โ—€โ”€โ”€ ๋„๊ตฌ + ๊ธฐ์–ต        โ”‚
โ”‚  โ”‚ (๊ฒ€์ƒ‰, ๊ณ„์‚ฐ, โ”‚    โ”‚ (๋Œ€ํ™” ์ด๋ ฅ, โ”‚                         โ”‚
โ”‚  โ”‚  ์ฝ”๋“œ์‹คํ–‰)   โ”‚    โ”‚  ์ง€์‹ ๋ฒ ์ด์Šค)โ”‚                         โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                         โ”‚
โ”‚                                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

์—์ด์ „ํŠธ vs ์ฑ—๋ด‡

ํ•ญ๋ชฉ ์ฑ—๋ด‡ ์—์ด์ „ํŠธ
์‘๋‹ต ๋ฐฉ์‹ ๋‹จ์ผ ์‘๋‹ต ๋‹ค๋‹จ๊ณ„ ์ถ”๋ก 
๋„๊ตฌ ์‚ฌ์šฉ ์ œํ•œ์  ๋‹ค์–‘ํ•œ ๋„๊ตฌ
์ž์œจ์„ฑ ๋‚ฎ์Œ ๋†’์Œ
๊ณ„ํš ์ˆ˜๋ฆฝ ์—†์Œ ์žˆ์Œ
์˜ˆ์‹œ ๊ณ ๊ฐ ์ง€์› ๋ด‡ AutoGPT, Copilot

2. ReAct (Reasoning + Acting)

ReAct ํŒจํ„ด

Thought: ๋ฌธ์ œ๋ฅผ ๋ถ„์„ํ•˜๊ณ  ๋‹ค์Œ ํ–‰๋™ ๊ฒฐ์ •
Action: ๋„๊ตฌ ์„ ํƒ ๋ฐ ์ž…๋ ฅ ๊ฒฐ์ •
Observation: ๋„๊ตฌ ์‹คํ–‰ ๊ฒฐ๊ณผ
... (๋ฐ˜๋ณต)
Final Answer: ์ตœ์ข… ๋‹ต๋ณ€

ReAct ๊ตฌํ˜„

from openai import OpenAI

client = OpenAI()

# ๋„๊ตฌ ์ •์˜
tools = {
    "calculator": lambda expr: eval(expr),
    "search": lambda query: f"๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ: {query}์— ๋Œ€ํ•œ ์ •๋ณด...",
    "get_weather": lambda city: f"{city}์˜ ๋‚ ์”จ: ๋ง‘์Œ, 25๋„",
}

def react_agent(question, max_steps=5):
    """ReAct ์—์ด์ „ํŠธ"""

    system_prompt = """๋‹น์‹ ์€ ๋ฌธ์ œ๋ฅผ ๋‹จ๊ณ„๋ณ„๋กœ ํ•ด๊ฒฐํ•˜๋Š” ์—์ด์ „ํŠธ์ž…๋‹ˆ๋‹ค.

์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋„๊ตฌ:
- calculator: ์ˆ˜ํ•™ ๊ณ„์‚ฐ (์˜ˆ: "2 + 3 * 4")
- search: ์ •๋ณด ๊ฒ€์ƒ‰ (์˜ˆ: "ํŒŒ์ด์ฌ ์ฐฝ์‹œ์ž")
- get_weather: ๋‚ ์”จ ์กฐํšŒ (์˜ˆ: "์„œ์šธ")

๋‹ค์Œ ํ˜•์‹์„ ๋”ฐ๋ฅด์„ธ์š”:

Thought: [ํ˜„์žฌ ์ƒํ™ฉ ๋ถ„์„ ๋ฐ ๋‹ค์Œ ํ–‰๋™ ๊ณ„ํš]
Action: [๋„๊ตฌ ์ด๋ฆ„]
Action Input: [๋„๊ตฌ ์ž…๋ ฅ]

๋„๊ตฌ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ›์œผ๋ฉด:
Observation: [๊ฒฐ๊ณผ]

์ตœ์ข… ๋‹ต๋ณ€์ด ์ค€๋น„๋˜๋ฉด:
Final Answer: [๋‹ต๋ณ€]
"""

    messages = [
        {"role": "system", "content": system_prompt},
        {"role": "user", "content": question}
    ]

    for step in range(max_steps):
        response = client.chat.completions.create(
            model="gpt-4",
            messages=messages,
            temperature=0
        )

        assistant_message = response.choices[0].message.content
        messages.append({"role": "assistant", "content": assistant_message})

        print(f"=== Step {step + 1} ===")
        print(assistant_message)

        # Final Answer ์ฒดํฌ
        if "Final Answer:" in assistant_message:
            final_answer = assistant_message.split("Final Answer:")[-1].strip()
            return final_answer

        # Action ํŒŒ์‹ฑ
        if "Action:" in assistant_message and "Action Input:" in assistant_message:
            action_line = assistant_message.split("Action:")[-1].split("\n")[0].strip()
            input_line = assistant_message.split("Action Input:")[-1].split("\n")[0].strip()

            # ๋„๊ตฌ ์‹คํ–‰
            if action_line in tools:
                try:
                    observation = tools[action_line](input_line)
                except Exception as e:
                    observation = f"Error: {str(e)}"

                observation_message = f"Observation: {observation}"
                messages.append({"role": "user", "content": observation_message})
                print(observation_message)
            else:
                messages.append({"role": "user", "content": f"Error: Unknown tool '{action_line}'"})

    return "์ตœ๋Œ€ ๋‹จ๊ณ„ ๋„๋‹ฌ, ๋‹ต๋ณ€ ์‹คํŒจ"

# ์‚ฌ์šฉ
answer = react_agent("์„œ์šธ์˜ ๋‚ ์”จ๋ฅผ ํ™•์ธํ•˜๊ณ , ๊ธฐ์˜จ์„ ์„ญ์”จ์—์„œ ํ™”์”จ๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ์„ธ์š”.")
print(f"\n์ตœ์ข… ๋‹ต๋ณ€: {answer}")

3. ๋„๊ตฌ ์‚ฌ์šฉ (Tool Use)

Function Calling (OpenAI)

from openai import OpenAI
import json

client = OpenAI()

# ๋„๊ตฌ ์ •์˜
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "ํŠน์ • ๋„์‹œ์˜ ํ˜„์žฌ ๋‚ ์”จ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "๋„์‹œ ์ด๋ฆ„ (์˜ˆ: Seoul, Tokyo)"
                    },
                    "unit": {
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"],
                        "description": "์˜จ๋„ ๋‹จ์œ„"
                    }
                },
                "required": ["city"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "search_web",
            "description": "์›น์—์„œ ์ •๋ณด๋ฅผ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค.",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {
                        "type": "string",
                        "description": "๊ฒ€์ƒ‰์–ด"
                    }
                },
                "required": ["query"]
            }
        }
    }
]

# ๋„๊ตฌ ๊ตฌํ˜„
def get_weather(city, unit="celsius"):
    # ์‹ค์ œ๋กœ๋Š” API ํ˜ธ์ถœ
    weather_data = {
        "Seoul": {"temp": 25, "condition": "Sunny"},
        "Tokyo": {"temp": 28, "condition": "Cloudy"},
    }
    data = weather_data.get(city, {"temp": 20, "condition": "Unknown"})
    if unit == "fahrenheit":
        data["temp"] = data["temp"] * 9/5 + 32
    return json.dumps(data)

def search_web(query):
    return json.dumps({"results": f"'{query}'์— ๋Œ€ํ•œ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ..."})

tool_implementations = {
    "get_weather": get_weather,
    "search_web": search_web,
}

def agent_with_tools(user_message):
    """Function Calling ์—์ด์ „ํŠธ"""
    messages = [{"role": "user", "content": user_message}]

    response = client.chat.completions.create(
        model="gpt-4",
        messages=messages,
        tools=tools,
        tool_choice="auto"  # ์ž๋™์œผ๋กœ ๋„๊ตฌ ์„ ํƒ
    )

    assistant_message = response.choices[0].message

    # ๋„๊ตฌ ํ˜ธ์ถœ ํ•„์š” ์—ฌ๋ถ€ ํ™•์ธ
    if assistant_message.tool_calls:
        messages.append(assistant_message)

        # ๊ฐ ๋„๊ตฌ ํ˜ธ์ถœ ์ฒ˜๋ฆฌ
        for tool_call in assistant_message.tool_calls:
            function_name = tool_call.function.name
            function_args = json.loads(tool_call.function.arguments)

            # ๋„๊ตฌ ์‹คํ–‰
            function_response = tool_implementations[function_name](**function_args)

            # ๊ฒฐ๊ณผ ์ถ”๊ฐ€
            messages.append({
                "tool_call_id": tool_call.id,
                "role": "tool",
                "name": function_name,
                "content": function_response
            })

        # ์ตœ์ข… ์‘๋‹ต
        final_response = client.chat.completions.create(
            model="gpt-4",
            messages=messages
        )
        return final_response.choices[0].message.content

    return assistant_message.content

# ์‚ฌ์šฉ
result = agent_with_tools("์„œ์šธ๊ณผ ๋„์ฟ„์˜ ๋‚ ์”จ๋ฅผ ๋น„๊ตํ•ด์ฃผ์„ธ์š”.")
print(result)

์ฝ”๋“œ ์‹คํ–‰ ๋„๊ตฌ

import subprocess
import tempfile
import os

def execute_python(code):
    """Python ์ฝ”๋“œ ์•ˆ์ „ํ•˜๊ฒŒ ์‹คํ–‰"""
    with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f:
        f.write(code)
        temp_path = f.name

    try:
        result = subprocess.run(
            ['python', temp_path],
            capture_output=True,
            text=True,
            timeout=10  # ํƒ€์ž„์•„์›ƒ ์„ค์ •
        )
        output = result.stdout if result.returncode == 0 else result.stderr
        return {"success": result.returncode == 0, "output": output}
    except subprocess.TimeoutExpired:
        return {"success": False, "output": "Timeout"}
    finally:
        os.unlink(temp_path)

# ์ฝ”๋“œ ์‹คํ–‰ ๋„๊ตฌ ์ •์˜
code_tool = {
    "type": "function",
    "function": {
        "name": "execute_python",
        "description": "Python ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.",
        "parameters": {
            "type": "object",
            "properties": {
                "code": {
                    "type": "string",
                    "description": "์‹คํ–‰ํ•  Python ์ฝ”๋“œ"
                }
            },
            "required": ["code"]
        }
    }
}

4. LangChain Agent

๊ธฐ๋ณธ ์—์ด์ „ํŠธ

from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor, create_openai_tools_agent
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.tools import Tool, tool
from langchain_community.tools import DuckDuckGoSearchRun

# LLM
llm = ChatOpenAI(model="gpt-4", temperature=0)

# ๋„๊ตฌ ์ •์˜
search = DuckDuckGoSearchRun()

@tool
def calculator(expression: str) -> str:
    """์ˆ˜ํ•™ ๊ณ„์‚ฐ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ž…๋ ฅ: ์ˆ˜ํ•™ ํ‘œํ˜„์‹ (์˜ˆ: '2 + 3 * 4')"""
    try:
        return str(eval(expression))
    except:
        return "๊ณ„์‚ฐ ์˜ค๋ฅ˜"

@tool
def get_current_time() -> str:
    """ํ˜„์žฌ ์‹œ๊ฐ„์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค."""
    from datetime import datetime
    return datetime.now().strftime("%Y-%m-%d %H:%M:%S")

tools = [
    Tool(name="Search", func=search.run, description="์›น ๊ฒ€์ƒ‰"),
    calculator,
    get_current_time,
]

# ํ”„๋กฌํ”„ํŠธ
prompt = ChatPromptTemplate.from_messages([
    ("system", "๋‹น์‹ ์€ ๋„์›€์ด ๋˜๋Š” AI ์–ด์‹œ์Šคํ„ดํŠธ์ž…๋‹ˆ๋‹ค. ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์งˆ๋ฌธ์— ๋‹ตํ•˜์„ธ์š”."),
    MessagesPlaceholder(variable_name="chat_history", optional=True),
    ("human", "{input}"),
    MessagesPlaceholder(variable_name="agent_scratchpad"),
])

# ์—์ด์ „ํŠธ ์ƒ์„ฑ
agent = create_openai_tools_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

# ์‹คํ–‰
result = agent_executor.invoke({"input": "ํ˜„์žฌ ์‹œ๊ฐ„๊ณผ ์˜ค๋Š˜์˜ ์ฃผ์š” ๋‰ด์Šค๋ฅผ ์•Œ๋ ค์ฃผ์„ธ์š”."})
print(result["output"])

ReAct Agent (LangChain)

from langchain.agents import create_react_agent, AgentExecutor
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(model="gpt-4", temperature=0)

# ReAct ํ”„๋กฌํ”„ํŠธ
react_prompt = PromptTemplate.from_template("""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 answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question

Begin!

Question: {input}
Thought:{agent_scratchpad}""")

# ์—์ด์ „ํŠธ ์ƒ์„ฑ
react_agent = create_react_agent(llm, tools, react_prompt)
agent_executor = AgentExecutor(agent=react_agent, tools=tools, verbose=True, handle_parsing_errors=True)

# ์‹คํ–‰
result = agent_executor.invoke({"input": "2024๋…„ ๋ฏธ๊ตญ ๋Œ€ํ†ต๋ น ์„ ๊ฑฐ ๊ฒฐ๊ณผ๋ฅผ ๊ฒ€์ƒ‰ํ•˜๊ณ  ์š”์•ฝํ•ด์ฃผ์„ธ์š”."})

๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์žˆ๋Š” ์—์ด์ „ํŠธ

from langchain.memory import ConversationBufferMemory
from langchain.agents import AgentExecutor, create_openai_tools_agent

# ๋ฉ”๋ชจ๋ฆฌ
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)

# ํ”„๋กฌํ”„ํŠธ (๋ฉ”๋ชจ๋ฆฌ ํฌํ•จ)
prompt = ChatPromptTemplate.from_messages([
    ("system", "๋‹น์‹ ์€ ๋„์›€์ด ๋˜๋Š” AI ์–ด์‹œ์Šคํ„ดํŠธ์ž…๋‹ˆ๋‹ค."),
    MessagesPlaceholder(variable_name="chat_history"),
    ("human", "{input}"),
    MessagesPlaceholder(variable_name="agent_scratchpad"),
])

# ์—์ด์ „ํŠธ
agent = create_openai_tools_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, memory=memory, verbose=True)

# ๋Œ€ํ™”
agent_executor.invoke({"input": "๋‚ด ์ด๋ฆ„์€ ๊น€์ฒ ์ˆ˜์•ผ."})
agent_executor.invoke({"input": "๋‚ด ์ด๋ฆ„์ด ๋ญ๋ผ๊ณ  ํ–ˆ์ง€?"})

5. ์ž์œจ ์—์ด์ „ํŠธ ์‹œ์Šคํ…œ

Plan-and-Execute

from langchain.experimental.plan_and_execute import (
    PlanAndExecute,
    load_agent_executor,
    load_chat_planner
)
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(model="gpt-4", temperature=0)

# Planner์™€ Executor ์ƒ์„ฑ
planner = load_chat_planner(llm)
executor = load_agent_executor(llm, tools, verbose=True)

# Plan-and-Execute ์—์ด์ „ํŠธ
agent = PlanAndExecute(planner=planner, executor=executor, verbose=True)

# ๋ณต์žกํ•œ ์ž‘์—… ์‹คํ–‰
result = agent.run("ํŒŒ์ด์ฌ์˜ ์—ญ์‚ฌ์— ๋Œ€ํ•ด ์กฐ์‚ฌํ•˜๊ณ , ์ฃผ์š” ๋ฒ„์ „๋ณ„ ํŠน์ง•์„ ์š”์•ฝํ•œ ๋งˆํฌ๋‹ค์šด ๋ฌธ์„œ๋ฅผ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”.")

AutoGPT ์Šคํƒ€์ผ ์—์ด์ „ํŠธ

class AutoGPTAgent:
    """์ž์œจ ์—์ด์ „ํŠธ"""

    def __init__(self, llm, tools, goals):
        self.llm = llm
        self.tools = {t.name: t for t in tools}
        self.goals = goals
        self.memory = []
        self.completed_tasks = []

    def plan(self):
        """๋ชฉํ‘œ ๋‹ฌ์„ฑ์„ ์œ„ํ•œ ๊ณ„ํš ์ˆ˜๋ฆฝ"""
        prompt = f"""๋‹น์‹ ์€ ์ž์œจ AI ์—์ด์ „ํŠธ์ž…๋‹ˆ๋‹ค.

๋ชฉํ‘œ: {self.goals}

์™„๋ฃŒ๋œ ์ž‘์—…:
{self.completed_tasks}

์ด์ „ ์ž‘์—… ๊ฒฐ๊ณผ:
{self.memory[-5:] if self.memory else "์—†์Œ"}

์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋„๊ตฌ:
{list(self.tools.keys())}

๋‹ค์Œ ์ž‘์—…์„ JSON ํ˜•์‹์œผ๋กœ ์ถœ๋ ฅํ•˜์„ธ์š”:
{{"task": "์ž‘์—… ์„ค๋ช…", "tool": "์‚ฌ์šฉํ•  ๋„๊ตฌ", "input": "๋„๊ตฌ ์ž…๋ ฅ"}}

๋ชจ๋“  ๋ชฉํ‘œ๊ฐ€ ๋‹ฌ์„ฑ๋˜์—ˆ๋‹ค๋ฉด:
{{"task": "COMPLETE", "summary": "๊ฒฐ๊ณผ ์š”์•ฝ"}}
"""
        response = self.llm.invoke(prompt)
        return json.loads(response.content)

    def execute(self, task):
        """์ž‘์—… ์‹คํ–‰"""
        if task["task"] == "COMPLETE":
            return {"status": "complete", "summary": task["summary"]}

        tool = self.tools.get(task["tool"])
        if tool:
            result = tool.run(task["input"])
            return {"status": "success", "result": result}
        return {"status": "error", "message": f"Unknown tool: {task['tool']}"}

    def run(self, max_iterations=10):
        """์—์ด์ „ํŠธ ์‹คํ–‰"""
        for i in range(max_iterations):
            print(f"\n=== Iteration {i+1} ===")

            # ๊ณ„ํš
            task = self.plan()
            print(f"Task: {task}")

            # ์™„๋ฃŒ ํ™•์ธ
            if task.get("task") == "COMPLETE":
                print(f"Goals achieved: {task['summary']}")
                return task["summary"]

            # ์‹คํ–‰
            result = self.execute(task)
            print(f"Result: {result}")

            # ๋ฉ”๋ชจ๋ฆฌ ์—…๋ฐ์ดํŠธ
            self.memory.append({"task": task, "result": result})
            if result["status"] == "success":
                self.completed_tasks.append(task["task"])

        return "Max iterations reached"

# ์‚ฌ์šฉ
agent = AutoGPTAgent(
    llm=ChatOpenAI(model="gpt-4"),
    tools=tools,
    goals=["์„œ์šธ์˜ ์ธ๊ตฌ ์กฐ์‚ฌ", "์ธ๊ตฌ ํ†ต๊ณ„ ๋ถ„์„", "๋ณด๊ณ ์„œ ์ž‘์„ฑ"]
)
result = agent.run()

6. ๋ฉ€ํ‹ฐ ์—์ด์ „ํŠธ ์‹œ์Šคํ…œ

์—์ด์ „ํŠธ ๊ฐ„ ํ˜‘์—…

class ResearcherAgent:
    """์—ฐ๊ตฌ ์—์ด์ „ํŠธ"""
    def __init__(self, llm):
        self.llm = llm

    def research(self, topic):
        prompt = f"'{topic}'์— ๋Œ€ํ•ด ์กฐ์‚ฌํ•˜๊ณ  ํ•ต์‹ฌ ์ •๋ณด๋ฅผ ์ •๋ฆฌํ•ด์ฃผ์„ธ์š”."
        return self.llm.invoke(prompt).content

class WriterAgent:
    """์ž‘๋ฌธ ์—์ด์ „ํŠธ"""
    def __init__(self, llm):
        self.llm = llm

    def write(self, research_results, style="formal"):
        prompt = f"๋‹ค์Œ ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ {style} ์Šคํƒ€์ผ์˜ ๋ฌธ์„œ๋ฅผ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”:\n{research_results}"
        return self.llm.invoke(prompt).content

class ReviewerAgent:
    """๊ฒ€ํ†  ์—์ด์ „ํŠธ"""
    def __init__(self, llm):
        self.llm = llm

    def review(self, document):
        prompt = f"๋‹ค์Œ ๋ฌธ์„œ๋ฅผ ๊ฒ€ํ† ํ•˜๊ณ  ๊ฐœ์„ ์ ์„ ์ œ์•ˆํ•ด์ฃผ์„ธ์š”:\n{document}"
        return self.llm.invoke(prompt).content

class MultiAgentSystem:
    """๋ฉ€ํ‹ฐ ์—์ด์ „ํŠธ ์‹œ์Šคํ…œ"""

    def __init__(self, llm):
        self.researcher = ResearcherAgent(llm)
        self.writer = WriterAgent(llm)
        self.reviewer = ReviewerAgent(llm)

    def create_document(self, topic, max_revisions=2):
        # 1. ์—ฐ๊ตฌ
        print("=== ์—ฐ๊ตฌ ๋‹จ๊ณ„ ===")
        research = self.researcher.research(topic)
        print(research[:200] + "...")

        # 2. ์ž‘์„ฑ
        print("\n=== ์ž‘์„ฑ ๋‹จ๊ณ„ ===")
        document = self.writer.write(research)
        print(document[:200] + "...")

        # 3. ๊ฒ€ํ†  ๋ฐ ์ˆ˜์ •
        for i in range(max_revisions):
            print(f"\n=== ๊ฒ€ํ†  {i+1} ===")
            review = self.reviewer.review(document)
            print(review[:200] + "...")

            # ์ˆ˜์ •
            if "์ˆ˜์ • ํ•„์š” ์—†์Œ" in review:
                break
            document = self.writer.write(f"์›๋ณธ:\n{document}\n\n๊ฒ€ํ† :\n{review}", style="revised")

        return document

# ์‚ฌ์šฉ
llm = ChatOpenAI(model="gpt-4")
system = MultiAgentSystem(llm)
final_doc = system.create_document("์ธ๊ณต์ง€๋Šฅ์˜ ๋ฏธ๋ž˜")

7. ์—์ด์ „ํŠธ ํ‰๊ฐ€

๋„๊ตฌ ์„ ํƒ ์ •ํ™•๋„

def evaluate_tool_selection(agent, test_cases):
    """๋„๊ตฌ ์„ ํƒ ์ •ํ™•๋„ ํ‰๊ฐ€"""
    correct = 0
    total = len(test_cases)

    for case in test_cases:
        query = case["query"]
        expected_tool = case["expected_tool"]

        # ์—์ด์ „ํŠธ ์‹คํ–‰ (๋„๊ตฌ ์„ ํƒ๋งŒ)
        result = agent.plan(query)
        selected_tool = result.get("tool")

        if selected_tool == expected_tool:
            correct += 1
            print(f"[CORRECT] Query: {query}, Tool: {selected_tool}")
        else:
            print(f"[WRONG] Query: {query}, Expected: {expected_tool}, Got: {selected_tool}")

    accuracy = correct / total
    print(f"\nTool Selection Accuracy: {accuracy:.2%}")
    return accuracy

# ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค
test_cases = [
    {"query": "2 + 3 * 4๋ฅผ ๊ณ„์‚ฐํ•ด์ค˜", "expected_tool": "calculator"},
    {"query": "์˜ค๋Š˜ ์„œ์šธ ๋‚ ์”จ ์–ด๋•Œ?", "expected_tool": "get_weather"},
    {"query": "ํŒŒ์ด์ฌ ์ฐฝ์‹œ์ž๊ฐ€ ๋ˆ„๊ตฌ์•ผ?", "expected_tool": "search"},
]

# ํ‰๊ฐ€
evaluate_tool_selection(agent, test_cases)

์ž‘์—… ์™„๋ฃŒ์œจ

def evaluate_task_completion(agent, tasks):
    """์ž‘์—… ์™„๋ฃŒ์œจ ํ‰๊ฐ€"""
    results = []

    for task in tasks:
        try:
            result = agent.run(task["input"])
            success = task["validator"](result)
            results.append({
                "task": task["description"],
                "success": success,
                "result": result
            })
        except Exception as e:
            results.append({
                "task": task["description"],
                "success": False,
                "error": str(e)
            })

    completion_rate = sum(r["success"] for r in results) / len(results)
    print(f"Task Completion Rate: {completion_rate:.2%}")
    return results

# ์ž‘์—… ์ •์˜
tasks = [
    {
        "description": "๋‚ ์”จ ์กฐํšŒ ๋ฐ ์˜ท์ฐจ๋ฆผ ์ถ”์ฒœ",
        "input": "์„œ์šธ ๋‚ ์”จ๋ฅผ ํ™•์ธํ•˜๊ณ  ์˜ค๋Š˜ ์˜ท์ฐจ๋ฆผ์„ ์ถ”์ฒœํ•ด์ค˜",
        "validator": lambda r: "์„œ์šธ" in r and ("์˜ท" in r or "์˜๋ฅ˜" in r)
    },
    {
        "description": "์ˆ˜ํ•™ ๊ณ„์‚ฐ",
        "input": "123 * 456์˜ ๊ฒฐ๊ณผ๋Š”?",
        "validator": lambda r: "56088" in r
    },
]

์ •๋ฆฌ

์—์ด์ „ํŠธ ์•„ํ‚คํ…์ฒ˜ ๋น„๊ต

์•„ํ‚คํ…์ฒ˜ ํŠน์ง• ์‚ฌ์šฉ ์‹œ์ 
ReAct ์ถ”๋ก -ํ–‰๋™ ๋ฐ˜๋ณต ๋‹จ๊ณ„๋ณ„ ๋ฌธ์ œ ํ•ด๊ฒฐ
Function Calling ๊ตฌ์กฐํ™”๋œ ๋„๊ตฌ ํ˜ธ์ถœ API ์—ฐ๋™
Plan-and-Execute ๊ณ„ํš ํ›„ ์‹คํ–‰ ๋ณต์žกํ•œ ์ž‘์—…
AutoGPT ์ž์œจ ๋ชฉํ‘œ ๋‹ฌ์„ฑ ์žฅ๊ธฐ ์ž‘์—…
Multi-Agent ์—ญํ•  ๋ถ„๋‹ด ํ˜‘์—… ์ „๋ฌธ์„ฑ ํ•„์š”

ํ•ต์‹ฌ ์ฝ”๋“œ

# ReAct ํŒจํ„ด
Thought: ๋ฌธ์ œ ๋ถ„์„
Action: ๋„๊ตฌ ์„ ํƒ
Observation: ๊ฒฐ๊ณผ ํ™•์ธ
Final Answer: ์ตœ์ข… ๋‹ต๋ณ€

# Function Calling (OpenAI)
response = client.chat.completions.create(
    model="gpt-4",
    messages=messages,
    tools=tools,
    tool_choice="auto"
)

# LangChain Agent
agent = create_openai_tools_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools)
result = executor.invoke({"input": query})

์—์ด์ „ํŠธ ์„ค๊ณ„ ์ฒดํฌ๋ฆฌ์ŠคํŠธ

โ–ก ๋ช…ํ™•ํ•œ ๋„๊ตฌ ์ •์˜ (์ด๋ฆ„, ์„ค๋ช…, ํŒŒ๋ผ๋ฏธํ„ฐ)
โ–ก ์—๋Ÿฌ ์ฒ˜๋ฆฌ (๋„๊ตฌ ์‹คํŒจ, ํŒŒ์‹ฑ ์˜ค๋ฅ˜)
โ–ก ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ (๋Œ€ํ™” ์ด๋ ฅ, ์ปจํ…์ŠคํŠธ)
โ–ก ๋ฃจํ”„ ๋ฐฉ์ง€ (์ตœ๋Œ€ ๋ฐ˜๋ณต ํšŸ์ˆ˜)
โ–ก ์•ˆ์ „ ์žฅ์น˜ (์œ„ํ—˜ํ•œ ์ž‘์—… ์ œํ•œ)
โ–ก ๋กœ๊น… ๋ฐ ๋ชจ๋‹ˆํ„ฐ๋ง

๋‹ค์Œ ๋‹จ๊ณ„

16_Evaluation_Metrics.md์—์„œ LLM ํ‰๊ฐ€ ์ง€ํ‘œ์™€ ๋ฒค์น˜๋งˆํฌ๋ฅผ ํ•™์Šตํ•ฉ๋‹ˆ๋‹ค.

to navigate between lessons