15_agent_tools.py

Download
python 536 lines 16.0 KB
  1"""
  215. LLM ์—์ด์ „ํŠธ (LLM Agents) ์˜ˆ์ œ
  3
  4ReAct ํŒจํ„ด, Tool Use, LangChain Agent ์‹ค์Šต
  5"""
  6
  7import json
  8import re
  9from typing import Dict, List, Callable, Any
 10
 11print("=" * 60)
 12print("LLM ์—์ด์ „ํŠธ (LLM Agents)")
 13print("=" * 60)
 14
 15
 16# ============================================
 17# 1. ๋„๊ตฌ ์ •์˜
 18# ============================================
 19print("\n[1] ๋„๊ตฌ (Tools) ์ •์˜")
 20print("-" * 40)
 21
 22class Tool:
 23    """๋„๊ตฌ ๊ธฐ๋ณธ ํด๋ž˜์Šค"""
 24
 25    def __init__(self, name: str, description: str, func: Callable):
 26        self.name = name
 27        self.description = description
 28        self.func = func
 29
 30    def run(self, input_str: str) -> str:
 31        """๋„๊ตฌ ์‹คํ–‰"""
 32        try:
 33            return str(self.func(input_str))
 34        except Exception as e:
 35            return f"Error: {str(e)}"
 36
 37
 38# ๋„๊ตฌ ๊ตฌํ˜„
 39def calculator(expression: str) -> float:
 40    """์•ˆ์ „ํ•œ ์ˆ˜ํ•™ ๊ณ„์‚ฐ"""
 41    # ๊ฐ„๋‹จํ•œ ์•ˆ์ „ ์ฒดํฌ
 42    allowed_chars = set("0123456789+-*/.() ")
 43    if not all(c in allowed_chars for c in expression):
 44        raise ValueError("Invalid characters in expression")
 45    return eval(expression)
 46
 47def search(query: str) -> str:
 48    """๊ฒ€์ƒ‰ ์‹œ๋ฎฌ๋ ˆ์ด์…˜"""
 49    # ์‹ค์ œ๋กœ๋Š” API ํ˜ธ์ถœ
 50    results = {
 51        "ํŒŒ์ด์ฌ ์ฐฝ์‹œ์ž": "ํŒŒ์ด์ฌ์€ 1991๋…„ ๊ท€๋„ ๋ฐ˜ ๋กœ์„ฌ(Guido van Rossum)์ด ๊ฐœ๋ฐœํ–ˆ์Šต๋‹ˆ๋‹ค.",
 52        "์ธ๊ณต์ง€๋Šฅ": "์ธ๊ณต์ง€๋Šฅ(AI)์€ ์ธ๊ฐ„์˜ ์ง€๋Šฅ์„ ๋ชจ๋ฐฉํ•˜๋Š” ์ปดํ“จํ„ฐ ์‹œ์Šคํ…œ์ž…๋‹ˆ๋‹ค.",
 53        "์„œ์šธ ์ธ๊ตฌ": "์„œ์šธ์˜ ์ธ๊ตฌ๋Š” ์•ฝ 950๋งŒ ๋ช…์ž…๋‹ˆ๋‹ค (2024๋…„ ๊ธฐ์ค€).",
 54    }
 55    for key, value in results.items():
 56        if key in query:
 57            return value
 58    return f"'{query}'์— ๋Œ€ํ•œ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."
 59
 60def get_weather(city: str) -> str:
 61    """๋‚ ์”จ ์กฐํšŒ ์‹œ๋ฎฌ๋ ˆ์ด์…˜"""
 62    weather_data = {
 63        "์„œ์šธ": {"temp": 25, "condition": "๋ง‘์Œ"},
 64        "๋ถ€์‚ฐ": {"temp": 28, "condition": "ํ๋ฆผ"},
 65        "์ œ์ฃผ": {"temp": 27, "condition": "๊ตฌ๋ฆ„ ์กฐ๊ธˆ"},
 66    }
 67    if city in weather_data:
 68        data = weather_data[city]
 69        return f"{city} ๋‚ ์”จ: {data['temp']}๋„, {data['condition']}"
 70    return f"{city}์˜ ๋‚ ์”จ ์ •๋ณด๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."
 71
 72
 73# ๋„๊ตฌ ๋“ฑ๋ก
 74tools = [
 75    Tool("calculator", "์ˆ˜ํ•™ ๊ณ„์‚ฐ. ์ž…๋ ฅ: ์ˆ˜ํ•™ ํ‘œํ˜„์‹ (์˜ˆ: '2 + 3 * 4')", calculator),
 76    Tool("search", "์ •๋ณด ๊ฒ€์ƒ‰. ์ž…๋ ฅ: ๊ฒ€์ƒ‰์–ด", search),
 77    Tool("get_weather", "๋‚ ์”จ ์กฐํšŒ. ์ž…๋ ฅ: ๋„์‹œ ์ด๋ฆ„", get_weather),
 78]
 79
 80print("์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋„๊ตฌ:")
 81for tool in tools:
 82    print(f"  - {tool.name}: {tool.description}")
 83
 84
 85# ============================================
 86# 2. ReAct ํŒจํ„ด ์‹œ๋ฎฌ๋ ˆ์ด์…˜
 87# ============================================
 88print("\n[2] ReAct ํŒจํ„ด ์‹œ๋ฎฌ๋ ˆ์ด์…˜")
 89print("-" * 40)
 90
 91class ReActAgent:
 92    """ReAct (Reasoning + Acting) ์—์ด์ „ํŠธ"""
 93
 94    def __init__(self, tools: List[Tool]):
 95        self.tools = {t.name: t for t in tools}
 96        self.history = []
 97
 98    def think(self, question: str, observations: List[str]) -> Dict[str, str]:
 99        """
100        ์ƒ๊ฐ ๋‹จ๊ณ„ (์‹ค์ œ๋กœ๋Š” LLM ํ˜ธ์ถœ)
101        ์—ฌ๊ธฐ์„œ๋Š” ๊ทœ์น™ ๊ธฐ๋ฐ˜์œผ๋กœ ์‹œ๋ฎฌ๋ ˆ์ด์…˜
102        """
103        question_lower = question.lower()
104
105        # ๊ทœ์น™ ๊ธฐ๋ฐ˜ ์˜์‚ฌ๊ฒฐ์ • (์‹ค์ œ๋กœ๋Š” LLM์ด ์ˆ˜ํ–‰)
106        if "๋‚ ์”จ" in question_lower:
107            # ๋„์‹œ ์ถ”์ถœ
108            cities = ["์„œ์šธ", "๋ถ€์‚ฐ", "์ œ์ฃผ"]
109            for city in cities:
110                if city in question:
111                    return {
112                        "thought": f"{city}์˜ ๋‚ ์”จ๋ฅผ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
113                        "action": "get_weather",
114                        "action_input": city
115                    }
116
117        if "๊ณ„์‚ฐ" in question_lower or any(op in question for op in ["+", "-", "*", "/"]):
118            # ์ˆ˜์‹ ์ถ”์ถœ
119            numbers = re.findall(r'[\d\+\-\*\/\.\(\)\s]+', question)
120            if numbers:
121                expr = numbers[0].strip()
122                return {
123                    "thought": f"'{expr}'์„ ๊ณ„์‚ฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
124                    "action": "calculator",
125                    "action_input": expr
126                }
127
128        if any(keyword in question_lower for keyword in ["๋ˆ„๊ตฌ", "๋ฌด์—‡", "์–ด๋””", "๊ฒ€์ƒ‰"]):
129            return {
130                "thought": f"'{question}'์— ๋Œ€ํ•ด ๊ฒ€์ƒ‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.",
131                "action": "search",
132                "action_input": question
133            }
134
135        # ์ด๋ฏธ ์ถฉ๋ถ„ํ•œ ์ •๋ณด๊ฐ€ ์žˆ์œผ๋ฉด ์ตœ์ข… ๋‹ต๋ณ€
136        if observations:
137            return {
138                "thought": "์ถฉ๋ถ„ํ•œ ์ •๋ณด๋ฅผ ์ˆ˜์ง‘ํ–ˆ์Šต๋‹ˆ๋‹ค.",
139                "final_answer": " ".join(observations)
140            }
141
142        return {
143            "thought": "์งˆ๋ฌธ์„ ์ดํ•ดํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.",
144            "final_answer": "์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค, ์งˆ๋ฌธ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."
145        }
146
147    def act(self, action: str, action_input: str) -> str:
148        """ํ–‰๋™ ๋‹จ๊ณ„"""
149        if action in self.tools:
150            return self.tools[action].run(action_input)
151        return f"Error: Unknown tool '{action}'"
152
153    def run(self, question: str, max_steps: int = 5) -> str:
154        """์—์ด์ „ํŠธ ์‹คํ–‰"""
155        observations = []
156
157        print(f"\n์งˆ๋ฌธ: {question}")
158        print("-" * 30)
159
160        for step in range(max_steps):
161            # ์ƒ๊ฐ
162            result = self.think(question, observations)
163
164            print(f"\n[Step {step + 1}]")
165            print(f"Thought: {result.get('thought', '')}")
166
167            # ์ตœ์ข… ๋‹ต๋ณ€ ํ™•์ธ
168            if "final_answer" in result:
169                print(f"Final Answer: {result['final_answer']}")
170                return result["final_answer"]
171
172            # ํ–‰๋™
173            action = result.get("action")
174            action_input = result.get("action_input")
175
176            if action:
177                print(f"Action: {action}")
178                print(f"Action Input: {action_input}")
179
180                observation = self.act(action, action_input)
181                observations.append(observation)
182                print(f"Observation: {observation}")
183
184        return "์ตœ๋Œ€ ๋‹จ๊ณ„ ๋„๋‹ฌ"
185
186
187# ํ…Œ์ŠคํŠธ
188agent = ReActAgent(tools)
189
190questions = [
191    "์„œ์šธ ๋‚ ์”จ ์–ด๋•Œ?",
192    "15 + 27 * 3์„ ๊ณ„์‚ฐํ•ด์ค˜",
193    "ํŒŒ์ด์ฌ ์ฐฝ์‹œ์ž๊ฐ€ ๋ˆ„๊ตฌ์•ผ?",
194]
195
196for q in questions:
197    result = agent.run(q)
198
199
200# ============================================
201# 3. Function Calling ํ˜•์‹
202# ============================================
203print("\n" + "=" * 60)
204print("[3] Function Calling ํ˜•์‹")
205print("-" * 40)
206
207# OpenAI Function Calling ํ˜•์‹
208function_definitions = [
209    {
210        "type": "function",
211        "function": {
212            "name": "get_weather",
213            "description": "ํŠน์ • ๋„์‹œ์˜ ํ˜„์žฌ ๋‚ ์”จ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.",
214            "parameters": {
215                "type": "object",
216                "properties": {
217                    "city": {
218                        "type": "string",
219                        "description": "๋„์‹œ ์ด๋ฆ„ (์˜ˆ: Seoul, Tokyo)"
220                    },
221                    "unit": {
222                        "type": "string",
223                        "enum": ["celsius", "fahrenheit"],
224                        "description": "์˜จ๋„ ๋‹จ์œ„"
225                    }
226                },
227                "required": ["city"]
228            }
229        }
230    },
231    {
232        "type": "function",
233        "function": {
234            "name": "search_web",
235            "description": "์›น์—์„œ ์ •๋ณด๋ฅผ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค.",
236            "parameters": {
237                "type": "object",
238                "properties": {
239                    "query": {
240                        "type": "string",
241                        "description": "๊ฒ€์ƒ‰์–ด"
242                    }
243                },
244                "required": ["query"]
245            }
246        }
247    }
248]
249
250print("Function Calling ์ •์˜:")
251print(json.dumps(function_definitions, indent=2, ensure_ascii=False))
252
253
254# ============================================
255# 4. ๋ฉ€ํ‹ฐ ์—์ด์ „ํŠธ ์‹œ๋ฎฌ๋ ˆ์ด์…˜
256# ============================================
257print("\n" + "=" * 60)
258print("[4] ๋ฉ€ํ‹ฐ ์—์ด์ „ํŠธ ์‹œ๋ฎฌ๋ ˆ์ด์…˜")
259print("-" * 40)
260
261class ResearcherAgent:
262    """์—ฐ๊ตฌ ์—์ด์ „ํŠธ"""
263
264    def __init__(self):
265        self.name = "Researcher"
266
267    def research(self, topic: str) -> str:
268        """์ฃผ์ œ ์—ฐ๊ตฌ (์‹œ๋ฎฌ๋ ˆ์ด์…˜)"""
269        research_db = {
270            "์ธ๊ณต์ง€๋Šฅ": "AI๋Š” 1956๋…„ ๋‹คํŠธ๋จธ์Šค ํšŒ์˜์—์„œ ์‹œ์ž‘๋˜์—ˆ์œผ๋ฉฐ, "
271                       "๋จธ์‹ ๋Ÿฌ๋‹, ๋”ฅ๋Ÿฌ๋‹, ์ž์—ฐ์–ด์ฒ˜๋ฆฌ ๋“ฑ์œผ๋กœ ๋ฐœ์ „ํ–ˆ์Šต๋‹ˆ๋‹ค.",
272            "ํŒŒ์ด์ฌ": "ํŒŒ์ด์ฌ์€ 1991๋…„ ๊ท€๋„ ๋ฐ˜ ๋กœ์„ฌ์ด ๊ฐœ๋ฐœํ•œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๋กœ, "
273                     "๊ฐ„๊ฒฐํ•œ ๋ฌธ๋ฒ•๊ณผ ํ’๋ถ€ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ํŠน์ง•์ž…๋‹ˆ๋‹ค.",
274        }
275        for key, value in research_db.items():
276            if key in topic:
277                return value
278        return f"{topic}์— ๋Œ€ํ•œ ์—ฐ๊ตฌ ๊ฒฐ๊ณผ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."
279
280
281class WriterAgent:
282    """์ž‘๋ฌธ ์—์ด์ „ํŠธ"""
283
284    def __init__(self):
285        self.name = "Writer"
286
287    def write(self, research_results: str, style: str = "formal") -> str:
288        """๋ฌธ์„œ ์ž‘์„ฑ (์‹œ๋ฎฌ๋ ˆ์ด์…˜)"""
289        if style == "formal":
290            return f"## ์—ฐ๊ตฌ ๋ณด๊ณ ์„œ\n\n{research_results}\n\n๋ณธ ๋‚ด์šฉ์€ ์—ฐ๊ตฌ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์ž‘์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค."
291        else:
292            return f"# ์š”์•ฝ\n\n{research_results}"
293
294
295class ReviewerAgent:
296    """๊ฒ€ํ†  ์—์ด์ „ํŠธ"""
297
298    def __init__(self):
299        self.name = "Reviewer"
300
301    def review(self, document: str) -> str:
302        """๋ฌธ์„œ ๊ฒ€ํ†  (์‹œ๋ฎฌ๋ ˆ์ด์…˜)"""
303        issues = []
304        if len(document) < 100:
305            issues.append("๋‚ด์šฉ์ด ๋„ˆ๋ฌด ์งง์Šต๋‹ˆ๋‹ค.")
306        if "์ฐธ๊ณ ๋ฌธํ—Œ" not in document:
307            issues.append("์ฐธ๊ณ ๋ฌธํ—Œ์ด ์—†์Šต๋‹ˆ๋‹ค.")
308
309        if issues:
310            return "๊ฒ€ํ†  ๊ฒฐ๊ณผ:\n" + "\n".join(f"- {issue}" for issue in issues)
311        return "๊ฒ€ํ†  ๊ฒฐ๊ณผ: ์ˆ˜์ • ํ•„์š” ์—†์Œ"
312
313
314class MultiAgentSystem:
315    """๋ฉ€ํ‹ฐ ์—์ด์ „ํŠธ ์‹œ์Šคํ…œ"""
316
317    def __init__(self):
318        self.researcher = ResearcherAgent()
319        self.writer = WriterAgent()
320        self.reviewer = ReviewerAgent()
321
322    def create_document(self, topic: str) -> str:
323        """๋ฌธ์„œ ์ƒ์„ฑ ํŒŒ์ดํ”„๋ผ์ธ"""
324        print(f"\nํ† ํ”ฝ: {topic}")
325
326        # 1. ์—ฐ๊ตฌ
327        print(f"\n[{self.researcher.name}] ์—ฐ๊ตฌ ์ค‘...")
328        research = self.researcher.research(topic)
329        print(f"์—ฐ๊ตฌ ๊ฒฐ๊ณผ: {research[:50]}...")
330
331        # 2. ์ž‘์„ฑ
332        print(f"\n[{self.writer.name}] ์ž‘์„ฑ ์ค‘...")
333        document = self.writer.write(research)
334        print(f"์ž‘์„ฑ ๊ฒฐ๊ณผ: {document[:50]}...")
335
336        # 3. ๊ฒ€ํ† 
337        print(f"\n[{self.reviewer.name}] ๊ฒ€ํ†  ์ค‘...")
338        review = self.reviewer.review(document)
339        print(f"๊ฒ€ํ†  ๊ฒฐ๊ณผ: {review}")
340
341        return document
342
343
344# ํ…Œ์ŠคํŠธ
345system = MultiAgentSystem()
346doc = system.create_document("์ธ๊ณต์ง€๋Šฅ์˜ ์—ญ์‚ฌ")
347print(f"\n์ตœ์ข… ๋ฌธ์„œ:\n{doc}")
348
349
350# ============================================
351# 5. LangChain Agent ์ฝ”๋“œ ์˜ˆ์‹œ
352# ============================================
353print("\n" + "=" * 60)
354print("[5] LangChain Agent ์ฝ”๋“œ ์˜ˆ์‹œ")
355print("-" * 40)
356
357langchain_code = '''
358from langchain_openai import ChatOpenAI
359from langchain.agents import AgentExecutor, create_openai_tools_agent
360from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
361from langchain.tools import tool
362
363# LLM
364llm = ChatOpenAI(model="gpt-4", temperature=0)
365
366# ๋„๊ตฌ ์ •์˜
367@tool
368def calculator(expression: str) -> str:
369    """์ˆ˜ํ•™ ๊ณ„์‚ฐ. ์ž…๋ ฅ: ์ˆ˜ํ•™ ํ‘œํ˜„์‹ (์˜ˆ: '2 + 3 * 4')"""
370    return str(eval(expression))
371
372@tool
373def get_current_time() -> str:
374    """ํ˜„์žฌ ์‹œ๊ฐ„ ๋ฐ˜ํ™˜"""
375    from datetime import datetime
376    return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
377
378tools = [calculator, get_current_time]
379
380# ํ”„๋กฌํ”„ํŠธ
381prompt = ChatPromptTemplate.from_messages([
382    ("system", "๋‹น์‹ ์€ ๋„์›€์ด ๋˜๋Š” AI์ž…๋‹ˆ๋‹ค."),
383    MessagesPlaceholder(variable_name="chat_history", optional=True),
384    ("human", "{input}"),
385    MessagesPlaceholder(variable_name="agent_scratchpad"),
386])
387
388# ์—์ด์ „ํŠธ ์ƒ์„ฑ
389agent = create_openai_tools_agent(llm, tools, prompt)
390agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
391
392# ์‹คํ–‰
393result = agent_executor.invoke({"input": "ํ˜„์žฌ ์‹œ๊ฐ„๊ณผ 15 + 27์˜ ๊ฒฐ๊ณผ๋ฅผ ์•Œ๋ ค์ค˜"})
394print(result["output"])
395'''
396print(langchain_code)
397
398
399# ============================================
400# 6. ์ž์œจ ์—์ด์ „ํŠธ (AutoGPT ์Šคํƒ€์ผ)
401# ============================================
402print("\n" + "=" * 60)
403print("[6] ์ž์œจ ์—์ด์ „ํŠธ (AutoGPT ์Šคํƒ€์ผ)")
404print("-" * 40)
405
406class AutoGPTLikeAgent:
407    """AutoGPT ์Šคํƒ€์ผ ์ž์œจ ์—์ด์ „ํŠธ"""
408
409    def __init__(self, tools: List[Tool], goals: List[str]):
410        self.tools = {t.name: t for t in tools}
411        self.goals = goals
412        self.memory = []
413        self.completed_tasks = []
414
415    def plan(self) -> Dict[str, Any]:
416        """๋‹ค์Œ ์ž‘์—… ๊ณ„ํš (๊ทœ์น™ ๊ธฐ๋ฐ˜ ์‹œ๋ฎฌ๋ ˆ์ด์…˜)"""
417        # ์•„์ง ๋‹ฌ์„ฑํ•˜์ง€ ์•Š์€ ๋ชฉํ‘œ ํ™•์ธ
418        remaining_goals = [g for g in self.goals if g not in self.completed_tasks]
419
420        if not remaining_goals:
421            return {"task": "COMPLETE", "summary": "๋ชจ๋“  ๋ชฉํ‘œ ๋‹ฌ์„ฑ!"}
422
423        current_goal = remaining_goals[0]
424
425        # ๊ฐ„๋‹จํ•œ ๊ทœ์น™ ๊ธฐ๋ฐ˜ ๊ณ„ํš
426        if "๋‚ ์”จ" in current_goal:
427            return {
428                "task": current_goal,
429                "tool": "get_weather",
430                "input": "์„œ์šธ"
431            }
432        elif "๊ณ„์‚ฐ" in current_goal or "์ˆซ์ž" in current_goal:
433            return {
434                "task": current_goal,
435                "tool": "calculator",
436                "input": "100 + 200"
437            }
438        else:
439            return {
440                "task": current_goal,
441                "tool": "search",
442                "input": current_goal
443            }
444
445    def execute(self, plan: Dict[str, Any]) -> Dict[str, Any]:
446        """๊ณ„ํš ์‹คํ–‰"""
447        if plan.get("task") == "COMPLETE":
448            return {"status": "complete", "summary": plan["summary"]}
449
450        tool_name = plan.get("tool")
451        tool_input = plan.get("input")
452
453        if tool_name in self.tools:
454            result = self.tools[tool_name].run(tool_input)
455            return {"status": "success", "result": result}
456
457        return {"status": "error", "message": f"Unknown tool: {tool_name}"}
458
459    def run(self, max_iterations: int = 5) -> str:
460        """์—์ด์ „ํŠธ ์‹คํ–‰"""
461        print(f"\n๋ชฉํ‘œ: {self.goals}")
462        print("-" * 30)
463
464        for i in range(max_iterations):
465            print(f"\n=== Iteration {i + 1} ===")
466
467            # ๊ณ„ํš
468            plan = self.plan()
469            print(f"Plan: {plan}")
470
471            if plan.get("task") == "COMPLETE":
472                print(f"์™„๋ฃŒ: {plan['summary']}")
473                return plan["summary"]
474
475            # ์‹คํ–‰
476            result = self.execute(plan)
477            print(f"Result: {result}")
478
479            # ๋ฉ”๋ชจ๋ฆฌ ๋ฐ ์™„๋ฃŒ ๋ชฉ๋ก ์—…๋ฐ์ดํŠธ
480            self.memory.append({"plan": plan, "result": result})
481            if result["status"] == "success":
482                self.completed_tasks.append(plan["task"])
483
484        return "์ตœ๋Œ€ ๋ฐ˜๋ณต ํšŸ์ˆ˜ ๋„๋‹ฌ"
485
486
487# ํ…Œ์ŠคํŠธ
488auto_agent = AutoGPTLikeAgent(
489    tools=tools,
490    goals=["์„œ์šธ ๋‚ ์”จ ํ™•์ธ", "๊ฐ„๋‹จํ•œ ๊ณ„์‚ฐ"]
491)
492auto_agent.run()
493
494
495# ============================================
496# ์ •๋ฆฌ
497# ============================================
498print("\n" + "=" * 60)
499print("LLM ์—์ด์ „ํŠธ ์ •๋ฆฌ")
500print("=" * 60)
501
502summary = """
503LLM ์—์ด์ „ํŠธ ํ•ต์‹ฌ ๊ฐœ๋…:
504
5051. ReAct ํŒจํ„ด:
506   Thought -> Action -> Observation -> ... -> Final Answer
507
5082. ๋„๊ตฌ ์ •์˜:
509   - ์ด๋ฆ„: ๊ณ ์œ  ์‹๋ณ„์ž
510   - ์„ค๋ช…: LLM์ด ์‚ฌ์šฉ ์‹œ์  ํŒ๋‹จ์šฉ
511   - ํ•จ์ˆ˜: ์‹ค์ œ ์‹คํ–‰ ๋กœ์ง
512
5133. Function Calling (OpenAI):
514   - tools ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ํ•จ์ˆ˜ ์ •์˜
515   - tool_choice="auto"๋กœ ์ž๋™ ์„ ํƒ
516   - ๊ฒฐ๊ณผ๋ฅผ role="tool"๋กœ ์ „๋‹ฌ
517
5184. ๋ฉ€ํ‹ฐ ์—์ด์ „ํŠธ:
519   - ์—ญํ•  ๋ถ„๋‹ด (์—ฐ๊ตฌ, ์ž‘์„ฑ, ๊ฒ€ํ† )
520   - ํŒŒ์ดํ”„๋ผ์ธ ์—ฐ๊ฒฐ
521   - ํ˜‘์—… ํ”„๋กœํ† ์ฝœ
522
5235. ์ž์œจ ์—์ด์ „ํŠธ:
524   - ๋ชฉํ‘œ ๊ธฐ๋ฐ˜ ๊ณ„ํš
525   - ๋ฉ”๋ชจ๋ฆฌ ์œ ์ง€
526   - ๋ฐ˜๋ณต์  ์‹คํ–‰
527
528์—์ด์ „ํŠธ ์„ค๊ณ„ ์ฒดํฌ๋ฆฌ์ŠคํŠธ:
529โ–ก ๋ช…ํ™•ํ•œ ๋„๊ตฌ ์„ค๋ช…
530โ–ก ์—๋Ÿฌ ์ฒ˜๋ฆฌ
531โ–ก ๋ฌดํ•œ ๋ฃจํ”„ ๋ฐฉ์ง€ (max_steps)
532โ–ก ์•ˆ์ „ ์žฅ์น˜ (์œ„ํ—˜ํ•œ ์ž‘์—… ์ œํ•œ)
533โ–ก ๋กœ๊น… ๋ฐ ๋””๋ฒ„๊น…
534"""
535print(summary)