Inicio / Artículos

Reconstrucción de un agente al estilo Claude Code con LangChain Deep Agents

Cómo funciona el arnés de un agente de programación, desde el bucle principal hasta las herramientas, la planificación, la memoria, los subagentes y las aprobaciones en entorno aislado, reconstruido paso a paso con DeepAgents.

Editor: REACTAPP.TOP16 min de lectura3592 palabras

Fuentes: **Build Your Own Claude Code Using Langchin: A Deepdive Into LangChain’s Deep Agents**

Un agente de programación no es un chatbot

Es fácil imaginar a un agente de programación como un chatbot más inteligente, pero esa idea es incorrecta. La capacidad de Claude Code no proviene únicamente del modelo; proviene del sistema de control desarrollado alrededor de él. Ese sistema es el plano de control: el mecanismo invisible que proporciona al modelo herramientas para leer archivos, un entorno donde ejecutar código, memoria de lo que ya ha hecho y una lista de verificación para mantenerse en el camino correcto. Sin ese sistema, un modelo de lenguaje es simplemente un generador de texto; con él, el mismo modelo se comporta como un ingeniero de software autónomo y capaz.

El objetivo aquí es analizar cómo funciona un agente al estilo de Claude Code, pieza por pieza, y reconstruir cada componente con la biblioteca deepagents de LangChain. Al final, la estructura debería resultar comprensible porque habrás creado uno tú mismo.

El bucle en el centro

Una sesión comienza con el mensaje del usuario. Puede tratarse de una pregunta casual sobre el contenido de una carpeta o de una tarea compleja como reestructurar el sistema de inicio de sesión y actualizar sus pruebas. El modelo lee ese mensaje antes de elegir una acción, y a partir de ahí el camino se divide. Cuando se necesita tomar alguna medida, solicita una herramienta, recibe la respuesta y vuelve a empezar el proceso; este ciclo es idéntico tanto para una tarea de un paso como para una de cincuenta pasos. Una vez que responde en prosa ordinaria sin solicitar ninguna herramienta, el bucle se interrumpe y el usuario ve el resultado. La ausencia de una solicitud de herramienta es precisamente la señal de que el agente considera que ha terminado su trabajo.

El cuadro “decide el siguiente paso” combina tres fases que describe la investigación sobre agentes: recopilar contexto leyendo archivos, actuar editando y ejecutando comandos, y verificar realizando pruebas. Estas no se ejecutan como modos separados; son simplemente diferentes llamadas a herramientas que fluyen dentro de un mismo bucle.

Existe un cambio intencional de fase: el modo de planificación. Normalmente, el agente planea y actúa al mismo tiempo. En el modo de planificación, hay una barrera antes del bucle; el agente investiga y elabora una propuesta sin modificar nada: se leen los archivos y se desarrollan las ideas, pero las ediciones y comandos esperan a que el usuario dé su aprobación, y solo entonces se ejecuta el bucle normal. Esto funciona como una válvula de seguridad para tareas grandes o riesgosas. No es lo mismo que la lista de tareas que el agente mantiene para sí mismo en cada tarea, ya que el modo de planificación es una pausa activada por el usuario y que este debe aprobar explícitamente.

El diagrama sigue omitiendo la maquinaria que rodea el bucle: la compactación cuando se llena la memoria, los subagentes que ejecutan sus propios bucles privados, y la capa de permisos y sandbox que se interpone entre “ejecutar una herramienta” y el sistema real. Ninguno de estos elementos modifica el bucle; simplemente lo rodean.

Dentro de este sistema hay cuatro capacidades que son las responsables de mantener el bucle estable. La planificación hace que el agente piense primero. Las herramientas actúan como sus manos. La gestión del contexto evita que las tareas largas agoten su memoria. Los subagentes realizan tareas secundarias en sus propios bucles privados para que el hilo principal permanezca despejado. Ellos suministran recursos al bucle, pero nunca lo reescriben.

