문서 분류 및 라우팅
유형별로 문서를 분류하고 전문 에이전트 처리기로 라우팅합니다.
문서 분류 및 라우팅은(는) CoddyKit의 무료 AI Agents 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 AI Agents 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. AI Agents 강의에는 총 4개의 강의가 포함되어 있습니다.
문서 분류가 중요한 이유
문서 지능 에이전트는 송장, 계약서, 보고서, 이메일, 영수증 등 다양한 문서 유형을 입력으로 받을 수 있습니다. 각 유형에는 서로 다른 추출 로직과 업무 규칙이 필요합니다.
문서 분류는 추가 처리 전에 각 문서를 올바른 처리기로 전달하며, 에이전트의 접수 로직 역할을 합니다.
LLM 기반 분류
가장 간단하고 유연한 분류기는 LLM을 사용합니다. 문서 텍스트의 일부를 전달하고 모델에 문서 유형을 식별하도록 요청하세요. 문서 유형이 명확히 구분될 때 잘 작동합니다.
import openai
import os
client = openai.OpenAI(api_key=os.getenv('OPENAI_API_KEY'))
DOC_TYPES = ['invoice', 'contract', 'report', 'email', 'receipt', 'form', 'letter', 'other']
CLASSIFY_PROMPT = '''Classify this document into one of these types: {types}
Document excerpt (first 1000 characters):
{text}
Respond with ONLY the document type as a single word from the list above.'''
def classify_with_llm(text):
response = client.chat.completions.create(
model='gpt-4o-mini', # cheap and fast for classification
messages=[{
'role': 'user',
'content': CLASSIFY_PROMPT.format(
types=', '.join(DOC_TYPES),
text=text[:1000]
)
}],
max_tokens=10,
temperature=0
)
predicted = response.choices[0].message.content.strip().lower()
return predicted if predicted in DOC_TYPES else 'other'규칙 기반 분류로 대체하기
LLM 분류는 정확하지만 비용이 들고 지연 시간이 추가됩니다. 일반적이고 잘 정의된 문서 유형에는 키워드 기반 규칙 분류기가 빠르고 무료이며 해석하기 쉽습니다.
빠른 경로로 규칙 기반 분류를 사용하고, 모호한 경우에는 LLM으로 전환하세요.
CLASSIFICATION_RULES = {
'invoice': [
'invoice', 'invoice number', 'bill to', 'amount due',
'total amount', 'tax invoice', 'payment terms'
],
'contract': [
'agreement', 'terms and conditions', 'hereby agrees',
'party a', 'party b', 'whereas', 'obligations'
],
'report': [
'executive summary', 'quarterly report', 'annual report',
'findings', 'recommendations', 'methodology'
],
'email': ['from:', 'to:', 'subject:', 'date:', 'dear ', 'regards,'],
'receipt': ['receipt', 'thank you for your purchase', 'transaction id', 'cashier']
}
def classify_with_rules(text):
text_lower = text.lower()
scores = {}
for doc_type, keywords in CLASSIFICATION_RULES.items():
score = sum(1 for kw in keywords if kw in text_lower)
if score > 0:
scores[doc_type] = score
if not scores:
return None # no match — fall through to LLM
return max(scores, key=scores.get)
if __name__ == '__main__':
demo_text = 'INVOICE\nBill To: Acme Corp\nAmount Due: $500\nPayment Terms: Net 30'
print('Classified as:', classify_with_rules(demo_text))
단계적 분류 전략
단계적 접근 방식으로 규칙 기반 분류와 LLM 분류를 결합하세요. 먼저 빠른 규칙을 적용하고, 규칙으로 판단할 수 없을 때만 LLM을 사용합니다. 이렇게 하면 예외적인 경우의 정확도를 유지하면서 비용을 최소화할 수 있습니다.
def classify_document(text):
# Tier 1: rule-based (free, fast)
result = classify_with_rules(text)
if result:
print(f'Rule-based classification: {result}')
return {'type': result, 'method': 'rules', 'confidence': None}
# Tier 2: LLM (accurate, slower)
result = classify_with_llm(text)
print(f'LLM classification: {result}')
return {'type': result, 'method': 'llm', 'confidence': None}신뢰도 임계값
모든 분류 결과의 신뢰도가 같은 것은 아닙니다. LLM 분류기에는 분류 결과와 함께 신뢰도 점수를 요청하세요. 신뢰도가 낮으면 사람이 검토할 수 있도록 문서를 표시하세요.
import json
CLASSIFY_WITH_CONFIDENCE_PROMPT = '''Classify this document. Return JSON:
{{"type": "invoice", "confidence": 0.95, "reason": "Contains invoice number and payment terms"}}
Valid types: {types}
Document excerpt: {text}
JSON:'''
def classify_with_confidence(text):
response = client.chat.completions.create(
model='gpt-4o-mini',
messages=[{'role': 'user', 'content': CLASSIFY_WITH_CONFIDENCE_PROMPT.format(
types=', '.join(DOC_TYPES),
text=text[:1000]
)}],
temperature=0
)
try:
result = json.loads(response.choices[0].message.content)
return result
except json.JSONDecodeError:
return {'type': 'other', 'confidence': 0.0, 'reason': 'Parse error'}
LOW_CONFIDENCE_THRESHOLD = 0.6
def classify_and_check(text):
result = classify_with_confidence(text)
if result['confidence'] < LOW_CONFIDENCE_THRESHOLD:
result['needs_review'] = True
print(f'Low confidence ({result["confidence"]}) — flagging for review')
return result문서 라우팅
분류가 완료되면 라우터가 문서를 해당 전문 처리기로 전달합니다. 각 처리기는 해당 문서 유형과 관련된 특정 필드를 추출하는 방법을 알고 있습니다.
def handle_invoice(text):
# Extract: vendor, invoice number, total, due date
extract_prompt = f'''Extract from this invoice (JSON):
{{"vendor": "", "invoice_number": "", "total": 0, "due_date": "", "line_items": []}}
{text[:2000]}\n\nJSON:'''
return llm_call(extract_prompt)
def handle_contract(text):
# Extract: parties, effective date, term, key obligations
extract_prompt = f'''Extract from this contract (JSON):
{{"parties": [], "effective_date": "", "term_months": 0, "key_obligations": []}}
{text[:2000]}\n\nJSON:'''
return llm_call(extract_prompt)
ROUTERS = {
'invoice': handle_invoice,
'contract': handle_contract,
'report': lambda t: llm_call(f'Summarize this report in 3 bullet points:\n{t[:2000]}'),
'email': lambda t: llm_call(f'Extract: sender, subject, action required from this email:\n{t[:1000]}')
}
def route_document(text):
classification = classify_document(text)
doc_type = classification['type']
handler = ROUTERS.get(doc_type, lambda t: llm_call(f'Describe this document:\n{t[:1000]}'))
return handler(text)하위 유형 분류
'계약서'와 같은 상위 유형에는 고용 계약서, NDA, 서비스 계약서, 임대차 계약서와 같은 하위 유형이 있을 수 있습니다. 두 번째 단계의 하위 유형 분류기를 사용하면 필드를 더 정확하게 추출할 수 있습니다.
CONTRACT_SUBTYPES = {
'employment': ['employment', 'employee', 'employer', 'salary', 'compensation', 'job title'],
'nda': ['non-disclosure', 'confidential', 'nda', 'proprietary information'],
'service': ['service agreement', 'scope of work', 'deliverables', 'milestone'],
'lease': ['lease', 'landlord', 'tenant', 'rent', 'premises', 'square feet']
}
def classify_contract_subtype(text):
text_lower = text.lower()
scores = {
subtype: sum(1 for kw in keywords if kw in text_lower)
for subtype, keywords in CONTRACT_SUBTYPES.items()
}
best = max(scores, key=scores.get)
if scores[best] == 0:
return 'general'
return best
def handle_contract_routed(text):
subtype = classify_contract_subtype(text)
print(f'Contract subtype: {subtype}')
# Route to specialized extractor
if subtype == 'nda':
return extract_nda_fields(text)
elif subtype == 'employment':
return extract_employment_fields(text)
else:
return handle_contract(text)일괄 분류 처리 과정
운영 환경에서는 문서가 일괄적으로 도착합니다. 효율적으로 처리하려면 먼저 모든 문서를 분류하고, 유형별로 그룹화한 다음, 각 그룹을 병렬로 처리하세요.
from concurrent.futures import ThreadPoolExecutor
import time
def classify_batch(documents):
results = []
for doc in documents:
text = extract_text(doc['path']) # PDF, OCR, or plain text
classification = classify_document(text[:1500])
results.append({
'doc_id': doc['id'],
'path': doc['path'],
'type': classification['type'],
'method': classification['method'],
'text': text
})
return results
def process_batch(documents, max_workers=4):
# Step 1: classify all (fast)
classified = classify_batch(documents)
# Step 2: group by type
from collections import defaultdict
by_type = defaultdict(list)
for doc in classified:
by_type[doc['type']].append(doc)
# Step 3: process each group
all_results = {}
for doc_type, docs in by_type.items():
handler = ROUTERS.get(doc_type)
if handler:
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = {executor.submit(handler, d['text']): d for d in docs}
for fut, doc in futures.items():
all_results[doc['doc_id']] = fut.result()
return all_results'기타' 및 알 수 없는 유형 처리하기
'기타'로 분류되었거나 신뢰도가 낮은 문서에는 대체 처리 전략이 필요합니다. 사람의 검토를 위해 표시하거나, 일반적인 추출을 시도하거나, 사용자에게 문서 유형을 물어보는 방법이 있습니다.
HUMAN_REVIEW_QUEUE = []
def process_document(doc_path):
text = extract_text(doc_path)
classification = classify_with_confidence(text[:1500])
# High confidence path
if classification['confidence'] >= 0.8 and classification['type'] != 'other':
handler = ROUTERS.get(classification['type'])
return {
'result': handler(text),
'type': classification['type'],
'auto_processed': True
}
# Low confidence or unknown type
HUMAN_REVIEW_QUEUE.append({
'path': doc_path,
'predicted_type': classification['type'],
'confidence': classification['confidence'],
'reason': classification.get('reason', '')
})
print(f'Added to review queue: {doc_path} ({classification["confidence"]:.0%} confident)')
return {'auto_processed': False, 'queued_for_review': True}분류 피드백 순환
사람이 잘못된 분류를 수정하면 그 수정 내용을 기록하세요. 이 기록을 활용해 규칙 기반 분류기를 개선하고, 시간이 지나면서 LLM 분류기를 미세 조정하거나 소수 예제 프롬프트 방식으로 개선할 수 있습니다.
correction_log = []
def log_correction(doc_path, predicted_type, correct_type, text_sample):
correction_log.append({
'doc_path': doc_path,
'predicted': predicted_type,
'correct': correct_type,
'text_sample': text_sample[:200]
})
print(f'Logged correction: {predicted_type} -> {correct_type}')
def build_few_shot_examples(n=5):
recent = correction_log[-n:] # use most recent corrections
examples = []
for entry in recent:
examples.append(
f'Text: {entry["text_sample"]}\nCorrect type: {entry["correct"]}'
)
return '\n\n'.join(examples)
def classify_with_few_shot(text):
few_shot = build_few_shot_examples()
prompt = f'''Examples of correct classifications:\n{few_shot}\n\nNow classify:\n{text[:800]}\n\nType:'''
return llm_call(prompt).strip().lower()분류 후 구조화된 데이터 추출
문서 유형이 결정되면 해당 유형에 중요한 특정 필드를 추출하세요. JSON 스키마를 사용하는 LLM 구조화 추출을 통해 일관되고 구문 분석 가능한 결과를 얻을 수 있습니다.
import json
class FakeMsg:
def __init__(self, content): self.content = content
class FakeChoice:
def __init__(self, content): self.message = FakeMsg(content)
class FakeResponse:
def __init__(self, content): self.choices = [FakeChoice(content)]
class _Completions:
@staticmethod
def create(model, messages, temperature):
return FakeResponse('{"vendor_name": "Acme", "invoice_number": "123", "total": 100}')
class _Chat:
completions = _Completions()
class FakeClient:
chat = _Chat()
client = FakeClient()
EXTRACTION_SCHEMAS = {
'invoice': '{vendor_name: , invoice_number: , invoice_date: , due_date: , subtotal: 0, tax: 0, total: 0, line_items: []}',
}
def extract_structured_fields(text, doc_type):
schema = EXTRACTION_SCHEMAS.get(doc_type)
if not schema:
return {'error': f'No extraction schema for type: {doc_type}'}
prompt = (f'Extract fields from this {doc_type}. Return JSON matching this schema:\n'
f'{schema}\n\nDocument:\n{text[:2000]}\n\nJSON:')
response = client.chat.completions.create(model='gpt-4o-mini', messages=[{'role': 'user', 'content': prompt}], temperature=0)
try:
return json.loads(response.choices[0].message.content)
except json.JSONDecodeError:
return {'error': 'Could not parse extraction result'}
print(extract_structured_fields('Invoice #123 from Acme for $100', 'invoice'))지식 확인
단계적 분류 전략(먼저 규칙을 적용하고, 필요할 때 LLM으로 대체)의 장점은 무엇입니까?
복습: 문서 분류 및 라우팅
문서 분류는 들어오는 문서를 전문 처리기로 전달합니다. 단계적 접근 방식을 사용하세요. 일반적인 유형에는 빠른 키워드 규칙을 적용하고, 예외적인 경우에는 신뢰도 점수와 함께 LLM 분류를 사용하며, 신뢰도가 낮은 문서는 사람의 검토 대기열로 보냅니다.
각 문서 유형에는 고유한 추출 처리기를 할당합니다. 하위 유형 분류(예: NDA와 고용 계약서 구분)를 사용하면 필드를 더 정확하게 추출할 수 있습니다. 피드백 순환을 통해 수정 내용을 기록하고 시간이 지나면서 분류기를 개선하세요.
자주 묻는 질문
“문서 분류 및 라우팅” 강의는 무료인가요?
네 — “문서 분류 및 라우팅” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 AI Agents 강의 전체를 잠금 해제할 수 있습니다. AI Agents 강의에는 총 4개의 강의가 포함되어 있습니다.
“문서 분류 및 라우팅”에서 뭘 배우나요?
유형별로 문서를 분류하고 전문 에이전트 처리기로 라우팅합니다. 브라우저에서 직접 실행하는 실습 코드로 AI Agents을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
AI Agents을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 AI Agents은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.
“문서 분류 및 라우팅” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 AI Agents 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 AI Agents 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.