[Daily morning study] AI Agent์™€ Tool Use ๊ฐœ๋…

#daily morning study

Image


AI Agent๋ž€

AI Agent๋Š” ๋‹จ์ˆœํžˆ ์งˆ๋ฌธ์— ๋‹ตํ•˜๋Š” ๊ฒƒ์„ ๋„˜์–ด, ๋ชฉํ‘œ๋ฅผ ๋‹ฌ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ์Šค์Šค๋กœ ๊ณ„ํš์„ ์„ธ์šฐ๊ณ  ํ–‰๋™(action)์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์‹œ์Šคํ…œ์ด๋‹ค.

์ผ๋ฐ˜ LLM ํ˜ธ์ถœ๊ณผ์˜ ์ฐจ์ด:

๊ตฌ๋ถ„์ผ๋ฐ˜ LLMAI Agent
์ž…๋ ฅํ”„๋กฌํ”„ํŠธ๋ชฉํ‘œ(goal)
์ถœ๋ ฅํ…์ŠคํŠธ ์‘๋‹ตํ–‰๋™ ๊ฒฐ๊ณผ
๋ฐ˜๋ณต๋‹จ๋ฐœ์„ฑ๋ฃจํ”„(loop) ๋ฐ˜๋ณต
๋„๊ตฌ ์‚ฌ์šฉ๋ถˆ๊ฐ€๊ฐ€๋Šฅ (Tool Use)

Agent๋Š” โ€œ์ƒ๊ฐ โ†’ ํ–‰๋™ โ†’ ๊ด€์ฐฐ โ†’ ๋‹ค์‹œ ์ƒ๊ฐโ€ ์˜ ์‚ฌ์ดํด์„ ๋ฐ˜๋ณตํ•˜๋ฉด์„œ ๋ชฉํ‘œ์— ๋„๋‹ฌํ•œ๋‹ค.


ReAct ํŒจํ„ด

Agent์˜ ๋Œ€ํ‘œ์ ์ธ ๋™์ž‘ ๋ฐฉ์‹์ด ReAct(Reasoning + Acting) ํŒจํ„ด์ด๋‹ค.

Thought: ํ˜„์žฌ ์ƒํ™ฉ์„ ๋ถ„์„ํ•˜๊ณ  ๋ฌด์—‡์„ ํ•ด์•ผ ํ• ์ง€ ํŒ๋‹จ
Action: ๋„๊ตฌ๋ฅผ ํ˜ธ์ถœํ•˜๊ฑฐ๋‚˜ ํ–‰๋™ ์ˆ˜ํ–‰
Observation: ํ–‰๋™ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธ
Thought: ๊ฒฐ๊ณผ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ๋‹ค์Œ ๋‹จ๊ณ„ ํŒ๋‹จ
...
Answer: ์ตœ์ข… ๋‹ต ๋„์ถœ

์˜ˆ์‹œ โ€” โ€œ์„œ์šธ ๋‚ ์”จ ์•Œ๋ ค์ค˜โ€๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” Agent:

Thought: ํ˜„์žฌ ์„œ์šธ ๋‚ ์”จ๋ฅผ ๋ชจ๋ฅด๋‹ˆ ๋‚ ์”จ API๋ฅผ ํ˜ธ์ถœํ•ด์•ผ๊ฒ ๋‹ค
Action: weather_tool(city="Seoul")
Observation: {"temp": 28, "condition": "๋ง‘์Œ"}
Thought: ๊ฒฐ๊ณผ๋ฅผ ๋ฐ›์•˜์œผ๋‹ˆ ๋‹ต๋ณ€์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค
Answer: ์„œ์šธ์€ ํ˜„์žฌ 28๋„์ด๋ฉฐ ๋ง‘์Šต๋‹ˆ๋‹ค

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

Tool Use๋Š” LLM์ด ์™ธ๋ถ€ ํ•จ์ˆ˜๋‚˜ API๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ธฐ๋Šฅ์ด๋‹ค.

๋ชจ๋ธ์€ ํ…์ŠคํŠธ๋ฅผ ์ง์ ‘ ์ƒ์„ฑํ•˜๋Š” ๋Œ€์‹ , โ€œ์ด ๋„๊ตฌ๋ฅผ ์ด ์ธ์ž๋กœ ํ˜ธ์ถœํ•˜๋ผโ€๋Š” ๊ตฌ์กฐํ™”๋œ ์ถœ๋ ฅ์„ ๋‚ด๋ณด๋‚ธ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ์ฝ”๋“œ๊ฐ€ ์‹ค์ œ๋กœ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ๋‹ค์‹œ ๋ชจ๋ธ์— ๋„˜๊ธด๋‹ค.

๊ธฐ๋ณธ ํ๋ฆ„