Bajo todo eso se encuentra la capa que más importancia tiene. Cuando el agente desea interactuar con el mundo real editando un archivo, ejecutando una orden o utilizando la red, la acción no llega directamente a la máquina. Primero pasa por los permisos y una zona de seguridad, y ninguna otra medida de protección en el diseño tiene tanta relevancia. Imagine intentar mantener seguro a un agente añadiendo una frase como “por favor, no elimine nada importante” a sus instrucciones. Eso es como una nota adhesiva: el modelo puede pasarla por alto, malinterpretarla o ignorarla. La protección real se encuentra fuera del modelo: una regla de permisos que rechaza categóricamente cualquier llamada a herramientas, y una zona de seguridad del sistema operativo que detiene una orden peligrosa sin importar lo que prefiera el modelo.

El motor básico

Antes de recurrir a una biblioteca, es útil ver el motor sin nada adicional. Se trata de un ciclo de agente completo y funcional.

def run_agent_loop(client, user_message, tools, tool_functions, max_turns=20):
    messages = [{"role": "user", "content": user_message}]
    for _ in range(max_turns):
        # 1. Ask the model what to do next.
        response = client.messages.create(
            model="claude-sonnet-4-6", max_tokens=1024, tools=tools, messages=messages
        )
        messages = [*messages, {"role": "assistant", "content": response.content}]

        # 2. Plain text and no tool request? The job is done.
        tool_uses = [b for b in response.content if b.type == "tool_use"]
        if not tool_uses:
            return "".join(b.text for b in response.content if b.type == "text")
        # 3. Run each requested tool and hand the results back. 4. Repeat.
        results = [
            {"type": "tool_result", "tool_use_id": call.id,
             "content": tool_functions[call.name](**call.input)}
            for call in tool_uses
        ]
        messages = [*messages, {"role": "user", "content": results}]
    raise RuntimeError(f"agent loop did not finish within {max_turns} turns")

Consta aproximadamente de treinta líneas, y el resto del diseño es este bucle al que se le añaden elementos adicionales. Las herramientas más potentes se incluyen en tool_functions. Un planificador anota notas en messages. Un compactador elimina contenido de messages cuando este se vuelve excesivo. Un subagente es simplemente la misma función llamada nuevamente con una lista de messages nueva y vacía. La verificación de permisos se realiza en el intervalo entre el modelo que solicita una herramienta y la línea que la ejecuta. El límite de max_turns es esa medida de precaución que mantiene todo sistema real, ya que un modelo atrapado en un bucle gasta recursos sin fin.

