5857 words
29 minutes
[Agent]에이전트 엔지니어링: Improvement Loop

01. Overview#

  • 멀티에이전트 시스템에서 실패는 버그가 아니라 필연임.
  • 아래 사이클은 강화학습의 Agent-Environment 상호작용과 동일한 구조를 따름
Agent → Action → Environment → Reward + New Observation → Agent (반복)

3단계 Improvement Loop#

┌─────────────┐ ┌─────────────────┐ ┌──────────────────┐
│ Feedback │────▶│ Experimentation │────▶│ Continuous │
│ Pipelines │ │ │ │ Learning │
│ │ │ Shadow Deploy │ │ │
│ 관찰/진단/ │ │ A/B Testing │ │ In-Context │
│ 우선순위화 │◀────│ Bayesian Bandit │◀────│ Offline Retrain │
└─────────────┘ └─────────────────┘ └──────────────────┘
▲ │
└──────────────────────────────────────────────┘
단계역할핵심 질문
Feedback Pipelines관찰 · 진단 · 우선순위화무엇이, 왜 실패했는가?
Experimentation통제된 환경에서 변경 검증이 개선이 실제로 효과가 있는가?
Continuous Learning시스템에 개선 내재화어떻게 지속적으로 반영할 것인가?

Improvement Loop 시뮬레이션#

from dataclasses import dataclass, field
from enum import Enum
class Phase(Enum):
FEEDBACK = "feedback"
EXPERIMENT = "experiment"
LEARN = "learn"
@dataclass
class ImprovementLoop:
"""멀티에이전트 시스템의 피드백 기반 개선 사이클"""
cycle: int = 0
insights: list[str] = field(default_factory=list)
validated: list[str] = field(default_factory=list)
def feedback(self, failures: list[str]) -> list[str]:
"""1단계: 실패 관찰 → 패턴 클러스터링 → 우선순위화"""
# 실제로는 자동 RCA + HITL 리뷰가 여기서 수행됨
prioritized = sorted(failures, key=lambda f: failures.count(f), reverse=True)
self.insights = list(dict.fromkeys(prioritized)) # 중복 제거, 빈도순 유지
return self.insights
def experiment(self, insight: str, improved: bool) -> bool:
"""2단계: Shadow deploy / A/B test로 개선안 검증"""
if improved:
self.validated.append(insight)
return improved
def learn(self, insight: str, method: str = "in_context") -> str:
"""3단계: 검증된 개선을 시스템에 내재화"""
self.cycle += 1
return f"[Cycle {self.cycle}] '{insight}' applied via {method}"
# --- 시뮬레이션 ---
loop = ImprovementLoop()
# 1) Feedback: 반복 실패 패턴 수집
raw_failures = [
"tool_selection_error", "prompt_ambiguity",
"tool_selection_error", "tool_selection_error", # 빈도 높음
"timeout", "prompt_ambiguity",
]
insights = loop.feedback(raw_failures)
print("우선순위화된 인사이트:", insights)
# → ['tool_selection_error', 'prompt_ambiguity', 'timeout']
# 2) Experiment: 가장 빈도 높은 이슈 검증
top_issue = insights[0]
is_valid = loop.experiment(top_issue, improved=True)
print(f"'{top_issue}' 개선 검증: {is_valid}")
# 3) Learn: 시스템 반영
result = loop.learn(top_issue, method="prompt_refinement")
print(result)
# → [Cycle 1] 'tool_selection_error' applied via prompt_refinement

Key Takeaways#

  • 기술 + 조직 문제: 엔지니어링, 데이터 사이언스, PM, UX 간 정렬 필요. 실패 자체도 학습의 input으로 보는 문화가 전제조건
  • 순환 구조: 세 단계는 선형이 아니라 사이클. 학습 결과가 다시 피드백 파이프라인의 입력이 되는 구조
  • Fine-tuning만이 답이 아님: 프롬프트 조정, 툴 리팩토링, in-context learning 등 비파라메트릭 접근이 선행되어야함

