Agente de investigación LangGraph con recuperación mediante LlamaIndex y codificación HITL
El particionamiento consciente del formato, la enrutación estructurada de intenciones, los recuperadores especializados en paralelo y la codificación de CLI con control por interrupciones permiten convertir los documentos en experimentos ejecutables de manera segura.
Convertir artículos académicos en experimentos funcionales suele ser un proceso lento que implica leer, resumir, programar y depurar. Un sistema multiagente con estado puede asumir una mayor parte de esta carga: LangGraph para el enrutamiento, LlamaIndex para la recuperación especializada, y una herramienta de programación por línea de comandos que requiere aprobación humana.
1. Ingestión inteligente de datos: no todos los fragmentos son iguales
Cortar ciegamente PDFs, archivos en Python y CSV con una misma estrategia destruye la calidad de la recuperación. Antes de indexarlos en ChromaDB, aplique un proceso de fragmentación sensible al formato: segmentos narrativos para el texto prosaico, bloques lógicos para el código y secciones basadas en filas para las tablas:
# 5. Setup Chunkers
pdf_chunker = SentenceSplitter(chunk_size=512, chunk_overlap=50)
python_chunker = CodeSplitter(language='python')
csv_reader = CSVReader(concat_rows=False)
# 6. Route and Chunk Data
for doc in documents:
file_name = doc.metadata.get("file_name", "")
if file_name.endswith(".pdf"):
nodes = pdf_chunker.get_nodes_from_documents([doc])
elif file_name.endswith(".py"):
nodes = python_chunker.get_nodes_from_documents([doc])
elif file_name.endswith(".csv"):
# CSVs are chunked row-by-row by our custom reader
all_nodes.append(doc)
Las embeddings (por ejemplo, all-minilm de Ollama) reflejan entonces la estructura en lugar de ventanas arbitrarias de caracteres.
2. El cerebro de LangGraph: clasificación de intenciones
LangChain proporciona modelos, prompts y herramientas; LangGraph se encarga del flujo de control. Un diccionario compartido State permite avanzar por cada nodo. La ruta basada en palabras clave de texto libre es frágil, por lo que las salidas estructuradas mediante Pydantic obligan a un intento Literal desde un modelo local como llama3.2:3b:
class IntentClassifier(BaseModel):
message_intent: Literal['chat', 'code', 'knowledge'] = Field(
...,
description="Classify the user intent. 'code' for implementation, 'knowledge' for research details, 'chat' for general tasks."
)
Los bordes condicionales luego envían los datos de manera fiable sin tener que analizar texto sin sentido.
3. La ruta del conocimiento: recuperación paralela
Las preguntas de investigación complejas rara vez se ajustan a una sola búsqueda por similitud. Se puede distribuir la ruta del conocimiento entre cuatro agentes de LlamaIndex al mismo tiempo: abstract_agent, methodology_agent, limitations_agent y conclusion_agent, cada uno con una consulta específica. Se fusionan los hallazgos parciales con el reductor operator.add de LangGraph para que los autores no interfieran entre sí:
class State(TypedDict):
messages: Annotated[list, add_messages]
message_intent: str | None
next_node: str | None
context: Annotated[list[str], operator.add] # Safely appends parallel findings
Un analyst_agent lee el contexto combinado y sintetiza los pros, contras y carencias.
4. Conexión entre la investigación y la acción: programación con intervención humana
La ruta de programación convierte la intención en una orden para una CLI de programación a través de un subproceso. El acceso no controlado al shell es peligroso, por lo que LangGraph utiliza interrupt() para solicitar aprobación:
def accept_coding(state: State):
user_prompt = state['messages'][-1].content
decision = interrupt(f'About to run Claude Code with request:\n\n{user_prompt}\n\nApprove? (yes/no, or type a revised request)')
text = str(decision).strip().lower()
if text in ['y', 'yes', 'approve', 'ok']:
return {'next_node': 'coding_agent'}
if text in ['n', 'no', 'deny', 'cancel']:
return {'messages': [{'role': 'assistant', 'content': 'Coding request denied.'}], 'next_node': 'denied'}
# Allows the user to dynamically edit the prompt mid-flight
return {'messages': [{'role': 'user', 'content': text}], 'next_node': 'accept_coding'}
Una vez obtenida la aprobación, la ejecución se lleva a cabo en un espacio de trabajo aislado y los resultados se envían nuevamente al chat.
Conclusión
Separar la recuperación de información (LlamaIndex) del manejo de estado y enrutamiento (LangGraph) genera un grafo modular que puede investigar y actuar mientras una persona se encarga del proceso crítico de aprobación.
Lecciones operativas
- El fragmentado basado en el formato se compensa por sí mismo la primera vez que la recuperación de código deja de devolver funciones incompletas.
El grafo representa el producto final: los nodos son reemplazables, pero el contrato de estado y las puertas de aprobación son los elementos que garantizan que el proceso de investigación a código sea lo suficientemente seguro como para ejecutarse diariamente.
Recorrido completo de punta a punta
- Procese los artículos y cuadernos de notas con el divisor específico del formato; reconstruya la colección Chroma cuando cambien las fuentes.
- Clasifique la expresión del usuario como conocimiento o código (o con categorías más detalladas a medida que crece el producto).
operator.add, y luego sintetízalos a través del analista.Ese ciclo es suficiente para reemplazar un montón de cuadernos informales por una única interfaz de agente duradera.