์ด๋ฒˆ ํŽธ์€ **โ€œ์š”์ฒญ์„ ๋ฐ›์•„ ๋„๊ตฌ๋ฅผ ํ˜ธ์ถœํ•˜๊ณ , ๊ฒฐ๊ณผ๋ฅผ ๊ฒ€์ฆํ•ด PASS/RETRY๋ฅผ ๋‚ด๋Š” QA ๋ฏธ๋‹ˆ ํ”„๋กœ์ ํŠธโ€**๋ฅผ ์žฌํ˜„ํ•œ๋‹ค.
ํ•ต์‹ฌ์€ CodeAgent + @tool + ํ’ˆ์งˆ ๊ฒŒ์ดํŠธ๋ฅผ ๋ฌถ์–ด, ๊ฒฐ๊ณผ๋ฅผ ์ž๋™์œผ๋กœ ๊ฒ€์ฆ ๊ฐ€๋Šฅํ•œ ํ˜•ํƒœ(JSON)๋กœ ๋‚จ๊ธฐ๋Š” ๊ฒƒ์ด๋‹ค.

  • ์ด์ „ ํŽธ: ๐Ÿค— 30. ๋ณธํŽธ 18
  • ๋‹ค์Œ ํŽธ ์˜ˆ๊ณ : ์‹ค์ŠตํŽธ 09-2 (๋‹ค์ค‘ ์ž…๋ ฅ ๋ฐฐ์น˜ ํ‰๊ฐ€ + ์‹คํŒจ ์ผ€์ด์Šค ๋ฆฌํฌํŠธ)
flowchart TD
  A[task.txt ์ž…๋ ฅ] --> B[CodeAgent]
  B --> C[tool: normalize_request]
  C --> D[tool: make_answer]
  D --> E[tool: validate_answer]
  E --> F{valid?}
  F -- yes --> G[qa_result.json ์ €์žฅ]
  F -- no --> H[RETRY ์‚ฌ์œ  ๊ธฐ๋ก]
  H --> G

0) ์‹ค์Šต ๋ชฉํ‘œ

task.txt๋ฅผ ์ž…๋ ฅํ•˜๋ฉด qa_result.json์— ์•„๋ž˜ 4๊ฐœ ํ•„๋“œ๊ฐ€ ์ƒ์„ฑ๋˜๋„๋ก ๋งŒ๋“ ๋‹ค.

  • request_normalized
  • answer
  • valid (true/false)
  • reason

1) ํ™˜๊ฒฝ ์ค€๋น„

  • ๋„๊ตฌ: ํ„ฐ๋ฏธ๋„
  • ์ž…๋ ฅ: Python 3.10+
  • ์‹คํ–‰๋ช…๋ น:
mkdir -p ~/hf-agents-lab31
cd ~/hf-agents-lab31
python3 -m venv .venv
source .venv/bin/activate
pip install -U "smolagents[toolkit]" litellm
  • ์„ฑ๊ณตํŒ์ •:
python -V
pip show smolagents | head -n 5

2) ๋ชจ๋ธ ์„ค์ •

  • ๋„๊ตฌ: ํ„ฐ๋ฏธ๋„
  • ์ž…๋ ฅ: API ํ‚ค, ๋ชจ๋ธ ID
  • ์‹คํ–‰๋ช…๋ น:
export OPENAI_API_KEY="YOUR_API_KEY"
export MODEL_ID="openai/gpt-4o-mini"
  • ์„ฑ๊ณตํŒ์ •:
python - <<'PY'
import os
print('MODEL_ID =', os.getenv('MODEL_ID'))
print('OPENAI_API_KEY set =', bool(os.getenv('OPENAI_API_KEY')))
PY

3) ์ž…๋ ฅ ๋ฐ์ดํ„ฐ ์ค€๋น„

  • ๋„๊ตฌ: ํ„ฐ๋ฏธ๋„
  • ์ž…๋ ฅ: ์‚ฌ์šฉ์ž ์š”์ฒญ ์ƒ˜ํ”Œ
  • ์‹คํ–‰๋ช…๋ น:
cat > task.txt <<'TXT'
๋‹ค์Œ์ฃผ ์›”์š”์ผ ์˜ค์ „ 9์‹œ์— ์ œํ’ˆ ๋ฐ๋ชจ ํšŒ์˜ ์žก์•„์ค˜. ์ฐธ์„์ž๋Š” minji@acme.ai, dev@acme.ai์•ผ.
TXT
  • ์„ฑ๊ณตํŒ์ •:
cat task.txt