Concept

  • Improvement Loop : 피드백 → 실험 → 학습의 반복 사이클. 에이전트 시스템이 실패로부터 자기 개선하는 구조
  • Feedback Pipeline : 시스템 인터랙션 데이터를 수집·분석·우선순위화하여 액셔너블 인사이트를 도출하는 자동화 파이프라인
  • Experimentation Framework : Shadow deploy, A/B test 등을 통해 개선안을 프로덕션 투입 전 통제된 환경에서 검증하는 체계
  • Continuous Learning : In-context 조정(즉시) 또는 Offline retraining(주기적)으로 개선을 시스템에 내재화하는 메커니즘
  • Reinforcement Learning 비유 : Agent가 Environment와 상호작용하며 Reward를 받아 행동을 개선하는 구조 — Improvement Loop의 개념적 기반

2. Feedback Pipelines#

  • 멀티에이전트 시스템은 매일 수천~수만 건의 인터랙션을 처리함
  • 사람이 로그를 하나하나 뒤질 수 없으므로, 자동화된 파이프라인이 패턴을 탐지하고 클러스터링하여 액셔너블 인사이트로 변환하는 루프가 필요

자동 프롬프트 최적화 루프 (APO)#

Initial Prompt → Target Model → Output
Evaluation Model → Score
Optimization Model → New Prompt (반복)

이 루프의 핵심은 사람이 프롬프트를 수동으로 튜닝하는 대신, 데이터셋 기반 스코어링으로 자동 개선한다는 것

대표 프레임워크 3가지:

프레임워크핵심 접근특징
DSPyLM 파이프라인을 모듈러 프로그램으로 선언, 옵티마이저가 프롬프트/few-shot 자동 생성Signature → Module → Optimizer 구조
Microsoft Trace그래디언트 없이 일반 피드백(점수, 자연어 비평, 쌍별 선호)으로 최적화Black-box 시스템에 적합
APO평가 모델의 점수를 기반으로 최적화 모델이 새 프롬프트 제안가장 범용적 루프 구조

DSPy 스타일 자동 프롬프트 최적화 시뮬레이션#

from dataclasses import dataclass
@dataclass
class PromptCandidate:
text: str
score: float = 0.0
def evaluate_prompt(prompt: str, test_cases: list[dict]) -> float:
"""평가 모델 시뮬레이션: 프롬프트가 테스트 케이스에 얼마나 부합하는지 점수화"""
score = 0.0
for case in test_cases:
# 실제로는 LLM 호출 → 출력 → 메트릭 비교
if case["keyword"] in prompt:
score += 1.0
return score / len(test_cases)
def optimize_prompt(current: str, feedback: str) -> str:
"""최적화 모델 시뮬레이션: 피드백 기반으로 프롬프트 수정 제안"""
# 실제로는 LLM이 피드백을 보고 새 프롬프트를 생성
return f"{current}\nAdditional guidance: {feedback}"
# --- APO 루프 ---
test_cases = [
{"input": "Suspicious IP login", "expected": "lookup threat intel", "keyword": "threat intel"},
{"input": "Malware hash detected", "expected": "query logs", "keyword": "logs"},
{"input": "Unusual outbound traffic", "expected": "triage incident", "keyword": "triage"},
]
prompt = "You are a SOC analyst. Investigate security alerts."
history: list[PromptCandidate] = []
for iteration in range(3):
score = evaluate_prompt(prompt, test_cases)
history.append(PromptCandidate(text=prompt, score=score))
print(f"[Iter {iteration}] Score: {score:.2f}")
if score >= 1.0:
break
# 점수가 낮은 케이스에서 피드백 생성
missing = [c["keyword"] for c in test_cases if c["keyword"] not in prompt]
feedback = f"Always consider: {', '.join(missing)}"
prompt = optimize_prompt(prompt, feedback)
print(f"\n최종 프롬프트:\n{prompt}")
print(f"최종 점수: {history[-1].score:.2f}{evaluate_prompt(prompt, test_cases):.2f}")

자동 이슈 탐지와 Root Cause Analysis (RCA)#

수동 디버깅은 스케일하지 않는다. 자동 탐지는 다음 패턴을 감시

  • 반복 실패: 특정 tool/skill에서 반복적으로 에러 발생
  • 에러율 스파이크: 갑작스러운 오류 증가
  • 사용자 만족도 이상: engagement 메트릭 하락
  • 버전 간 행동 차이: 배포 환경별 divergent behavior

