Flujo de trabajo del agente LangChain
En la sección anterior conocimoscreate_agent()los parámetros de.
Esta sección profundiza en el interior del agente para comprender cómo funciona: cómo colaboran el modelo y las herramientas, y cuándo se detiene el agente.
Bucle de ejecución del agente
El núcleo del agente es un bucle simple:Llamar al modelo → verificar si se necesitan herramientas → ejecutar herramientas → repetirHasta que el modelo deja de solicitar llamadas a herramientas, el agente se detiene y devuelve el resultado final.

A continuación, entendemos este proceso rastreando cada paso del agente.
Ejemplo
load_dotenv()
from langchain.tools import tool
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.messages import HumanMessage
@tool
def get_weather(city: str) -> str:
"""Consultar el clima de la ciudad especificada.
Args:
city: nombre de la ciudad
"""
weather_data = {
"Hangzhou": "Soleado, 25°C",
"Pekín": "Nublado, 18°C",
}
return weather_data.get(city, f"No se encontraron datos del clima para {city}")
@tool
def get_time(city: str) -> str:
"""Consultar la hora actual de la ciudad especificada.
Args:
city: nombre de la ciudad
"""
time_data = {
"Hangzhou": "14:30",
"Pekín": "14:30",
"Nueva York": "02:30",
}
return time_data.get(city, fNo se encontraron datos de hora para {city})
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
agent = create_agent(
model=model,
tools=[get_weather, get_time],
system_prompt=Eres un asistente servicial.,
)
# Usar stream_mode="updates" para ver cada paso
print(=== Seguimiento del proceso de ejecución del agente ===\n")
step = 0
for chunk in agent.stream(
{"messages": [HumanMessage(content=¿Cómo está el clima en Hangzhou ahora? ¿Qué hora es?)]},
stream_mode="updates",
):
step += 1
print(f--- Paso {step} ---)
for node_name, update in chunk.items():
print(fNodo: {node_name})
if "messages" in update:
for msg in update["messages"]:
if hasattr(msg, 'tool_calls') and msg.tool_calls:
# Los mensajes de AI contienen llamadas a herramientas
for tc in msg.tool_calls:
print(f→ Solicitar llamada a herramienta: {tc['name']}({tc['args']}))
elif msg.type == "tool":
print(f→ Resultado de la herramienta [{msg.name}]: {msg.content})
elif msg.type == "ai" and msg.content:
print(f→ Respuesta de AI: {msg.content[:100]})
Resultado de la ejecución:
=== Agent 执行过程追踪 ===
--- 步骤 1 ---
Nodo: model
→ 请求调用工具: get_weather({'city': '杭州'})
→ 请求调用工具: get_time({'city': '杭州'})
--- 步骤 2 ---
Nodo: tools
→ 工具结果 [get_weather]: 晴,25°C
→ 工具结果 [get_time]: 14:30
--- 步骤 3 ---
Nodo: model
→ AI 回复: 杭州现在天气晴朗,气温25°C,当前时间是14:30.
De este rastreo se puede ver que el agente ejecutó3 pasos:
- Paso 1 (nodo model): el modelo recibe la pregunta, determina que necesita llamar a las herramientas get_weather y get_time, y devuelve dos tool_call
- Paso 2 (nodo tools): ejecuta las dos herramientas y obtiene los resultados del clima y la hora
- Paso 3 (nodo model): el modelo recibe los resultados de las herramientas, determina que la información es suficiente y genera la respuesta final
Explicación detallada de stream_mode
stream() admite varios modos de stream_mode, cada uno proporciona información con diferente granularidad:
| Modo | Contenido devuelto | Escenario de aplicación |
|---|---|---|
| updates | Actualización de estado después de la ejecución de cada nodo | Rastrear los pasos de ejecución del agente, mostrar resultados intermedios |
| values | Estado completo después de la ejecución de cada nodo | Necesita ver el historial de mensajes completo en cada paso |
| messages | Flujo de mensajes token por token | El frontend muestra en streaming el efecto de escritura de la IA |
| custom | Eventos personalizados | El middleware envía eventos personalizados mediante stream_writer |
stream_mode="values": ver los cambios completos de estado
Ejemplo
for i, chunk in enumerate(agent.stream(
{"messages": [HumanMessage(content=¿Cómo está el clima en Hangzhou?)]},
stream_mode="values",
)):
messages = chunk.get("messages", [])
print(fEstado {i}: {len(messages)} mensajes)
for msg in messages:
print(f" [{msg.type}] {str(msg.content)[:80]}")
if i >= 3: # Solo ver los primeros estados
break
Resultado de la ejecución:
=== stream_mode='values' === Estado 0: 1 条消息 [human] 杭州天气怎么样? Estado 1: 2 条消息 [human] 杭州天气怎么样? [ai] Estado 2: 3 条消息 [human] 杭州天气怎么样? [ai] [tool] 晴,25°C Estado 3: 4 条消息 [human] 杭州天气怎么样? [ai] [tool] 晴,25°C [ai] 杭州今天天气晴朗,气温25°C,适合出门活动。
stream_mode="messages": salida en streaming token por token
Ejemplo
for msg_chunk, metadata in agent.stream(
{"messages": [HumanMessage(content=Presenta EJEMPLO en una frase.)]},
stream_mode="messages",
):
# msg_chunk es un AIMessageChunk, cada uno contiene solo un Token
if hasattr(msg_chunk, 'content') and msg_chunk.content:
print(msg_chunk.content, end="", flush=True)
print()
Condiciones de salida del agente
¿Cuándo se detiene el agente? Principalmente en los siguientes casos:
| Condición de salida | Descripción | Ejemplo |
|---|---|---|
| Sin llamadas a herramientas | El AIMessage devuelto por el modelo tiene tool_calls vacío | El modelo considera que la tarea está completa y responde directamente |
| return_direct=True | La herramienta está marcada como de retorno directo, finaliza inmediatamente después de ejecutarse | Herramientas de consulta, el resultado es la respuesta final |
| structured_response | El modelo ha producido una salida estructurada | La salida estructurada especificada por response_format está completada |
| jump_to="end" | El middleware finaliza activamente mediante el control de estado | Se detecta un problema de exceso de permisos y se termina antes de tiempo |
Ejemplo
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.messages import HumanMessage
# Caso 1: el modelo responde directamente (no requiere herramientas)
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
agent = create_agent(
model=model,
tools=[], # Sin herramientas
)
result = agent.invoke({
"messages": [HumanMessage(content="Presenta el Tutorial para novatos en una frase")]
})
# El modelo responde directamente, el bucle se ejecuta solo una vez
print(f"Escenario sin herramientas, número de mensajes: {len(result['messages'])}")
print(f"Respuesta: {result['messages'][-1].content[:80]}...")
# Caso 2: se requiere llamada a herramientas (bucle de múltiples rondas)
@tool
def search_course(keyword: str) -> str:
"""Buscar cursos del Tutorial para novatos"""
return f"Se encontraron 3 cursos relacionados con {keyword}"
agent_with_tools = create_agent(
model=model,
tools=[search_course],
)
result = agent_with_tools.invoke({
"messages": [HumanMessage(content="Buscar cursos de Python")]
})
print(f"\n"Escenario con herramientas, número de mensajes: {len(result['messages'])}")
# Normalmente habrá: human, ai(tool_call), tool(result), ai(final)
for msg in result["messages"]:
print(f" [{msg.type}]", end="")
if hasattr(msg, 'tool_calls') and msg.tool_calls:
print(f" tool_calls: {[tc['name'] for tc in msg.tool_calls]}")
else:
print(f" {str(msg.content)[:50]}")
Resultado de la ejecución:
无工具场景,消息数: 2 Respuesta: Ejemplo es una plataforma de aprendizaje de programación para principiantes, con tutoriales y ejemplos completos... 有工具场景,消息数: 4 [human] 搜索 Python 课程 [ai] tool_calls: ['search_course'] [tool] 找到 python 相关课程 3 门 [ai] Buscando en Ejemplo "cursos de Python" se encontraron 3 cursos relacionados...
Comparación entre invoke y stream
| Método | Momento de retorno | Escenario de aplicación | Experiencia de usuario |
|---|---|---|---|
| invoke() | Retorna de una sola vez después de completar todo | Scripts, interfaces de API, procesamiento por lotes | Ver el resultado completo después de esperar |
| stream() | Retorna estados intermedios paso a paso | Interfaz de chat, procesos que necesitan mostrarse | Ver el progreso en tiempo real |
| ainvoke() | Retorna asincrónicamente después de completar todo | Servicios web, marcos asincrónicos | No bloquea el bucle de eventos |
| astream() | Retorna asincrónicamente paso a paso | Push WebSocket, SSE | Push en tiempo real desde el servidor |
Pasar el ID de hilo en with_config
Si usas un checkpointer (se detallará en capítulos posteriores), debes pasar thread_id a través de config para gestionar los hilos de conversación:
Ejemplo
# thread_id se usa para diferenciar distintos hilos de conversación
config = {"configurable": {"thread_id": "conversation-001"}}
# método invoke
result = agent.invoke(
{"messages": [HumanMessage(content="Hola")]},
config=config,
)
# el modo stream también admite config
for chunk in agent.stream(
{"messages": [HumanMessage(content="Hola")]},
config=config,
stream_mode="updates",
):
print(chunk)