4) ๋ฏธ๋‹ˆ ํ”„๋กœ์ ํŠธ ์ฝ”๋“œ ์ž‘์„ฑ

  • ๋„๊ตฌ: ์—๋””ํ„ฐ/ํ„ฐ๋ฏธ๋„
  • ์ž…๋ ฅ: ์•„๋ž˜ ์ฝ”๋“œ
  • ์‹คํ–‰๋ช…๋ น:
cat > lab31_tool_validation_agent.py <<'PY'
from __future__ import annotations
 
import json
import os
from pathlib import Path
 
from smolagents import CodeAgent, LiteLLMModel, tool
 
INPUT_PATH = Path("task.txt")
OUTPUT_PATH = Path("qa_result.json")
 
 
@tool
def normalize_request(text: str) -> str:
    """์š”์ฒญ ๋ฌธ์žฅ์„ ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ํ•œ ์ค„ ํฌ๋งท์œผ๋กœ ์ •๊ทœํ™”ํ•œ๋‹ค."""
    return " ".join(text.strip().split())
 
 
@tool
def make_answer(normalized: str) -> str:
    """์ •๊ทœํ™”๋œ ์š”์ฒญ ๊ธฐ๋ฐ˜์˜ ์‹คํ–‰ ์ œ์•ˆ ๋‹ต๋ณ€์„ ๋งŒ๋“ ๋‹ค."""
    return f"ํšŒ์˜ ์ƒ์„ฑ ์š”์ฒญ ์ ‘์ˆ˜: {normalized}"
 
 
@tool
def validate_answer(answer: str) -> str:
    """๋‹ต๋ณ€ ์ตœ์†Œ ํ’ˆ์งˆ์„ ๊ฒ€์ฆํ•˜๊ณ  JSON ๋ฌธ์ž์—ด์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค."""
    valid = all(k in answer for k in ["ํšŒ์˜", "์š”์ฒญ", "์ ‘์ˆ˜"])
    reason = "PASS" if valid else "ํ•„์ˆ˜ ํ‚ค์›Œ๋“œ ๋ˆ„๋ฝ"
    return json.dumps({"valid": valid, "reason": reason}, ensure_ascii=False)
 
 
def strip_code_fence(text: str) -> str:
    t = text.strip()
    if t.startswith("```"):
        lines = t.splitlines()
        if lines and lines[0].startswith("```"):
            lines = lines[1:]
        if lines and lines[-1].startswith("```"):
            lines = lines[:-1]
        t = "\n".join(lines).strip()
    return t
 
 
def main() -> None:
    raw = INPUT_PATH.read_text(encoding="utf-8")
 
    model = LiteLLMModel(model_id=os.getenv("MODEL_ID", "openai/gpt-4o-mini"))
    agent = CodeAgent(
        tools=[normalize_request, make_answer, validate_answer],
        model=model,
        max_steps=6,
        additional_authorized_imports=["json"],
    )
 
    prompt = f"""
์•„๋ž˜ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•ด ์ตœ์ข… JSON๋งŒ ๋ฐ˜ํ™˜ํ•˜๋ผ.
 
์š”์ฒญ:
{raw}
 
ํ•„์ˆ˜ ์ ˆ์ฐจ:
1) normalize_request ํ˜ธ์ถœ
2) make_answer ํ˜ธ์ถœ
3) validate_answer ํ˜ธ์ถœ
 
์ตœ์ข… ์ถœ๋ ฅ ์Šคํ‚ค๋งˆ:
{{
  "request_normalized": "string",
  "answer": "string",
  "valid": true,
  "reason": "string"
}}
 
์ฃผ์˜:
- JSON ์™ธ ํ…์ŠคํŠธ ๊ธˆ์ง€
"""
 
    result = agent.run(prompt)
    obj = json.loads(strip_code_fence(str(result)))
 
    OUTPUT_PATH.write_text(json.dumps(obj, ensure_ascii=False, indent=2), encoding="utf-8")
    print(f"saved: {OUTPUT_PATH}")
 
 
if __name__ == "__main__":
    main()
PY
  • ์„ฑ๊ณตํŒ์ •:
    • CodeAgent, @tool, validate_answer ํ•จ์ˆ˜ ์กด์žฌ

5) ์‹คํ–‰

  • ๋„๊ตฌ: Python
  • ์ž…๋ ฅ: task.txt
  • ์‹คํ–‰๋ช…๋ น:
python lab31_tool_validation_agent.py
cat qa_result.json
  • ์„ฑ๊ณตํŒ์ •:
    • saved: qa_result.json ์ถœ๋ ฅ
    • JSON์— 4๊ฐœ ํ•„๋“œ ๋ชจ๋‘ ์กด์žฌ

