์ด๋ฒˆ 2ํŽธ์€ ์—์ด์ „ํŠธ๊ฐ€ ์‹ค์ œ๋กœ ์ผ์„ ํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ํ•ต์‹ฌ์ธ Tool Calling์„ ๋‹ค๋ฃฌ๋‹ค. ๋ชฉํ‘œ๋Š” โ€œ๋„๊ตฌ๋ฅผ ๋ถ™์ธ๋‹คโ€ ์ˆ˜์ค€์ด ์•„๋‹ˆ๋ผ, ์‹คํŒจํ•˜์ง€ ์•Š๋Š” ๋„๊ตฌ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์„ค๊ณ„ํ•˜๊ณ  ์‹คํ–‰ ๋ฃจํ”„์— ์•ˆ์ •์ ์œผ๋กœ ์—ฐ๊ฒฐํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

flowchart LR
  A[User Goal] --> B[Planner]
  B --> C[Tool Router]
  C --> D[Tool 1: Search]
  C --> E[Tool 2: Fetch]
  C --> F[Tool 3: Summarize]
  D --> G[Observation]
  E --> G
  F --> G
  G --> H[Final Answer]

0) 30์ดˆ ์š”์•ฝ

  • Tool Calling์€ โ€œ๋ชจ๋ธ์ด ๋ฐ”๊นฅ ์„ธ์ƒ๊ณผ ์—ฐ๊ฒฐ๋˜๋Š” ์ž…์ถœ๋ ฅ ๊ทœ์•ฝโ€์ด๋‹ค.
  • ์„ฑ๋Šฅ๋ณด๋‹ค ๋จผ์ € ๋„๊ตฌ ๊ณ„์•ฝ(schema), ์‹คํŒจ ์ฒ˜๋ฆฌ, ๊ฒ€์ฆ ๊ทœ์น™์„ ๊ณ ์ •ํ•ด์•ผ ์šด์˜์ด ๋œ๋‹ค.
  • ์˜ค๋Š˜ ์‚ฐ์ถœ๋ฌผ: ๋„๊ตฌ 3๊ฐœ + ์‹คํ–‰ ๋ฃจํ”„ + ์‹คํŒจ ๋Œ€์‘์ด ํฌํ•จ๋œ ๋ฏธ๋‹ˆ ์—์ด์ „ํŠธ ์„ค๊ณ„/์‹คํ–‰๋ณธ.

1) ์™œ Tool Calling์ด ์ค‘์š”ํ• ๊นŒ

LLM์ด ์•„๋ฌด๋ฆฌ ๋˜‘๋˜‘ํ•ด๋„, ์ตœ์‹  ๋ฐ์ดํ„ฐยท์‚ฌ๋‚ด ๋ฐ์ดํ„ฐยท์‹คํ–‰ ๋ช…๋ น์€ ์Šค์Šค๋กœ ๊ฐ€์ ธ์˜ค์ง€ ๋ชปํ•œ๋‹ค. Tool Calling์€ ์ด ํ•œ๊ณ„๋ฅผ ๋„˜๋Š” ๊ตฌ์กฐ๋‹ค.

ํ•ต์‹ฌ ํšจ๊ณผ:

  1. ์ตœ์‹ ์„ฑ ํ™•๋ณด: ๊ฒ€์ƒ‰/์กฐํšŒ ๋„๊ตฌ๋กœ ์‹ค์‹œ๊ฐ„ ์ •๋ณด ๋ฐ˜์˜
  2. ์žฌํ˜„์„ฑ ํ™•๋ณด: ๊ฐ™์€ ์ž…๋ ฅ์ด๋ฉด ๊ฐ™์€ ์ ˆ์ฐจ๋กœ ์‹คํ–‰
  3. ์ฑ…์ž„์„ฑ ํ™•๋ณด: ์–ด๋–ค ๋„๊ตฌ๋ฅผ ์™œ ํ˜ธ์ถœํ–ˆ๋Š”์ง€ ์ถ”์  ๊ฐ€๋Šฅ

2) ๋„๊ตฌ ์„ค๊ณ„ ์›์น™ (์‹ค๋ฌด์šฉ)

