๐ ์์ด์ ํธ ๋๊ตฌ ์ค๊ณ
๋๊ตฌ ์ ์, ์ค๊ณ ํจํด, ๋๊ตฌ ํ ์คํธ
์์ด์ ํธ ๋๊ตฌ ์ค๊ณ
๋๊ตฌ ์ ์, ์ค๊ณ ํจํด, ๋๊ตฌ ํ ์คํธ
๋๊ตฌ๋ฅผ ์ฐ๋ AI๊ฐ ์์ด์ ํธ๋ค
LLM์ด ์์จ์ ์ผ๋ก ํ๋จํ๊ณ ํ๋ํ๋ ์์คํ ์ ํต์ฌ ์๋ฆฌ
๋๊ตฌ ์ค๊ณ
ํจ์ vs ๋๊ตฌ ยท @tool ๋ณํ ยท ์คํค๋ง ยท ์ค๊ณ ์์น
๋๊ตฌ ์ค๊ณ
์ผ๋ฐ ํจ์๊ฐ LLM์ด ์ธ ์ ์๋ ๋๊ตฌ๊ฐ ๋๊ธฐ๊น์ง์ ์ค๊ณ ์์ ์กฐ๊ฐ
๋๊ตฌ = ํจ์ + JSON Schema
LLM์ ์ฝ๋๋ฅผ ์ฝ์ง ๋ชปํฉ๋๋ค โ nameยทdescriptionยทinputSchema ์ค๋ช ์๊ฐ ๋๊ตฌ ์ ํ์ ์ ์ผํ ๊ทผ๊ฑฐ์ ๋๋ค.
- ํจ์ vs ๋๊ตฌ โ ํธ์ถ ์ฃผ์ฒด๊ฐ ๊ฐ๋ฐ์์์ LLM์ผ๋ก
- @tool ๋ฐ์ฝ๋ ์ดํฐ โ Python ํจ์๋ฅผ ๋๊ตฌ๋ก ์๋ ๋ณํ
- ์คํค๋ง 3์์ โ name ยท description ยท inputSchema
- ์ค๊ณ ์์น โ ๋จ์ผ ์ฑ ์ ยท ๊ตฌ์กฐํ ๋ฐํ ยท ๋ช ํํ ์๋ฌ
์ข์ description์ "์ธ์ + ๋ญ ๋ฃ๊ณ + ๋ญ ๋ฐ๋ + ์ฃผ์์ฌํญ"์ ๋ด์ต๋๋ค โ ์ค๋ช ์ ํ์ง์ด ๋๊ตฌ ์ ํ ์ ํ๋๋ฅผ ๊ฒฐ์ ํฉ๋๋ค.
ํจ์ vs ๋๊ตฌ
ํจ์๋ฅผ ๋๊ตฌ๋ก ๋ง๋๋ ์กฐ๊ฑด โ JSON Schema(์ค๋ช ์)
์ผ๋ฐ ํจ์
๊ฐ๋ฐ์๊ฐ ์ง์ ํธ์ถ
- ํธ์ถ ์ฃผ์ฒด: ๊ฐ๋ฐ์ (์ฝ๋์์ ์ง์ )
- ์ค๋ช ๋ถํ์ โ ์ฝ๋๋ฅผ ๋ณด๋ฉด ๋จ
- ํ๋ผ๋ฏธํฐ: ํจ์ ์๊ทธ๋์ฒ
- ์ ํ ๊ธฐ์ค: ๊ฐ๋ฐ์ ํ๋จ
๊ฐ๋ฐ์๊ฐ ํจ์๋ช ๊ณผ ํ๋ผ๋ฏธํฐ๋ฅผ ์๊ณ ์์ผ๋ฏ๋ก ์ถ๊ฐ ์ค๋ช ์์ด ํธ์ถ ๊ฐ๋ฅํฉ๋๋ค.
์์ด์ ํธ ๋๊ตฌ
LLM์ด ์์จ ๊ฒฐ์
- ํธ์ถ ์ฃผ์ฒด: LLM (์ถ๋ก ํ ์์จ)
- ์ค๋ช ํ์ โ LLM์ ์ฝ๋๋ฅผ ๋ชป ์ฝ์
- ํ๋ผ๋ฏธํฐ: JSON Schema
- ์ ํ ๊ธฐ์ค: description ํ ์คํธ
LLM์ ์ฝ๋๋ฅผ ๋ณผ ์ ์์ผ๋ฏ๋ก, nameยทdescriptionยทinputSchema๋ง์ผ๋ก "์ด ๋๊ตฌ๋ฅผ ์ง๊ธ ์จ์ผ ํ๋" ํ๋จํฉ๋๋ค.
@tool ๋ฐ์ฝ๋ ์ดํฐ
Python ํจ์๋ฅผ ์์ด์ ํธ ๋๊ตฌ๋ก ์๋ ๋ณํ
from strands import Agent, tool
# โ ๋๊ตฌ ์ ์ โ docstring์ด LLM์๊ฒ ์ ๋ฌ๋๋ ๋๊ตฌ ์ค๋ช
@tool
def search_restaurants(location: str, cuisine: str = None) -> str:
"""์ง์ญ๊ณผ ์๋ฆฌ ์ข
๋ฅ๋ก ์๋น์ ๊ฒ์ํฉ๋๋ค.
์ฌ์ฉ์๊ฐ ์๋น ์ถ์ฒ์ ์์ฒญํ ๋ ํธ์ถํ์ธ์.
location: ๊ฒ์ํ ์ง์ญ๋ช
(์: "๊ฐ๋จ์ญ")
cuisine: ์๋ฆฌ ์ข
๋ฅ (์: "์ดํ๋ฆฌ์"). ์๋ตํ๋ฉด ์ ์ฒด ๊ฒ์
"""
return f"{location} ๊ทผ์ฒ ์๋น 3๊ณณ ๋ฐ๊ฒฌ"
# โก Agent์ ๋๊ตฌ ์ ๋ฌ โ ReAct ๋ฃจํ์์ ์์จ ํธ์ถ
agent = Agent(
model="us.anthropic.claude-sonnet-4-6",
tools=[search_restaurants],
)
# โข ์คํ โ ๋ชจ๋ธ์ด ์ค์ค๋ก ๋๊ตฌ ํธ์ถ์ ๊ฒฐ์ ํ๊ณ ๊ฒฐ๊ณผ๋ฅผ ์ข
ํฉ
result = agent("๊ฐ๋จ์ญ ์ดํ๋ฆฌ์ ์ถ์ฒํด์ค")
- @tool ๋ฐ์ฝ๋ ์ดํฐ โ ํ๋ฒํ Python ํจ์๊ฐ ๊ทธ๋๋ก ๋๊ตฌ๊ฐ ๋จ. docstringยทํ์ ํํธ๊ฐ nameยทdescriptionยทinputSchema๋ก ์๋ ๋ณํ
- tools=[...] ๋ฑ๋ก โ ๋ชจ๋ธ์ด ์ ํํ ์ ์๋ ํ๋ ๋ชฉ๋ก. ๋ฆฌ์คํธ์ ๋ด๋ ์๊ฐ ReAct ๋ฃจํ์ ์ ํ์ง
- ์คํ โ ํธ์ถ ์ฌ๋ถยท์ธ์๋ฅผ ๋ชจ๋ธ์ด ์ค์ค๋ก ๊ฒฐ์ ํ๊ณ , Observation์ ์ข ํฉํด ์ต์ข ๋ต๋ณ
๋๊ตฌ ์คํค๋ง 3์์
LLM์ด ๋๊ตฌ๋ฅผ ์ฌ๋ฐ๋ฅด๊ฒ ์ ํํ๊ธฐ ์ํ ํ์ ์ ๋ณด
name
๋์ฌ+๋ช ์ฌ ํจํด โ search_restaurants, get_weather, create_ticket
description
์ธ์ ์ฐ๋์ง + ์ ๋ ฅ ํ์ + ๋ฐํ๊ฐ โ ๋๊ตฌ ์ ํ์ ์ ์ผํ ๊ทผ๊ฑฐ
inputSchema
required/optional ๊ตฌ๋ถ, ํ์ ์ ์ฝ, enum์ผ๋ก ์ ๋ ฅ ๊ฒ์ฆ
name์ผ๋ก 1์ฐจ ํํฐ๋ง, description์ผ๋ก ์ต์ข ์ ํ, inputSchema๋ก ์ ๋ ฅ ๊ฒ์ฆ โ ์ธ ์์๊ฐ ํจ๊ป ๋๊ตฌ ์ ํ ์ ํ๋๋ฅผ ๋ง๋ญ๋๋ค.
๋๊ตฌ ์ค๊ณ ์์น
์ข์ ๋๊ตฌ = LLM์ด ์ ํํ ์ ํํ๊ณ ์์ ํ๊ฒ ์คํ
| ์์น | ์ข์ ์ | ๋์ ์ |
|---|---|---|
| ๋จ์ผ ์ฑ ์ | search_restaurants (๊ฒ์๋ง) | manage_restaurant (๊ฒ์+์์ฝ+์ทจ์) |
| ๋ช ํํ ์ด๋ฆ | get_weather (๋์ฌ+๋ช ์ฌ) | do_stuff, helper, process |
| ๊ตฌ์ฒด์ ์ค๋ช | "๋น์ฉ ์ง๋ฌธ ์ ํธ์ถ. service, period ํ์" | "๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ต๋๋ค" |
| ๊ตฌ์กฐํ ๋ฐํ | JSON: {name, rating, price} | ์์ ํ์ ํ ์คํธ |
| ์๋ฌ ๋ฉ์์ง | {"error": "์๋น์ค๋ช ์ด ์ ํจํ์ง ์์ต๋๋ค"} | ๋น ๋ฌธ์์ด ๋๋ ์์ธ |
ํ ๋๊ตฌ๋ ํ ๊ฐ์ง ์ผ๋ง ํ๊ณ , ๋ฐํ์ ๊ตฌ์กฐํํ๋ฉฐ, ์๋ฌ์๋ ์ด์ ๋ฅผ ๋ด์ต๋๋ค โ LLM์ด ๋ค์ ํ๋จ์ ์ด์ด๊ฐ ์ ์๋ ํํ๋ก ๋๋ ค์ฃผ๋ ๊ฒ์ด ํต์ฌ์ ๋๋ค.
Human-in-the-Loop
์์ด์ ํธ ์ค๋ฅ ยท ๋๊ตฌ ๋ถ๋ฅ ยท HITL 3ํจํด ยท ๊ตฌํ
Human-in-the-Loop
์์ด์ ํธ์ ์คํจ ๋ฐฉ์์ ์๊ณ ์ํํ ํธ์ถ ์์๋ง ์ฌ๋์ ํ์ธ์ ์ฌ๋ ์ค๊ณ
์ฝ๊ธฐ = ์๋ ยท ์ฐ๊ธฐ = ์น์ธ ยท ๊ณ ์ํ = ์ด์ค ํ์ธ
์์จ์ฑ์ด ๋์์๋ก ์ํ๋ ์ปค์ง๋๋ค โ ๋๊ตฌ๋ฅผ ์ํ๋๋ก ๋ถ๋ฅํ๊ณ ์ํํ ํธ์ถ ์์์๋ง ๋ฉ์ถฅ๋๋ค.
- ์คํจ ๋ฐฉ์ โ ํ๊ฐ ยท ์๋ชป๋ ๋๊ตฌ ์ ํ ยท ๋ฒ์ ์ด๊ณผ
- HITL 3ํจํด โ ์น์ธ ๊ฒ์ดํธ ยท ์์ค์ปฌ๋ ์ด์ ยท ๊ฐ์ฌ ๋ก๊ทธ
- Hook ๊ตฌํ โ BeforeToolCallEvent + event.interrupt()
- ์ค๊ณ ์์น โ ์ต์ ๊ถํ ยท ์ ์ง์ ์์จ ยท ์คํจ ์์
๋๊ตฌ๋ฅผ ์ฝ๊ธฐยท์ฐ๊ธฐยท๊ณ ์ํ์ผ๋ก ๋ถ๋ฅํ๊ณ ์ํํ ํธ์ถ๋ง ์ฌ๋์ด ํ์ธํฉ๋๋ค โ ์์ ๊ณผ ํจ์จ์ ๊ท ํ์ด HITL ์ค๊ณ์ ๋ชฉํ์ ๋๋ค.
์์ด์ ํธ๊ฐ ์คํจํ๋ ๋ฐฉ์
์์จ์ฑ์ ๋น๋กํด ์ปค์ง๋ ์ํ
ํ๊ฐ (Hallucination)
์กด์ฌํ์ง ์๋ ๋๊ตฌ๋ฅผ ํธ์ถํ๊ฑฐ๋ ์๋ชป๋ ํ๋ผ๋ฏธํฐ ์์ฑ
์๋ชป๋ ๋๊ตฌ ์ ํ
delete๋ฅผ ์จ์ผ ํ๋๋ฐ create๋ฅผ ํธ์ถ โ description ๋ชจํธ ์ ๋น๋ฒ
๋ฒ์ ์ด๊ณผ (Excessive Agency)
ํ์ฉ๋์ง ์์ ์์คํ ์ ์ ๊ทผ ์๋ โ OWASP LLM Top 10์ Excessive Agency ํญ๋ชฉ
์ธ ๊ฐ์ง ๋ชจ๋ ์์จ์ฑ์ด ๋ง๋๋ ์คํจ์ ๋๋ค โ ๋๊ตฌ ํผ๋์ description ์ฐจ๋ณํ๋ก, ๋ฒ์ ์ด๊ณผ๋ ์ต์ ๊ถํ ์์น์ผ๋ก ์ค์ ๋๋ค.
๋๊ตฌ ์ํ ๋ถ๋ฅ
์ฝ๊ธฐ = ์๋, ์ฐ๊ธฐ = ์น์ธ, ๊ณ ์ํ = ์ด์ค ํ์ธ
์ฝ๊ธฐ ์ ์ฉ (์กฐํ)
์๋ ์คํ ํ์ฉ
- search_restaurants
- get_weather
- query_status
์ธ์ โ ๋ถ์์ฉ ์์ โ ๋ฐ์ดํฐ ๋ณ๊ฒฝ ์์ด ์ ๋ณด๋ง ๋ฐํ
์ฐ๊ธฐ (๋ณ๊ฒฝ)
์ฌ์ฉ์ ์น์ธ ํ ์คํ
- create_reservation
- update_profile
- send_email
์ธ์ โ ๋๋๋ฆฌ๊ธฐ ์ด๋ ค์ด ๋ณ๊ฒฝ โ ์์ฑ/์์ /์ญ์
๊ณ ์ํ (๋น๊ฐ์ญ)
์ด์ค ํ์ธ + ๊ฐ์ฌ ๋ก๊ทธ
- cancel_order
- process_refund
- delete_account
์ธ์ โ ๊ธ์ ์ ์ํฅ, ๋ฐ์ดํฐ ์์ค, ๋ฒ์ ์ฑ ์
๋ถ์์ฉ์ด ์์ผ๋ฉด ์๋, ๋๋๋ฆฌ๊ธฐ ์ด๋ ค์ฐ๋ฉด ์น์ธ, ๊ธ์ ยท๋ฒ์ ์ํฅ์ด๋ฉด ์ด์ค ํ์ธ โ ๋ถ์์ฉ์ ํฌ๊ธฐ๊ฐ ๊ฒ์ดํธ์ ๊ฐ๋๋ฅผ ๊ฒฐ์ ํฉ๋๋ค.
HITL 3ํจํด
์ฌ๋์ด ์์ด์ ํธ๋ฅผ ์์ ํ๊ฒ ์ ์ดํ๋ ์ธ ๊ฐ์ง ๋ฐฉ๋ฒ
-
1
์น์ธ ๊ฒ์ดํธ
- ์์ด์ ํธ ์คํ ์ผ์ ์ค์ง
- ์ฌ์ฉ์์๊ฒ ๋์ ์ค๋ช + ์น์ธ ์์ฒญ
- ์น์ธ โ ์คํ ์ฌ๊ฐ, ๊ฑฐ๋ถ โ ์ค๋จ
-
2
์์ค์ปฌ๋ ์ด์
- ๋ถํ์คํ ์์ฒญ ๊ฐ์ง
- ์ฌ๋(์๋ด์)์๊ฒ ์ ํ
- ํด๊ฒฐ ํ ์์ด์ ํธ๋ก ๋ณต๊ท ๊ฐ๋ฅ
-
3
๊ฐ์ฌ ๋ก๊ทธ
- ๋๊ตฌ ํธ์ถ ์ด๋ ฅ ์ ๋ถ ์ ์ฅ
- ๋น์ ์ ํจํด ํ์ง
- ์ ์ฑ ๊ท์น ์ ๋ฐ์ดํธ ๊ทผ๊ฑฐ
์น์ธ ๊ฒ์ดํธ๋ ์คํ ์ ์, ์์ค์ปฌ๋ ์ด์ ์ ํ๋จ์ด ๋งํ ๋, ๊ฐ์ฌ ๋ก๊ทธ๋ ์คํ ํ์ ์๋ํฉ๋๋ค โ ์์ ์ด ๋ค๋ฅธ ์ธ ๊ฒน์ ์์ ์ฅ์น์ ๋๋ค.
Hook ๊ธฐ๋ฐ ์น์ธ ๊ฒ์ดํธ
Strands SDK๋ก HITL ๊ตฌํ
from strands.hooks import BeforeToolCallEvent, HookProvider, HookRegistry
# โ Hook ์ ์ โ ๋๊ตฌ ์คํ ์ง์ (BeforeToolCallEvent)์ ๊ฐ์
class ApprovalGate(HookProvider):
"""๊ณ ์ํ ๋๊ตฌ ํธ์ถ ์ ์ฌ์ฉ์ ์น์ธ์ ์์ฒญํ๋ Hook"""
def register_hooks(self, registry: HookRegistry):
registry.add_callback(BeforeToolCallEvent, self.check)
def check(self, event: BeforeToolCallEvent):
# โก ์ฐ๊ธฐ/์ญ์ ๋๊ตฌ๋ง ์น์ธ ์์ฒญ โ ์ฝ๊ธฐ ๋๊ตฌ๋ ํต๊ณผ
dangerous = ["cancel_reservation", "delete_account"]
if event.tool_use["name"] in dangerous:
event.interrupt(
"approval_needed",
reason=f"'{event.tool_use['name']}' ์คํ์ ์น์ธํ์๊ฒ ์ต๋๊น?"
)
# โข ์์ด์ ํธ์ Hook ์ ์ฉ
agent = Agent(
model="us.anthropic.claude-sonnet-4-6",
tools=[cancel_reservation, search_restaurants],
hooks=[ApprovalGate()]
)
- BeforeToolCallEvent โ ๋๊ตฌ๊ฐ ์ค์ ๋ก ์คํ๋๊ธฐ ์ง์ ์ ๊ฐ์ ์ง์ . ์ฌ๊ธฐ์ ๋ฉ์ถ๋ฉด ๋๊ตฌ๋ ์คํ๋์ง ์์
- ์ ๋ณ ์น์ธ โ ๊ณ ์ํ ๋๊ตฌ๋ง event.interrupt()๋ก ์ผ์ ์ค์ง. ์น์ธ โ ์ฌ๊ฐ, ๊ฑฐ๋ถ โ ์ค๋จ
- hooks=[...] โ Agent์ ๋ถ์ด๋ ์๊ฐ ๋ชจ๋ ๋๊ตฌ ํธ์ถ์ด ์ด ๊ฒ์ดํธ๋ฅผ ๊ฑฐ์นจ. ๋๊ตฌ ์ฝ๋๋ ๋ฌด๋ณ๊ฒฝ
HITL ์ค๊ณ ์์น
์์ ๊ณผ ํจ์จ์ ๊ท ํ
์ต์ ๊ถํ ยท ์ ์ง์ ์์จ ยท ์คํจ ์์
ํ์ํ ๋๊ตฌ๋ง ์ ๊ณตํ๊ณ , ์ ๋ขฐ๊ฐ ์์ด๋ฉด ์๋ ์น์ธ์ ํ๋ํ๋ฉฐ, ์ค๋ฅ ์ ๋ฐ๋์ ์ค๋จํฉ๋๋ค
- Least Privilege
- Graduated Autonomy
- Fail-Safe
๋๊ตฌ ยท ๋ฃจํ ยท ์์ ์ฅ์น๋ฅผ ๊ฐ์ถ ์์คํ ์ค๊ณ