
๐งฌ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํ์ฉํ AWS Strands ์์ด์ ํธํ Planner-Generator-Evaluator ํจํด: ๊ฐ๋จํ ๋ธ๋ก๊ทธ ํฌ์คํธ ์์ฑ๊ธฐ
์์ฝ
AWS Strands๋ฅผ ํ์ฉํ์ฌ Planner-Generator-Evaluator ํจํด์ ์์จํ ์์ด์ ํธ ์์คํ ์ ๊ตฌ์ถํ๋ ๋ฐฉ๋ฒ์ ์๊ฐํฉ๋๋ค. ๊ณ ์ ๋ ์ํฌํ๋ก์ฐ ๋์ ํ๋๋ ์์ด์ ํธ๊ฐ ์ํ๋ฅผ ํ๊ฐํ๊ณ ๋๊ตฌ๋ฅผ ๋์ ์ผ๋ก ์ ํํ์ฌ ํ๋ก์ธ์ค๋ฅผ ์ค์ผ์คํธ๋ ์ด์ ํฉ๋๋ค.
ํต์ฌ ํฌ์ธํธ
- Planner ์์ด์ ํธ๊ฐ ๋ฐํ์์ ๋๊ตฌ๋ฅผ ์ ํํ์ฌ ๋์ ์ํฌํ๋ก์ฐ ์ ์ด
- ์์ด์ ํธ๋ฅผ ๋๊ตฌ๋ก ์ฌ์ฉํ๋ 'agents-as-tools' ํจํด ์ ์ฉ
- Planner-Generator-Evaluator ๊ตฌ์กฐ๋ฅผ ํตํ ์์จ์ ์ฝํ ์ธ ์์ฑ ๋ฃจํ ๊ตฌํ
- AWS Strands๋ฅผ ์ด์ฉํ ์์ด์ ํธ ์ค์ฌ์ ์ค์ผ์คํธ๋ ์ด์ ์ํคํ ์ฒ
@ AWS Builder Center์์ ๊ฒ์๋จ
์ด์ ๋ธ๋ก๊ทธ ํฌ์คํธ์์, ์ ๋ LangGraph๋ฅผ ์ฌ์ฉํ์ฌ ํ๊ฐ์ ์์ด์ ํธ(Evaluator Agent)๊ฐ ์์ฑ์ ์์ด์ ํธ(Generator Agent)๊ฐ ์์ฑํ ์ฝํ ์ธ ๋ฅผ ๊ฒํ ํ๊ณ ๊ฐ์ ํ๋ ๊ฐ๋จํ GeneratorโEvaluator ํจํด์ ๊ตฌํํ์ต๋๋ค. ์ด๋ ํจ๊ณผ์ ์ด์์ง๋ง, ์ํฌํ๋ก์ฐ๊ฐ ์ ํ๋ ์์จ์ฑ์ ๊ฐ์ง ์ฌ์ ์ ์๋ ์ํ์ค๋ฅผ ๋ฐ๋์ต๋๋ค.
์ด๋ฒ ๋ฐ๋ณต์์๋ ๋ค์์ ์ํํ ์์ ์ ๋์ ์ผ๋ก ๊ฒฐ์ ํ ์ ์๋ **Planner Agent (ํ๋๋ ์์ด์ ํธ)**๋ฅผ ๋์ ํ์ฌ ์ํคํ ์ฒ๋ฅผ ํ ๋จ๊ณ ๋ ๋ฐ์ ์์ผฐ์ต๋๋ค. **๊ณ ์ ๋ ์ํฌํ๋ก์ฐ (fixed workflow)**๋ฅผ ๋ฐ๋ฅด๋ ๋์ , ํ๋๋๋ ํ์ฌ ์ํ๋ฅผ ํ๊ฐํ๊ณ , ์ด๋ค ์์ด์ ํธ๋ฅผ ํธ์ถํด์ผ ํ ์ง ๊ฒฐ์ ํ๋ฉฐ, ์ ์ฒด ํ๋ก์ธ์ค๋ฅผ ์ค์ผ์คํธ๋ ์ด์ (orchestrate)ํฉ๋๋ค. ์ด๋ฅผ ํตํด **๋ ์์ด์ ํธ ์ค์ฌ์ ์ด๊ณ ์์จ์ ์ธ ์์คํ (more agentic and autonomous system)**์ ๊ตฌ์ถํ์ฌ, ์ํฌํ๋ก์ฐ๊ฐ ๋จ์ํ ํ๋์ฝ๋ฉ๋ ์คํ ๊ฒฝ๋ก์ ์์กดํ๋ ๋์ ๋ฌธ๋งฅ์ ๋ฐ๋ผ ๋์์ ์กฐ์ ํ ์ ์๊ฒ ํฉ๋๋ค.
์ด ์์ ๊ฐ๋ ์ฆ๋ช (Proof-of-Concept) ํ๋ก์ ํธ์์, ์ ๋ AWS Strands Agents๋ฅผ ์ฌ์ฉํ์ฌ ์์ด์ ํธํ ๋ธ๋ก๊ทธ ํฌ์คํธ ์์ฑ๊ธฐ๋ฅผ ๊ตฌ์ถํ์ต๋๋ค. ์ํฌํ๋ก์ฐ๋ PlannerโGeneratorโEvaluator ์ํคํ ์ฒ๋ฅผ ๋ฐ๋ฅด๋ฉฐ, ์ฌ๊ธฐ์ ํ๋๋ ์์ด์ ํธ๋ ํ๋ก์ธ์ค๋ฅผ ์ค์ผ์คํธ๋ ์ด์ ํ๊ณ , ์์ฑ์ ์์ด์ ํธ๋ ์ฝํ ์ธ ๋ฅผ ์์ฑํ๋ฉฐ, ํ๊ฐ์ ์์ด์ ํธ๋ ๋ฐ๋ณต์ ์ธ ํผ๋๋ฐฑ์ ํตํด ๊ฒฐ๊ณผ๋ฌผ์ ๊ฒํ ํ๊ณ ๊ฐ์ ํฉ๋๋ค.
์ํคํ ์ฒ (Architecture):
PlannerAgent: ๋ค์์ ๋ฌด์์ ํ ์ง ๊ฒฐ์ (๋์ ์ผ๋ก plan / replan)
- @tool: generate_blog ํธ์ถ โ GeneratorAgent (๋ฆฌ์์น + ์์ฑ)
- @tool: evaluate_blog ํธ์ถ โ EvaluatorAgent (ํ์ง ๊ฒ์ดํธ)
...
ํ์ดํ๋ผ์ธ๊ณผ์ ์ฃผ์ ์ฐจ์ด์ :
- **Planner์ ์ถ๋ก ๋ฃจํ (Strands ์์ด์ ํธ ๋ฃจํ๋ฅผ ํตํด)**๋ ๋ฐํ์(runtime)์ ๋๊ตฌ(tool)๋ฅผ ์ ํํฉ๋๋ค. ์ด๋ ์ฌ๊ฒ์, ์ถ๊ฐ ์์ค ์์ฝ, ๋ค์ ์ฐ๊ธฐ, ํ๊ฐ ๊ฑด๋๋ฐ๊ธฐ ๋๋ ์กฐ๊ธฐ ์ข ๋ฃ๋ฅผ ์ํํ ์ ์์ต๋๋ค.
- GeneratorAgent์ EvaluatorAgent๋ ๊ทธ ์์ฒด๋ก @tool ํธ์ถ ๊ฐ๋ฅํ Strands ์์ด์ ํธ์ ๋๋ค (์์ด์ ํธ๋ฅผ ๋๊ตฌ๋ก ์ฌ์ฉํ๋ agents-as-tools ํจํด).
- SummarizerAgent๋ ์ ์ฉ ์ ์จ๋(low-temperature) ์์ด์ ํธ๋ก, ์ ์ฒด ํ์ด์ง๋ฅผ ๊ฐ์ ธ์จ ๋ค ์์ฑ์์๊ฒ ์ ๋ฌํ๊ธฐ ์ ๊ธฐ์ฌ๋น 5~10๊ฐ์ ํต์ฌ ๊ธฐ์ ๋ถ๋ ํฌ์ธํธ๋ฅผ ์ถ์ถํฉ๋๋ค.
- ๊ณ ์ ๋ ์ฃ์ง(edge)๊ฐ ์์ต๋๋ค: Planner๋ EvalResult ํผ๋๋ฐฑ๊ณผ ๋ฐ๋ณต ํ์๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ค์์ evaluate_blog๋ฅผ ํธ์ถํ ์ง ๋๋ generate_blog๋ฅผ ํธ์ถํ ์ง๋ฅผ ๊ฒฐ์ ํฉ๋๋ค.
Code GitHub Link: Project on GitHub
์์ด์ ํธ ์ค๊ณ๋ฅผ ํ๊ตฌ ์ค์ด๋ ์์ ๋ง์ ์์คํ ์ ๊ตฌ์ถ ์ค์ด๋ , ์ด ๋ด์ฉ์ ๋ช ํํ๊ณ ์ค์ฉ์ ์ธ ์์์ ์ ์ ๊ณตํ ๊ฒ์ ๋๋ค ๐
๋ชฉ์ฐจ
- ์์กด์ฑ ๋ฐ ์ค์ (Dependencies & Configuration)
- ๋ฉ๋ชจ๋ฆฌ ๊ตฌํ (Implementing Memory)
- ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ ๋ฐ ์์ฝ ์์ด์ ํธ (Fetching Information & Summary Agent)
- ์์ด์ ํธ ์ํ ๋ฐ ์์ด์ ํธ ๋๊ตฌ (Agents State & Agent Tools)
- Generator ๋ฐ Evaluator ์์ด์ ํธ (Generator & Evaluator Agents)
- Planner ์์ด์ ํธ ๋ฐ ๋ฉ์ธ ์คํ (Planner Agent & Main Run)
- ์ ์ฒด ์ฝ๋ ๋ฐ ๋ฐ๋ชจ (All Code & Demo)
- Memory.MD ํ์ผ (Memory.MD File)
- ์์ฑ๋ ๋ธ๋ก๊ทธ ํฌ์คํธ (Generated Blog Post)
- ๊ฒฐ๋ก (Conclusion)
- ์ฐธ๊ณ ๋ฌธํ (References)
์์กด์ฑ ๋ฐ ์ค์ (Dependencies & Configuration)
- ๋ค์ ์์กด์ฑ์ ์ค์นํด ์ฃผ์ธ์:
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
...
Requirements.txt:
strands-agents>=0.1.0
strands-agents-tools>=0.1.0
boto3>=1.34.0
...
-
์ฌ์ฉ ์ค์ธ ๋ฆฌ์ (์: eu-central-1, us-east-1)์์ AWS Bedrock ๋ชจ๋ธ ์ก์ธ์ค๋ฅผ ํ์ฑํํ์ธ์.
- AWS Bedrock > Bedrock Configuration > Model Access > AWS Nova-Pro ๋๋ Claude Sonnet, Opus
-
์ด ์ฝ๋์์๋ AWS๊ฐ ๋ค์ํ ๋ฆฌ์ ์์ ์ ๊ณตํ๋ AWS Nova-Pro๋ฅผ ์ฌ์ฉํ๊ฒ ์ต๋๋ค.
-
๋ชจ๋ธ ์ก์ธ์ค ํ, IAM์์ AWS Bedrock ์๋น์ค์ ์ ๊ทผํ ์ ์๋๋ก
AmazonBedrockFullAccess๊ถํ์ ๋ถ์ฌํ์ธ์. -
AWS ๊ณ์ ์ ์ฌ์ฉํ์ฌ AWS Bedrock ๋ชจ๋ธ์ ๋๋ฌํ๋ ๋ ๊ฐ์ง ์ต์ :
- AWS Config:
aws configure๋ฅผ ์ฌ์ฉํ์ฌconfig๋ฐcredentialsํ์ผ์ ์์ฑํฉ๋๋ค. - .env ํ์ผ์ ์ฌ์ฉํ์ฌ ๋ณ์ ๊ฐ์ ธ์ค๊ธฐ: .env ํ์ผ์ ์ถ๊ฐํฉ๋๋ค:
- AWS Config:
AWS_ACCESS_KEY_ID=์ฌ๊ธฐ์_์ก์ธ์ค_ํค_ID๋ฅผ_๋ถ์ฌ๋ฃ์ผ์ธ์
AWS_SECRET_ACCESS_KEY=์ฌ๊ธฐ์_๋น๋ฐ_์ก์ธ์ค_ํค๋ฅผ_๋ถ์ฌ๋ฃ์ผ์ธ์
- Bedrock API ํค๋ฅผ ์์ฑํ๋ ๋ ๋ค๋ฅธ ๋์์ ์ธ ๋ฐฉ๋ฒ (์ฅ๊ธฐ ๋๋ ๋จ๊ธฐ).
๋ฉ๋ชจ๋ฆฌ(Memory) ๊ตฌํ
- ๋ฉ๋ชจ๋ฆฌ ํจ์ (init, read, append):
import re, sys, json, datetime, requests
from pathlib import Path
from dotenv import load_dotenv
...
์ ๋ณด ๊ฒ์ ๋ฐ ์์ฝ ์์ด์ ํธ (Fetching Information & Summary Agent)
- ๋ฆฌ์์น ํจ์ (ํค์๋ ์์ฑ, DuckDuckGo ๊ฒ์, ๊ฐ์ ธ์ค๊ธฐ ๋ฐ ์์ง):
from strands import Agent, tool
from strands.models.bedrock import BedrockModel
...
์์ด์ ํธ ์ํ(Agents State) ๋ฐ ์์ด์ ํธ ๋๊ตฌ(Agent Tools)
- ์ํ(State)๋ ์์ด์ ํธ ๊ฐ์ ํต์ ํ ํ์๊ฐ ์์ต๋๋ค (EvalResult). Planner ์์ด์ ํธ๋ ์์ด์ ํธ ๋๊ตฌ๋ฅผ ํธ์ถํฉ๋๋ค.
from strands import Agent, tool
from typing import Optional
from pydantic import BaseModel, Field
...
์์ฑ(Generator) ๋ฐ ํ๊ฐ(Evaluator) ์์ด์ ํธ
- Generator๋ ํค์๋๋ฅผ ์์ฑํ๊ณ , ๋ฆฌ์์น๋ฅผ ์ํํ๋ฉฐ, ๋ฐ์ดํฐ(๋งํฌ, ์์ฝ)๋ฅผ ๋ฉ๋ชจ๋ฆฌ(Memory)์ ์ถ๊ฐํ ๋ค์, ๋ธ๋ก๊ทธ ํฌ์คํธ๋ฅผ ์์ฑํฉ๋๋ค.
from strands import Agent, tool
from strands.models.bedrock import BedrockModel
...
- Evaluator๋ ํน์ ๊ธฐ์ค(๊น์ด, ์ต์ ์ฑ, ๊ตฌ์กฐ, ๋ฌธ์ฒด)์ ๋ฐ๋ผ ํฌ์คํธ๋ฅผ ํ๊ฐํ๊ณ , ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ฉ๋ชจ๋ฆฌ(Memory)์ ์ถ๊ฐํฉ๋๋ค.
from strands import Agent, tool
from strands.models.bedrock import BedrockModel
...
Planner ์์ด์ ํธ ๋ฐ ๋ฉ์ธ ์คํ (Planner Agent & Main Run)
Planner ์์ด์ ํธ ๋ฐ ๋ฉ์ธ ์คํ (Planner Agent & Main Run)
[์ด๋ฒ ์ฒญํฌ]
- ๊ฐ ์์ด์ ํธ์ ๋ํด ChatBedrockConverse๋ฅผ ํ ๋นํฉ๋๋ค.
from strands import Agent, tool
from strands.models.bedrock import BedrockModel
# Planner Agent
_plan_model = BedrockModel(model_id="us.amazon.nova-pro-v1:0", temperature=0.3)
def run(topic: str, max_iter: int = MAX_ITER) -> None:
print(f"\n{'='*60}\n TOPIC : {topic}\n MAX ITER: {max_iter}\n{'='*60}")
_state_init(topic, max_iter)
_mem_init(topic)
planner = Agent(
model=_plan_model,
tools=[generate_blog, evaluate_blog, search_web, fetch_page,
summarize_article, read_memory, write_memory],
system_prompt=(
"You are an autonomous blog-pipeline orchestrator for senior AI/ML engineering content.\n\n"
"Goal: produce a high-quality technical blog post (all eval scores โฅ 4) "
f"within the allowed iterations.\n\n"
"Tools available:\n"
" โข generate_blog(extra_instructions) โ research + summarise + write / rewrite\n"
" โข evaluate_blog() โ score the current draft\n"
" โข search_web(query) โ ad-hoc article search\n"
" โข fetch_page(url) โ full raw text of a page\n"
" โข summarize_article(url, title) โ LLM-distilled key points from a page\n"
" โข read_memory() โ inspect research & critiques\n"
" โข write_memory(section, content) โ log planning notes\n\n"
"Flow:\n"
" 1. generate_blog() โ 2. evaluate_blog() โ 3. if rejected, generate_blog(extra_instructions=<fix>) โ repeat.\n"
" Stop when accepted or iteration budget exhausted.\n"
" Use summarize_article between steps when you need deeper insight on a specific source."
),
)
print("
[PlannerAgent] starting autonomous pipeline...\n")
planner(
f"Topic: **{topic}**\n"
f"Max iterations allowed: {max_iter}\n\n"
"์ ์ฒด ํ์ดํ๋ผ์ธ์ ์์จ์ ์ผ๋ก ์คํํฉ๋๋ค. ๊ฒ์๋ฌผ์ด ์น์ธ๋๊ฑฐ๋ ์์ฐ์ด ์์ง๋ ๋๊น์ง ์์ฑํ๊ณ , ํ๊ฐํ๋ฉฐ, ๋ฐ๋ณตํฉ๋๋ค."
)
print(f"\n{'='*60}")
print(f" ์๋ฃ โ ๋ฐ๋ณต {_state['iteration']} | ์น์ธ๋จ={_state['done']}")
print(f" ์ถ๋ ฅ: {BLOG} | ๋ฉ๋ชจ๋ฆฌ: {MEM}")
print(f"{'='*60}\n")
if __name__ == "__main__":
AI ์๋ ์์ฑ ์ฝํ ์ธ
๋ณธ ์ฝํ ์ธ ๋ Dev.to AI tag์ ์๋ฌธ์ AI๊ฐ ์๋์ผ๋ก ์์ฝยท๋ฒ์ญยท๋ถ์ํ ๊ฒ์ ๋๋ค. ์ ์ ์๊ถ์ ์์ ์์์๊ฒ ์์ผ๋ฉฐ, ์ ํํ ๋ด์ฉ์ ๋ฐ๋์ ์๋ฌธ์ ํ์ธํด ์ฃผ์ธ์.
์๋ฌธ ๋ฐ๋ก๊ฐ๊ธฐ