승인형 문서 에이전트 프로젝트
이 장에서 배울 것
이 장은 이 권의 최종 미니 프로젝트다. 지금까지 배운 에이전트 설계 요소를 하나로 묶어 승인형 문서 에이전트를 만든다.
완성할 시스템은 다음 일을 한다.
사용자가 문서를 업로드한다.
에이전트가 문서를 파싱하고 요약한다.
사내 정책이나 가이드 문서를 검색한다.
위험 문구를 찾고 수정안을 제안한다.
사람에게 승인 요청을 보낸다.
사람이 거절하면 피드백을 반영해 다시 작성한다.
승인되면 최종 문서를 저장하고 실행 로그를 남긴다.
목표는 멋진 데모가 아니다. 사람이 승인하고, 추적할 수 있고, 실패했을 때 고칠 수 있는 작은 운영형 에이전트를 만드는 것이다.

왜 이 프로젝트가 중요한가
문서 자동화는 단순한 텍스트 생성보다 어렵다. 실제 업무 문서는 형식이 고정되어 있지 않으면서도 반드시 들어가야 할 정보와 지켜야 할 표현이 있다. 특히 반정형 문서는 정해진 필드만 채우는 방식으로 처리하기 어렵고, 문맥을 읽어 누락 정보와 의미를 파악해야 한다.1
그래서 문서 에이전트는 '잘 쓴 초안'을 만드는 것에서 끝나면 안 된다. 문서 승인 시스템에서 중요한 것은 승인 알림, 라우팅, 권한, 변경 이력, 감사 추적이다.2 LLM 에이전트도 같은 기준을 만족해야 한다.
이 장의 프로젝트는 다음 질문에 답하기 위한 실습이다.
이 작업은 자동 실행해도 되는가?
어느 단계에서 사람 승인이 필요한가?
에이전트가 어떤 근거로 위험 문구를 판단했는가?
거절된 문서는 어떤 피드백으로 다시 작성되었는가?
나중에 문제가 생겼을 때 실행 기록을 재현할 수 있는가?
에이전트가 승인 필요한 도구를 호출할 때 실행을 멈추고, 호출자가 사람의 승인 또는 거절을 받아 다시 실행을 이어가는 방식은 대표적인 human-in-the-loop 패턴이다.3 이 프로젝트의 중심도 바로 그 중단과 재개 구조다.
프로젝트 요구사항
먼저 기능을 만들기 전에 사용자 시나리오를 고정한다.
사용자 시나리오
사용자가 검토할 문서를 업로드한다.
에이전트는 문서 유형을 분류한다. 예: 고객 공지, 계약 초안, 내부 정책, 제안서.
문서 파서가 본문과 메타데이터를 추출한다.
검색 도구가 관련 정책 문서를 찾는다.
초안 생성기가 문서를 요약하고 수정 후보를 만든다.
정책 검사기가 위험 문구를 표시한다.
승인 관리자가 사람에게 승인 요청을 생성한다.
승인자는 승인, 거절, 수정 요청 중 하나를 선택한다.
거절 또는 수정 요청이면 에이전트가 피드백을 반영해 다시 작성한다.
승인되면 최종 문서와 승인 로그를 저장한다.
최소 성공 기준
이 프로젝트는 다음 조건을 만족해야 완료로 본다.
| 영역 | 성공 기준 |
|---|---|
| 문서 처리 | 샘플 문서 3개 이상을 파싱하고 요약한다. |
| 정책 검색 | 문서 유형에 맞는 정책 근거를 1개 이상 연결한다. |
| 위험 탐지 | 금지 표현이나 과장 표현을 표시한다. |
| 승인 흐름 | 승인 대기 상태를 저장하고 재개할 수 있다. |
| 거절 루프 | 사람이 거절한 이유를 반영해 재작성한다. |
| 로그 | 각 단계의 입력, 출력, 상태 전이를 기록한다. |
| 평가 | 테스트 케이스별 기대 결과와 실제 결과를 비교한다. |
핵심 개념 설명
1. 전체 아키텍처
승인형 문서 에이전트는 하나의 거대한 프롬프트가 아니다. 역할이 나뉜 작은 구성요소의 조합이다.
사용자
↓
Router
↓
Document Parser
↓
Retrieval Tool ── Policy Store
↓
Draft Generator
↓
Policy Checker
↓
Approval Manager ── Human Reviewer
↓
Final Document Store
↓
Trace Store / Eval Set / Logs구성요소별 역할은 다음과 같다.
| 구성요소 | 역할 |
|---|---|
| Router | 문서 유형과 처리 경로를 결정한다. |
| Document Parser | PDF, DOCX, TXT 등에서 본문과 메타데이터를 추출한다. |
| Retrieval Tool | 관련 정책, 브랜드 가이드, 법무 기준을 검색한다. |
| Draft Generator | 요약, 수정안, 최종 문서 초안을 생성한다. |
| Policy Checker | 위험 문구, 누락 항목, 형식 위반을 검사한다. |
| Approval Manager | 승인 요청을 만들고 승인 대기 상태를 관리한다. |
| Trace Store | 실행 단계, 도구 호출, 승인 결정을 저장한다. |
| Eval Set | 재현 가능한 테스트 케이스와 기대 결과를 보관한다. |
도구 출력은 항상 신뢰할 수 있는 사실로 취급하면 안 된다. 외부 도구가 반환한 값은 검증해야 하며, 필요한 정보만 전달하고, 비밀키나 토큰을 프롬프트와 로그에 넣지 않아야 한다.4
2. 상태 설계
이 프로젝트의 핵심은 상태다. 승인 대기 중인 문서는 한 번에 끝나지 않는다. 사용자가 나갔다가 돌아올 수도 있고, 승인자가 하루 뒤에 거절할 수도 있다.
상태는 다음처럼 관리한다.
document_id: doc_001
user_id: user_123
document_type: customer_notice
current_step: approval_waiting
raw_text_path: storage/raw/doc_001.txt
summary: 고객 공지 초안 요약
policy_refs:
- policy_marketing_claims.md
risk_items:
- phrase: 업계 최고를 보장합니다
reason: 객관적 근거 없는 최상급 표현
severity: high
draft_path: storage/drafts/doc_001_v2.md
approval:
status: pending
reviewer: legal_team
requested_at: 2026-05-18T10:30:00+09:00
decision: null
feedback: null
iteration: 2
trace_id: trace_9f3a여기서 중요한 필드는 current_step, approval.status, iteration, trace_id다. 이 네 가지가 있어야 중단된 실행을 다시 이어갈 수 있다.
3. 승인 정책
모든 문서에 사람 승인이 필요한 것은 아니다. 승인 정책은 위험도에 따라 나눈다.
| 조건 | 처리 방식 |
|---|---|
| 내부 메모 요약 | 자동 완료 가능 |
| 고객에게 발송되는 문서 | 사람 승인 필요 |
| 법률, 계약, 가격, 보안 관련 표현 포함 | 법무 또는 보안 승인 필요 |
| 개인정보 포함 가능성 있음 | 마스킹 후 승인 필요 |
| 외부 이메일 발송 또는 시스템 등록 | 실행 직전 승인 필요 |
이 정책은 코드 안에 흩어 놓지 말고 별도 설정으로 둔다.
approval_rules:
- name: external_customer_document
when:
document_type: customer_notice
require_approval: true
reviewer_group: legal_team
- name: high_risk_phrase
when:
severity: high
require_approval: true
reviewer_group: compliance_team
- name: internal_summary
when:
document_type: internal_note
severity: low
require_approval: false4. 가드레일
이 프로젝트의 가드레일은 세 층으로 둔다.
첫째, 입력 가드레일이다. 업로드 문서에서 개인정보, 비밀키, 악성 지시문을 탐지한다.
둘째, 실행 가드레일이다. 에이전트가 외부 발송, 문서 삭제, 승인 상태 변경 같은 위험 도구를 실행하려 할 때 막거나 승인 요청으로 바꾼다.
셋째, 출력 가드레일이다. 최종 문서에 금지 문구, 근거 없는 주장, 개인정보가 남아 있는지 다시 검사한다.
가드레일은 단순 차단기로만 쓰지 않는다. 실패 원인을 기록하고 재작성 루프의 피드백으로 사용한다. 운영형 에이전트에서는 가드레일 개입 자체도 추적 이벤트로 남겨야 하며, 실패율을 지속적으로 모니터링해야 한다.5
쉬운 비유: 전자결재함과 검토 비서
이 프로젝트를 전자결재함으로 생각하면 쉽다.
기존 전자결재함은 문서를 올리고 결재선을 따라 승인받는다. 승인자는 반려 사유를 남길 수 있고, 작성자는 수정본을 다시 올린다.
승인형 문서 에이전트는 여기에 검토 비서가 붙은 구조다.
비서는 문서를 먼저 읽는다.
위험한 표현에 밑줄을 긋는다.
관련 규정을 찾아 붙인다.
수정안을 제안한다.
하지만 최종 발송 버튼은 사람이 누른다.
즉, 에이전트는 결재자를 대체하지 않는다. 결재자가 더 빨리, 더 일관되게 판단하도록 준비 작업을 맡는다.
직접 해보는 실습
아래 실습은 특정 클라우드 UI에 의존하지 않는다. 로컬에서 상태 파일과 로그 파일로 시작한다. 이후 실제 서비스에서는 DB, 큐, 객체 저장소, 관측 도구로 교체하면 된다.
1단계: 프로젝트 구조 만들기
doc-agent-project/
data/
samples/
customer_notice_risky.md
customer_notice_safe.md
internal_policy_note.md
policies/
marketing_claims.md
privacy_policy.md
legal_review.md
storage/
drafts/
final/
raw/
state/
logs/
events.jsonl
traces.jsonl
evals/
eval_cases.yaml
app.py샘플 정책 파일에는 짧고 명확한 규칙을 넣는다.
정책: 마케팅 표현 가이드
- 객관적 근거 없이 업계 최고, 완전 보장, 100퍼센트 안전 같은 표현을 쓰지 않는다.
- 고객에게 발송되는 문서의 가격, 성능, 법적 책임 관련 표현은 승인 후 발송한다.
- 개인정보가 포함된 문서는 외부 발송 전 마스킹 여부를 확인한다.2단계: 상태 스키마 정의하기
처음부터 DB를 쓰지 않아도 된다. JSON 파일 하나로 상태 저장을 시작한다.
from typing import TypedDict, List, Optional
class RiskItem(TypedDict):
phrase: str
reason: str
severity: str
class ApprovalState(TypedDict):
status: str # not_required, pending, approved, rejected
reviewer: str
decision: Optional[str]
feedback: Optional[str]
class DocumentState(TypedDict):
document_id: str
user_id: str
document_type: str
current_step: str
raw_text: str
summary: str
policy_refs: List[str]
risk_items: List[RiskItem]
draft: str
approval: ApprovalState
iteration: int
trace_id: str상태는 에이전트의 작업 기억이자 운영 기록의 뼈대다. LLM에게 모든 것을 기억시키지 말고, 애플리케이션이 명시적으로 들고 있어야 한다.
3단계: 도구를 작게 나누기
도구는 크게 만들수록 위험하다. 다음처럼 작게 나눈다.
def parse_document(file_path):
return {'text': read_text(file_path), 'metadata': {'source': file_path}}
def retrieve_policy(document_type):
if document_type == 'customer_notice':
return ['marketing_claims.md', 'privacy_policy.md']
return ['legal_review.md']
def detect_risk_phrases(text):
rules = [
('업계 최고', '객관적 근거 없는 최상급 표현', 'high'),
('완전 보장', '절대적 보장 표현', 'high'),
('책임지지 않습니다', '법적 책임 제한 표현', 'medium'),
]
found = []
for phrase, reason, severity in rules:
if phrase in text:
found.append({'phrase': phrase, 'reason': reason, 'severity': severity})
return found이 단계에서는 LLM 없이도 동작하는 규칙 기반 검사를 먼저 둔다. 이후 표현 변형을 잡기 위해 모델 기반 검사를 추가할 수 있다.
4단계: 초안 생성기 만들기
초안 생성기는 세 가지 입력만 받게 한다.
원문
관련 정책 요약
위험 문구 목록
출력은 자유 텍스트가 아니라 구조화된 결과로 받는다.
summary: 문서 요약
changes:
- original: 원래 문장
revised: 수정 문장
reason: 수정 이유
draft: 수정된 전체 문서
remaining_risks:
- 남아 있는 위험구조화된 출력은 평가하기 쉽다. 나중에 changes 개수, remaining_risks 유무, 정책 근거 포함 여부를 자동으로 검사할 수 있다.
5단계: 승인 대기 상태 구현하기
승인 요청이 필요한 경우 에이전트는 최종 실행을 하지 않는다. 상태만 pending으로 바꾸고 멈춘다.
def request_approval(state):
state['current_step'] = 'approval_waiting'
state['approval'] = {
'status': 'pending',
'reviewer': 'legal_team',
'decision': None,
'feedback': None,
}
write_event('approval_requested', state)
save_state(state)
return state승인자가 승인하면 다음 단계로 간다.
def resume_with_approval(document_id, decision, feedback=None):
state = load_state(document_id)
state['approval']['decision'] = decision
state['approval']['feedback'] = feedback
if decision == 'approved':
state['approval']['status'] = 'approved'
state['current_step'] = 'finalizing'
return finalize_document(state)
state['approval']['status'] = 'rejected'
state['current_step'] = 'revision_required'
return rewrite_with_feedback(state)중요한 점은 승인 요청 후 프로세스가 끝난 것이 아니라 일시 정지되었다는 점이다. 그래서 상태 저장소가 필요하다.
6단계: 거절 후 재작성 루프 만들기
사람이 거절하면 에이전트는 같은 초안을 반복 생성하면 안 된다. 반드시 거절 사유를 입력으로 넣어야 한다.
def rewrite_with_feedback(state):
if state['iteration'] >= 3:
state['current_step'] = 'manual_review_required'
write_event('max_revision_reached', state)
save_state(state)
return state
feedback = state['approval']['feedback'] or '구체적 피드백 없음'
state['draft'] = generate_revision(
raw_text=state['raw_text'],
previous_draft=state['draft'],
feedback=feedback,
risk_items=state['risk_items'],
)
state['iteration'] += 1
write_event('draft_rewritten', state)
return request_approval(state)반복 횟수 제한은 필수다. 무한 재작성은 비용과 지연을 키운다. 3회 이상 실패하면 자동화를 멈추고 사람에게 넘긴다.
7단계: 최종 산출물 저장하기
승인 후에는 최종 문서만 저장하지 않는다. 승인 기록과 실행 로그도 함께 묶어야 한다.
def finalize_document(state):
document_id = state['document_id']
final_path = f"storage/final/{document_id}.md"
write_file(final_path, state['draft'])
state['current_step'] = 'completed'
state['final_path'] = final_path
write_event('document_finalized', state)
save_state(state)
return {'final_path': final_path, 'trace_id': state['trace_id']}실습에서는 파일 저장으로 충분하지만, 실제 서비스에서는 객체 저장소와 DB 메타데이터를 함께 사용한다. 중요한 것은 최종 산출물, 승인 기록, trace ID가 서로 연결되어야 한다는 점이다.
8단계: 추적 이벤트 남기기
로그는 나중에 읽기 쉬워야 한다. 다음 형식을 추천한다.
event_id: evt_001
trace_id: trace_9f3a
document_id: doc_001
step: policy_checker
event_type: risk_detected
timestamp: 2026-05-18T10:12:00+09:00
input_ref: storage/raw/doc_001.txt
output_summary: high 위험 문구 2개 탐지
actor: policy_checker로그에 원문 전체를 무조건 넣지 않는다. 개인정보나 비밀 정보가 섞일 수 있다. 대신 파일 경로, 해시, 요약, 위험 항목처럼 필요한 정보만 남긴다.
9단계: 평가 데이터셋 만들기
evals/eval_cases.yaml에 테스트 케이스를 만든다.
cases:
- id: risky_customer_notice
input_file: data/samples/customer_notice_risky.md
expected:
approval_required: true
min_risk_count: 2
reviewer_group: legal_team
- id: safe_internal_note
input_file: data/samples/internal_policy_note.md
expected:
approval_required: false
min_risk_count: 0
- id: privacy_issue
input_file: data/samples/customer_notice_with_email.md
expected:
approval_required: true
must_mask_pii: true평가 기준은 다음처럼 둔다.
| 평가 항목 | 측정 방법 |
|---|---|
| 문서 유형 분류 | 기대 문서 유형과 실제 유형 비교 |
| 위험 문구 탐지 | 기대 위험 수 이상 탐지했는지 확인 |
| 승인 라우팅 | 올바른 승인 그룹으로 갔는지 확인 |
| 재작성 품질 | 거절 피드백이 반영되었는지 확인 |
| 최종 저장 | 승인 후 산출물과 로그가 생성되었는지 확인 |
| 추적 완전성 | 모든 주요 단계에 trace event가 있는지 확인 |
흔한 실수와 오류 분석
실수 1: 최종 발송까지 자동화한다
고객 발송, 계약 전달, 가격 확정, 법적 표현 포함 문서는 최종 실행 전에 사람 승인이 필요하다. 승인 없는 자동 발송은 데모에서는 편해 보이지만 운영에서는 위험하다.
실수 2: 상태를 대화 기록에만 맡긴다
LLM 대화 기록은 상태 저장소가 아니다. 승인 대기, 거절 사유, 반복 횟수, 최종 저장 경로는 애플리케이션 상태로 저장해야 한다.
실수 3: 정책 검사를 LLM에게만 맡긴다
LLM은 표현 변형을 잘 찾지만 항상 일관적이지 않다. 명확한 금지 표현, 개인정보 패턴, 필수 승인 조건은 규칙 기반 검사로 먼저 잡는다. 그다음 모델 기반 검사를 보조로 붙인다.
실수 4: 로그에 원문과 개인정보를 그대로 남긴다
로그는 디버깅에 필요하지만 보안 사고의 원인이 될 수 있다. 원문 전체, 이메일, 전화번호, 계좌번호, API 키는 마스킹하거나 참조 경로로 대체한다.
실수 5: 거절 루프가 없다
사람이 거절했는데 에이전트가 종료되면 자동화 효과가 작다. 거절 사유를 반영한 재작성 루프가 있어야 한다. 단, 반복 횟수 제한도 함께 있어야 한다.
실수 6: 멀티 에이전트부터 시작한다
문서 파서, 검색기, 검사기, 승인 관리자를 모두 별도 에이전트로 만들 필요는 없다. 처음에는 함수와 규칙으로 충분한 부분을 함수로 두고, 판단이 필요한 부분에만 LLM을 쓴다.
실수 7: 최종 답변만 평가한다
운영형 에이전트는 중간 단계가 중요하다. 문서 분류, 정책 검색, 위험 탐지, 승인 라우팅, 로그 기록을 각각 평가해야 한다.
실습 결과 제출 기준
최종 프로젝트 제출물은 다음 다섯 가지다.
실행 가능한 프로젝트 코드 또는 노트북
샘플 문서 3개 이상
정책 파일 2개 이상
승인 및 거절이 포함된 실행 로그
짧은 회고문
회고문에는 다음을 포함한다.
어떤 단계가 가장 불안정했는가?
어떤 가드레일이 실제로 동작했는가?
사람이 거절한 피드백이 재작성에 반영되었는가?
비용, 지연, 정확도 중 무엇이 가장 큰 병목이었는가?
운영에 넣는다면 어떤 권한과 로그 정책을 추가할 것인가?
요약
승인형 문서 에이전트는 이 권에서 배운 내용을 통합하는 프로젝트다.
핵심은 다음 네 가지다.
첫째, 문서 자동화는 생성보다 승인 흐름이 중요하다. 실제 업무에서는 누가 언제 무엇을 승인했는지 남아야 한다.
둘째, 상태를 명시적으로 설계해야 한다. 승인 대기, 거절, 재작성, 최종 저장은 모두 상태 전이이다.
셋째, 위험 작업은 사람 승인 앞에서 멈춰야 한다. 에이전트는 준비하고 제안하지만, 책임 있는 실행은 승인 후에 한다.
넷째, 로그와 평가가 있어야 개선할 수 있다. 실패한 문서를 다시 보고, 어떤 단계가 틀렸는지 찾아야 다음 버전을 만들 수 있다.
이 프로젝트를 완성했다면 독자는 에이전트를 단순 채팅이 아니라 운영 가능한 업무 시스템으로 설계하는 첫 경험을 한 것이다.
퀴즈
승인형 문서 에이전트에서
approval.status가 필요한 이유는 무엇인가?고객 발송 문서와 내부 요약 문서는 왜 승인 정책이 달라야 하는가?
도구 출력을 그대로 믿으면 어떤 문제가 생길 수 있는가?
거절 후 재작성 루프에 반복 횟수 제한이 필요한 이유는 무엇인가?
입력 가드레일과 출력 가드레일의 차이는 무엇인가?
로그에 원문 전체를 저장하면 어떤 위험이 있는가?
정책 검사를 규칙 기반과 모델 기반으로 나누는 이유는 무엇인가?
최종 답변만 평가하면 놓치는 것은 무엇인가?
승인 요청 후 에이전트 실행은 종료된 것인가, 일시 정지된 것인가?
이 프로젝트에서 trace_id는 어떤 역할을 하는가?
정답 예시
승인 대기, 승인, 거절, 재작성 상태를 구분하고 실행을 재개하기 위해 필요하다.
외부 발송 문서는 법적, 평판, 개인정보 위험이 더 크기 때문이다.
잘못된 값, 악성 출력, 권한 밖 정보가 다음 단계에 전달될 수 있다.
무한 루프, 비용 증가, 지연을 막기 위해 필요하다.
입력 가드레일은 모델 호출 전 검사를, 출력 가드레일은 사용자 반환 또는 실행 전 검사를 담당한다.
개인정보와 비밀 정보가 로그 시스템에 남아 2차 유출 위험이 생긴다.
명확한 규칙은 빠르고 일관적으로 잡고, 미묘한 의미 판단은 모델로 보완하기 위해서다.
문서 분류, 검색, 위험 탐지, 승인 라우팅 같은 중간 실패를 놓친다.
종료가 아니라 일시 정지다. 승인 또는 거절 입력으로 재개된다.
한 문서 처리 과정의 모든 이벤트를 연결해 추적하기 위한 식별자다.
체크리스트
문서 업로드 또는 샘플 입력 경로가 준비되어 있다.
문서 유형 분류 기준이 있다.
정책 파일이 별도로 관리된다.
위험 문구 탐지 결과가 구조화되어 있다.
승인 필요 조건이 설정 파일로 분리되어 있다.
승인 대기 상태가 저장된다.
승인, 거절, 수정 요청을 구분한다.
거절 피드백을 반영해 재작성한다.
반복 횟수 제한이 있다.
최종 문서와 승인 로그가 함께 저장된다.
trace_id로 전체 실행을 추적할 수 있다.
평가 데이터셋이 있다.
로그에 민감 정보가 그대로 남지 않는다.
외부 발송이나 삭제 같은 위험 도구는 자동 실행되지 않는다.
다음 권으로 연결
이 권은 에이전트 설계와 배포의 기본 구조를 다뤘다. 마지막 프로젝트에서는 도구 사용, 상태 관리, 승인, 가드레일, 추적, 평가를 하나의 작은 시스템으로 묶었다.
다음 단계는 조직 단위 운영이다. 여러 팀이 여러 에이전트를 만들면 소유권, 권한, 정책 버전, 감사, 비용 배분, 레드팀 테스트, 고급 AgentOps 자동화가 필요해진다. 이 장의 프로젝트는 그 출발점이다. 먼저 작은 승인형 에이전트를 안전하게 운영할 수 있어야 더 큰 에이전트 플랫폼도 설계할 수 있다.
참고
2Top 15 Document Approval Software to Consider for 2026
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.