Cada agente de programación cuenta con este corazón latiente: el bucle del agente que convierte las instrucciones en acciones, y es sorprendentemente simple:

  1. Preguntarle al modelo qué hacer a continuación.
  2. El modelo responde en texto plano o solicita usar una herramienta.
  • Si se solicita una herramienta, el sistema ejecuta dicha herramienta y devuelve el resultado al modelo.
  • Vuelva al paso 1.
  • Cuando el modelo responde en texto plano sin solicitar ninguna herramienta, la tarea está completada, por lo que debe detenerse y mostrar la respuesta.
  • La lectura de un archivo, la ejecución de una orden y la edición de código pasan todas por ese mismo ciclo. Imagine a un contratista al que se le da una tarea y una lista de verificación: verifica qué sigue, lo realiza, inspecciona el resultado, elige nuevamente y solo informa cuando la lista está vacía. El bucle cumple la función del juicio de ese contratista. También se escala por sí mismo, ya que una pregunta simple puede requerir una sola iteración mientras que una refactorización a gran escala implica decenas de llamadas a herramientas, y nadie necesita determinar de antemano cuántos pasos serán necesarios.

    Construyéndolo con Deep Agents

    No es necesario escribir el bucle while a mano. Al crear un Agente Profundo, se proporciona automáticamente un bucle funcional. Comience instalando la biblioteca y un proveedor de modelos.

    pip install deepagents langchain-anthropic
    

    Luego, cree el agente más simple posible.

    from deepagents import create_deep_agent
    
    def get_weather(city: str) -> str:
        """Get the weather for a given city."""
        return f"It's always sunny in {city}!"
    agent = create_deep_agent(
        model="anthropic:claude-sonnet-4-6",
        tools=[get_weather],
        system_prompt="You are a helpful assistant.",
    )
    result = agent.invoke(
        {"messages": [{"role": "user", "content": "What's the weather in San Francisco?"}]}
    )
    

    Esa llamada a create_deep_agent devuelve un agente con el bucle ya integrado, y .invoke() ejecuta exactamente el ciclo descrito anteriormente. El framework no se preocupa por el modelo subyacente, ya sea de OpenAI, Google o Anthropic; el bucle permanece igual.

    Las herramientas son las manos

    Mientras que el bucle es el motor, las herramientas son las manos. Sin ellas, un modelo solo puede “hablar”. Con herramientas adecuadas, puede leer un código fuente, modificarlo y verificar el resultado. Las herramientas reales de programación se dividen en cuatro categorías:

    • Las herramientas de lectura examinan el entorno sin cambiar nada: read_file abre un archivo, glob encuentra archivos según un patrón de nombre, como todos los archivos .py, y grep busca texto específico dentro de los archivos. Los principiantes a menudo confunden estas dos últimas, pero glob encuentra archivos por nombre y grep por contenido.
  • Las herramientas de edición modifican los contenidos de forma segura: write_file crea archivos nuevos y edit_file modifica parte de uno ya existente.
  • Una herramienta de ejecución ejecuta comandos de shell, los mismos que se escriben en una terminal.
  • Una herramienta de delegación, task, asigna una subtarea a un agente auxiliar.
  • ¿Por qué no permitir que el agente use cat para leer y sed para editar? Podría hacerlo, pero las herramientas diseñadas específicamente tienen ventajas en dos aspectos. El primero es la gestión del presupuesto de tokens: una herramienta de lectura dedicada puede verificar el tamaño del archivo antes de cargarlo y reducirlo si es necesario, mientras que cat muestra todo el contenido y podría superar el límite de la ventana de contexto. El segundo aspecto son los permisos más organizados: una llamada a la herramienta conocida edit_file indica al sistema exactamente qué está ocurriendo para que pueda aplicar reglas de seguridad, pero un comando de shell de formato libre es algo opaco y arriesgado.

    Hay una peculiaridad curiosa aquí. Los modelos se entrenaron con información de Internet pública llena de respuestas en Stack Overflow que utilizan cat y sed, por lo que incluso los más avanzados recurren al shell en bruto por costumbre. El prompt del sistema debe recordarles firmemente que utilicen sus herramientas reales y no los atajos del shell.

    Deep Agents incluye el conjunto completo integrado: planificación, herramientas del sistema de archivos, ejecución y delegación. Las herramientas personalizadas se añaden como funciones decoradas.

    import subprocess
    from langchain_core.tools import tool
    
    MAX_OUTPUT_CHARS = 20_000  # roughly 5k tokens
    @tool
    def run_tests(path: str = ".") -> str:
        """Run the project's pytest suite and return its output.
        Output is truncated to the last 20k characters (failures appear at the
        end). The run is killed after 5 minutes.
        """
        try:
            result = subprocess.run(["pytest", path], capture_output=True,
                                    text=True, check=False, timeout=300)
        except subprocess.TimeoutExpired:
            return "pytest timed out after 300s"
        output = result.stdout + result.stderr
        if len(output) > MAX_OUTPUT_CHARS:
            return ("[... output truncated to the last 20,000 characters ...]\n"
                    + output[-MAX_OUTPUT_CHARS:])
        return output
    agent = create_deep_agent(
        model="anthropic:claude-sonnet-4-6",
        tools=[run_tests],            # merged in alongside the built-ins
        system_prompt="You are a coding assistant. Always run tests after editing.",
    )
    

    El decorador @tool permite que una función ordinaria sea llamable por el modelo. Su documentación tiene gran importancia, ya que el modelo la considera como una guía que indica cuándo y cómo utilizar la herramienta.

    Planificar antes de actuar

    Si se le presenta una solicitud compleja a un modelo sin más funcionalidades, a menudo funciona de forma caótica: una llamada aquí, otra allá, sin un plan general, y a veces en círculos. Los buenos agentes de programación evitan esto al elaborar un plan antes de tocar cualquier cosa. Escriben una lista de tareas estructurada que divide el trabajo en pasos claros, luego la siguen y marcan cada ítem como en progreso o completado. Es como un cocinero que lee toda la receta antes de encender el fuego, para que el paso cuatro no se invente mientras el paso tres se quema.

    Hay un truco ingenioso de ingeniería detrás de esto. Durante una sesión larga, el objetivo original queda sepultado bajo páginas y páginas de resultados generados por las herramientas, y el modelo pierde de vista ese objetivo. Para contrarrestarlo, el sistema vuelve a incluir la lista actual de tareas en la conversación después de las llamadas a las herramientas, de forma similar a como un compañero de trabajo podría pasarle la lista de verificación por encima del escritorio como recordatorio.

    En Deep Agents, esta función la proporciona la herramienta integrada write_todos. No es necesario realizar ninguna configuración, ya que el prompt del sistema por defecto enseña al agente a planificar antes de actuar y a mantener la lista actualizada a lo largo del proceso.

    Gestión de contexto

    Aquí radica la ingeniería más compleja. Cada modelo cuenta con una ventana de contexto, un límite fijo en su memoria de trabajo. Un puñado de archivos grandes junto con unas pocas instrucciones la llenan rápidamente, y una vez que se sobrepasa ese límite, el agente comienza a descartar información, incluso las instrucciones con las que comenzó. Los agentes de programación manejan esto mediante dos tácticas:

    1. Los archivos funcionan como memoria externa. En lugar de insertar un enorme resultado de búsqueda en el chat, el agente lo escribe en el disco y solo conserva el nombre del archivo, abriéndolo nuevamente cuando necesita los detalles.
    2. Se activa la compactación a medida que la conversación se acerca al límite. El sistema detiene el bucle, condensa los intercambios anteriores, guarda los hechos clave en almacenamiento a largo plazo y borra la memoria a corto plazo sobrecargada.

    Imagínese escribir un informe extenso: nadie puede tener todos los datos en la memoria. Se toman notas, se archivan los datos y se deja un resumen de una página sobre el escritorio. Ambas funcionalidades están integradas en Deep Agents, que ofrece un sistema de archivos virtual para almacenar resultados voluminosos, además de un middleware de resumen automático; por lo tanto, esta gestión de la memoria está presente desde el momento en que se crea el agente.

    Subagentes: dividir y vencer

    Algunas tareas son demasiado grandes para abordarlas en una sola conversación. Buscar en un código muy extenso podría significar leer cincuenta archivos, y esos cincuenta archivos no deberían saturar la memoria del agente principal. La solución son los subagentes: el agente principal inicia un asistente, le asigna una tarea específica, como localizar dónde se encuentra la lógica de autenticación, y el asistente trabaja en su propia ventana de contexto separada. Al terminar, devuelve un resumen conciso. De la misma manera, un supervisor que delega una investigación a un empleado junior y recibe un resumen de un párrafo mantiene su trabajo organizado.

    También existe una medida de seguridad: está estrictamente prohibido que los subagentes inicien otros subagentes. De lo contrario, un agente confundido podría generar una cadena interminable de asistentes anidados, lo que causaría un proceso descontrolado que agotaría el presupuesto de la API. Un límite estricto de profundidad garantiza la seguridad en la delegación.

    Deep Agents incluye una herramienta task integrada para crear asistentes. Aquellos especializados, como un buscador de código dedicado, se describen mediante diccionarios sencillos.

    code_searcher = {
        "name": "code-searcher",
        "description": "Searches the codebase to find where specific logic lives. "
                       "Use this for any open-ended 'where is X?' question.",
        # ⚠️ NOTE: the key is `system_prompt`, NOT `prompt` (see correction below).
        "system_prompt": "You are an expert at navigating codebases. Use the grep "
                         "and glob tools to locate relevant files, then report a "
                         "concise summary of what you found and where. Do not make "
                         "any edits.",
    }
    
    agent = create_deep_agent(
        model="anthropic:claude-sonnet-4-6",
        tools=[run_tests],
        system_prompt="You are a coding assistant.",
        subagents=[code_searcher],
    )
    

    El agente principal ahora sabe que existe un especialista, lo cual deja su propia memoria limpia para el verdadero trabajo de programación.

    Seguridad y aprobación humana

    En esta etapa el agente puede editar archivos y ejecutar comandos de shell en una máquina real, lo cual merece ser considerado detenidamente. Un agente entusiasta podría eliminar archivos incorrectos o ejecutar algo destructivo. La fuerza sin frenos representa un verdadero riesgo. Los agentes de programación implementan controles en dos niveles:

    1. Las listas de permisos y las reglas de denegación permiten que herramientas seguras, como la lectura de archivos, se ejecuten automáticamente, mientras que las destructivas, como la eliminación de archivos, requieren aprobación.
    2. Las solicitudes de aprobación, también conocidas como intervención humana, hacen que el agente se detenga y pregunte antes de realizar acciones riesgosas. El usuario aprueba, edita o rechaza la acción, y luego el ciclo continúa.

    La regla fundamental de la seguridad de los agentes es que una instrucción no puede constituir una barrera de seguridad. Pedirle a un modelo que no elimine nada importante es una solicitud cortés y no una barrera, ya que el modelo puede ignorarla completamente. La aplicación de las reglas debe realizarse fuera del modelo, en el mecanismo de control. Una regla de permisos puede rechazar una llamada a una herramienta, y un entorno aislado puede bloquear comandos de shell peligrosos a nivel del sistema operativo, sin importar cuánto lo intente ejecutar el modelo. Por esa razón, la herramienta de ejecución necesita un backend adecuado. La cortesía no es seguridad.

    Deep Agents establece sus limitaciones en el mecanismo de control. El acceso al shell requiere un backend, y el mecanismo integrado de interrupción de LangGraph pausa la ejecución antes de realizar operaciones riesgosas.

    from deepagents import create_deep_agent
    # Import path for backends can vary by version - check the current
    # "Backends" page in the Deep Agents docs.
    from deepagents.backends import LocalShellBackend
    
    agent = create_deep_agent(
        model="anthropic:claude-sonnet-4-6",
        system_prompt="You are a coding assistant working inside this project.",
        backend=LocalShellBackend(),   # enables the `execute` shell tool
    )
    

    Si se solicita una herramienta restringida, el bucle se detiene y el resultado incluye un payload __interrupt__ que describe la acción pendiente y las opciones disponibles: approve, edit, reject o respond. Después de la decisión, la ejecución se reanuda.

    from langgraph.types import Command
    
    result = agent.invoke({"messages": [...]}, config)
    result["__interrupt__"]   # the pending action + allowed decisions — show your user
    
    # You decide; the loop picks up exactly where it paused:
    agent.invoke(Command(resume={"decisions": [{"type": "approve"}]}), config)
    # ... or {"type": "reject"} — the tool is never run, and the model is told so.
    

    Los límites reales deben estar en el arnés, nunca solo en la instrucción.

    Memoria entre sesiones

    Por defecto, un agente pierde todo tan pronto como se cierra una conversación. Un asistente verdaderamente útil debería recordar las convenciones de codificación de un proyecto, lo que le gusta al usuario y dónde dejó las cosas el día anterior. Dos mecanismos se encargan de eso: los puntos de control almacenan el estado preciso del agente, de modo que una tarea larga sobrevive a las interrupciones y continúa desde el mismo punto; la memoria a largo plazo guarda hechos que trascienden cualquier conversación individual, como por ejemplo que un proyecto se indenta con cuatro espacios.

    Dado que Deep Agents funciona en LangGraph, la persistencia se logra mediante la integración de un punto de control.

    from deepagents import create_deep_agent
    from langgraph.checkpoint.memory import InMemorySaver
    
    agent = create_deep_agent(
        model="anthropic:claude-sonnet-4-6",
        tools=[run_tests],
        system_prompt="You are a coding assistant.",
        checkpointer=InMemorySaver(),   # remembers state within a session
    )
    # A "thread_id" ties messages together into one ongoing conversation.
    config = {"configurable": {"thread_id": "project-alpha"}}
    agent.invoke(
        {"messages": [{"role": "user", "content": "Start refactoring the auth module."}]},
        config=config,
    )
    # Later, same thread_id - the agent remembers the earlier turn:
    agent.invoke(
        {"messages": [{"role": "user", "content": "Now update the tests too."}]},
        config=config,
    )
    

    InMemorySaver solo dura durante la ejecución del programa. Para lograr persistencia que sobreviva a los reinicios, cámbielo por un punto de control respaldado por una base de datos.

    Todo en un único agente

    Juntos, estos componentes forman un solo agente: un bucle principal más herramientas personalizadas, funcionalidades de planificación y manejo de contexto, un subagente auxiliar, una shell y el estado guardado; en total, siete partes.

    from deepagents import create_deep_agent
    from deepagents.backends import LocalShellBackend
    from langchain_core.tools import tool
    from langgraph.checkpoint.memory import InMemorySaver
    
    # --- A custom tool (Part 2) — token-budgeted, see Part 2 for the full body ---
    @tool
    def run_tests(path: str = ".") -> str:
        """Run the project's pytest suite and return its (truncated) output."""
        import subprocess
        try:
            result = subprocess.run(["pytest", path], capture_output=True,
                                    text=True, check=False, timeout=300)
        except subprocess.TimeoutExpired:
            return "pytest timed out after 300s"
        output = result.stdout + result.stderr
        return output if len(output) <= 20_000 else "[... truncated ...]\n" + output[-20_000:]
    
    # --- A specialized subagent (Part 5) — note the `system_prompt` key ---
    code_searcher = {
        "name": "code-searcher",
        "description": "Finds where specific logic lives in the codebase. "
                       "Use for open-ended 'where is X?' questions.",
        "system_prompt": "You navigate codebases using grep and glob, then report a "
                         "concise summary of what you found. You never make edits.",
    }
    
    # --- A system prompt that teaches good behavior (Parts 2 & 3) ---
    SYSTEM_PROMPT = """You are a careful coding assistant.
    Workflow:
    1. Plan the task as a to-do list before doing anything.
    2. Use your built-in read, grep, and glob tools to explore - never the raw shell
       equivalents like cat or grep.
    3. Make focused edits.
    4. ALWAYS run the tests after editing, and fix anything that breaks.
    5. Delegate broad codebase searches to the code-searcher subagent.
    """
    
    # --- Assemble the agent (Parts 1, 4, 6, 7 handled by the harness) ---
    agent = create_deep_agent(
        model="anthropic:claude-sonnet-4-6",                  # Part 1: the loop, model-agnostic
        tools=[run_tests],                                     # Part 2: custom hands
        system_prompt=SYSTEM_PROMPT,                           # Parts 2 & 3: behavior + planning
        subagents=[code_searcher],                             # Part 5: delegation
        backend=LocalShellBackend(root_dir=".", virtual_mode=False),  # Part 6: shell access
        interrupt_on={"execute": True,                         # Part 6: the brakes —
                      "write_file": True, "edit_file": True},  #   approval before anything destructive
        checkpointer=InMemorySaver(),                          # Part 7: memory across turns
    )
    # Part 4 (context management) and built-in planning come on automatically.
    
    config = {"configurable": {"thread_id": "my-project"}}
    result = agent.invoke(
        {"messages": [{"role": "user", "content": "The login tests are failing. Fix them."}]},
        config=config,
    )
    print(result["messages"][-1].content)
    

    Menos de cien líneas generan un agente de codificación que planifica, busca, edita, ejecuta pruebas, asigna tareas a ayudantes, permanece dentro de un entorno aislado y mantiene su contexto. Su estructura es similar a la de Claude Code: un bucle simple rodeado de herramientas, funciones de planificación, gestión de memoria, delegación y mecanismos de control que se encuentran fuera del modelo.