Clasificación y enrutamiento de documentos
Categorice los documentos por tipo y envíelos a gestores de agentes especializados.
Clasificación y enrutamiento de documentos es una lección gratuita de AI Agents en CoddyKit. Esta es la lección 4 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de AI Agents, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de AI Agents incluye 4 lecciones en total.
Por qué es importante clasificar los documentos
Un agente de inteligencia documental puede recibir muchos tipos de documentos: facturas, contratos, informes, correos electrónicos y recibos. Cada tipo requiere una lógica de extracción y unas reglas de negocio diferentes.
La clasificación de documentos dirige cada documento al manejador adecuado antes de cualquier otro procesamiento, actuando como la lógica de admisión del agente.
Clasificación basada en LLM
El clasificador más sencillo y flexible utiliza el LLM. Proporcione una muestra del texto del documento y pida al modelo que identifique el tipo de documento. Funciona bien cuando los tipos de documentos son claramente distintos.
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'Alternativa de clasificación basada en reglas
La clasificación mediante LLM es precisa, pero tiene un coste y añade latencia. Para tipos de documentos comunes y bien definidos, un clasificador basado en reglas y palabras clave es rápido, gratuito y fácil de interpretar.
Use las reglas como vía rápida y recurra al LLM en los casos ambiguos.
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))
Estrategia de clasificación por niveles
Combine la clasificación basada en reglas y la clasificación mediante LLM en un enfoque por niveles: aplique primero las reglas rápidas y use el LLM solo cuando las reglas no sean concluyentes. Esto minimiza el coste y mantiene la precisión en los casos límite.
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}Umbrales de confianza
No todas las clasificaciones tienen el mismo nivel de confianza. En los clasificadores basados en LLM, solicite una puntuación de confianza junto con la clasificación. Si la confianza es baja, marque el documento para que lo revise una persona.
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 resultEnrutadores de documentos
Una vez clasificado, un enrutador envía el documento a su manejador especializado. Cada manejador sabe cómo extraer los campos específicos relevantes para ese tipo de documento.
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)Clasificación de subtipos
Los tipos generales, como «contrato», pueden tener subtipos: contrato laboral, acuerdo de confidencialidad, contrato de servicios o contrato de arrendamiento. Un clasificador de subtipos en una segunda fase permite extraer los campos con mayor precisión.
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)Pipeline de clasificación por lotes
En producción, los documentos llegan en lotes. Procéselos de forma eficiente: clasifique primero todos los documentos, agrúpelos por tipo y, después, procese cada grupo en paralelo.
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_resultsGestión de tipos «otros» y desconocidos
Los documentos clasificados como «otros» o con baja confianza necesitan una estrategia alternativa. Algunas opciones son marcarlos para revisión humana, intentar una extracción genérica o preguntar al usuario qué tipo de documento es.
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}Bucle de retroalimentación de la clasificación
Cuando una persona corrija una clasificación incorrecta, registre la corrección. Use estos registros para mejorar el clasificador basado en reglas y para ajustar o crear prompts few-shot para el clasificador basado en LLM con el paso del tiempo.
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()Extracción de datos estructurados tras la clasificación
Una vez determinado el tipo de documento, extraiga los campos específicos importantes para ese tipo. Use la extracción estructurada mediante LLM con un esquema JSON para obtener resultados coherentes y analizables.
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'))Comprobación de conocimientos
¿Cuál es la ventaja de utilizar una estrategia de clasificación por niveles (primero reglas y después LLM como alternativa)?
Resumen: clasificación y enrutamiento de documentos
La clasificación de documentos dirige los documentos entrantes a manejadores especializados. Use un enfoque por niveles: reglas rápidas basadas en palabras clave para los tipos habituales, clasificación mediante LLM con puntuaciones de confianza para los casos límite y colas de revisión humana para los documentos con baja confianza.
Cada tipo de documento tiene su propio manejador de extracción. La clasificación de subtipos (por ejemplo, acuerdo de confidencialidad frente a contrato laboral) permite extraer los campos con mayor precisión. Registre las correcciones para mejorar los clasificadores con el tiempo mediante un bucle de retroalimentación.
Preguntas frecuentes
¿La lección «Clasificación y enrutamiento de documentos» es gratis?
Sí — el texto completo de «Clasificación y enrutamiento de documentos» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de AI Agents, actualiza a CoddyKit PRO. El curso de AI Agents incluye 4 lecciones en total.
¿Qué aprenderé en «Clasificación y enrutamiento de documentos»?
Categorice los documentos por tipo y envíelos a gestores de agentes especializados. Practicas AI Agents con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar AI Agents?
No se requiere experiencia previa. AI Agents en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 4 de 4.
¿Cuánto tiempo toma la lección «Clasificación y enrutamiento de documentos»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de AI Agents?
Sí. Cada lección de AI Agents incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Análisis de PDF con PyMuPDF y pdfplumber
- OCR para documentos escaneados
- Agentes de preguntas y respuestas sobre varios documentos
- Clasificación y enrutamiento de documentos