이슈가 탐지될 경우 RCA가 **“무엇이”가 아니라 “왜”**를 추적

Workflow Tracing → Fault Localization → Pattern Recognition → Impact Assessment
(결정 체인 재구성) (문제 컴포넌트 격리) (일회성 vs 반복 패턴) (빈도 × 심각도)

자동 이슈 탐지 + RCA 파이프라인#

from collections import Counter
from dataclasses import dataclass
from enum import Enum
class Severity(Enum):
LOW = 1
MEDIUM = 2
HIGH = 3
@dataclass
class FailureEvent:
component: str # 어떤 tool/skill에서 발생
error_type: str # 에러 분류
trace_id: str # 워크플로우 추적용
@dataclass
class RCAResult:
component: str
frequency: int
severity: Severity
root_cause: str
priority_score: float # frequency × severity
class AutomatedFeedbackPipeline:
def __init__(self):
self.events: list[FailureEvent] = []
def ingest(self, events: list[FailureEvent]):
self.events.extend(events)
def detect_patterns(self) -> dict[str, int]:
"""규칙 기반 + 통계적 클러스터링으로 반복 패턴 탐지"""
return Counter(
(e.component, e.error_type) for e in self.events
)
def run_rca(self, severity_map: dict[str, Severity]) -> list[RCAResult]:
"""탐지된 패턴에 대해 RCA 수행 → 우선순위화"""
patterns = self.detect_patterns()
results = []
for (component, error_type), freq in patterns.items():
sev = severity_map.get(error_type, Severity.LOW)
results.append(RCAResult(
component=component,
frequency=freq,
severity=sev,
root_cause=f"{component}에서 {error_type} 반복 발생",
priority_score=freq * sev.value,
))
# 우선순위 높은 순 정렬
return sorted(results, key=lambda r: r.priority_score, reverse=True)
# --- 시뮬레이션 ---
pipeline = AutomatedFeedbackPipeline()
# SOC 에이전트의 실패 이벤트 수집
pipeline.ingest([
FailureEvent("query_logs", "invalid_syntax", "trace-001"),
FailureEvent("query_logs", "invalid_syntax", "trace-002"),
FailureEvent("query_logs", "invalid_syntax", "trace-003"),
FailureEvent("lookup_threat_intel", "timeout", "trace-004"),
FailureEvent("triage_incident", "wrong_classification", "trace-005"),
FailureEvent("triage_incident", "wrong_classification", "trace-006"),
])
severity_map = {
"invalid_syntax": Severity.MEDIUM,
"timeout": Severity.LOW,
"wrong_classification": Severity.HIGH,
}
rca_results = pipeline.run_rca(severity_map)
for r in rca_results:
print(f"[Priority {r.priority_score:.0f}] {r.root_cause} "
f"(빈도:{r.frequency}, 심각도:{r.severity.name})")

출력:

[Priority 6] triage_incident에서 wrong_classification 반복 발생 (빈도:2, 심각도:HIGH)
[Priority 6] query_logs에서 invalid_syntax 반복 발생 (빈도:3, 심각도:MEDIUM)
[Priority 1] lookup_threat_intel에서 timeout 반복 발생 (빈도:1, 심각도:LOW)

Key Takeaway#

  • 자동 파이프라인의 역할: 패턴 탐지 + 클러스터링 + 인사이트 서피싱. 사람이 로그를 직접 보는 것을 대체
  • APO 루프: 프롬프트 → 모델 출력 → 평가 → 점수 → 최적화 모델 → 새 프롬프트의 반복. DSPy가 이 구조의 대표 구현체.
  • RCA는 “왜”를 묻는다: Workflow tracing → Fault localization → Pattern recognition → Impact assessment 순서로 진행.
  • 자동화의 한계: 맥락적 뉘앙스, 전략적 우선순위 판단은 사람이 해야 한다. 자동 파이프라인은 대체제가 아니라 증폭기(amplifier).

