Agentic Workflow를 위한 프로세스 분석, 태스크 분해, 에이전트 역할 정의, 워크플로우 DAG 설계 방법론을 체계적으로 정리합니다.
모든 비즈니스 프로세스가 에이전틱 자동화에 적합한 것은 아닙니다. 효과적인 자동화를 위해서는 체계적인 분석과 우선순위 결정이 필요합니다.
프로세스를 평가할 때 다음 4가지 축을 기준으로 점수를 매깁니다.
| 평가 축 | 높은 적합성 | 낮은 적합성 |
|---|---|---|
| 반복 빈도 | 일 수십~수백 회 발생 | 월 1~2회 발생 |
| 판단 복잡도 | 다수 변수 기반 복합 판단 | 단순 if-else로 충분 |
| 데이터 가용성 | 디지털화된 입력 데이터 존재 | 물리적/아날로그 입력 |
| 실패 비용 | 복구 가능, 중간 정도 영향 | 인명 피해, 법적 책임 |
실패 비용이 극단적으로 높은 프로세스(의료 진단의 최종 결정, 법적 구속력이 있는 계약 승인 등)는 에이전틱 자동화의 초기 대상으로 적합하지 않습니다. 이러한 프로세스는 충분한 신뢰가 구축된 후 Augmentation 단계에서 도입하는 것이 안전합니다.
우선순위가 높은 후보를 식별했다면, ROI(Return on Investment) 분석을 수행합니다. 에이전트 개발 비용, LLM API 호출 비용, 유지보수 비용을 합산한 총 비용과 절감되는 인건비, 처리 속도 향상, 에러율 감소 효과를 비교합니다.
선정된 프로세스를 에이전트가 실행할 수 있는 단위 태스크로 분해합니다. 이 과정은 Agentic Workflow 설계의 핵심입니다.
**단일 책임 원칙(Single Responsibility Principle)**을 태스크에 적용합니다. 각 태스크는 하나의 명확한 목적을 가져야 하며, 입력과 출력이 명확하게 정의되어야 합니다.
interface TaskDefinition {
id: string
name: string
description: string
inputs: Record<string, SchemaType>
outputs: Record<string, SchemaType>
requiredTools: string[]
estimatedDuration: Duration
failureStrategy: 'retry' | 'fallback' | 'escalate'
}
// 예시: 고객 문의 분류 태스크
const classifyInquiry: TaskDefinition = {
id: 'classify-inquiry',
name: '고객 문의 분류',
description: '고객 문의 내용을 분석하여 카테고리와 우선순위를 결정',
inputs: {
inquiryText: 'string',
customerHistory: 'CustomerHistory',
},
outputs: {
category: 'InquiryCategory',
priority: 'PriorityLevel',
confidence: 'number',
},
requiredTools: ['customer-db-lookup', 'category-classifier'],
estimatedDuration: { seconds: 5 },
failureStrategy: 'retry',
}태스크를 너무 잘게 분해하면 에이전트 간 통신 오버헤드가 증가하고, 너무 크게 묶으면 에이전트의 컨텍스트가 과부하됩니다. 적절한 분해 깊이를 결정하는 기준은 다음과 같습니다.
분해된 태스크를 기반으로 에이전트의 역할을 정의합니다. 각 에이전트는 페르소나(Persona), 도구 세트(Tool Set), **권한 범위(Permission Scope)**로 구성됩니다.
from dataclasses import dataclass
@dataclass
class AgentRole:
name: str
persona: str # 시스템 프롬프트로 주입
tools: list[str] # 허용된 도구 목록
permissions: PermissionSet
escalation_target: str # 에스컬레이션 대상
# 고객 지원 워크플로우의 에이전트 역할 정의
triage_agent = AgentRole(
name="triage",
persona="당신은 고객 문의를 분류하는 전문가입니다. "
"문의 내용을 분석하여 카테고리, 우선순위, "
"긴급도를 판단합니다.",
tools=["customer_lookup", "ticket_history", "category_classifier"],
permissions=PermissionSet(read=["customer_db"], write=["ticket_db"]),
escalation_target="human_supervisor",
)
resolver_agent = AgentRole(
name="resolver",
persona="당신은 기술 지원 전문가입니다. "
"분류된 문의에 대해 해결책을 조사하고 "
"고객에게 답변을 작성합니다.",
tools=["knowledge_base", "doc_search", "draft_response"],
permissions=PermissionSet(read=["kb_db", "doc_store"], write=["response_draft"]),
escalation_target="senior_engineer",
)에이전트의 페르소나를 정의할 때는 "무엇을 할 수 있는가"뿐만 아니라 "무엇을 하면 안 되는가"도 명확히 기술하는 것이 중요합니다. 이는 에이전트의 행동 범위를 제한하고 예측 가능성을 높이는 핵심 전략입니다.
각 에이전트에게 필요한 도구를 매핑합니다. 도구는 에이전트가 외부 시스템과 상호작용하는 유일한 수단이므로, 올바른 도구 설계가 워크플로우의 성공을 좌우합니다.
// MCP 기반 도구 스키마 정의
const customerLookupTool = {
name: 'customer_lookup',
description: '고객 ID 또는 이메일로 고객 정보를 조회합니다',
inputSchema: {
type: 'object',
properties: {
customerId: { type: 'string', description: '고객 고유 ID' },
email: { type: 'string', format: 'email', description: '고객 이메일' },
},
oneOf: [
{ required: ['customerId'] },
{ required: ['email'] },
],
},
outputSchema: {
type: 'object',
properties: {
id: { type: 'string' },
name: { type: 'string' },
tier: { type: 'string', enum: ['free', 'pro', 'enterprise'] },
history: { type: 'array', items: { type: 'object' } },
},
},
}태스크와 에이전트가 정의되면, 이를 **DAG(Directed Acyclic Graph)**로 구성합니다. DAG는 태스크 간의 의존성과 실행 순서를 명시적으로 표현합니다.
LangGraph는 상태 기반 그래프로 워크플로우를 표현하며, 내장된 체크포인팅 기능으로 장기 실행 워크플로우를 지원합니다.
from langgraph.graph import StateGraph, END
from typing import TypedDict
class WorkflowState(TypedDict):
inquiry: str
customer_info: dict | None
analysis: dict | None
priority: str | None
resolution: str | None
status: str
def receive_inquiry(state: WorkflowState) -> WorkflowState:
"""문의 접수 및 초기 상태 설정"""
return {**state, "status": "received"}
def lookup_customer(state: WorkflowState) -> WorkflowState:
"""고객 정보 조회"""
info = customer_db.lookup(state["inquiry"])
return {**state, "customer_info": info}
def analyze_inquiry(state: WorkflowState) -> WorkflowState:
"""문의 내용 분석 및 분류"""
analysis = triage_agent.analyze(state["inquiry"])
return {**state, "analysis": analysis}
def route_by_priority(state: WorkflowState) -> str:
"""우선순위에 따른 라우팅"""
if state["analysis"]["priority"] == "urgent":
return "escalate"
return "auto_resolve"
# 그래프 구성
graph = StateGraph(WorkflowState)
graph.add_node("receive", receive_inquiry)
graph.add_node("lookup", lookup_customer)
graph.add_node("analyze", analyze_inquiry)
graph.add_node("resolve", auto_resolve)
graph.add_node("escalate", escalate_to_human)
graph.set_entry_point("receive")
graph.add_edge("receive", "lookup")
graph.add_edge("receive", "analyze")
graph.add_conditional_edges("analyze", route_by_priority, {
"auto_resolve": "resolve",
"escalate": "escalate",
})
graph.add_edge("resolve", END)
graph.add_edge("escalate", END)
workflow = graph.compile()Temporal은 워크플로우 즉 코드(Workflow-as-Code) 패러다임으로, 일반적인 프로그래밍 언어로 워크플로우를 작성합니다. 내구성(Durability)이 내장되어 있어 서버가 재시작되어도 워크플로우가 정확히 중단된 지점부터 재개됩니다.
from temporalio import workflow, activity
from datetime import timedelta
@activity.defn
async def classify_inquiry(inquiry_text: str) -> Classification:
"""문의 분류 액티비티"""
return await triage_agent.classify(inquiry_text)
@activity.defn
async def generate_resolution(context: ResolutionContext) -> str:
"""해결책 생성 액티비티"""
return await resolver_agent.resolve(context)
@workflow.defn
class CustomerSupportWorkflow:
@workflow.run
async def run(self, inquiry: InquiryInput) -> WorkflowResult:
# 분류 (5초 타임아웃)
classification = await workflow.execute_activity(
classify_inquiry,
inquiry.text,
start_to_close_timeout=timedelta(seconds=30),
)
if classification.priority == "urgent":
# 긴급: 즉시 에스컬레이션
await workflow.execute_activity(
escalate_to_human,
EscalationRequest(inquiry=inquiry, reason="urgent"),
start_to_close_timeout=timedelta(minutes=5),
)
return WorkflowResult(status="escalated")
# 일반: 자동 해결 시도
resolution = await workflow.execute_activity(
generate_resolution,
ResolutionContext(inquiry=inquiry, classification=classification),
start_to_close_timeout=timedelta(minutes=2),
)
return WorkflowResult(status="resolved", resolution=resolution)LangGraph는 LLM 에이전트의 상태 관리와 체크포인팅에 특화되어 있고, Temporal은 범용 워크플로우 오케스트레이션에 강점이 있습니다. 에이전트의 추론 과정이 핵심인 워크플로우에는 LangGraph가, 여러 시스템을 조율하는 복잡한 비즈니스 프로세스에는 Temporal이 적합합니다.
이 장에서는 Agentic Workflow를 체계적으로 설계하는 방법론을 다루었습니다.
3장에서는 Agentic Workflow에서 가장 중요한 안전장치인 Human-in-the-Loop(HITL) 설계를 다룹니다. 승인 게이트 설계, 에스컬레이션 정책, 신뢰도 기반 라우팅, 그리고 점진적 자율성 확대 전략을 살펴보겠습니다.
이 글이 도움이 되셨나요?
관련 주제 더 보기
Agentic Workflow의 핵심 안전장치인 HITL과 Human-on-the-Loop 패턴, 승인 게이트, 에스컬레이션 정책, 신뢰도 기반 라우팅, 점진적 자율성 확대 전략을 다룹니다.
전통적 자동화에서 에이전틱 자동화로의 전환을 살펴보고, Agentic Workflow의 핵심 개념과 3단계 진화 모델, 구성 요소, 실제 사용 사례를 정리합니다.
시간/일 단위 워크플로우의 듀러블 실행, 체크포인팅, 일시 정지와 재개, 상태 직렬화, 타임아웃 관리, 분산 실행 전략을 정리합니다.