์›์น™ 1) ์ž…๋ ฅ์€ ์ข๊ฒŒ, ์ถœ๋ ฅ์€ ๋ช…ํ™•ํ•˜๊ฒŒ

  • ์ž…๋ ฅ ํ•„๋“œ๋Š” ์ตœ์†Œํ™” (query, limit์ฒ˜๋Ÿผ)
  • ์ถœ๋ ฅ์€ ๊ตฌ์กฐํ™” (title, url, snippet)

์›์น™ 2) ์‹คํŒจ๋ฅผ ์ •์ƒ ํ”Œ๋กœ์šฐ๋กœ ์ทจ๊ธ‰

  • timeout, empty result, invalid input์„ ์˜ˆ์™ธ๊ฐ€ ์•„๋‹ˆ๋ผ ๋ถ„๊ธฐ ์ฒ˜๋ฆฌ

์›์น™ 3) ๋„๊ตฌ๋ณ„ ์„ฑ๊ณต ํŒ์ • ๊ธฐ์ค€ ์ •์˜

  • Search ์„ฑ๊ณต: ์ตœ์†Œ 3๊ฐœ ์œ ํšจ ๋งํฌ
  • Fetch ์„ฑ๊ณต: ๋ณธ๋ฌธ ๊ธธ์ด n์ž ์ด์ƒ
  • Summarize ์„ฑ๊ณต: ๊ธˆ์ง€์–ด/๊ณผ์žฅ์–ด ํ•„ํ„ฐ ํ†ต๊ณผ
sequenceDiagram
  participant U as User
  participant A as Agent
  participant S as Search Tool
  participant F as Fetch Tool
  participant M as Summarize Tool

  U->>A: ์˜ค๋Š˜ AI ๋‰ด์Šค ๋ธŒ๋ฆฌํ•‘
  A->>S: query="AI news", limit=5
  S-->>A: urls[5]
  A->>F: fetch(urls)
  F-->>A: articles
  A->>M: summarize(articles)
  M-->>A: brief
  A-->>U: TOP3 + ๊ทผ๊ฑฐ ๋งํฌ

3) ์‹ค์Šต A โ€” ๋„๊ตฌ ๊ณ„์•ฝ์„œ ๋งŒ๋“ค๊ธฐ

์•„๋ž˜์ฒ˜๋Ÿผ ๋จผ์ € ๋„๊ตฌ ์Šคํ‚ค๋งˆ๋ถ€ํ„ฐ ์ ๋Š”๋‹ค.

{
  "tool": "web_search",
  "input": {
    "query": "string",
    "limit": "integer(1~10)"
  },
  "output": [
    {"title": "string", "url": "string", "snippet": "string"}
  ],
  "failure": ["timeout", "empty", "rate_limit"]
}

์„ฑ๊ณต ํŒ์ •:

  • ์ž…๋ ฅ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ์กด์žฌ
  • ์ถœ๋ ฅ ํ•„๋“œ ๊ณ ์ •
  • ์‹คํŒจ ์ฝ”๋“œ 3์ข… ์ •์˜

4) ์‹ค์Šต B โ€” Python ๋ฏธ๋‹ˆ ๋ฃจํ”„ ๊ตฌํ˜„

4-0) 5๋ถ„ ํ€ต์Šคํƒ€ํŠธ (๋จผ์ € 1ํšŒ ์„ฑ๊ณต)

  1. ๋ฌธ์„œ ํ•˜๋‹จ ์‹ค์Šต ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ์—์„œ hf-agents-day2-tool-calling.zip์„ ๋ฐ›๋Š”๋‹ค.
  2. ์••์ถ• ํ•ด์ œ ํ›„ ํด๋”๋กœ ์ด๋™ํ•œ๋‹ค.
  3. ์•„๋ž˜ 3์ค„์„ ์‹คํ–‰ํ•œ๋‹ค.
python3 -m venv .venv
source .venv/bin/activate   # Windows: .venv\Scripts\activate
pip install -r requirements.txt && python day2_tool_calling.py

์ค€๋น„

  • ๋„๊ตฌ: Python 3.10+
  • ํŒŒ์ผ: day2_tool_calling.py

์‹คํ–‰

python day2_tool_calling.py

์‹คํ–‰ ๊ฒฐ๊ณผ ์˜ˆ์‹œ (์ •์ƒ/์‹คํŒจ)