Concept

  • DSPy : Stanford NLP 개발. LM 파이프라인을 선언적 프로그램으로 취급, Signature(입출력 명세) → Module(CoT, ReAct) → Optimizer(BootstrapFewshot, MIPROv2)로 자동 프롬프트 최적화
  • DSPy Signature/Module/Optimizer 구조 : Signature가 타입 시스템 역할, Module이 추론 전략, Optimizer가 컴파일러 역할을 하는 3계층 아키텍처
  • Microsoft Trace : 그래디언트 없이 점수/자연어 비평/쌍별 선호 등 일반 피드백으로 AI 시스템을 최적화하는 프레임워크. Black-box 시스템에 특히 유용
  • APO (Automatic Prompt Optimization) : 평가 모델의 점수를 기반으로 최적화 모델이 새 프롬프트를 반복 제안하는 루프 구조
  • Root Cause Analysis (RCA) : “무엇이 실패했나”가 아니라 “왜 실패했나”를 추적하는 체계적 분석. Workflow tracing → Fault localization → Pattern recognition → Impact assessment
  • Drift : 사용자 쿼리 패턴, 외부 데이터, 위협 벡터 등이 시간에 따라 변화하면서 기존 프롬프트/모델의 성능이 저하되는 현상
  • Fault Localization : RCA의 핵심 단계. 실패의 원인이 된 정확한 컴포넌트(프롬프트, 스킬 선택, 툴 파라미터 등)를 격리하는 과정

3. Human-in-the-Loop (HITL) Review#

  • 자동화 파이프라인은 패턴 탐지에는 강하지만, 맥락적 판단에는 약하다.
  • 모호한 사용자 의도, 윤리적 뉘앙스, 상충하는 목표, 처음 보는 엣지 케이스는 사람의 직관과 도메인 전문성이 필요.
  • HITL은 자동화의 안전망”이 아닌 일종의 구조화된 에스컬레이션 프로세스

HITL 워크플로우#

Input Data → Agent → Generated Output Candidates
Human Reviewer ←── Manual Feedback
Human-Approved Output → End Users
System Feedback (루프 백)

핵심은 모든 케이스를 사람이 보는 것이 아니라, 에스컬레이션 기준에 따라 필터링된 케이스만 사람에게 라우팅하는 것

에스컬레이션 기준 설계: 두 축#

측정 방법예시
불확실성 (Low Certainty)모델 self-assessed confidence score, 엔트로피, 앙상블 분산confidence < 0.7이면 에스컬레이션
영향도 (High Consequence)도메인별 심각도, 영향 자산의 중요도데이터 유출 가능성, admin 계정 관련

최종 에스컬레이션 결정은 두 축의 조합: risk_score = uncertainty × consequence

에스컬레이션 라우터#

from dataclasses import dataclass
from enum import Enum
class Decision(Enum):
AUTO_APPROVE = "auto_approve"
ESCALATE = "escalate"
@dataclass
class AgentOutput:
incident_id: str
response: str
confidence: float # 0~1, 모델 자체 평가
severity: str # "low", "medium", "high", "critical"
affects_critical_asset: bool
SEVERITY_WEIGHT = {"low": 1, "medium": 2, "high": 3, "critical": 4}
def compute_risk_score(output: AgentOutput) -> float:
"""uncertainty × consequence 기반 리스크 스코어"""
uncertainty = 1.0 - output.confidence
consequence = SEVERITY_WEIGHT.get(output.severity, 1)
if output.affects_critical_asset:
consequence *= 1.5
return uncertainty * consequence
def escalation_router(output: AgentOutput, threshold: float = 1.0) -> Decision:
"""리스크 스코어 기반 에스컬레이션 판단"""
score = compute_risk_score(output)
decision = Decision.ESCALATE if score > threshold else Decision.AUTO_APPROVE
return decision
# --- 시뮬레이션 ---
outputs = [
AgentOutput("INC-001", "False positive from VPN",
confidence=0.92, severity="low", affects_critical_asset=False),
AgentOutput("INC-002", "Possible data exfiltration",
confidence=0.55, severity="critical", affects_critical_asset=True),
AgentOutput("INC-003", "Suspicious login attempt",
confidence=0.75, severity="medium", affects_critical_asset=False),
AgentOutput("INC-004", "Malware hash detected on admin server",
confidence=0.60, severity="high", affects_critical_asset=True),
]
for o in outputs:
score = compute_risk_score(o)
decision = escalation_router(o)
print(f"[{o.incident_id}] risk={score:.2f}{decision.value}"
f" (conf={o.confidence}, sev={o.severity})")

