Практичні нотатки: Створення циклу перегляду коду з двома агентами за допомогою A2A, LangGraph та
Покрокова інструкція з практичних нотаток: створення циклу перегляду коду з участю двох агентів за допомогою A2A, LangGraph та механізмів контрактів, перевірок та готових блоків коду для команд, які використовують цю схему.
Джерела: Building a Two-Agent Code Review Loop with A2A, LangGraph, and LM Studio
Наведені нижче примітки описують практичний підхід до створення циклу перегляду коду з участю двох агентів за допомогою A2A, LangGraph та LM Studio. Основна увага приділяється контрактам, перевіркам та шаблонам коду, які можна легко вставити, а не мотиваційним аспектам. Під час роботи над оглядом спочатку запишіть контракт: необхідні вхідні дані, сигнал про успіх та наслідки часткової невдачі. Такий перелік допоможе зберегти чесність у подальших змінах коду. Зберігайте конфігурацію окремо від коду програми. Файли середовища, сховища конфіденційних даних та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь граф.
Що насправді є A2A (один абзац)
Що насправді є A2A (один абзац): цей підхід найкраще функціонує, якщо його розглядати як вимірювану поверхню. Зафіксуйте один ідеальний варіант виконання, один випадок збою та примітки щодо скасування змін перед розширенням обсягу. Документуйте як успішний, так і відновлювальний сценарії роботи. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації. Розділіть політику часткової обробки даних від політики їх отримання – зміна однієї не повинна змушувати переписувати іншу при зміні показників якості.
Компоненти
Ці елементи найкраще функціонують, якщо їх розглядати як вимірювану поверхню. Запишіть один ідеальний приклад роботи, один випадок збою та примітку про скасування змін перед розширенням обсягу завдань. Віддавайте перевагу невеликим, тестованим одиницям перед складними скриптами. Якщо якийсь крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану послідовність дій. Зберігайте стан графа у простій та типованій формі. Вкладені структури приховують інформацію про те, який вузол заповнив яке поле, що ускладнює продовження роботи після перерв.
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
Файл конфігурації
Файл Config працює найкраще, коли його розглядають як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу роботи. Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Позначте всі елементи, визначте критерії успіху та не допускайте мовчазного часткового виконання завдань. Зберігайте стан графа у випрямленому та типованому форматі. Вкладені структури приховують інформацію про те, який вузол заповнив певне поле, що ускладнює продовження роботи після перерв. Файл Config працює найкраще, коли його розглядають як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу роботи. Зберігайте конфігурацію окремо від коду програми. Файли середовища, сховища конфіденційних даних та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь граф.
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
Фабрика LLM
Для фабрики LLM необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Необхідно документувати як шлях успішного виконання, так і шлях відновлення одночасно. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації. У разі, коли наступним кроком є код або виклик інструменту, краще використовувати структуровані результати з перевіркою схеми, ніж вільний текст.
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,
)
Граф кодера
Для графа The Coder необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Краще використовувати невеликі, перевірювані одиниці коду замість об’ємних скриптів. Коли крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану структуру процесу. Встановлюйте людське схвалення для дій, які призводять до витрат грошей чи змінюють дані у продакшені. Підключення елементів під час компіляції не є гарантією повноти бізнес-функціоналу.
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()
Завертання графа як виконувача A2A
Щоб оформити граф як виконувач A2A, необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Розглядайте цю стадію як контракт між вхідними даними та перевіреними результатами. Позначте елементи проекту, визначте критерії успіху та не допускайте беззвучного часткового завершення. Встановіть людське схвалення для операцій, які спричиняють витрати грошей чи змінюють дані виробництва. Підключення під час компіляції не є гарантією повноти бізнес-процесу. Щоб оформити граф як виконувач A2A, необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Зберігайте конфігурацію поза кодом додатку. Файли середовища, сховища секретних даних та флаги функцій мають знаходитися в одному місці, яке оператори можуть перевіряти, не читаючи весь код.
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()
...
Під час роботи над пунктом «Обслуговування кожного агента» спочатку запишіть умови контракту: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Такий перелік допомагає зберігати чесність подальших змін у коді. Документуйте як шлях успішного виконання, так і шлях відновлення. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації. Робіть контрольні точки після дорогих кроків. Функція відновлення не повинна знову стягувати плату за той самий виклик LLM, коли оператор намагається знову виконати пізнішу операцію.
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()
Під час роботи над The Orchestrator спочатку запишіть контракт: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Такий перелік допомагає зберігати чесність пізніших змін у коді. Віддавайте перевагу невеликим, тестованим одиницям перед об’ємними скриптами. Коли якась крок виявляється невдалою, причина має вказувати на конкретну відповідальність, а не на заплутану послідовність операцій. Робіть перевірки після дорогих кроків. Функція відновлення не повинна знову оплачувати один і той самий виклик LLM, коли оператор намагається виконати наступний етап.
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()
Запуск
Під час роботи над етапом «Запуск» спочатку запишіть умови виконання: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність подальших змін у коді. Розглядайте цей етап як угоду між вхідними даними та перевіреними результатами. Призначте назви елементам, визначте критерії успіху та не допускайте беззвучного часткового виконання. Робіть контрольні пункти після дорогих кроків. Система відновлення не повинна знову оплачувати один і той самий виклик LLM, коли оператор перезапускає пізніший етап. Під час роботи над етапом «Запуск» спочатку запишіть умови виконання: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність подальших змін у коді. Зберігайте конфігурацію окремо від коду додатку. Файли середовища, сховища конфіденційних даних та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь алгоритм.
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
Приклад реального запуску
Приклад реального виконання працює найкраще, якщо його розглядати як вимірювану поверхню. Запишіть один ідеальний результат, один випадок збою та примітку про скасування змін перед розширенням обсягу. Документуйте як успішний, так і відновлювальний шляхи. Повторні спроби, людські контролі та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації. Зберігайте стан графа у простому та типованому вигляді. Вкладені блоки приховують інформацію про те, який вузол заповнив яке поле, і ускладнюють продовження роботи після перерв.
Те, що зламалося, та як це виправити
Питання, пов’язані з пошкодженнями та способами їх усунення, найкраще розглядати як вимірювану структуру. Збережіть один ідеальний запис, один випадок збою та примітки щодо скасування змін перед розширенням обсягу роботи. Віддавайте перевагу невеликим, тестованим одиницям перед складними скриптами. Якщо якийсь крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану послідовність дій. Зберігайте стан графа у простій та типованій формі. Вкладені структури приховують інформацію про те, який вузол заповнив певне поле, що ускладнює продовження роботи після перерв.
ModuleNotFoundError: No module named 'a2a.server.apps'
ModuleNotFoundError: No module named 'a2a.server.apps' найкраще розглядати як вимірювану поверхню. Зафіксуйте один ідеальний запис, один випадок збою та примітку щодо скасування змін перед розширенням обсягу роботи.
Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Позначте всі елементи, визначте критерії успіху та не допускайте мовчазного часткового виконання завдань.
Зберігайте стан графа у випрямленому та типованому форматі. Вкладені структури приховують інформацію про те, який вузол заповнив певне поле, що ускладнює продовження роботи після перерв.
ModuleNotFoundError: No module named 'a2a.server.apps' найкраще розглядати як вимірювану поверхню. Зафіксуйте один ідеальний запис, один випадок збою та примітку щодо скасування змін перед розширенням обсягу роботи.
Зберігайте конфігурацію окремо від коду програми. Файли середовища, сховища конфіденційних даних та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь граф.
ModuleNotFoundError: No module named 'sse_starlette'
У випадку помилки ModuleNotFoundError: No module named 'sse_starlette' необхідно спочатку визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори мають мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан.
Необхідно документувати як шлях успішного виконання, так і шлях відновлення. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації.
Встановлюйте людське схвалення для тих кроків, які призводять до витрат грошей або змінюють дані у продакшені. Підключення на етапі компіляції не є гарантією повності функціоналу продукту.
Таємничі таймаути близько 5 секунд
У разі загадкових перерв у роботі тривалістю близько 5 секунд необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись здогадатися про прихований стан. Краще використовувати невеликі, перевірювані одиниці коду замість об’ємних скриптів. Коли крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану структуру процесу. Необхідно встановити людське схвалення для операцій, які призводять до витрат грошей чи змінюють дані у продакшені. Підключення елементів під час компіляції не є гарантією повноти бізнес-функціоналу.
Моделі міркувань псують все
Для моделей Reasoning перед зміною коду необхідно визначити вхідні дані, виконавця кроку та критерії завершення. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Розглядайте цю стадію як контракт між вхідними даними та перевіреними результатами. Позначте всі елементи, визначте критерії успіху та не допускайте мовчазного часткового виконання. У разі, коли наступним кроком є написання коду чи виклик інструменту, віддавайте перевагу структурованим результатам із перевіркою за схемою перед вільним текстом. Для моделей Reasoning перед зміною коду необхідно визначити вхідні дані, виконавця кроку та критерії завершення. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Зберігайте конфігурацію окремо від коду програми. Файли середовища, хранилища секретів та флаги функцій мають знаходитися в одному місці, яке оператори можуть перевірити, не читаючи весь кодовий граф.
Імена енумерацій Protobuf
Під час роботи з іменами енумерацій Protobuf спочатку складіть опис контракту: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Такий перелік допомагає зберігати чесність пізніших змін у коді. Одночасно задокументуйте шлях успішного виконання та шлях відновлення. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації. Робіть контрольні пункти після дорогих кроків. Функція відновлення не повинна знову стягувати плату за той самий виклик LLM, коли оператор повторює спробу з пізнішого етапу.
Part більше не обгортає TextPart
Коли ви працюєте над тим, щоб Part більше не обгортав TextPart, спочатку складіть опис вимог: необхідні вхідні дані, сигнал про успіх та те, що відбувається при частковій невдачі. Такий перелік допомагає зберігати чесність пізніших змін у коді.
Віддавайте перевагу малим, тестованим одиницям перед величезними скриптами. Якщо якийсь крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану послідовність операцій.
Робіть перевірки після дорогих кроків. Функція відновлення не повинна знову оплачувати один і той самий виклик LLM, коли оператор намагається виконати наступний етап.
Місце для розширення
Під час розробки розширень за допомогою Room спочатку запишіть угоду: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність подальших змін у коді. Розглядайте цей етап як угоду між вхідними даними та перевіреними результатами. Дайте назви елементам, визначте критерії успіху та не допускайте беззвучного часткового виконання. Робіть перевірки після дорогих кроків. Система відновлення не повинна знову оплачувати один і той самий виклик LLM, коли оператор перепробовує пізніший етап. Під час розробки розширень за допомогою Room спочатку запишіть угоду: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність подальших змін у коді. Зберігайте конфігурацію окремо від коду програми. Файли середовища, сховища секретів та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь граф.
Посилання
Референсні праці найкраще функціонують, якщо їх розглядати як вимірювану поверхню. Збережіть один ідеальний приклад роботи, один випадок збою та запис про скасування змін перед розширенням обсягу роботи. Документуйте як успішний, так і відновлювальний сценарії роботи разом. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації. Зберігайте стан графа у простому та типованому вигляді. Вкладені структури приховують інформацію про те, який вузол заповнив яке поле, і ускладнюють продовження роботи після перерв.
Чек-лист операцій
Для чек-листу операцій необхідно визначити вхідні дані, відповідальну особу за кожен крок та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не здогадуючись про прихований стан. Записуйте час виконання та витрати на обробку токенів чи запитів поруч із функціональними результатами. Чітке бачення витрат заздалегідь запобігає несподіваним рахункам під час переходу з демо-середовища у спільні середовища.
Необхідно отримувати схвалення людини для операцій, які спричиняють витрати грошей чи змінюють дані виробництва. Підключення під час компіляції не є гарантією повноти бізнес-функціоналу.
Напишіть короткий посібник: як змінювати ключі, як спорожнювати чергу, як скасовувати останні зміни.
Зберігайте конфігурацію окремо від коду додатку. Файли середовища, сховища конфіденційних даних та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь код.
Необхідно отримувати схвалення людини для операцій, які спричиняють витрати грошей чи змінюють дані виробництва. Підключення під час компіляції не є гарантією повноти бізнес-функціоналу.
Перед підвищенням версії стека заморозьте поточні версії, зробіть копію критичного шляху виконання та підтвердьте кроки скасування змін. У спільних середовищах необхідні обмеження на швидкість виконання, перевірки прав доступу та чіткий власник для зміни конфіденційних даних. Краще обирати надійність, ніж креативні, але одноразові демонстрації.
Примітка до пакету для 684455491963: не включайте ключі постачальників у репозиторій, встановіть ліміт токенів на сеанс та зберігайте транскрипції поруч із фіксами для оцінки, щоб подальша заміна моделей залишалася порівнянною.
Під час роботи над приміткою 0 про посилення безпеки спочатку запишіть контракт: необхідні вхідні дані, сигнал про успіх та те, що відбувається при частковій невдачі. Цей перелік допомагає зберігати чесність у подальших змінах коду. Зберігайте конфігурацію окремо від коду додатку. Файли середовища, сховища секретів та флаги функцій мають знаходитися в одному місці, яке оператори можуть перевіряти, не читаючи весь код.
Деталь посилення безпеки 0/640: вимірюйте час виконання, клас помилки та витрату токенів для цієї примітки, а потім вирішуйте, чи залишити зміни, ґрунтуючись на фіксованому наборі питань, а не на індивідуальних спостереженнях.
Примітка щодо посилення безпеки 1 найкраще функціонує, якщо її розглядати як вимірювану поверхню. Збережіть один ідеальний зразок роботи, один випадок збою та примітку щодо скасування змін перед розширенням обсягу роботи. Краще використовувати невеликі, перевірювані одиниці замість складних скриптів. Якщо якийсь крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану послідовність дій.
Деталь посилення безпеки 1/640: виміряйте час виконання, клас помилки та кількість витрачених токенів для цієї примітки, а потім вирішіть, чи залишити зміни, ґрунтуючись на фіксованому наборі питань, а не на окремих прикладах.
Для примітки щодо посилення безпеки 2 визначте вхідні дані, відповідальну особу за крок та критерії завершення перед зміною коду. Оператори мають мати можливість повторно виконати крок з відомої точки контролю, не здогадуючись про прихований стан. Запишіть час виконання та витрати на токени або запити поруч із функціональними результатами. Відомість витрат заздалегідь запобігає несподіваним рахункам, коли процес переходить від демо-середовища до спільних середовищ.
Деталь посилення безпеки 2/640: виміряйте час обробки стіни, клас помилки та витрату токенів для цього запису, а потім вирішіть, чи залишити зміни, ґрунтуючись на фіксованому наборі запитань, а не на окремих випадках.