6) ํ’ˆ์งˆ ๊ฒŒ์ดํŠธ ๊ฒ€์ฆ

  • ๋„๊ตฌ: Python
  • ์ž…๋ ฅ: qa_result.json
  • ์‹คํ–‰๋ช…๋ น:
python - <<'PY'
import json
obj = json.load(open('qa_result.json', encoding='utf-8'))
 
for key in ['request_normalized', 'answer', 'valid', 'reason']:
    assert key in obj, f'missing: {key}'
 
assert isinstance(obj['request_normalized'], str) and obj['request_normalized'].strip()
assert isinstance(obj['answer'], str) and obj['answer'].strip()
assert isinstance(obj['valid'], bool)
assert isinstance(obj['reason'], str)
 
print('PASS: ํ’ˆ์งˆ ๊ฒŒ์ดํŠธ ํ†ต๊ณผ')
PY
  • ์„ฑ๊ณตํŒ์ •:
    • PASS: ํ’ˆ์งˆ ๊ฒŒ์ดํŠธ ํ†ต๊ณผ

ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… (์‹ค์ „ 4์„ )

  1. AuthenticationError / ํ˜ธ์ถœ ์‹คํŒจ
  • ์›์ธ: OPENAI_API_KEY ๋ฏธ์„ค์ • ๋˜๋Š” ๋ชจ๋ธ ID ์˜คํƒ€
  • ์กฐ์น˜:
python - <<'PY'
import os
print(bool(os.getenv('OPENAI_API_KEY')), os.getenv('MODEL_ID'))
PY
  1. ModuleNotFoundError: smolagents
  • ์›์ธ: ๊ฐ€์ƒํ™˜๊ฒฝ ๋ฏธํ™œ์„ฑํ™”
  • ์กฐ์น˜:
source .venv/bin/activate
pip install -U "smolagents[toolkit]" litellm
  1. json.loads ํŒŒ์‹ฑ ์‹คํŒจ
  • ์›์ธ: ๋ชจ๋ธ์ด JSON ์™ธ ์„ค๋ช… ๋ฌธ์žฅ ์ถœ๋ ฅ
  • ์กฐ์น˜: ํ”„๋กฌํ”„ํŠธ์— JSON ์™ธ ํ…์ŠคํŠธ ๊ธˆ์ง€ ์œ ์ง€, strip_code_fence() ์œ ์ง€
  1. valid๊ฐ€ ๊ณ„์† false
  • ์›์ธ: make_answer ์ถœ๋ ฅ ํ˜•์‹ ๋ณ€๊ฒฝ์œผ๋กœ ๊ฒ€์ฆ ๊ทœ์น™ ๋ถˆ์ผ์น˜
  • ์กฐ์น˜: validate_answer ํ‚ค์›Œ๋“œ ๊ทœ์น™๊ณผ make_answer ํฌ๋งท์„ ๊ฐ™์ด ์ˆ˜์ •

์ฒดํฌ๋ฆฌ์ŠคํŠธ

  • ํ™˜๊ฒฝ ๊ตฌ์„ฑ ์™„๋ฃŒ
  • lab31_tool_validation_agent.py ์‹คํ–‰ ์„ฑ๊ณต
  • qa_result.json ์ƒ์„ฑ ํ™•์ธ
  • ํ’ˆ์งˆ ๊ฒŒ์ดํŠธ PASS
  • ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… 3๊ฐœ ์ด์ƒ ์ ๊ฒ€

์ฐธ๊ณ  ๋งํฌ (์šฐ์„ ์ˆœ์œ„)

  1. https://github.com/huggingface/agents-course
  2. https://huggingface.co/learn/agents-course
  3. https://huggingface.co/docs/smolagents

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

์ด ๋ฌธ์„œ๋Š” ์ƒ์„ฑํ˜• AI๋กœ ์ดˆ์•ˆ์„ ์ž‘์„ฑํ•˜๊ณ , ์‚ฌ๋žŒ์ด ์‹ค์Šต ์žฌํ˜„์„ฑ(๋„๊ตฌ/์ž…๋ ฅ/์‹คํ–‰๋ช…๋ น/์„ฑ๊ณตํŒ์ •), ์˜ค๋ฅ˜ ๋Œ€์‘, ๋งํฌ ๋ฐ ๋‚ด๋ถ€ ์—ฐ๊ฒฐ์„ ๊ฒ€ํ† ํ•ด ํ™•์ •ํ–ˆ๋‹ค.