출력:

[INC-001] risk=0.08 → auto_approve (conf=0.92, sev=low)
[INC-002] risk=2.70 → escalate (conf=0.55, sev=critical)
[INC-003] risk=0.50 → auto_approve (conf=0.75, sev=medium)
[INC-004] risk=1.80 → escalate (conf=0.60, sev=high)

다학제 리뷰 프로세스#

에스컬레이션된 케이스는 단일 엔지니어가 아니라 다학제 팀이 분석한다:

역할기여
엔지니어Trace 분석, 기술적 fault localization
데이터 사이언티스트패턴/엣지 케이스 인식, 통계적 분석
PM사용자 니즈와의 정렬 여부 판단
UX 리서처자동 메트릭이 놓치는 사용자 마찰 포인트 발견

리뷰 프로세스 4단계:

Contextual Analysis → Trace Inspection → Impact Assessment → Resolution Design
(통제 환경에서 재현) (로그/트레이스 검토) (범위·심각도 평가) (수정안 설계)

리뷰 기록 및 지식 베이스 축적#

from dataclasses import dataclass, field
from datetime import datetime
@dataclass
class HITLReview:
incident_id: str
reviewer: str
role: str # "engineer", "data_scientist", "pm", "ux"
finding: str
resolution: str
reviewed_at: datetime = field(default_factory=datetime.now)
@dataclass
class HITLKnowledgeBase:
"""리뷰 결과를 축적하여 조직 학습에 활용"""
reviews: list[HITLReview] = field(default_factory=list)
def add_review(self, review: HITLReview):
self.reviews.append(review)
def find_similar(self, keyword: str) -> list[HITLReview]:
"""과거 유사 케이스 검색 → 반복 문제 식별"""
return [r for r in self.reviews if keyword in r.finding]
def recurrence_report(self) -> dict[str, int]:
"""동일 resolution이 반복되면 시스템적 문제 시그널"""
from collections import Counter
return Counter(r.resolution for r in self.reviews)
# --- 시뮬레이션 ---
kb = HITLKnowledgeBase()
kb.add_review(HITLReview(
"INC-002", "Alice", "engineer",
finding="triage_incident가 credential stuffing을 IP brute-force로 오분류",
resolution="프롬프트에 credential stuffing 예시 추가",
))
kb.add_review(HITLReview(
"INC-004", "Bob", "data_scientist",
finding="isolate_host가 critical ops 서버를 확인 없이 격리",
resolution="isolate_host에 confirmation step 추가",
))
kb.add_review(HITLReview(
"INC-007", "Carol", "engineer",
finding="새로운 공격 벡터(API key 탈취)를 기존 프롬프트가 커버 못함",
resolution="프롬프트에 credential stuffing 예시 추가", # 같은 resolution 반복!
))
# 반복 resolution 확인 → 시스템적 문제 시그널
report = kb.recurrence_report()
for resolution, count in report.most_common():
flag = "시스템적 문제!" if count > 1 else ""
print(f"[{count}회] {resolution}{flag}")

출력:

[2회] 프롬프트에 credential stuffing 예시 추가 시스템적 문제!
[1회] isolate_host에 confirmation step 추가

에스컬레이션 비율 최적화#

핵심 균형: 너무 많이 에스컬레이션하면 human fatigue, 너무 적으면 위험한 케이스를 놓친다.

  • 실무 가이드라인: 전체 케이스의 ~10% 이하가 에스컬레이션되도록 threshold를 튜닝.
  • DSPy 같은 프레임워크로 과거 데이터 기반 시뮬레이션으로 최적 threshold를 찾을 수 있다.

핵심 Takeaway#

  • HITL은 안전망이 아니라 구조화된 에스컬레이션: 모든 케이스가 아니라 uncertainty × consequence > threshold인 케이스만 라우팅
  • 불확실성 측정 방법: self-assessed confidence, 엔트로피, 앙상블 분산(3~5회 추론 후 출력 divergence > 20%), 외부 critic 모델
  • 리뷰 결과는 지식 베이스로 축적: 동일 resolution이 반복되면 시스템적 문제의 시그널. 이 데이터가 다시 피드백 파이프라인의 입력이 된다
  • ~10% 에스컬레이션 비율 목표: human fatigue와 위험 누락 사이의 균형점