์ •์ƒ ์˜ˆ์‹œ:

{'ok': True, 'count': 3, 'result': [...]} 

์‹คํŒจ ์˜ˆ์‹œ:

{'ok': False, 'stage': 'search', 'error': 'invalid_query'}

์ฝ”๋“œ

from dataclasses import dataclass
from typing import Dict, List, Optional
 
@dataclass
class ToolResult:
    ok: bool
    data: List[Dict]
    error: Optional[str] = None
 
 
def web_search(query: str, limit: int = 5) -> ToolResult:
    if not query or not query.strip():
        return ToolResult(ok=False, data=[], error="invalid_query")
 
    mock_results = [
        {"title": "AI News A", "url": "https://example.com/a", "snippet": "update A"},
        {"title": "AI News B", "url": "https://example.com/b", "snippet": "update B"},
        {"title": "AI News C", "url": "https://example.com/c", "snippet": "update C"},
        {"title": "AI News D", "url": "https://example.com/d", "snippet": "update D"},
        {"title": "AI News E", "url": "https://example.com/e", "snippet": "update E"},
    ]
    return ToolResult(ok=True, data=mock_results[:limit])
 
 
def validate(items: List[Dict]) -> ToolResult:
    if len(items) < 3:
        return ToolResult(ok=False, data=[], error="insufficient_results")
 
    valid = [x for x in items if x.get("url", "").startswith("https://")]
    if len(valid) < 3:
        return ToolResult(ok=False, data=[], error="invalid_url_format")
 
    return ToolResult(ok=True, data=valid)
 
 
def summarize(items: List[Dict]) -> ToolResult:
    if not items:
        return ToolResult(ok=False, data=[], error="empty_input")
 
    summaries = [
        {
            "summary": f"{item['title']} - ํ•ต์‹ฌ ํ•œ ์ค„ ์š”์•ฝ",
            "url": item["url"],
        }
        for item in items[:3]
    ]
    return ToolResult(ok=True, data=summaries)
 
 
def run_briefing(query: str):
    searched = web_search(query, limit=5)
    if not searched.ok:
        return {"ok": False, "stage": "search", "error": searched.error}
 
    validated = validate(searched.data)
    if not validated.ok:
        return {"ok": False, "stage": "validate", "error": validated.error}
 
    summarized = summarize(validated.data)
    if not summarized.ok:
        return {"ok": False, "stage": "summarize", "error": summarized.error}
 
    return {"ok": True, "count": len(summarized.data), "result": summarized.data}
 
 
if __name__ == "__main__":
    output = run_briefing("today ai updates")
    print(output)

์„ฑ๊ณต ํŒ์ •:

  • ok=True ๊ฒฐ๊ณผ ์ถœ๋ ฅ
  • count=3 ํ™•์ธ
  • ์‹คํŒจ ์‹œ stage, error๊ฐ€ ์ถœ๋ ฅ๋จ

5) ์‹ค์Šต C โ€” HF/smolagents ์—ฐ๊ฒฐ ํฌ์ธํŠธ

smolagents๋ฅผ ์“ธ ๋•Œ๋„ ํ•ต์‹ฌ์€ ๋™์ผํ•˜๋‹ค. ์ค‘์š”ํ•œ ๊ฑด ๋ชจ๋ธ์ด ์•„๋‹ˆ๋ผ ํˆด์˜ ๊ณ„์•ฝ ํ’ˆ์งˆ์ด๋‹ค.

# pip install smolagents
from smolagents import tool
 
@tool
def get_stock_price(symbol: str) -> str:
    """์ฃผ์‹ ์‹ฌ๋ณผ์„ ์ž…๋ ฅ๋ฐ›์•„ ๊ฐ€๊ฒฉ ์š”์•ฝ์„ ๋ฐ˜ํ™˜"""
    # ์‹ค์ œ๋กœ๋Š” ์™ธ๋ถ€ API ํ˜ธ์ถœ
    return f"{symbol}: 123.45 USD"

์ฒดํฌํฌ์ธํŠธ:

  • ํ•จ์ˆ˜ ์„ค๋ช…(docstring)์„ ๋ชจ๋ธ์ด ์ฝ๊ณ  ์„ ํƒํ•œ๋‹ค.
  • ํ•จ์ˆ˜ ์ž…๋ ฅ ํƒ€์ž…์ด ์• ๋งคํ•˜๋ฉด ์˜คํ˜ธ์ถœ์ด ๋Š˜์–ด๋‚œ๋‹ค.

6) ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… TOP 5

  1. ๋„๊ตฌ ๋ฏธํ˜ธ์ถœ: ํ”„๋กฌํ”„ํŠธ์— โ€œ๋„๊ตฌ ์‚ฌ์šฉ ์กฐ๊ฑดโ€์„ ๋ช…์‹œํ•˜์ง€ ์•Š์Œ
  2. ์˜คํ˜ธ์ถœ: ๋„๊ตฌ ์„ค๋ช…์ด ๋ชจํ˜ธํ•จ
  3. ์ถœ๋ ฅ ํŒŒ์‹ฑ ์‹คํŒจ: JSON ์Šคํ‚ค๋งˆ ๊ณ ์ • ์•ˆ ํ•จ
  4. ๋ฌดํ•œ ์žฌ์‹œ๋„: retry ์ƒํ•œ ๋ถ€์žฌ
  5. ์‚ฌ์‹ค ์˜ค๋ฅ˜: ์š”์•ฝ ๊ฒฐ๊ณผ ๊ฒ€์ฆ ๋‹จ๊ณ„ ๋ˆ„๋ฝ

7) ์˜ค๋Š˜ ์ œ์ถœ๋ฌผ

### HF Day2 ์ œ์ถœ๋ฌผ
- ๋ชฉํ‘œ ์ž‘์—…:
- ์‚ฌ์šฉ ๋„๊ตฌ(3๊ฐœ):
- ๋„๊ตฌ ๊ณ„์•ฝ์„œ(schema):
- ์‹คํŒจ ์ฒ˜๋ฆฌ ๊ทœ์น™(์ตœ์†Œ 3๊ฐœ):
- ์‹คํ–‰ ๊ฒฐ๊ณผ(์„ฑ๊ณต/์‹คํŒจ ๋กœ๊ทธ):
- ๊ฐœ์„  ํฌ์ธํŠธ(๋‹ค์Œ ํŽธ ๋ฐ˜์˜):

8) ๋‹ค์Œ ํŽธ ์˜ˆ๊ณ  (03ํŽธ)

03ํŽธ์—์„œ๋Š” Tool Calling์— ๋งž๋Š” ํ”„๋กฌํ”„ํŠธ ์„ค๊ณ„ ํŒจํ„ด์„ ๋‹ค๋ฃฌ๋‹ค.

  • ์‹œ์Šคํ…œ/๊ฐœ๋ฐœ์ž/์‚ฌ์šฉ์ž ์—ญํ•  ๋ถ„๋ฆฌ
  • ๋„๊ตฌ ์„ ํƒ ๊ธฐ์ค€ ๋ฌธ์žฅ ํ…œํ”Œ๋ฆฟ
  • hallucination ์ค„์ด๋Š” ์ถœ๋ ฅ ๊ฐ€๋“œ๋ ˆ์ผ

์ฐธ๊ณ  ๋งํฌ

์‹ค์Šต ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ

ํŒŒ์ผ์ด ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋ฐ”๋กœ ์—ด๋ฆฌ๋ฉด, ์šฐํด๋ฆญ ํ›„ **โ€œ๋‹ค๋ฅธ ์ด๋ฆ„์œผ๋กœ ์ €์žฅโ€**์„ ์„ ํƒํ•ด ๋‹ค์šด๋กœ๋“œํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

์ƒ์„ฑํ˜• AI ํ™œ์šฉ ๊ณ ์ง€

์ด ๋ฌธ์„œ๋Š” ์ƒ์„ฑํ˜• AI๋ฅผ ํ™œ์šฉํ•ด ์ดˆ์•ˆ ์ž‘์„ฑ ๋ฐ ๊ตฌ์กฐํ™”๋ฅผ ์ˆ˜ํ–‰ํ–ˆ์œผ๋ฉฐ, ์ตœ์ข… ๊ณต๊ฐœ ์ „ ์‚ฌ๋žŒ์ด ๊ฒ€ํ† ยท๋ณด์ •ํ•œ๋‹ค.