1. ์‚ฌ์šฉ์ž ๋ฉ”์‹œ์ง€ + ๋„๊ตฌ ๋ชฉ๋ก โ†’ LLM
2. LLM์ด ๋„๊ตฌ ํ˜ธ์ถœ ๊ฒฐ์ • โ†’ tool_call ์ถœ๋ ฅ
3. ํ˜ธ์ŠคํŠธ ์ฝ”๋“œ๊ฐ€ ์‹ค์ œ ํ•จ์ˆ˜ ์‹คํ–‰
4. ์‹คํ–‰ ๊ฒฐ๊ณผ โ†’ LLM์— ๋‹ค์‹œ ์ „๋‹ฌ
5. LLM์ด ๊ฒฐ๊ณผ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์ตœ์ข… ์‘๋‹ต ์ƒ์„ฑ

๋„๊ตฌ ์ •์˜ ์˜ˆ์‹œ (Anthropic API)

{
  "name": "get_weather",
  "description": "ํŠน์ • ๋„์‹œ์˜ ํ˜„์žฌ ๋‚ ์”จ๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค",
  "input_schema": {
    "type": "object",
    "properties": {
      "city": {
        "type": "string",
        "description": "๋‚ ์”จ๋ฅผ ์กฐํšŒํ•  ๋„์‹œ ์ด๋ฆ„"
      }
    },
    "required": ["city"]
  }
}

๋ชจ๋ธ์ด ๋„๊ตฌ๋ฅผ ์„ ํƒํ•˜๋Š” ๊ธฐ์ค€

LLM์€ ์งˆ๋ฌธ์˜ ์˜๋„์™€ ๋„๊ตฌ์˜ description์„ ๋งค์นญํ•ด์„œ ํ˜ธ์ถœ ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•œ๋‹ค. ๊ทธ๋ž˜์„œ description์„ ์ž˜ ์ž‘์„ฑํ•˜๋Š” ๊ฒŒ ์ค‘์š”ํ•˜๋‹ค.


Agent์˜ ๋ฉ”๋ชจ๋ฆฌ ๊ตฌ์กฐ

Agent๊ฐ€ ๋ณต์žกํ•œ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•˜๋ ค๋ฉด ์ƒํƒœ(state)๋ฅผ ๊ธฐ์–ตํ•ด์•ผ ํ•œ๋‹ค.

๋ฉ”๋ชจ๋ฆฌ ์ข…๋ฅ˜์„ค๋ช…์˜ˆ์‹œ
In-context memoryLLM์˜ ์ปจํ…์ŠคํŠธ ์œˆ๋„์šฐ ์•ˆ์— ์žˆ๋Š” ์ •๋ณด๋Œ€ํ™” ํžˆ์Šคํ† ๋ฆฌ
External memory์™ธ๋ถ€ DB๋‚˜ ๋ฒกํ„ฐ ์Šคํ† ์–ดRAG ์‹œ์Šคํ…œ
Episodic memory๊ณผ๊ฑฐ ์—ํ”ผ์†Œ๋“œ ๊ธฐ๋กโ€œ์ง€๋‚œ๋ฒˆ์— A๋ฅผ ํ–ˆ์œผ๋‹ˆโ€ฆโ€
Procedural memory์–ด๋–ป๊ฒŒ ํ–‰๋™ํ• ์ง€์— ๋Œ€ํ•œ ์ง€์‹์‹œ์Šคํ…œ ํ”„๋กฌํ”„ํŠธ

๋‹จ๊ธฐ ์ž‘์—…์€ ์ปจํ…์ŠคํŠธ ์œˆ๋„์šฐ๋งŒ์œผ๋กœ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ, ์žฅ๊ธฐ ์ž‘์—…์ด๋‚˜ ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ๋Š” ์™ธ๋ถ€ ๋ฉ”๋ชจ๋ฆฌ(๋ฒกํ„ฐ DB ๋“ฑ)๋ฅผ ๋ถ™์—ฌ์•ผ ํ•œ๋‹ค.


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

๋ณต์žกํ•œ ์ž‘์—…์€ ํ•˜๋‚˜์˜ Agent๊ฐ€ ๋ชจ๋‘ ์ฒ˜๋ฆฌํ•˜๋Š” ๋Œ€์‹  ์—ฌ๋Ÿฌ Agent๋ฅผ ์กฐํ•ฉํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ํ•ด๊ฒฐํ•œ๋‹ค.

Orchestrator Agent
โ”œโ”€โ”€ Research Agent  (๊ฒ€์ƒ‰ ๋‹ด๋‹น)
โ”œโ”€โ”€ Code Agent      (์ฝ”๋“œ ์ž‘์„ฑ ๋‹ด๋‹น)
โ””โ”€โ”€ Review Agent    (๊ฒ€ํ†  ๋‹ด๋‹น)

๊ตฌ์„ฑ ํŒจํ„ด

  • Sequential: Agent A์˜ ์ถœ๋ ฅ์ด Agent B์˜ ์ž…๋ ฅ์œผ๋กœ ์ „๋‹ฌ (ํŒŒ์ดํ”„๋ผ์ธ)
  • Parallel: ์—ฌ๋Ÿฌ Agent๊ฐ€ ๋™์‹œ์— ๋ณ‘๋ ฌ ์‹คํ–‰
  • Hierarchical: Orchestrator๊ฐ€ ํ•˜์œ„ Agent๋“ค์„ ์ง€ํœ˜