Concept

  • Human-in-the-Loop (HITL) : 자동화 파이프라인이 처리하기 어려운 모호하거나 고위험 케이스를 사람에게 라우팅하는 구조화된 에스컬레이션 프로세스
  • Escalation Router : uncertainty × consequence 기반 리스크 스코어로 에스컬레이션 여부를 판단하는 라우팅 로직
  • Confidence Score : 모델이 자체 출력에 대해 평가하는 확신도 (0~1). 프롬프트에 지시하여 출력 끝에 포함시킬 수 있음
  • Ensemble Variance : 동일 입력에 대해 3~5회 추론 후 출력 간 divergence를 측정하여 불확실성을 정량화하는 방법
  • Risk Score : (1 - confidence) × severity_weight로 계산. 불확실하면서 영향이 큰 케이스를 우선 에스컬레이션
  • Multidisciplinary Review : 엔지니어, 데이터 사이언티스트, PM, UX가 각자의 관점에서 에스컬레이션 케이스를 분석하는 다학제 리뷰
  • Organizational Learning : HITL 리뷰 결과를 지식 베이스로 축적하여 유사 문제 재발 방지, 신규 팀원 온보딩, 시스템 설계 개선에 활용
  • Calibration (모델 보정) : 모델이 “confidence 0.8”이라고 말할 때 실제로 80% 확률로 맞는지 검증하는 기법. 잘 보정되지 않은 모델의 confidence score는 에스컬레이션 기준으로 신뢰하기 어렵다

4. Prompt & Tool Refinement#

  • 피드백 파이프라인과 HITL 리뷰가 **“무엇이, 왜 문제인지”**를 밝혀냈다면, 이제 실제로 고칠 차례.
  • 에이전트 시스템에서 가장 직접적이고 효과가 큰 두 레버는 프롬프트(모델에 주는 지시)와 (모델이 호출하는 외부 함수/API)이다.

4-1. Prompt Refinement#

피드백 루프에서 반복적으로 드러나는 프롬프트 문제 유형:

문제증상개선 방향
모호한 지시일관성 없는 응답명시적 포맷/제약 추가
너무 넓은 프롬프트할루시네이션, 탈선태스크 바운더리 좁히기
너무 좁은 프롬프트실제 변동성에 대응 못함예시 다양화, 컨텍스트 확장
에러 처리 부재실패 시 멈추거나 엉뚱한 행동에스컬레이션/폴백 지시 추가

개선 전략 4가지:

Rewriting for Clarity → 명확한 지시, 응답 포맷 명시
Adding Exemplars → positive/negative 예시로 추론 앵커링
Decomposing Tasks → 복잡한 멀티스텝을 순차적 서브프롬프트로 분리
Context Expansion → 추가 배경/제약/도메인 지식 주입

DSPy ReAct + MIPROv2로 프롬프트 자동 최적화#

import dspy
# LM 설정
dspy.configure(lm=dspy.LM("openai/gpt-4o-mini"))
# 모의 툴 정의
def lookup_threat_intel(indicator: str) -> str:
"""위협 인텔리전스 조회"""
return f"Intel for {indicator}: potentially malicious"
def query_logs(query: str) -> str:
"""보안 로그 검색"""
return f"Logs for '{query}': suspicious activity detected"
# 학습 데이터: alert → 기대 response
trainset = [
dspy.Example(
alert="Suspicious login from IP 203.0.113.45 to admin account.",
response="Lookup threat intel for IP, query auth logs, triage as true positive."
).with_inputs('alert'),
dspy.Example(
alert="Unusual file download from example.com/malware.exe.",
response="Lookup intel for URL and hash, query endpoint logs, isolate host."
).with_inputs('alert'),
dspy.Example(
alert="Multiple failed logins from new device.",
response="Query auth logs, lookup device IP intel, triage if attack pattern."
).with_inputs('alert'),
]
# ReAct 모듈: 추론 + 툴 호출을 자동으로 엮음
react = dspy.ReAct("alert -> response", tools=[lookup_threat_intel, query_logs])
# MIPROv2 옵티마이저: 프롬프트/few-shot을 자동 생성
optimizer = dspy.MIPROv2(
metric=dspy.evaluate.answer_exact_match,
auto="light",
num_threads=24,
)
optimized_react = optimizer.compile(react, trainset=trainset)
# optimized_react는 내부 프롬프트가 자동 개선된 ReAct 모듈
# → 기존 SOC 에이전트 워크플로우에 drop-in 교체 가능

