Agent Workflow 실습 정리: Tool Calling, Reflection, Multi-Agent
Agent를 공부하면서 처음에는 “LLM이 도구를 쓰면 Agent”라고 생각했습니다. 그러나 흐름을 직접 나눠보니 도구 호출은 시작일 뿐이었습니다.
실제로 중요한 것은 도구를 언제 호출할지, 결과를 어떻게 검증할지, 여러 역할을 나눌 때 latency와 비용을 어떻게 감당할지였습니다.
flowchart LR
W["Workflow<br/>순서가 고정된 처리"] --> T["Tool Calling<br/>외부 기능 호출"]
T --> R["Reflection<br/>출력과 근거 재점검"]
R --> M["Multi-Agent<br/>역할별 책임 분리"]
M --> H["HITL<br/>민감 작업 승인 경계"]
T --> L["Trace Log<br/>도구 선택과 결과 기록"]
R --> B["Budget<br/>반복 횟수와 비용 제한"]
M --> C["Coordination<br/>latency와 실패 전파 관리"]
Tool Calling은 실행 경계다
Tool Calling은 모델이 외부 기능을 호출하는 구조입니다. 검색, 메일, 일정, DB 조회, 메시지 전송 같은 작업이 여기에 들어갑니다.
하지만 모든 도구 호출이 같은 위험도를 갖지는 않습니다.
| 도구 유형 | 위험 |
|---|---|
| 검색 | 결과가 틀려도 다시 검색할 수 있다 |
| DB 조회 | 권한과 범위가 중요하다 |
| 메시지 전송 | 외부 상태를 바꾼다 |
| 일정 생성/삭제 | 사용자 생활에 직접 영향을 준다 |
| 결제/주문 | 반드시 승인 경계가 필요하다 |
따라서 Agent workflow를 설계할 때는 도구를 호출할 수 있는가보다 호출해도 되는가를 먼저 고려하게 됐습니다.
Reflection과 Self-Refine
Reflection은 모델이 자기 출력이나 중간 결과를 다시 점검하는 흐름입니다.
예를 들어 메일 요약에서는 개별 요약이 빠뜨린 내용을 보정하거나, 최종 리포트가 요구 형식을 지키는지 확인할 수 있습니다. RAG에서는 검색 근거가 부족할 때 질문을 다시 쓰거나 groundedness를 확인한 뒤 재생성할 수 있습니다.
| 패턴 | 역할 |
|---|---|
| Self-Refine | 초안 답변을 다시 고친다 |
| Groundedness Check | 근거에 없는 내용을 줄인다 |
| Evaluator-Optimizer | 평가자가 피드백하고 생성자가 다시 만든다 |
| Query Rewrite Loop | 검색 실패 시 질문을 바꿔 다시 검색한다 |
이 패턴은 품질을 높일 수 있지만 비용과 latency를 늘립니다. 따라서 모든 요청에 붙이기보다 실패 가능성이 큰 구간에 제한적으로 붙이는 편이 낫습니다.
Multi-Agent는 역할 분리다
Multi-Agent는 여러 모델 인스턴스를 붙이는 것이 아니라 역할을 분리하는 방식으로 이해하는 편이 좋습니다.
| 역할 | 예 |
|---|---|
| Planner | 작업 순서 설계 |
| Retriever | 필요한 근거 검색 |
| Domain Agent | 특정 영역 분석 |
| Evaluator | 결과 품질 점검 |
| Reporter | 최종 보고서 생성 |
복잡한 리포트 생성에서는 역할 분리가 자연스럽습니다. 기업 분석, 뉴스, 재무, 거시경제, 차트처럼 근거 축이 다르면 하나의 prompt에 모두 넣는 것보다 역할을 나누는 편이 읽기 쉽습니다.
다만 역할을 나누면 workflow가 길어집니다. 직렬 Agent 구조에서는 2~3분 수준의 latency가 문제가 될 수 있습니다. 따라서 Multi-Agent는 품질 이득과 대기시간을 함께 봐야 합니다.
Workflow와 Agent를 구분한다
모든 자동화가 Agent일 필요는 없습니다.
| 상황 | 적합한 구조 |
|---|---|
| 순서가 고정된 요약 파이프라인 | Workflow |
| 질문에 따라 도구가 달라짐 | Agent |
| 품질 점검 후 재생성 필요 | Evaluator-Optimizer |
| 여러 도메인 분석을 합침 | Multi-Agent |
| 민감한 외부 실행 포함 | Human-in-the-Loop |
제가 실습한다면 고정된 부분은 workflow로 두고, 동적으로 판단해야 하는 부분만 Agent로 옮길 것입니다.
체크리스트
Agent workflow를 설계할 때 다음 질문을 남겨두면 좋습니다.
| 체크 | 질문 |
|---|---|
| 도구 선택 | 모델이 왜 이 도구를 골랐는지 추적 가능한가 |
| 권한 경계 | 외부 상태 변경 전에 승인 단계가 있는가 |
| 실패 처리 | tool call 실패 시 재시도와 fallback이 있는가 |
| 평가 루프 | 결과를 다시 점검하는 기준이 있는가 |
| latency | 사용자가 기다릴 수 있는 시간 안에 끝나는가 |
| 로그 | 중간 tool call과 결과가 기록되는가 |
Reflection loop를 제한하는 코드
Agent workflow를 코드로 적어보면 핵심은 모델 호출이 아니라 반복 상태입니다. 생성 결과를 만들고 평가하며, 기준을 넘지 못하면 reflection을 저장한 뒤 다시 생성합니다. 이 구조는 단순한 prompt chaining보다 실패 원인을 남기기 좋습니다.
간단히 적으면 다음과 같은 형태가 됩니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def run_reflection_loop(task: str, max_iter: int = 3) -> str:
# feedback은 다음 시도에만 쓰고, 원래 task는 건드리지 않는다.
memory = []
best_answer = ""
best_score = 0.0
for _ in range(max_iter):
draft = generator.run(task, reflections=memory)
score, feedback = evaluator.score(task, draft)
# 마지막 답변이 항상 제일 좋은 것은 아니라서 최고 점수 답변은 따로 보관한다.
if score > best_score:
best_score = score
best_answer = draft
# reflection loop는 좋아 보여도 비용이 늘어난다. 멈출 조건이 없으면 바로 길어진다.
if score >= PASS_THRESHOLD:
break
memory.append(reflector.summarize(feedback))
return best_answer
여기서 중요한 것은 반복 횟수와 통과 기준입니다. reflection loop는 품질을 높일 수 있지만, 무제한으로 실행하면 비용과 latency가 커집니다. 따라서 Agent workflow에는 항상 budget이 함께 있어야 합니다.
실행 통제가 먼저다
Agent Workflow를 공부하고 나니 관심이 모델 성능에서 실행 통제로 옮겨갔습니다.
Agent를 붙이기 전에 tool boundary, reflection 기준, latency budget을 먼저 설계해야 합니다. 이 세 가지가 없으면 Agent는 자동화라기보다 통제하기 어려운 실행 흐름에 가까워집니다.