๐ฏ Strands ์ ์ด์ ํ์ฅ
Hooks, Plugins, Interventions, Sandbox
Strands ์ ์ด์ ํ์ฅ
Hooks ยท Plugins ยท Interventions ยท Sandbox
์์ด์ ํธ๋ฅผ ์์ ํ๊ฒ ์ ์ดํ๋ค
Hooks์์ Sandbox๊น์ง โ 4๊ณ์ธต ์ ์ด ์ฒด๊ณ
์ ์ด์ ํ์ฅ ์ฒด๊ณ
4๊ณ์ธต ๋ฐฉ์ด ์ฌ๋
์์จ์ ํ์ํ ๊ฒฝ๊ณ
๋ชจ๋ธ์ด ์ค์ค๋ก ๋๊ตฌ์ ๋ฐ๋ณต์ ๊ฒฐ์ ํ๋ ์์ด์ ํธ โ ํต์ ์๋ ์์จ์ด ๋ง๋๋ ์ฌ๊ณ ๋ค
์ํ ๋๊ตฌ ์คํ
๋ชจ๋ธ์ด ๊ณ ๋ฅธ ๋๊ตฌ๊ฐ ํญ์ ์์ ํ์ง๋ ์์
๋น์ฉ ํญ์ฃผ
๋ฃจํ๊ฐ ๋ ๋๋ง๋ค ํ ํฐ ๊ณผ๊ธ โ ์ํ ์๋ ์ฌ์๋
๋ฌดํ ๋ฃจํ
๊ฐ์ ๋๊ตฌ๋ฅผ ๊ณ์ ๋ฐ๋ณต โ ์ค์ค๋ก๋ ๋ฉ์ถ์ง ๋ชปํจ
๋ฐ์ดํฐ ์ ์ถ
๋๊ตฌ ๊ฒฐ๊ณผ์ ์๋ต์ ๋ฏผ๊ฐ ์ ๋ณด๊ฐ ์์ฌ ๋๊ฐ
์์จ์ฑ์ ์ฃฝ์ด์ง ์๊ณ ๊ฒฝ๊ณ๋ฅผ ์ธ์ฐ๋ ๊ฒ โ ๊ทธ ํด๋ฒ์ด ์ง๊ธ๋ถํฐ ๋ณผ ์ ์ด 4๊ณ์ธต์ ๋๋ค.
์ ์ด์ 4๊ณ์ธต
์์จ์ ๋จ๊ธฐ๊ณ ๊ฒฝ๊ณ๋ง ์ธ์ฐ๋ 4๊ณ์ธต โ ๊ฐ๋ณ๊ฒ๋ Hooks, ์ตํ์๋ Sandbox
Sandbox
์คํ ํ๊ฒฝ ์์ฒด๋ฅผ ๊ฒฉ๋ฆฌ โ ์ตํ์ ๋ฐฉ์ด์
Interventions
Cedar ์ ์ฑ ์ผ๋ก ๋์ ํ์ฉ/๊ฑฐ๋ถ ํ์
Plugins
deny/steer ์ ์ฑ ์ ์ด ๋ฑ ํ์ฅ ๋ชจ๋ ๋ถ์ฐฉ
Hooks
์ด๋ฒคํธ ์ฝ๋ฐฑ์ผ๋ก ๊ด์ฐฐยท๊ฐ์ โ ๊ฐ์ฅ ๊ฐ๋ฒผ์ด ์ ์ด
๊ณ์ธต๋ณ ๋น๊ต
์ ์ฉ ์์ ยท์์คยท๊ตฌํ ๋ฐฉ์์ด ๋ชจ๋ ๋ค๋ฅธ 4๊ฐ ๋ ์ด์ด
| ๊ณ์ธต | ์ ์ฉ ์์ | ์ ์ด ์์ค | ๊ตฌํ ๋ฐฉ์ |
|---|---|---|---|
| Hooks | ์ด๋ฒคํธ ๋ฐ์ ์ | ๊ด์ฐฐ + ์ ํ์ ์ค๋จ | ์ฝ๋ฐฑ ํจ์ ๋ฑ๋ก |
| Plugins | ์์ด์ ํธ ์ด๊ธฐํ ์ | ์ ์ฑ ์ ์ดยท๊ธฐ๋ฅ ์ฃผ์ | ํด๋์ค ์ธ์คํด์ค ๋ถ์ฐฉ |
| Interventions | ๋๊ตฌ ํธ์ถ ์ง์ | ํ์ฉ/๊ฑฐ๋ถ/์น์ธ ๋๊ธฐ | Cedar ์ ์ฑ ํ์ผ |
| Sandbox | ํ๋ก์ธ์ค ์์ ์ | ํ๊ฒฝ ๊ฒฉ๋ฆฌ | Docker/SSH ์ค์ |
๊ฐ์ฅ ๊ฐ๋ฒผ์ด Hooks๋ถํฐ ์์ํด ํ์ํ ๋งํผ ์๋ ๊ณ์ธต์ ๋ํฉ๋๋ค โ ๋ค ์ธต์ ๋ค ์ฐ๋ ๊ฒ ์๋๋ผ, ์ํฌ๋ก๋์ ์ํ๋๋งํผ ์๋๋ค.
์์ฒญ์ด ์ง๋๋ ๊ด๋ฌธ ์์
Hook ๊ฒ์ฌ โ Plugin ์ ์ฑ โ Intervention ํ์ โ Sandbox ๊ฒฉ๋ฆฌ โ ๋ค ๊ณ์ธต์ด ๋ฒ๊ฐ์ ๊ฐ์ ํ๋ ๊ฒฝ๋ก
-
1
Hook: BeforeModelCallEvent
๋ชจ๋ธ ํธ์ถ ์ ๊ฒ์ฌ โ ํ๋กฌํํธ ๊ฒ์ฆ, ๋น์ฉ ์ฒดํฌ
-
2
Plugin: ์ ์ฑ ํ์ธ
deny/steer ๊ท์น ํ๊ฐ โ ์๋ฐ ์ ์ฐจ๋จํ๊ฑฐ๋ ๋ฐฉํฅ ์์
-
3
Hook: BeforeToolCallEvent
๋๊ตฌ ํธ์ถ ์ ์ธ์ ๊ฒ์ฆยท๋ก๊น
-
4
Intervention: ํ์
Cedar ์ ์ฑ ์ด permit/forbid ํ์ , HumanInTheLoop์ด ๋ฏผ๊ฐ ๋๊ตฌ ์น์ธ ์๊ตฌ โ ๋ณ๊ฐ ํด๋์ค
-
5
Sandbox: ๊ฒฉ๋ฆฌ ์คํ
ํ์ฉ๋ ๋๊ตฌ๋ฅผ ์ปจํ ์ด๋ ๋ด๋ถ์์ ์คํ
ํ ๊ด๋ฌธ์ด๋ผ๋ ๋งํ๋ฉด ์คํ๋์ง ์์ต๋๋ค โ ๊ฒน๊ฒน์ ๊ด๋ฌธ์ด ๊ณง ๋ฐฉ์ด ์ฌ๋์ ๋๋ค.
Hooks
Event Callbacks ยท cancel_tool ยท ๋ฃจํ ์ ์ด
Hooks
๊ฐ์ฅ ๊ฐ๋ฒผ์ด ์ ์ด ๊ณ์ธต โ ์ด๋ฒคํธ๋ง๋ค ์ฝ๋ฐฑ์ ๊ฑธ์ด ๊ด์ฐฐํ๊ณ ๊ฐ์
Hooks
์์ด์ ํฑ ๋ฃจํ์ ๋ชจ๋ ์ง์ ์์ ์ด๋ฒคํธ ์ฝ๋ฐฑ์ผ๋ก ๊ด์ฐฐํ๊ณ , ํ์ํ๋ฉด ๊ฐ์ ํฉ๋๋ค.
- ์ด๋ฒคํธ 8์ข โ ๋ฃจํ ์ ๊ตฌ๊ฐ ์ปค๋ฒ
- cancel_tool โ ๋๊ตฌ ์คํ ์ทจ์
- ๊ธฐ์กด ์ฝ๋ ๋ณ๊ฒฝ ์์ด ๋ถ์ฐฉ
- ๊ฐ์ฅ ๊ฐ๋ฒผ์ด ์ ์ด
๊ด์ฐฐ์ด ๋จผ์ ์ ๋๋ค โ ๋ณด์ด์ง ์์ผ๋ฉด ์ ์ดํ ์ ์์ต๋๋ค
8๊ฐ์ง Hook Event
์์ด์ ํฑ ๋ฃจํ ์ ๊ตฌ๊ฐ์ ๊ด์ฐฐํ ์ ์๋ ๊ฐํ์ ์ด๋ฒคํธ ํฌ์ธํธ
| Hook Event | ๋ฐ์ ์์ | ์ฃผ์ ์ฉ๋ |
|---|---|---|
| AgentInitializedEvent | ์์ด์ ํธ ์์ฑ์ ์๋ฃ ํ | ์ด๊ธฐ ์ค์ ๊ฒ์ฆ, ํ๋ฌ๊ทธ์ธ ๋ฐ์ธ๋ฉ |
| BeforeInvocationEvent | ์์ด์ ํธ ํธ์ถ ์์ ์ | ์์ฒญ ๋ก๊น , ์นด์ดํฐ ๋ฆฌ์ |
| BeforeModelCallEvent | ๋ชจ๋ธ ์ถ๋ก ํธ์ถ ์ง์ | ํ๋กฌํํธ ๊ฒ์ฆ, ๋น์ฉ ์ฌ์ ์ฒดํฌ |
| AfterModelCallEvent | ๋ชจ๋ธ ์๋ต ์์ ํ | ์๋ต ํํฐ๋ง, ์ฌ์๋ ํ์ |
| BeforeToolCallEvent | ๋๊ตฌ ์คํ ์ง์ | ์ธ์ ๊ฒ์ฆ, ์ํ ๋๊ตฌ ์ฐจ๋จ/๊ต์ฒด |
| AfterToolCallEvent | ๋๊ตฌ ์คํ ์๋ฃ ํ | ๊ฒฐ๊ณผ ์์ , ์ฌ์๋ ํ์ |
| MessageAddedEvent | ๋ํ ์ด๋ ฅ์ ๋ฉ์์ง ์ถ๊ฐ ์ | ๋ก๊น , ๋ฉ์์ง ๊ฐ์ฌ |
| AfterInvocationEvent | ์์ด์ ํธ ํธ์ถ ์ข ๋ฃ ํ | ์ต์ข ์๋ต ๊ฒ์ฌ, ๊ฐ์ฌ ๋ก๊น |
Before ์ด๋ฒคํธ๋ ๋ง์ ์ ์๋ ์ง์ , After ์ด๋ฒคํธ๋ ๋ฐ๋ก์ก์ ์ ์๋ ์ง์ ์ ๋๋ค โ ๊ฐ์ ์์น ์ ํ์ด ๊ณง ์ ์ด ์ ๋ต์ ๋๋ค.
Hook ๋ฑ๋ก
HookProvider ํ๋กํ ์ฝ๋ก ๊ธฐ์กด ์์ด์ ํธ ์ฝ๋ ๋ณ๊ฒฝ ์์ด ์ฝ๋ฐฑ ๋ถ์ฐฉ
from strands import Agent
from strands.hooks import BeforeToolCallEvent, HookProvider, HookRegistry
class BlockDangerousTools(HookProvider):
"""์ํ ๋๊ตฌ ํธ์ถ์ ์ฌ์ ์ฐจ๋จ"""
BLOCKED = {"delete_file", "drop_table", "rm_rf"}
def register_hooks(self, registry: HookRegistry, **kwargs) -> None:
registry.add_callback(BeforeToolCallEvent, self.check_tool)
def check_tool(self, event: BeforeToolCallEvent) -> None:
tool_name = event.tool_use["name"]
if tool_name in self.BLOCKED:
event.cancel_tool = f"Blocked: {tool_name} is not allowed"
agent = Agent(
model="us.anthropic.claude-sonnet-4-6",
hooks=[BlockDangerousTools()],
)
Hooks ๊ฐ์ ํจํด
๊ด์ฐฐ์์ ๊ฐ์ ์ผ๋ก โ ์ทจ์ยท์์ธยท๋ง์คํน์ ์กฐํฉํ 4๊ฐ์ง ๋ํ ์๋๋ฆฌ์ค
๋น์ฉ ํญ์ฃผ ์ฐจ๋จ
ํ ํฐ ์ฌ์ฉ๋์ด ์๊ณ๊ฐ ์ด๊ณผ ์ ์ฆ์ ์ค๋จ
์ํ ๋๊ตฌ ์ฐจ๋จ
ํ์ฉ ๋ชฉ๋ก์ ์๋ ๋๊ตฌ ํธ์ถ ์ ๊ฑฐ๋ถ
๋ฌดํ ๋ฃจํ ํ์ง
๋์ผ ๋๊ตฌ ๋ฐ๋ณต ํธ์ถ ํจํด ๊ฐ์ง ์ ํ์ถ
PII ์ ์ถ ๋ฐฉ์ง
์๋ต์ ๊ฐ์ธ์ ๋ณด ํจํด ๊ฐ์ง ์ ์ฐจ๋จ
๋ค ํจํด์ ๋ผ๋๋ ๊ฐ์ต๋๋ค โ ์ด๋ฒคํธ์์ ์กฐ๊ฑด์ ๊ฒ์ฌํ๊ณ , cancel_toolยท์์ธยท๋ง์คํน์ผ๋ก ๊ฐ์ ํฉ๋๋ค.
Plugins
AgentControlPlugin ยท Skills ยท ContextOffloader
Plugins
ํ์ฅ ๊ณ์ธต โ ์ ์ด ๋ก์ง๊ณผ ๋๊ตฌ๋ฅผ ํ๋์ ๋ชจ๋๋ก ๋ฌถ์ด ์ฅ์ฐฉ
Plugins
ํ ๊ณผ ๋๊ตฌ๋ฅผ ํ๋์ ํด๋์ค๋ก ๋ฌถ์ด ์์ด์ ํธ ์ด๊ธฐํ ์ ์ฅ์ฐฉํฉ๋๋ค.
- @hook + @tool ๋ฐ์ฝ๋ ์ดํฐ
- AgentControlPlugin โ deny/steer ์ ์ฑ
- ContextOffloader โ ์ปจํ ์คํธ ์ ์ฝ
- ์ด๊ธฐํ ์ 1ํ ์ฅ์ฐฉ
์ ์ฑ ๊ณผ ๊ธฐ๋ฅ์ ๋ชจ๋๋ก โ ์์ด์ ํธ ์ฝ๋๋ ๊ทธ๋๋ก ๋ก๋๋ค.
ํต์ฌ ํ๋ฌ๊ทธ์ธ 5์ข
์ ์ฑ ์ ์ดยท์ง์์ด ๋ก๋ยท์ปจํ ์คํธ ์คํ๋ก๋ฉยท๊ฐ์ด๋์คยท์ค์๊ฐ ํ ์คํธ ์ฃผ์ ์ ๋ถ๋ดํ๋ ํ๋ฌ๊ทธ์ธ
AgentControlPlugin
์๋ฒ ๊ด๋ฆฌ ๊ท์น์ผ๋ก ๋๊ตฌ ํธ์ถ์ deny/steer ์ ์ด โ ๋ณ๋ ํจํค์ง
Skills
๋ชจ๋์ ์ง์์ด ํจํค์ง(SKILL.md)๋ฅผ ํ์ํ ๋๋ง ๋ก๋
Context Offloader
๊ณผ๋ ๋๊ตฌ ๊ฒฐ๊ณผ๋ฅผ ์คํ ๋ฆฌ์ง๋ก ์คํ๋ก๋ํ์ฌ ์ปจํ ์คํธ ์ ์ฝ
Steering
์งํ ์ค์ธ ์์ด์ ํธ์ ์ปจํ ์คํธ ์ธ์ง ๊ฐ์ด๋์ค๋ฅผ ์ฃผ์
Context Injector
๋ชจ๋ธ ํธ์ถ ์ง์ ์ ์ค์๊ฐ ํ ์คํธ๋ฅผ ์ฃผ์
SkillsยทContextOffloaderยทSteeringยทContext Injector๋ SDK๊ฐ ์ ๊ณตํ๊ณ , AgentControlPlugin๋ง ๋ณ๋ ํจํค์ง์ ๋๋ค โ ์ ์ฑ ์ ์ด๋ ์๋ฒยทcontrols.yaml ์ฐ๋์ด ์ ์ ์ ๋๋ค.
์ปค์คํ ํ๋ฌ๊ทธ์ธ ์์ฑ
Plugin์ ์์ํ์ฌ ์์ฒด ์ ์ด ๋ก์ง ๊ตฌํ
from strands import Agent
from strands.plugins import Plugin, hook
from strands.hooks import AfterModelCallEvent, BeforeInvocationEvent
class CostGuardPlugin(Plugin): # ํ ํฐ ๋น์ฉ ๋์ ์ถ์ , ์์ฐ ์ด๊ณผ ์ ์ค๋จ
name = "cost-guard"
def __init__(self, budget_usd: float = 1.0):
super().__init__()
self.budget_usd = budget_usd
self.baseline_tokens, self.spent_usd = 0, 0.0
@hook
def reset_on_invocation(self, event: BeforeInvocationEvent) -> None:
# ํธ์ถ ์์ ์์ ์ ๋์ ํ ํฐ์ ๊ธฐ์ค์ ์ผ๋ก ๊ธฐ๋ก
metrics = event.agent.event_loop_metrics
self.baseline_tokens = metrics.accumulated_usage["totalTokens"]
self.spent_usd = 0.0
@hook
def check_cost(self, event: AfterModelCallEvent) -> None:
total = event.agent.event_loop_metrics.accumulated_usage["totalTokens"]
# 0.000003์ ํ ํฐ๋น ์์ ๋จ๊ฐ โ ์ฌ์ฉํ๋ ๋ชจ๋ธ์ ์ค์ ๋จ๊ฐ๋ก ๊ต์ฒด
self.spent_usd = (total - self.baseline_tokens) * 0.000003
if self.spent_usd >= self.budget_usd:
raise RuntimeError(f"Budget exceeded: ${self.spent_usd:.4f}")
agent = Agent(model="us.anthropic.claude-sonnet-4-6", plugins=[CostGuardPlugin(budget_usd=0.50)])
ํ๋ฌ๊ทธ์ธ ๋ผ์ดํ์ฌ์ดํด
ํ๋ฌ๊ทธ์ธ ์ด๊ธฐํ๋ถํฐ ์ด๋ฒคํธ ๊ตฌ๋ ๊น์ง์ ๋์ ์์
-
1
Discovery
Plugin ํด๋์ค์์ @hook, @tool ๋ฐ์ฝ๋ ์ดํฐ๊ฐ ๋ถ์ ๋ฉ์๋๋ฅผ ์ค์บํ์ฌ ์์ง
-
2
Hook Registration
๊ฐ @hook ๋ฉ์๋์ ํ์ ํํธ๋ฅผ ๋ถ์ํ์ฌ ํด๋น ์ด๋ฒคํธ ํ์ ์ ์ฝ๋ฐฑ ๋ฑ๋ก
-
3
Tool Registration
@tool ๋ฉ์๋๋ฅผ ์์ด์ ํธ์ ๋๊ตฌ ๋ชฉ๋ก์ ์ถ๊ฐ โ ๋ชจ๋ธ์ด ํธ์ถ ๊ฐ๋ฅ
-
4
init_agent ํธ์ถ
์ปค์คํ ์ด๊ธฐํ ๋ก์ง ์คํ โ ์ํ ์ค์ , ์กฐ๊ฑด๋ถ ํ ์ถ๊ฐ, ์์ด์ ํธ ์์ฑ ์ ๊ทผ
-
5
Plugin Ready
๋ฑ๋ก ์๋ฃ ํ ์์ด์ ํธ ์คํ ์ ์ด๋ฒคํธ ๋ฐ์๋ง๋ค ์ฝ๋ฐฑ ์๋ ํธ์ถ
๋ฐ์ฝ๋ ์ดํฐ๋ง ๋ถ์ด๋ฉด ๋ฐ๊ฒฌ๊ณผ ๋ฑ๋ก์ ํ๋ ์์ํฌ๊ฐ ํฉ๋๋ค โ ํ๋ฌ๊ทธ์ธ ์ฝ๋๋ ๋ก์ง์๋ง ์ง์ค
Interventions์ Cedar
์ ์ฑ ๊ธฐ๋ฐ ์ ๊ทผ ์ ์ด ยท HITL
Interventions
์ ์ฑ ๊ณ์ธต โ ๋๊ตฌ ํธ์ถ์ ํ์ฉยท๊ฑฐ๋ถยท์น์ธ ๋๊ธฐ๋ก ํ์
Interventions
Cedar ์ ์ธํ ์ ์ฑ ์ผ๋ก ๋๊ตฌ ํธ์ถ์ ์ผ๊ด๋๊ฒ ํ์ ํฉ๋๋ค.
- permit / forbid ์ ์ธํ ๊ท์น
- ๋ณด์ํ์ด ์ ์ฑ ๋ ๋ฆฝ ๊ด๋ฆฌ
- HITL ์น์ธ ์ํฌํ๋ก์ฐ ๋ด์ฅ
- principal ํด์ ์คํจ ์ fail-closed
์ฝ๋๊ฐ ์๋๋ผ ์ ์ฑ ์ด ํ๋จํฉ๋๋ค โ ์ผ๊ด์ฑ์ ์ ์ธ์์ ๋์ต๋๋ค.
Hooks vs Interventions
๊ด์ฐฐํ ์ ์ด์ ์ ์ฑ ํ ์ ์ด์ ๊ทผ๋ณธ์ ์ฐจ์ด
Hooks
์ด๋ฒคํธ ๊ธฐ๋ฐ ๊ด์ฐฐ
- ์ฝ๋๋ก ์กฐ๊ฑด ๋ถ๊ธฐ ์์ฑ
- ๊ฐ๋ฐ์๊ฐ ๋ก์ง ์ง์ ๊ตฌํ
- ์ ์ฐํ์ง๋ง ์ผ๊ด์ฑ ๋ณด์ฅ ์ด๋ ค์
- cancel_tool๋ก ๋๊ตฌ ์คํ ์ทจ์ ๊ฐ๋ฅํ๋ ์ ํ์
๊ด์ฐฐ๊ณผ ๊ฐ๋ฒผ์ด ๊ฐ์ ์ ์ ํฉ. ๋ก์ง์ด ์ฝ๋์ ํฉ์ด์ง๋ฏ๋ก ํ ์ฐจ์์ ์ผ๊ด๋ ํต์ ์๋ ํ๊ณ๊ฐ ์์ต๋๋ค.
Interventions
์ ์ฑ ๊ธฐ๋ฐ ํ์
- Cedar ์ ์ธํ ์ ์ฑ ์ผ๋ก ๊ท์น ์ ์
- ๋ณด์ํ์ด ์ ์ฑ ๋ ๋ฆฝ ๊ด๋ฆฌ ๊ฐ๋ฅ
- ์ผ๊ด๋ permit/forbid ํ์ ๋ณด์ฅ
- HITL ์น์ธ ์ํฌํ๋ก์ฐ ๋ด์ฅ
ํต์ ๋ฅผ ์ฝ๋ ๋ฐ์ผ๋ก ๋ถ๋ฆฌ. ์ ์ฑ ํ์ผ๋ง ๋ฐ๊พธ๋ฉด ์ ์ฒด ์์ด์ ํธ์ ์ผ๊ด ์ ์ฉ๋ฉ๋๋ค.
๊ฒน์น๋ ๊ด๊ณ๊ฐ ์๋๋ผ ์ญํ ๋ถ๋ด์ ๋๋ค โ ๊ฐ๋ฐ์์ ์ ์ฐํ ๊ฐ์ ์ Hooks, ์กฐ์ง์ ์ผ๊ด๋ ํต์ ๋ Interventions๋ก ๋๋ ๋งก๊น๋๋ค.
Cedar ์ ์ฑ
permit/forbid ๊ท์น์ผ๋ก ๋๊ตฌ ์ ๊ทผ์ ์ ์ธ์ ์ ์ด
// policy.cedar โ ์์ด์ ํธ ๋๊ตฌ ์ ๊ทผ ์ ์ฑ
(action์ด ๊ณง ๋๊ตฌ ์ด๋ฆ)
// ์ฝ๊ธฐ ๋๊ตฌ๋ ๋ชจ๋ principal์ ํ์ฉ
permit(
principal,
action == Action::"read_file",
resource
);
// ํ๋ก๋์
DB ์ฐ๊ธฐ๋ admin ์ญํ ๋ง โ context_enricher๊ฐ ์ฃผ์
ํ role ๊ฒ์ฌ
permit(
principal,
action == Action::"write_database",
resource
) when {
context.session.role == "admin"
};
// ํ์ผ ์ญ์ ๋ ์ ๋ฉด ๊ธ์ง
forbid(
principal,
action == Action::"delete_file",
resource
);
// ํธ์ถ ์ธก โ agent(prompt, invocation_state={"role": "admin"})
// principal์ principal_resolver๊ฐ invocation_state์์ ํด์ (์คํจ ์ fail-closed)
HITL ์น์ธ ์ํฌํ๋ก์ฐ
๋ฏผ๊ฐ ์์ ์คํ ์ ์ธ๊ฐ ์น์ธ์ ์๊ตฌํ๋ Intervention ํ๋ฆ
-
1
๋๊ตฌ ํธ์ถ ์์ฒญ
์์ด์ ํธ๊ฐ ๋ฏผ๊ฐ ๋๊ตฌ(์: deploy_production) ํธ์ถ ์๋
-
2
HumanInTheLoop ํ์
allowed_tools์ ์๋ ๋ฏผ๊ฐ ๋๊ตฌ๋ฉด ์น์ธ ์๊ตฌ
-
3
์น์ธ ๋๊ธฐ
์คํ ์ผ์ ์ค์ง, ask ์ฝ๋ฐฑ์ผ๋ก ์ด์์ ์ง์ โ ๊ธฐ๋ณธ stdio, ์ฝ๋ฐฑ์ผ๋ก Slack ๋ฑ ์ฐ๋
-
4
์ธ๊ฐ ํ๋จ
์ด์์๊ฐ ์ปจํ ์คํธ ํ์ธ ํ approve ๋๋ deny ์๋ต
-
5
์คํ ๋๋ ์ค๋จ
approve ์ ๋๊ตฌ ์คํ ์ํ, deny ์ ์์ด์ ํธ์ ๊ฑฐ๋ถ ์ฌ์ ์ ๋ฌ
์น์ธ ์์ด๋ ์คํ๋์ง ์์ต๋๋ค โ ์ฌ๋์ด ๋ง์ง๋ง ๊ด๋ฌธ์ ๋๋ค.
Sandbox
Docker ยท SSH ยท Strands Shell
Sandbox
๊ฒฉ๋ฆฌ ๊ณ์ธต โ ์คํ ํ๊ฒฝ ์์ฒด๋ฅผ ๋ถ๋ฆฌํ๋ ์ตํ์ ๋ฐฉ์ด์
Sandbox
์์ด์ ํธ์ ํ์ผยท์ ธ ์์ ์ ํธ์คํธ์์ ๋ถ๋ฆฌ๋ ํ๊ฒฝ์์ ์คํํฉ๋๋ค.
- Docker โ ์ปจํ ์ด๋ ๊ฒฉ๋ฆฌ
- SSH โ ๋ณ๋ ์๋ฒ ๋ฌผ๋ฆฌ ๋ถ๋ฆฌ
- Strands Shell โ ๊ฒฉ๋ฆฌ ๋ฐฑ์๋ ์ ธ
- ์ ๊ณ์ธต์ด ๋ซ๋ ค๋ ์ฌ๊ธฐ์ ๋ฉ์ถค
๋ชจ๋ ์ ์ด๊ฐ ์คํจํด๋ ํผํด ๋ฐ๊ฒฝ์ ์๋๋ฐ์ค ์์ ๋๋ค.
๊ฒฉ๋ฆฌ ์คํ 3๊ฐ์ง ๋ฐฉ์
์ปจํ ์ด๋ ๊ฒฝ๊ณ(Docker) ยท ๋ฌผ๋ฆฌ ๋ถ๋ฆฌ(SSH) ยท ๋๊ตฌํ ์ ธ(Strands Shell) โ ๊ฒฉ๋ฆฌ ์์ค๊ณผ ์ด์ ๋ถ๋ด์ ํธ๋ ์ด๋์คํ
Docker ์ปจํ ์ด๋
ํ์ผ์์คํ ยท๋คํธ์ํฌยทํ๋ก์ธ์ค๋ฅผ ์ปจํ ์ด๋ ๊ฒฝ๊ณ๋ก ๊ฒฉ๋ฆฌ
- โ ํ๊ฒฝ ์ฌํ์ฑ ๋์
- โ ๋คํธ์ํฌ ์ ์ฑ ์ธ๋ฐ ์ ์ด
- โ ์ด๋ฏธ์ง ๊ธฐ๋ฐ ๋น ๋ฅธ ํ๋ก๋น์ ๋
- โ Docker ๋ฐ๋ชฌ ํ์
- โ GPU ํจ์ค์ค๋ฃจ ์ค์ ๋ณต์ก
SSH ์๊ฒฉ ์คํ
๋ณ๋ ์๋ฒ์์ ๋ช ๋ น ์คํ โ ํธ์คํธ์ ๋ฌผ๋ฆฌ์ ๋ถ๋ฆฌ
- โ ์์ ํ ํ๊ฒฝ ๋ถ๋ฆฌ
- โ ๊ธฐ์กด ์ธํ๋ผ ํ์ฉ ๊ฐ๋ฅ
- โ ๋ฉํฐ ํ ๋ํธ ์ง์
- โ ๋คํธ์ํฌ ์ง์ฐ ๋ฐ์
- โ ์๋ฒ ๊ด๋ฆฌ ์ค๋ฒํค๋
Strands Shell
DockerยทSSH ๊ฒฉ๋ฆฌ ๋ฐฑ์๋ ์์์ ์คํ๋๋ ์ ธ ํ๊ฒฝ
- โ ์ ธ ์คํ์ ๋๊ตฌ๋ก ๋ฐ๋ก ์ ๊ณต
- โ ๋ฐฑ์๋ ์ ํ์ผ๋ก ๊ฒฉ๋ฆฌ ์์ค ์กฐ์
- โ ๋น ๋ฅธ ์์
- โ ๊ฒฉ๋ฆฌ ๋ฐฑ์๋ ๋ณ๋ ๊ตฌ์ฑ ํ์
- โ ๋ณต์กํ ์ํฌํ๋ก์ฐ์ ํ๊ณ
์ธ ๋ฐฉ์์ ๋ฐฐํ์ ์ด์ง ์์ต๋๋ค โ Strands Shell์ด DockerยทSSH ๋ฐฑ์๋ ์์์ ๋๊ตฌ๋ฅผ ์ ๊ณตํ๋ ์กฐํฉ์ด ๊ธฐ๋ณธํ์ ๋๋ค.
Docker Sandbox ์ฐ๊ฒฐ
Agent ์์ฑ ์ sandbox=๋ก ์คํ ์ค ์ปจํ ์ด๋์ ์ฐ๊ฒฐ โ ํ์ผยท์ ธ ์์ ๊ฒฉ๋ฆฌ
from strands import Agent
from strands.sandbox.docker import DockerSandbox
# ์ฌ์ ์ ์คํ ์ค์ธ ์ปจํ
์ด๋์ ์ฐ๊ฒฐ
sandbox = DockerSandbox(container="agent-workspace")
agent = Agent(
model="us.anthropic.claude-sonnet-4-6",
sandbox=sandbox,
# sandbox_bash, sandbox_file_editor ๋๊ตฌ๊ฐ ์๋ ๋ฑ๋ก๋จ
)
# ์์ด์ ํธ์ ๋ชจ๋ ํ์ผ/์
ธ ์์
์ด ์ปจํ
์ด๋ ๋ด๋ถ์์ ์คํ
result = agent("ํ๋ก์ ํธ์ ํ
์คํธ๋ฅผ ์คํํ๊ณ ๊ฒฐ๊ณผ๋ฅผ ์์ฝํด์ค")
๊ฒฉ๋ฆฌ ์์ค ์ ํ
๋ณด์ ์๊ตฌ์ฌํญ์ ๋ฐ๋ผ ์ ํํ๋ 3๋จ๊ณ ๊ฒฉ๋ฆฌ ์์ค
Level 1: Strands Shell
๊ฒฉ๋ฆฌ ๋ฐฑ์๋(DockerยทSSH) ์์์ ์คํ๋๋ ์ ธ ๋๊ตฌ
Level 2: Docker
์ปจํ ์ด๋ ๊ฒฉ๋ฆฌ โ ํ์ผยท๋คํธ์ํฌยท์์ ๊ฒฝ๊ณ ์ค์
Level 3: SSH + ์ ์ฉ ์๋ฒ
๋ฌผ๋ฆฌ ๋ถ๋ฆฌ โ ํธ์คํธ์ ์์ ํ ๋ค๋ฅธ ๋จธ์ ์์ ์คํ
๊ฒฉ๋ฆฌ ์์ค์ ๋๊ตฌ์ ํ๊ดด๋ ฅ์ ๋น๋กํด ์ฌ๋ฆฝ๋๋ค โ ๋๋ถ๋ถ์ ํ๋ก๋์ ์ Level 2(Docker)๊ฐ ์ถ๋ฐ์ ์ ๋๋ค.
Hooks ยท Plugins ยท Interventions ยท Sandbox โ 4๊ณ์ธต์ผ๋ก ์์ด์ ํธ๋ฅผ ํต์