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는 자동화라기보다 통제하기 어려운 실행 흐름에 가까워진다.