Inicio / Artículos

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.

Editor: REACTAPP.TOP4 min de lectura839 palabras

Fuentes: Building a Multi-Agent Research and Coding Assistant with LangGraph and LlamaIndex- Week3 of Learning AI engineering

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.
  • Los literales de intención estructurada superan a los enrutadores basados en regex cuando los usuarios reformulan el mismo objetivo de cinco maneras diferentes.
  • Los recuperadores especializados en paralelo necesitan reducidores; el modelo de “el último que escribe gana” representa un error silencioso de pérdida de datos.
  • El HITL en comandos de shell es indispensable fuera de un entorno aislado desechable.
  • Mantenga las versiones de los índices de LlamaIndex alineadas con el hash del corpus de artículos, para que la metodología y las limitaciones sigan coincidiendo tras volver a procesar los datos.
  • 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

    1. Procese los artículos y cuadernos de notas con el divisor específico del formato; reconstruya la colección Chroma cuando cambien las fuentes.
    2. Clasifique la expresión del usuario como conocimiento o código (o con categorías más detalladas a medida que crece el producto).
  • Sobre el conocimiento: distribúyelo entre los cuatro recuperadores especializados, reduce los resultados con operator.add, y luego sintetízalos a través del analista.
  • Sobre la codificación: redacta una orden de línea de comandos, solicita aprobación antes de ejecutarla, úsala solo en el entorno aislado, y devuelve los datos de stdout/stderr al estado del chat.
  • Mantén los puntos de control del grafo en caso de que sea necesario reanudar sesiones largas de investigación después de que la computadora entre en modo de suspensión.
  • Ese ciclo es suficiente para reemplazar un montón de cuadernos informales por una única interfaz de agente duradera.