๋ฉ€ํ‹ฐ ์—์ด์ „ํŠธ๋Š” ์—ญํ•  ๋ถ„๋ฆฌ๋กœ ๊ฐ Agent๊ฐ€ ๋” ์ง‘์ค‘๋œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๊ณ , ์ถœ๋ ฅ ํ’ˆ์งˆ์ด ์˜ฌ๋ผ๊ฐ„๋‹ค. ๋ฐ˜๋ฉด Agent ๊ฐ„ ํ†ต์‹  ๋น„์šฉ๊ณผ ์˜ค๋ฅ˜ ์ „ํŒŒ ์œ„ํ—˜์ด ์žˆ๋‹ค.


์‹ค์ œ ๊ตฌํ˜„ ์˜ˆ์‹œ (Python, Anthropic SDK)

import anthropic
import json

client = anthropic.Anthropic()

tools = [
    {
        "name": "get_stock_price",
        "description": "ํŠน์ • ์ฃผ์‹์˜ ํ˜„์žฌ ๊ฐ€๊ฒฉ์„ ๊ฐ€์ ธ์˜จ๋‹ค",
        "input_schema": {
            "type": "object",
            "properties": {
                "ticker": {"type": "string", "description": "์ฃผ์‹ ํ‹ฐ์ปค ์‹ฌ๋ณผ (์˜ˆ: AAPL)"}
            },
            "required": ["ticker"]
        }
    }
]

def get_stock_price(ticker: str) -> dict:
    # ์‹ค์ œ๋กœ๋Š” ์™ธ๋ถ€ API ํ˜ธ์ถœ
    prices = {"AAPL": 189.5, "GOOGL": 175.2}
    return {"ticker": ticker, "price": prices.get(ticker, 0)}

messages = [{"role": "user", "content": "์• ํ”Œ ์ฃผ์‹ ํ˜„์žฌ๊ฐ€๊ฐ€ ์–ผ๋งˆ์•ผ?"}]

while True:
    response = client.messages.create(
        model="claude-opus-4-8",
        max_tokens=1024,
        tools=tools,
        messages=messages
    )

    if response.stop_reason == "end_turn":
        print(response.content[0].text)
        break

    # ๋„๊ตฌ ํ˜ธ์ถœ ์ฒ˜๋ฆฌ
    tool_use = next(b for b in response.content if b.type == "tool_use")
    result = get_stock_price(**tool_use.input)

    messages.append({"role": "assistant", "content": response.content})
    messages.append({
        "role": "user",
        "content": [{
            "type": "tool_result",
            "tool_use_id": tool_use.id,
            "content": json.dumps(result)
        }]
    })

Agent ์„ค๊ณ„ ์‹œ ๊ณ ๋ ค์‚ฌํ•ญ

๋ฃจํ”„ ์ข…๋ฃŒ ์กฐ๊ฑด์„ ๋ช…ํ™•ํžˆ ์ •์˜ํ•ด์•ผ ํ•œ๋‹ค. ๋ฌดํ•œ ๋ฃจํ”„์— ๋น ์ง€์ง€ ์•Š๋„๋ก ์ตœ๋Œ€ ๋ฐ˜๋ณต ํšŸ์ˆ˜๋‚˜ ํƒ€์ž„์•„์›ƒ์„ ์„ค์ •ํ•œ๋‹ค.

์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ: ๋„๊ตฌ ํ˜ธ์ถœ์ด ์‹คํŒจํ–ˆ์„ ๋•Œ Agent๊ฐ€ ์–ด๋–ป๊ฒŒ ๋Œ€์‘ํ• ์ง€ ๊ฒฐ์ •ํ•ด์•ผ ํ•œ๋‹ค. ์žฌ์‹œ๋„, ๋Œ€์•ˆ ๋„๊ตฌ ์‚ฌ์šฉ, ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ๋ฐ˜ํ™˜ ๋“ฑ.

ํ• ๋ฃจ์‹œ๋„ค์ด์…˜ ๋ฐฉ์ง€: Agent๊ฐ€ ๋„๊ตฌ๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š๊ณ  ๊ฐ€์ƒ์˜ ๊ฒฐ๊ณผ๋ฅผ ๋งŒ๋“ค์–ด๋‚ด๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค. ๋„๊ตฌ ๊ฒฐ๊ณผ ์—†์ด๋Š” ํ™•์ •์ ์ธ ๋‹ต์„ ๋‚ด๋ฆฌ์ง€ ์•Š๋„๋ก ํ”„๋กฌํ”„ํŠธ ์„ค๊ณ„๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

๋น„์šฉ ๊ด€๋ฆฌ: Agent ๋ฃจํ”„๊ฐ€ ๊ธธ์–ด์งˆ์ˆ˜๋ก API ํ˜ธ์ถœ ํšŸ์ˆ˜์™€ ํ† ํฐ์ด ๋Š˜์–ด๋‚œ๋‹ค. ๋‹จ๊ณ„๋ณ„ ๋น„์šฉ ์ถ”์ ์ด ํ•„์š”ํ•˜๋‹ค.