Notas prácticas: Creación de un bucle de revisión de código con dos agentes utilizando A2A, LangGraph y
Guía paso a paso práctica: Cómo crear un ciclo de revisión de código con dos agentes utilizando A2A, LangGraph, además de contratos, verificaciones y espacios para código listos para uso destinados a los equipos que implementan este patrón.
Fuentes: Building a Two-Agent Code Review Loop with A2A, LangGraph, and LM Studio
Las notas siguientes reconstruyen un camino práctico para “Crear un bucle de revisión de código con dos agentes utilizando A2A, LangGraph y LM Studio”. Se da énfasis en los contratos, las verificaciones y los marcadores de posición para el código, en lugar de en un enfoque motivacional. Al trabajar en la sección de Resumen, anote primero el contrato: las entradas requeridas, la señal de éxito y qué ocurre en caso de un fallo parcial. Esa lista de verificación garantiza que los cambios posteriores en el código sean transparentes. Mantenga la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de datos secretos y las banderas de funcionalidad deben estar en un lugar donde los operadores puedan auditarlos sin tener que leer todo el grafo.
Qué es realmente A2A (un párrafo)
Lo que realmente es A2A (un párrafo) funciona mejor cuando se trata como una superficie medible. Capture un registro ideal, un caso de fallo y la nota de reversión antes de ampliar el alcance. Documente tanto el camino óptimo como el de recuperación juntos. Las reintentos, los controles humanos y el manejo de mensajes no entregados forman parte del producto, no son mejoras posteriores. Separe la política de fragmentación de la política de recuperación; cambiar una no debe obligar a reescribir la otra cuando cambian las métricas de calidad.
Las partes
Las piezas funcionan mejor cuando se tratan como una superficie medible. Capture un registro exitoso, un caso de fallo y la nota de reversión antes de ampliar el alcance. Prefiera unidades pequeñas y verificables en lugar de scripts extensos. Cuando un paso falla, el fallo debe apuntar a una única responsabilidad y no a un proceso complicado. Mantenga el estado del grafo simple y tipado; los bloques anidados ocultan qué nodo escribió qué campo y causan interrupciones en la continuación del proceso.
a2a-coder-reviewer/
├── shared/
│ ├── config.py # tunables: system prompts, model, temperature, MAX_ROUNDS
│ ├── llm.py # LM Studio ChatOpenAI factory
│ ├── coder_graph.py # LangGraph graph: spec (+ feedback) -> code
│ └── reviewer_graph.py # LangGraph graph: spec + code -> verdict + feedback
├── coder_agent/
│ ├── agent_executor.py # A2A executor wrapping the coder graph
│ └── __main__.py # A2A server, port 9001
├── reviewer_agent/
│ ├── agent_executor.py # A2A executor wrapping the reviewer graph
│ └── __main__.py # A2A server, port 9002
├── orchestrator.py # A2A client driving the back-and-forth
└── requirements.txt
El archivo de configuración
El archivo Config funciona mejor cuando se trata como una superficie medible. Capture un registro ideal, un caso de fallo y la nota de reversión antes de ampliar el alcance. Considere esta etapa como un contrato entre las entradas y las salidas validadas. Asigne nombres a los artefactos, defina verificaciones de éxito y rechace completaciones parciales silenciosas. Mantenga el estado del grafo plano y tipado. Los bloques anidados ocultan qué nodo escribió qué campo y provocan interrupciones en la continuación del proceso. El archivo Config funciona mejor cuando se trata como una superficie medible. Capture un registro ideal, un caso de fallo y la nota de reversión antes de ampliar el alcance. Mantenga la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de secretos y las banderas de funcionalidad deben estar en un lugar donde los operadores puedan auditarlos sin tener que leer todo el grafo.
from dataclasses import dataclass
@dataclass(frozen=True)
class AgentConfig:
model: str
temperature: float
system_prompt: str
CODER_CONFIG = AgentConfig(
model="local-model",
temperature=0.2,
system_prompt="""You are a careful Python engineer.
Write a single Python function that satisfies the spec.
If reviewer feedback is provided, revise your previous code to address it.
Return ONLY the code in a single ```python fenced block, no extra commentary.
""",
)
REVIEWER_CONFIG = AgentConfig(
model="local-model",
temperature=0.0,
system_prompt="""You are a strict senior Python code reviewer performing a
rigorous code review. Do not approve code that has any of these problems:
- Missing a docstring explaining parameters, return value, and behavior.
- Missing type hints on parameters and the return type.
- Failing to handle edge cases implied by the spec (empty input, None,
zero, negative numbers, single-element collections, etc.) unless the
spec explicitly says not to.
- Silently swallowing errors or returning a sentinel value (None, "", -1,
an error string) instead of raising, unless the spec asks for that.
- Using a data structure that visibly discards information the spec
requires (e.g. a `set` when order or duplicate counts must be kept).
If you see ANY of the above, or anything else that would fail a real code
review, you MUST respond with VERDICT: CHANGES_REQUESTED and list the
concrete problems as bullet points, even if the code "basically works."
Only respond with VERDICT: APPROVED if the code has none of these
problems. Never approve code and then list problems as "suggestions" or
"nice to haves" — if it's worth mentioning, it's worth requesting changes
for.
Start your reply with exactly one line:
VERDICT: APPROVED
or
VERDICT: CHANGES_REQUESTED
Then, on the following lines, give concise, actionable feedback
(bullet points). If APPROVED, feedback can just be brief praise/notes.
""",
)
# Max coder<->reviewer rounds before orchestrator.py gives up.
MAX_ROUNDS = 3
La fábrica de LLM
Para The LLM factory, defina las entradas, el responsable de la etapa y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la etapa a partir de un punto de control conocido sin tener que adivinar el estado oculto. Documente tanto la ruta óptima como la ruta de recuperación. Las reintentos, los controles humanos y el manejo de mensajes no entregados forman parte del producto, no son mejoras posteriores. Prefiera salidas estructuradas con validación de esquema sobre texto en formato libre cuando el siguiente paso sea escribir código o realizar una llamada a una herramienta.
import os
from langchain_openai import ChatOpenAI
LM_STUDIO_BASE_URL = os.environ.get(
"LM_STUDIO_BASE_URL", "http://localhost:7777/v1"
)
def get_llm(model: str = "local-model", temperature: float = 0.2) -> ChatOpenAI:
return ChatOpenAI(
base_url=LM_STUDIO_BASE_URL,
api_key="lm-studio", # placeholder, LM Studio ignores it
model=model,
temperature=temperature,
)
El grafo del codificador
Para el grafo de The Coder, defina las entradas, el responsable de la etapa y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la etapa desde un punto de control conocido sin tener que adivinar el estado oculto. Prefiera unidades pequeñas y verificables en lugar de scripts extensos. Cuando una etapa falla, el error debe indicar una única responsabilidad y no un proceso complicado. Incluya la aprobación humana en las conexiones que implican gastos o modificaciones en los datos de producción. La configuración en tiempo de compilación no equivale a la completitud del proceso empresarial.
from typing import TypedDict, Optional
from langgraph.graph import StateGraph, END
from shared.config import CODER_CONFIG
from shared.llm import get_llm
class CoderState(TypedDict):
spec: str
previous_code: Optional[str]
feedback: Optional[str]
code: str
def _generate(state: CoderState) -> CoderState:
llm = get_llm(model=CODER_CONFIG.model, temperature=CODER_CONFIG.temperature)
user_msg = f"Spec:\n{state['spec']}\n"
if state.get("previous_code"):
user_msg += f"\nPrevious code:\n{state['previous_code']}\n"
if state.get("feedback"):
user_msg += f"\nReviewer feedback to address:\n{state['feedback']}\n"
response = llm.invoke(
[
{"role": "system", "content": CODER_CONFIG.system_prompt},
{"role": "user", "content": user_msg},
]
)
state["code"] = response.content
return state
def build_coder_graph():
graph = StateGraph(CoderState)
graph.add_node("generate", _generate)
graph.set_entry_point("generate")
graph.add_edge("generate", END)
return graph.compile()
coder_graph = build_coder_graph()
import re
from typing import TypedDict, Optional
from langgraph.graph import StateGraph, END
from shared.config import REVIEWER_CONFIG
from shared.llm import get_llm
class ReviewerState(TypedDict):
spec: str
code: str
verdict: str
feedback: str
def _review(state: ReviewerState) -> ReviewerState:
llm = get_llm(model=REVIEWER_CONFIG.model, temperature=REVIEWER_CONFIG.temperature)
user_msg = f"Spec:\n{state['spec']}\n\nCode to review:\n{state['code']}\n"
response = llm.invoke(
[
{"role": "system", "content": REVIEWER_CONFIG.system_prompt},
{"role": "user", "content": user_msg},
]
)
text = response.content
match = re.search(r"VERDICT:\s*(APPROVED|CHANGES_REQUESTED)", text, re.IGNORECASE)
verdict = match.group(1).upper() if match else "CHANGES_REQUESTED"
feedback = text[match.end():].strip() if match else text.strip()
state["verdict"] = verdict
state["feedback"] = feedback
return state
def build_reviewer_graph():
graph = StateGraph(ReviewerState)
graph.add_node("review", _review)
graph.set_entry_point("review")
graph.add_edge("review", END)
return graph.compile()
reviewer_graph = build_reviewer_graph()
Envolver un grafo como ejecutor A2A
Para encapsular un grafo como ejecutor A2A, defina las entradas, el responsable de la tarea y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la tarea desde un punto de control conocido sin tener que adivinar el estado oculto. Trate esta etapa como un contrato entre las entradas y los resultados validados. Asigne nombres a los artefactos, defina verificaciones de éxito y rechace las completaciones parciales silenciosas. Incluya la aprobación humana en aquellos procesos que generen gastos o modifiquen datos de producción. La conexión en tiempo de compilación no equivale a la completitud del proceso empresarial. Para encapsular un grafo como ejecutor A2A, defina las entradas, el responsable de la tarea y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la tarea desde un punto de control conocido sin tener que adivinar el estado oculto. Mantenga la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de datos secretos y las banderas de funcionalidad deben estar en un lugar que los operadores puedan auditar sin tener que leer todo el código.
aph.
from a2a.server.agent_execution.agent_executor import AgentExecutor
from a2a.server.agent_execution.context import RequestContext
from a2a.server.events.event_queue import EventQueue
from a2a.server.tasks.task_updater import TaskUpdater
from a2a.types import Part, Task, TaskState, TaskStatus
from shared.coder_graph import coder_graph
class CoderAgentExecutor(AgentExecutor):
async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
user_message = context.message
task_id = context.task_id
context_id = context.context_id
await event_queue.enqueue_event(
Task(
id=task_id,
context_id=context_id,
status=TaskStatus(state=TaskState.TASK_STATE_SUBMITTED),
history=[user_message] if user_message else [],
)
)
updater = TaskUpdater(
event_queue=event_queue, task_id=task_id, context_id=context_id
)
working_message = updater.new_agent_message(
parts=[Part(text="Generating code...")]
)
await updater.start_work(message=working_message)
query = context.get_user_input()
spec, previous_code, feedback = _parse_input(query)
try:
result = coder_graph.invoke(
{
"spec": spec,
"previous_code": previous_code,
"feedback": feedback,
"code": "",
}
)
except Exception as exc:
error_message = updater.new_agent_message(
parts=[Part(text=f"❌ Code generation failed: {exc}")]
)
await updater.failed(message=error_message)
return
await updater.add_artifact(
parts=[Part(text=result["code"])], name="code", last_chunk=True
)
await updater.complete()
...
Atención a cada agente
Al trabajar en la atención a cada agente, anote primero el contrato: los datos necesarios, la señal de éxito y qué ocurre en caso de fallo parcial. Esa lista de verificación mantiene honestas las futuras modificaciones del código. Documente tanto el camino óptimo como el de recuperación. Las reintentos, los controles humanos y el manejo de mensajes no entregados forman parte del producto, no son mejoras posteriores. Haga un punto de control después de los pasos costosos. La función de reanudación no debe volver a facturar la misma llamada al LLM cuando un operador vuelve a intentar un nodo posterior.
import uvicorn
from starlette.applications import Starlette
from a2a.server.request_handlers import DefaultRequestHandler
from a2a.server.routes import create_agent_card_routes, create_jsonrpc_routes
from a2a.server.tasks.inmemory_task_store import InMemoryTaskStore
from a2a.types import AgentCapabilities, AgentCard, AgentInterface, AgentSkill
from coder_agent.agent_executor import CoderAgentExecutor
HOST = "127.0.0.1"
PORT = 9001
RPC_URL = "/a2a/jsonrpc"
def build_agent_card() -> AgentCard:
skill = AgentSkill(
id="write_code",
name="Write / revise Python code",
description="Writes a Python function from a spec, or revises "
"previous code given reviewer feedback.",
tags=["coding", "python"],
examples=["SPEC: write a function that reverses a linked list"],
input_modes=["text"],
output_modes=["text", "task-status"],
)
return AgentCard(
name="Coder Agent",
description="Generates and revises Python code based on a spec "
"and optional reviewer feedback.",
version="0.1.0",
capabilities=AgentCapabilities(streaming=True, push_notifications=False),
default_input_modes=["text"],
default_output_modes=["text", "task-status"],
skills=[skill],
supported_interfaces=[
AgentInterface(
protocol_binding="JSONRPC",
protocol_version="1.0",
url=f"http://{HOST}:{PORT}{RPC_URL}",
),
],
)
def main():
agent_card = build_agent_card()
request_handler = DefaultRequestHandler(
agent_executor=CoderAgentExecutor(),
task_store=InMemoryTaskStore(),
agent_card=agent_card,
)
routes = []
routes.extend(create_agent_card_routes(agent_card=agent_card))
routes.extend(create_jsonrpc_routes(request_handler=request_handler, rpc_url=RPC_URL))
app = Starlette(routes=routes)
uvicorn.run(app, host=HOST, port=PORT)
if __name__ == "__main__":
main()
El orquestador
Al trabajar con The Orchestrator, anote primero el contrato: las entradas requeridas, la señal de éxito y qué ocurre en caso de fallo parcial. Esa lista de verificación mantiene honestas las futuras modificaciones del código. Prefiera unidades pequeñas y probables sobre scripts extensos. Cuando un paso falla, el fallo debe apuntar a una única responsabilidad y no a un proceso complicado. Haga una verificación después de los pasos costosos. El sistema de reanudación no debe volver a facturar la misma llamada al LLM cuando un operador intente nuevamente un nodo posterior.
import asyncio
import sys
import uuid
import httpx
from a2a.client import A2ACardResolver, ClientConfig, create_client
from a2a.helpers import get_artifact_text, get_message_text
from a2a.types import Message, Part, Role, SendMessageRequest
from shared.config import MAX_ROUNDS
CODER_URL = "http://127.0.0.1:9001"
REVIEWER_URL = "http://127.0.0.1:9002"
async def get_client_for(httpx_client: httpx.AsyncClient, base_url: str):
resolver = A2ACardResolver(httpx_client, base_url)
card = await resolver.get_agent_card()
return await create_client(card, client_config=ClientConfig(httpx_client=httpx_client))
async def send_and_collect(client, text: str) -> str:
message = Message(
role=Role.ROLE_USER,
message_id=str(uuid.uuid4()),
parts=[Part(text=text)],
context_id=str(uuid.uuid4()),
)
request = SendMessageRequest(message=message)
result_text = ""
async for event in client.send_message(request):
if event.HasField("message"):
result_text = get_message_text(event.message, delimiter="\n")
elif event.HasField("artifact_update"):
result_text += get_artifact_text(
event.artifact_update.artifact, delimiter="\n"
)
elif event.HasField("status_update"):
status = event.status_update.status
if status.state == TaskState.TASK_STATE_FAILED:
error_text = (
get_message_text(status.message, delimiter="\n")
if status.HasField("message")
else "Task failed with no error message."
)
raise RuntimeError(error_text)
return result_text
async def run(spec: str) -> dict:
"""
Runs the coder<->reviewer loop for `spec`. Returns a result dict:
{"approved": bool, "rounds": int, "code": str, "review": str}
or, if a task failed, {"approved": False, "error": str, "code": str | None}.
"""
async with httpx.AsyncClient(timeout=120) as httpx_client:
coder = await get_client_for(httpx_client, CODER_URL)
reviewer = await get_client_for(httpx_client, REVIEWER_URL)
code = None
feedback = None
review_text = ""
try:
for round_num in range(1, MAX_ROUNDS + 1):
print(f"\n=== Round {round_num}: Coder ===")
coder_prompt = f"SPEC: {spec}\n"
if code:
coder_prompt += f"PREVIOUS_CODE: {code}\nFEEDBACK: {feedback}\n"
code = await send_and_collect(coder, coder_prompt)
print(code)
print(f"\n=== Round {round_num}: Reviewer ===")
reviewer_prompt = f"SPEC: {spec}\nCODE: {code}"
review_text = await send_and_collect(reviewer, reviewer_prompt)
print(review_text)
if review_text.startswith("VERDICT: APPROVED"):
print("\n✅ Approved. Final code:\n")
print(code)
return {
"approved": True,
"rounds": round_num,
"code": code,
"review": review_text,
}
feedback = review_text.split("\n", 1)[-1]
print(f"\n⚠️ Max rounds ({MAX_ROUNDS}) reached without approval. Last code:\n")
print(code)
return {
"approved": False,
"rounds": MAX_ROUNDS,
"code": code,
"review": review_text,
}
except RuntimeError as exc:
print(f"\n{exc}")
return {"approved": False, "error": str(exc), "code": code}
finally:
await coder.close()
await reviewer.close()
Ejecutarlo
Al trabajar en “Running it”, anote primero el contrato: las entradas requeridas, la señal de éxito y qué ocurre en caso de fallo parcial. Esa lista de verificación mantiene honestas las futuras modificaciones del código. Trate esta etapa como un contrato entre las entradas y las salidas validadas. Asigne nombres a los artefactos, defina comprobaciones de éxito y rechace las completaciones parciales silenciosas. Haga una verificación después de los pasos costosos. La función de reanudación no debe volver a facturar la misma llamada al LLM cuando un operador intenta nuevamente un nodo posterior. Al trabajar en “Running it”, anote primero el contrato: las entradas requeridas, la señal de éxito y qué ocurre en caso de fallo parcial. Esa lista de verificación mantiene honestas las futuras modificaciones del código. Mantenga la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de secretos y las banderas de funcionalidad deben estar en un lugar donde los operadores puedan auditarlos sin tener que leer todo el grafo.
pip install -r requirements.txt
# terminal 1
python -m coder_agent
# terminal 2
python -m reviewer_agent
# terminal 3
python orchestrator.py "write a function that checks if a string is a palindrome"
curl http://127.0.0.1:9001/.well-known/agent-card.json
Un ejemplo de una ejecución real
Un ejemplo de una ejecución real funciona mejor cuando se trata como una superficie medible. Capture un registro exitoso, un caso de fallo y la nota de reversión antes de ampliar el alcance. Documente tanto el camino óptimo como el camino de recuperación juntos. Las reintentos, los controles humanos y el manejo de mensajes no entregados forman parte del producto, no son mejoras posteriores. Mantenga el estado de los gráficos simple y tipado. Los bloques anidados ocultan qué nodo escribió qué campo y provocan interrupciones en la continuación después de las interrupciones.
Cosas que fallaron y cómo solucionarlas
Los casos de cosas que se dañaron y cómo repararlas funcionan mejor cuando se tratan como una superficie medible. Capture una transcripción clave, un caso de fallo y la nota de reversión antes de ampliar el alcance. Prefiera unidades pequeñas y verificables en lugar de scripts extensos. Cuando un paso falla, el fallo debe apuntar a una sola responsabilidad y no a un proceso complicado. Mantenga el estado del grafo simple y tipado; los bloques anidados ocultan qué nodo escribió qué campo y causan interrupciones en la continuación del proceso.
ModuleNotFoundError: No module named 'a2a.server.apps'
ModuleNotFoundError: No module named 'a2a.server.apps' funciona mejor cuando se trata como una superficie medible. Capture una transcripción ideal, un caso de fallo y la nota de reversión antes de ampliar el alcance.
Trate esta etapa como un contrato entre las entradas y las salidas validadas. Asigne nombres a los artefactos, defina verificaciones de éxito y rechace completaciones parciales silenciosas.
Mantenga el estado del grafo plano y tipado. Los bloques anidados ocultan qué nodo escribió qué campo e interrumpen la continuación tras las interrupciones.
ModuleNotFoundError: No module named 'a2a.server.apps' funciona mejor cuando se trata como una superficie medible. Capture una transcripción ideal, un caso de fallo y la nota de reversión antes de ampliar el alcance.
Mantenga la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de secretos y las banderas de funcionalidad deben estar en un lugar donde los operadores puedan auditarlos sin tener que leer todo el grafo.
ModuleNotFoundError: No module named 'sse_starlette'
Para el error ModuleNotFoundError: No module named 'sse_starlette', defina las entradas, el responsable de la etapa y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la etapa a partir de un punto de control conocido sin tener que adivinar el estado oculto.
Documente tanto la ruta normal como la ruta de recuperación. Las reintentos, los controles humanos y el manejo de mensajes no entregados forman parte del producto, no son mejoras posteriores.
Incluya la aprobación humana en aquellos casos que impliquen gastos o cambios en los datos de producción. La configuración en tiempo de compilación no equivale a la completitud del proceso empresarial.
Tiempos de espera misteriosos alrededor de 5 segundos
Para los tiempos de espera misteriosos de alrededor de 5 segundos, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Prefiera unidades pequeñas y verificables en lugar de scripts extensos. Cuando un paso falla, el error debe indicar una única responsabilidad y no un proceso complicado. Incluya la aprobación humana en aquellas tareas que implican gastos o modificaciones en datos de producción. La conexión en tiempo de compilación no equivale a la completitud del proceso empresarial.
Los modelos de razonamiento arruinan todo
En los modelos de razonamiento, antes de modificar el código, se deben definir las entradas, el responsable de la tarea y los criterios de finalización. Los operadores deben poder volver a ejecutar la tarea a partir de un punto de control conocido, sin tener que adivinar el estado oculto. Considere esta etapa como un contrato entre las entradas y los resultados validados. Asigne nombres a los artefactos, defina verificaciones de éxito y rechace las completaciones parciales silenciosas. Prefiera resultados estructurados con validación de esquema sobre textos en formato libre cuando el siguiente paso sea escribir código o realizar una llamada a una herramienta. En los modelos de razonamiento, antes de modificar el código, se deben definir las entradas, el responsable de la tarea y los criterios de finalización. Los operadores deben poder volver a ejecutar la tarea a partir de un punto de control conocido, sin tener que adivinar el estado oculto. Mantenga la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de datos secretos y las banderas de funcionalidad deben estar en un lugar donde los operadores puedan auditarlos sin necesidad de leer todo el sistema.
Nombres de los enums de Protobuf
Al trabajar con los nombres de los enums de Protobuf, primero escribe el contrato: entradas requeridas, señal de éxito y qué ocurre en caso de fallo parcial. Esa lista de verificación mantiene honestas las futuras modificaciones del código. Documenta tanto el camino óptimo como el de recuperación. Las reintentos, los controles humanos y el manejo de mensajes no entregados forman parte del producto, no de mejoras posteriores. Haz un punto de control después de los pasos costosos. La función de reanudación no debe volver a facturar la misma llamada al LLM cuando un operador vuelve a intentar un nodo posterior.
Part ya no envuelve a TextPart
Cuando trabaje con un Parte que ya no envuelve a un TextPart, anote primero el contrato: las entradas requeridas, la señal de éxito y qué ocurre en caso de fallo parcial. Esa lista de verificación mantiene honestas las futuras modificaciones del código.
Prefiera unidades pequeñas y probables sobre scripts extensos. Cuando un paso falla, el fallo debe apuntar a una única responsabilidad y no a un proceso complicado.
Haga una verificación después de los pasos costosos. La función de reanudación no debe volver a facturar la misma llamada al LLM cuando un operador intenta nuevamente un nodo posterior.
Espacio para ampliar
Al trabajar en la extensión de Room, anote primero el contrato: las entradas requeridas, la señal de éxito y qué ocurre en caso de fallo parcial. Esa lista de verificación mantiene honestas las futuras modificaciones del código. Trate esta etapa como un contrato entre las entradas y las salidas validadas. Asigne nombres a los artefactos, defina comprobaciones de éxito y evite completaciones parciales silenciosas. Haga una verificación después de los pasos costosos. La función de reanudación no debe volver a facturar la misma llamada al LLM cuando un operador intenta nuevamente un nodo posterior. Al trabajar en la extensión de Room, anote primero el contrato: las entradas requeridas, la señal de éxito y qué ocurre en caso de fallo parcial. Esa lista de verificación mantiene honestas las futuras modificaciones del código. Mantenga la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de secretos y las banderas de funcionalidad deben estar en un lugar donde los operadores puedan auditarlos sin tener que leer todo el grafo.
Referencias
Las referencias funcionan mejor cuando se tratan como una superficie medible. Capture una transcripción exitosa, un caso de fallo y la nota de reversión antes de ampliar el alcance. Documente tanto el camino óptimo como el de recuperación juntos. Las reintentos, los controles humanos y el manejo de mensajes no entregados forman parte del producto, no son ajustes realizados posteriormente. Mantenga el estado de los gráficos simple y tipado. Los bloques anidados ocultan qué nodo escribió qué campo y provocan interrupciones en la continuación del proceso.
Lista de verificación operativa
Para la lista de verificación operativa, defina las entradas, el responsable de cada paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Registre los tiempos de ejecución y el costo de tokens o consultas junto con los resultados funcionales. La visibilidad temprana del costo evita facturas inesperadas cuando el proceso pasa de entornos de demostración a entornos compartidos.
Se debe obtener la aprobación humana para las operaciones que generan gastos o modifican los datos de producción. La configuración en tiempo de compilación no equivale a una solución completa para los negocios.
Escriba un manual breve: cómo rotar las claves, cómo vaciar la cola de tareas y cómo revertir la última operación de inserción.
Mantenga la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de datos secretos y las banderas de funcionalidad deben estar en un único lugar al que los operadores puedan auditar sin tener que leer todo el sistema.
Se debe obtener la aprobación humana para las operaciones que generan gastos o modifican los datos de producción. La configuración en tiempo de compilación no equivale a una solución completa para los negocios.
Antes de promocionar la solución, congele las versiones, guarde una copia de referencia del flujo crítico y confirme los pasos para revertir cambios. Los entornos compartidos requieren límites de velocidad, verificaciones de asignación y un responsable claro para la rotación de datos secretos. Prefiera una fiabilidad sólida a demostraciones ingeniosas pero puntuales.
Nota de lote para 684455491963: mantenga las claves del proveedor fuera del repositorio, establezca un límite para los tokens por sesión y almacene las transcripciones junto a los archivos de evaluación para que los cambios posteriores en el modelo sigan siendo comparables.
Al trabajar en la nota de fortalecimiento 0, anote primero el contrato: entradas requeridas, señal de éxito y qué ocurre en caso de fallo parcial. Esa lista de verificación garantiza que los cambios posteriores en el código sean transparentes. Mantenga la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de secretos y las banderas de funcionalidad deben estar en un lugar donde los operadores puedan auditarlos sin tener que leer todo el sistema.
Detalle de fortalecimiento 0/640: mida el tiempo de ejecución, la clase del error y el consumo de tokens para esta nota, y luego decida si mantener el cambio basándose en un conjunto fijo de criterios en lugar de en observaciones anecdóticas.
La nota de fortalecimiento 1 funciona mejor cuando se trata como una superficie medible. Capture una transcripción ejemplar, un caso de fallo y la nota de reversión antes de ampliar el alcance. Prefiera unidades pequeñas y verificables en lugar de scripts extensos. Cuando un paso falla, el fallo debe apuntar a una única responsabilidad y no a un proceso complicado.
Detalle de fortalecimiento 1/640: mida el tiempo de ejecución, la clase del error y el consumo de tokens para esta nota, y luego decida si mantener el cambio basándose en un conjunto fijo de preguntas en lugar de anécdotas.
Para la nota de fortalecimiento 2, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso desde un punto de control conocido sin tener que adivinar el estado oculto. Registre los tiempos y el costo en tokens o consultas junto con los resultados funcionales. La visibilidad temprana de los costos evita facturas inesperadas cuando el proceso pasa de entornos de demostración a entornos compartidos.
Detalle de endurecimiento 2/640: mida el tiempo de ejecución, la clase de error y el consumo de tokens para esta nota, y luego decida si mantener el cambio basándose en un conjunto fijo de preguntas en lugar de en anécdotas.