핵심 포인트: 사람이 프롬프트를 수동으로 튜닝하는 대신, 데이터셋 + 메트릭 기반으로 옵티마이저가 자동으로 더 나은 프롬프트를 생성한다. MIPROv2는 few-shot 예시 선택과 프롬프트 구조까지 자동 최적화한다.


4-2. Tool Refinement#

프롬프트만으로는 부족한 경우가 많다. 에이전트가 호출하는 툴 자체의 내부 로직도 개선 대상이다.

피드백에서 드러나는 툴 문제 유형:

문제예시
잘못된 툴 선택threat intel 조회가 필요한데 log query를 호출
파라미터 오류query_logs에 파싱 불가능한 SQL 전달
툴셋 갭분류 기능이 없어서 triage 단계를 건너뜀
체이닝 실패앞 툴의 출력 포맷이 뒤 툴의 입력과 불일치

툴 리파인먼트 3계층:

Refining Internal Logic → 툴 내부의 프롬프트/모델 최적화
Expanding Capabilities → 새로운 시나리오 커버를 위한 기능 확장
Integration Improvements → 툴 간 입출력 호환성 보장

Python 예제: DSPy BootstrapFewshot로 Tool 내부 분류 로직 최적화#

import dspy
dspy.configure(lm=dspy.LM("openai/gpt-4o-mini"))
# 위협 분류기의 Signature 정의
class ThreatClassifier(dspy.Signature):
"""Classify threat level of an indicator as 'benign', 'suspicious', or 'malicious'."""
indicator: str = dspy.InputField(desc="IP, URL, or file hash")
threat_level: str = dspy.OutputField(desc="benign, suspicious, or malicious")
# Chain of Thought으로 추론 과정을 거쳐 분류
class ThreatClassificationModule(dspy.Module):
def __init__(self):
super().__init__()
self.classify = dspy.ChainOfThought(ThreatClassifier)
def forward(self, indicator):
return self.classify(indicator=indicator)
# 학습 데이터: indicator → 정답 threat_level
trainset = [
dspy.Example(indicator="203.0.113.45", threat_level="suspicious").with_inputs('indicator'),
dspy.Example(indicator="example.com/malware.exe", threat_level="malicious").with_inputs('indicator'),
dspy.Example(indicator="benign-site.net", threat_level="benign").with_inputs('indicator'),
dspy.Example(indicator="abc123def456", threat_level="malicious").with_inputs('indicator'),
dspy.Example(indicator="192.168.1.1", threat_level="benign").with_inputs('indicator'),
dspy.Example(indicator="obfuscated.url/with?params", threat_level="suspicious").with_inputs('indicator'),
]
# 정확도 메트릭
def threat_match(example, pred, trace=None):
return example.threat_level.lower() == pred.threat_level.lower()
# BootstrapFewshot: 학습 데이터에서 few-shot 예시를 자동 선별하여 프롬프트에 삽입
optimizer = dspy.BootstrapFewshotWithRandomSearch(
metric=threat_match,
max_bootstrapped_demos=4, # 자동 생성 예시 최대 수
max_labeled_demos=4, # 라벨된 예시 최대 수
)
optimized_classifier = optimizer.compile(
ThreatClassificationModule(), trainset=trainset
)
# 최적화된 분류기를 tool 함수에 통합
def classify_threat(indicator: str) -> str:
"""최적화된 DSPy 모듈로 위협 수준 분류"""
prediction = optimized_classifier(indicator=indicator)
return prediction.threat_level

MIPROv2 vs BootstrapFewshot 비교:

옵티마이저최적화 대상적합한 상황
MIPROv2프롬프트 구조 + few-shot 예시 + 지시문전체 에이전트 워크플로우의 프롬프트 최적화
BootstrapFewshotfew-shot 예시 선별·생성개별 툴/모듈 내부의 분류/추론 로직 최적화

리파인먼트 원칙#

모든 프롬프트/툴 변경은 반드시:

  1. 문서화: 어떤 문제 → 어떤 변경 → 어떻게 효과 측정할 것인지
  2. 오프라인 검증: held-out 데이터 또는 합성 케이스로 테스트
  3. 라이브 검증: shadow deployment 또는 A/B test로 프로덕션 검증
  4. 모니터링: 사소한 프롬프트 수정도 시스템 전체에 파급 효과 가능

핵심 Takeaway#

  • 프롬프트는 사용자 의도와 에이전트 행동 사이의 브릿지: 미세한 워딩 변화가 추론, 툴 선택, 출력 품질에 큰 영향
  • DSPy의 핵심 가치: 수동 trial-and-error 대신 데이터 기반 자동 최적화. “프롬프트 엔지니어링”을 “프롬프트 컴파일링”으로 전환
  • 툴 리파인먼트는 3계층: 내부 로직 최적화, 기능 확장, 통합 개선. 프롬프트만 고치면 안 되고 툴도 함께 개선해야 한다
  • 변경 → 문서화 → 오프라인 검증 → 라이브 검증 → 모니터링: 이 파이프라인을 건너뛰면 리그레션 위험

Concept

  • Prompt Refinement : 피드백 분석 결과를 바탕으로 프롬프트의 워딩, 구조, 예시, 컨텍스트를 개선하는 과정. 명확화, 예시 추가, 태스크 분해, 컨텍스트 확장 4가지 전략
  • Tool Refinement : 에이전트가 호출하는 외부 함수/API의 내부 로직, 파라미터 구조, 체이닝 호환성을 개선하는 과정. 3계층(내부 로직, 기능 확장, 통합 개선)
  • DSPy ReAct Module : 추론(Chain of Thought)과 행동(Tool 호출)을 자동으로 엮는 모듈. 옵티마이저와 결합하면 추론-행동 프롬프트가 자동 최적화됨
  • MIPROv2 : 프롬프트 구조, few-shot 예시, 지시문을 동시에 최적화하는 DSPy 옵티마이저. 전체 워크플로우 수준의 최적화에 적합
  • BootstrapFewshotWithRandomSearch : 학습 데이터에서 효과적인 few-shot 예시를 자동 선별·생성하는 DSPy 옵티마이저. 개별 모듈/툴 수준의 최적화에 적합
  • DSPy Signature : 태스크의 입출력을 선언적으로 정의하는 타입 명세. indicator -> threat_level처럼 무엇을 받아 무엇을 내놓는지를 명시
  • Exemplar Anchoring : 프롬프트에 positive/negative 예시를 포함하여 모델의 추론을 원하는 방향으로 고정(앵커링)하는 기법
  • DSPy Assertions & Constraints : 모듈 출력에 런타임 제약을 걸어 hallucination이나 포맷 위반을 자동 차단하는 DSPy 기능


관련 문서#

  • [[agentic_design_pattern_01|[Agent]AI Agent 디자인패턴: 01. Agent개념의 이해와 병렬처리 패턴]] tag overlap 1.00
  • [[agentic_design_pattern_02|[Agent]AI Agent 디자인패턴: 핵심 워크플로우 관련 패턴들]] tag overlap 1.00
  • [[agentic_design_pattern_03|[Agent]AI Agent 디자인패턴: MCP와 A2A관련 패턴들]] tag overlap 1.00
  • [[agentic_design_pattern_04|[Agent]AI Agent 디자인패턴: 리소스 핸들링과 가드레일 패턴들]] tag overlap 1.00
  • [[agentic_design_pattern_05|[Agent]AI Agent 디자인패턴: 우선순위 전략패턴 및 Agent Frameworks 선택]] tag overlap 1.00
[Agent]에이전트 엔지니어링: Improvement Loop
https://yjinheon.netlify.app/posts/04ml/agent/11_improvement_loop/
Author
Datamind
Published at
2026-03-21
License
CC BY-NC-SA 4.0