Галоўная / Артыкулы / Практычныя прытамкі: Системы з калькольнікаў агентаў: Калі 2 агентаў перамагаюць 1 (і калі няма)

Практычныя прытамкі: Системы з калькольнікаў агентаў: Калі 2 агентаў перамагаюць 1 (і калі няма)

Практычныя прыказкі: Системы з калькулярамі: калі 2 калькуляры перамагаюць 1 (і калі няўсёлкі): контракты, перакрыцчы і месца для коду для команд, якія викорыстоўваюць гэты патэрн.

3090 слоў

У гэтым карыце парадоксальны спосаб перадбудаваець шлях ад сыр'ёў да рабочай системы для: Системаў з калькамі агентаў: Калі 2 агентаў перамагаюць 1 (і калі няма). Акцэнт ставится на практычныя крокі, чысткія перакананні та код, які можна проста дадаць у репазітарый без неабязковасці з'ясаваць мету. У стадыі агляду неабходна з'явіць вхідныя даны, адпаведальнага за крок та критэрыя завершэння прычым перад зменай коду. Аперацыйныя працавнікі должны магчымае перадзначыць крок з вядомай точкі контролю без неабязковасці з'ясаваць схованы стан. Конфігурацыю трэба залічыць паза кодам прыкладнага програму. Файлы сераў, хранільнікі секрэтных дадзеных та флагі функцый належыць у аднам месца, якое працавнікі можуць пераглядаць без неабязковасці чытання всей структуры.

Проблема перагляду коду

Калі працюеце над стадзіяй «Аналіз коду», спачатку запісайце умовы працы: неабяжлівыя даннэ, сігнал успеху і тое, што выходзіць у разе частковага нявыпання. Такі список контролю дапамагае заліцьварыць чыстасцю пазнейшых змян у кодзе. Документавайце як шлях успеху, так і шлях вярнення да нормальнага стану. Перапрыбуткі, людзкія перакрыцця і обробка некоректных паведамленняў ёсцю частью продукту, а не пазнейшым дапрацоўкам. Зробіце контрольны пункт пасля дорогіх крокаў. Система вярнення не павінна зноў ставіць плату за той самы вызов LLM, калі аператар перапрыбуе пазнейшы вузел.

--- a/src/billing/invoice.py
+++ b/src/billing/invoice.py
@@ -42,7 +42,9 @@
 class InvoiceService:
-    def calculate_total(self, items):
-        return sum(i.price * i.qty for i in items)
+    def calculate_total(self, items, discount_pct=0):
+        subtotal = sum(i.price * i.qty for i in items)
+        return subtotal * (1 - discount_pct)

--- a/src/billing/api.py
+++ b/src/billing/api.py
@@ -18,6 +18,8 @@
 @router.post("/invoice")
 def create_invoice(req: InvoiceRequest):
+    discount = req.discount_pct   # NEW: from user input
     svc = InvoiceService()
-    total = svc.calculate_total(req.items)
+    total = svc.calculate_total(req.items, discount)
     return {"total": total}

--- a/config/feature_flags.yaml
+++ b/config/feature_flags.yaml
@@ -5,3 +5,4 @@
 flags:
   new_dashboard: true
+  discount_billing: true   # rollout: 100% immediately

Дизайн 1: Адзіны агент-рэвізор

Калі працуеце над стадзіяй «Дыяграма 1: Адна агэнтка», спачатку запісайце контракт: неабяжлівыя даны, сігнал успеху і тое, што выходзіць у разе частковага неяксамоства. Такі список контроля дапамагае заліцвачыць змяны ў кодзе. Валіце маленькія, тэставаныя елементы замест большых скрыптов. Калі якісь крок не выйшае, неяксамоства должны вказываць на адну адпаведальнасць, а не на заплутаны ланцюг задач. Зробіце контрольную паузу пасля дорогіх крокаў. Програма не должна зноў платіць за той самы вызов LLM, калі аператар прабуе зноў выконаць пазнейшы вузел.

from langchain_core.tools import tool
import textwrap

MOCK_DIFF = """...""" # The diff shown above
MOCK_FILES = {
    "src/billing/invoice.py": "class InvoiceService:\n    def calculate_total(self, items, discount_pct=0):\n        subtotal = sum(i.price * i.qty for i in items)\n        return subtotal * (1 - discount_pct)\n",
    "src/billing/api.py": '@router.post("/invoice")\ndef create_invoice(req: InvoiceRequest):\n    discount = req.discount_pct\n    svc = InvoiceService()\n    total = svc.calculate_total(req.items, discount)\n    return {"total": total}\n',
    "tests/test_billing.py": "def test_calculate_total():\n    # only tests no-discount path\n    assert svc.calculate_total(items) == 300\n",
}

@tool
def get_diff(pr_id: str) -> str:
    """Fetch the PR diff."""
    return MOCK_DIFF

@tool
def read_file(path: str) -> str:
    """Read a file from the repo."""
    return MOCK_FILES.get(path, f"FILE NOT FOUND: {path}")

@tool
def search_symbol(name: str) -> str:
    """Search for a symbol across the codebase."""
    if "discount" in name.lower():
        return "Found: InvoiceService.calculate_total(discount_pct) — src/billing/invoice.py:43"
    return f"No results for '{name}'"

@tool
def list_tests(path: str) -> str:
    """List test files covering a source path."""
    if "billing" in path:
        return "tests/test_billing.py — covers calculate_total (no-discount path only)"
    return "No tests found"

ALL_TOOLS = [get_diff, read_file, search_symbol, list_tests]
from typing import Literal
from typing_extensions import TypedDict
from pydantic import BaseModel
from langchain_google_genai import ChatGoogleGenerativeAI
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
from langgraph.graph import END, START, StateGraph

class ReviewFinding(BaseModel):
    severity: Literal["critical", "high", "medium", "low", "info"]
    category: Literal["bug", "regression", "missing_test", "rollout_risk",
                       "security", "edge_case", "style"]
    file: str
    description: str
    confidence: Literal["high", "medium", "low"]

class SingleAgentState(TypedDict):
    pr_id: str
    messages: list
    diff: str
    findings: list[ReviewFinding]

llm = ChatGoogleGenerativeAI(model="gemini-2.5-flash", temperature=0)
def sa_fetch(state: SingleAgentState) -> dict:
    diff = get_diff.invoke({"pr_id": state["pr_id"]})
    tests = list_tests.invoke({"path": "src/billing"})
    return {"diff": diff, "messages": [
        AIMessage(content=f"Diff loaded. Test coverage: {tests}")
    ]}

def sa_review(state: SingleAgentState) -> dict:
    """Single agent does BOTH jobs: summarize + critique in one pass."""
    prompt = f"""\
You are a senior code reviewer. Read this PR diff, summarize the change,
and produce a list of risk findings. Be specific.

DIFF:
{state['diff']}

Respond with JSON: {{"summary": "...", "findings": [
  {{"severity": "...", "category": "...", "file": "...",
    "description": "...", "confidence": "..."}}
]}}
"""
    resp = llm.invoke([SystemMessage(content="You are a code review bot."),
                       HumanMessage(content=prompt)])

    # In a real app we parse the JSON response here.
    # We simulate the typical single-agent output for this diff.
    findings = [
        ReviewFinding(
            severity="medium",
            category="missing_test",
            file="tests/test_billing.py",
            description="Add unit tests for the new discount_pct parameter.",
            confidence="high"
        ),
        ReviewFinding(
            severity="low",
            category="style",
            file="src/billing/invoice.py",
            description="Consider adding type hints to the items list.",
            confidence="high"
        )
    ]
    return {"findings": findings, "messages": [resp]}

def build_single_agent():
    g = StateGraph(SingleAgentState)
    g.add_node("fetch", sa_fetch)
    g.add_node("review", sa_review)
    g.add_edge(START, "fetch")
    g.add_edge("fetch", "review")
    g.add_edge("review", END)
    return g.compile()

Перадача: чаму важлівы структураваныя схемы

Калі працуеце над стадзіяй «The Handoff Why Structured», спачатку запісайце контракт: неабяжлівыя даннэ, сигнал успеху і тое, што выходзіць у разе частковага нявыпання. Такі список перакладоў заходзіць пазнейшыя змены коду чыстымі. Спрыятлівае ставленне да гэтай стадзіі як да контракту межа даннемі і перакананымі выходамі. Дайце назву артыфактам, задаце правілы пераканання успеху і не падзволяйце частковаму завершэнню без паведамлення. Зробіце перапытку пасля дорогіх крокаў. Програма для продакцыі не павінна зноў стягваць плата за той самы вызов LLM, калі аператар праказвае пазнейшы вузел. Калі працуеце над стадзіяй «The Handoff Why Structured», спачатку запісайце контракт: неабяжлівыя даннэ, сигнал успеху і тое, што выходзіць у разе частковага нявыпання. Такі список перакладоў заходзіць пазнейшыя змены коду чыстымі. Зберагачыце настройкі за межамі коду прыемлівання. Файлы сераў, хранільнікі секрэтных дадзеных і флагі функций павінны знаходзіцца ў адном месцы, якое аператары можаць пераглядаць без неабяжлівага чытання всей структуры.

class ChangedInterface(BaseModel):
    file: str
    symbol: str
    change_type: Literal["added", "modified", "removed"]
    description: str

class ChangeModel(BaseModel):
    """Analyzer → Reviewer handoff. Structured, not prose."""
    purpose: str
    impacted_files: list[str]
    changed_interfaces: list[ChangedInterface]
    config_changes: list[str]
    migration_risk: bool
    assumptions: list[str]
    tests_touched: list[str]
    tests_likely_needed: list[str]

Дзяржаўна 2: Архітектура з двума агентамі

Этап Дзяржаўна 2 з двума агентамі працуе найэфектывней, калі яго розглядаць як вимерную паверхню. Зафіксавайце адна ідеальная транскрыпцыя, адзін прыклад неудачы і запіс пра вярнэнне да пачатку перш чым расширваць масштаб. Дакументавайце як шлях успеху, так і шлях вяснавання разам. Перапрыбуткі, людзкія контрольныя пункты і обработка некоректных паведамленняў є частью продукту, а не наступным этапам дорабкі. Храніце стан графа ў простам і типаваным формате. Вкладзеныя блокі маскуюць, який вузел запісаў канкрэтны поле, і спакоююць працу пасля перарываў.

class TwoAgentState(TypedDict):
    pr_id: str
    messages: list
    diff: str
    change_model: ChangeModel | None
    findings: list[ReviewFinding]
    conflict: bool
ANALYZER_PROMPT = """\
You are the Analyzer agent. Your ONLY job: understand the change.
Do NOT critique. Do NOT hunt for bugs. Just map what changed.
Produce a structured change model."""

def ta_analyzer(state: TwoAgentState) -> dict:
    """Analyzer: compress & clarify. Optimizes for coherence."""

    # We use LLM structured output to fill the ChangeModel.
    # For this demonstration, we simulate the accurate analysis.
    cm = ChangeModel(
        purpose="Add discount percentage support to invoice billing",
        impacted_files=["src/billing/invoice.py", "src/billing/api.py",
                        "config/feature_flags.yaml"],
        changed_interfaces=[
            ChangedInterface(file="src/billing/invoice.py",
                symbol="InvoiceService.calculate_total",
                change_type="modified",
                description="Added discount_pct param (default 0)"),
            ChangedInterface(file="src/billing/api.py",
                symbol="create_invoice",
                change_type="modified",
                description="Reads discount_pct from request, passes to service"),
        ],
        config_changes=["discount_billing flag added, 100% rollout"],
        migration_risk=False,
        assumptions=[
            "discount_pct is expected to be 0..1 (fraction, not percentage)",
            "No existing callers pass discount_pct yet",
            "Feature flag controls visibility, not the calculation",
        ],
        tests_touched=[],
        tests_likely_needed=[
            "test discount path in calculate_total",
            "test boundary: discount_pct = 0, 1, >1, <0",
            "test API validation of discount_pct input",
        ],
    )
    return {
        "change_model": cm,
        "messages": [AIMessage(content=f"Analyzer: change model built. "
                               f"{len(cm.changed_interfaces)} interfaces changed, "
                               f"{len(cm.assumptions)} assumptions made.")],
    }
REVIEWER_PROMPT = """\
You are the Risk Reviewer. The Analyzer gave you a change model.
DISTRUST it. Your job: find what's missing, broken, or dangerous.
Challenge every assumption. Check for missing tests, regressions,
rollout risks, and security issues."""

def ta_reviewer(state: TwoAgentState) -> dict:
    """Reviewer: expand & challenge. Optimizes for skepticism."""
    cm = state["change_model"]
    findings: list[ReviewFinding] = []

    # The Reviewer iterates through the Analyzer's assumptions
    for a in cm.assumptions:
        if "0..1" in a:
            findings.append(ReviewFinding(
                severity="critical",
                category="security",
                file="src/billing/api.py",
                description="ASSUMPTION CHALLENGED: discount_pct comes from user input "
                    "(req.discount_pct) with NO validation. Values <0 or >1 break billing. "
                    "Negative discount = price increase beyond subtotal. "
                    "Value >1 = negative total.",
                confidence="high"))

    # The Reviewer checks the test mapping
    if not cm.tests_touched and cm.tests_likely_needed:
        findings.append(ReviewFinding(
            severity="high",
            category="missing_test",
            file="tests/test_billing.py",
            description=f"NO tests touched but {len(cm.tests_likely_needed)} needed: "
                + "; ".join(cm.tests_likely_needed),
            confidence="high"))

    # The Reviewer checks the config changes
    for cc in cm.config_changes:
        if "100%" in cc:
            findings.append(ReviewFinding(
                severity="high",
                category="rollout_risk",
                file="config/feature_flags.yaml",
                description="Feature flag at 100% from day one — no gradual rollout. "
                    "Combined with unvalidated discount input, this is a billing incident risk.",
                confidence="high"))

    # The Reviewer looks for edge cases in the logic flow
    findings.append(ReviewFinding(
        severity="medium",
        category="edge_case",
        file="src/billing/api.py",
        description="Feature flag controls visibility but calculate_total always applies "
            "discount. If flag is off but API still receives discount_pct, discount "
            "is silently applied.",
        confidence="medium"))

    # We determine if there is a conflict worth escalating
    conflict = any(f.severity in ("critical", "high") for f in findings)

    return {
        "findings": findings,
        "conflict": conflict,
        "messages": [AIMessage(content=f"Reviewer: {len(findings)} findings, "
                               f"conflict={conflict}")],
    }

Арбітраж і участка чалавека

Этап арбітражу та участі людзя працюе найэфективней, калі яго розглядаць як меркаваную плошчу. Запісаце адна ідеальная версія, адзин прыклад неудачы і запіс працэў павернення да пярвоначальнага стану пры расшырэнні масштаба. Валіце маленькія, тэставаныя елементы замест большых скрыптов. Калі якісь крок не выйшае, прычына неудачы павінна вказываць на адную адпаведальнасць, а не на заплутаны ланцужок задач. Рэзультаты роботы графа павінны быць простымі та адпаведна типаванымі. Вярнутыя структуры дакладнаў прыводзяць да таго, што неясна, який вузел запісаў канкрэтны поле, і спакойная робота пры перарывах стае нэмагчымай.

def ta_merge(state: TwoAgentState) -> dict:
    """Merge findings. If conflict, surface for human review."""
    if state["conflict"]:
        return {
            "messages": [AIMessage(content=(
                "⚠ CONFLICT: Reviewer found critical/high issues. "
                "Routing to human review."
            ))],
        }
    return {
        "messages": [AIMessage(content="Findings merged. No escalation needed.")],
    }

def route_after_merge(state: TwoAgentState) -> str:
    return "escalate" if state["conflict"] else "emit"
from langgraph.types import Command, interrupt

def ta_escalate(state: TwoAgentState) -> Command:
    """Human-in-the-loop for conflicting findings."""

    # The interrupt function pauses execution and surfaces data to the caller
    decision = interrupt({
        "kind": "review_conflict",
        "pr_id": state["pr_id"],
        "change_model": state["change_model"].model_dump(),
        "findings": [f.model_dump() for f in state["findings"]],
        "prompt": "Review findings. Respond: "
                  '{"action":"accept_all"} or {"action":"override","drop_indices":[...]}',
    })

    # Execution resumes here when the human provides input
    action = decision.get("action", "accept_all")

    if action == "override":
        drop = set(decision.get("drop_indices", []))
        kept = [f for i, f in enumerate(state["findings"]) if i not in drop]

        # We use Command to update state and dynamically route to the next node
        return Command(update={"findings": kept}, goto="emit")

    return Command(goto="emit")
# How you resume the graph from your backend API
ta.invoke(Command(resume={"action": "accept_all"}), config)
def ta_emit(state: TwoAgentState) -> dict:
    """Final output: formatted review."""
    lines = [f"=== Code Review: PR {state['pr_id']} ==="]
    if state["change_model"]:
        lines.append(f"Purpose: {state['change_model'].purpose}")
    lines.append(f"Findings ({len(state['findings'])}):")

    for i, f in enumerate(state["findings"]):
        lines.append(f"  [{f.severity}] ({f.category}) {f.file}")
        lines.append(f"    {f.description}")

    return {"messages": [AIMessage(content="\n".join(lines))]}

Падключэнне графа з двума агентамі

Этап падчырання графа двух агентав працюе наяўней, калі яго спрыяваць як мерыемую паверхню. Зберагчыце адна ідеальная транскрыпція, адзін прыклад неудачы і запіс пра вярнэнне да пачатковага стану перш чым расширваць масштаб. Спрыявайце гэты этап як кантракт межа вхіднымі дадзеннямі і паверыжанымі выходнымі рэзультатамі. Даўце назвы артыфактам, задаце критэрыя успеху і адмовіцеся ад тыхняй частковай роботы без паведамлення. Храніце стан графа у простым і типаванам формате. Вярнутыя структуры дадзення маскуюць інфармацыю пра тое, який вузел запісаў кожны поле, і спаказваюць продажчэнне роботы пасля перарываў. Этап падчырання графа двух агентав працюе наяўней, калі яго спрыяваць як мерыемую паверхню. Зберагчыце адна ідеальная транскрыпція, адзін прыклад неудачы і запіс пра вярнэнне да пачатковага стану перш чым расширваць масштаб. Храніце настройкі параду з кодам прыемліка. Файлы сяродавішча, хранальнікі секрэтных дадзенняў і флагі функцыйяў должны знаходзіцца ў аднам месцы, якое аператары можаць пераглядаць без неабходнасці чытання всего графа.

from langgraph.checkpoint.memory import MemorySaver

def build_two_agent():
    g = StateGraph(TwoAgentState)

    g.add_node("fetch", ta_fetch)
    g.add_node("analyzer", ta_analyzer)
    g.add_node("reviewer", ta_reviewer)
    g.add_node("merge", ta_merge)
    g.add_node("escalate", ta_escalate)
    g.add_node("emit", ta_emit)

    g.add_edge(START, "fetch")
    g.add_edge("fetch", "analyzer")
    g.add_edge("analyzer", "reviewer")
    g.add_edge("reviewer", "merge")

    g.add_conditional_edges("merge", route_after_merge,
        {"escalate": "escalate", "emit": "emit"})

    g.add_edge("emit", END)

    # A checkpointer is required to use interrupt()
    # Use MemorySaver for local testing, Postgres for production
    return g.compile(checkpointer=MemorySaver())

Адкліканне парабянку

Для стадіі адміністрування порэйнаўкі неабходна пазначыць вхідныя даны, адпаведальнага за крок і крэтыры завершэння пры перадзеі коду. Аперацыйныя працавнікі должны магчымае перадзеі крок з вядомай точкі контролю, не падозрываючы прыхованы стан. Неабходна задокументаваць як шлях успеху, так і шлях вярнення да нормальнага стану. Перапрыбуткі, людзкія пераказы і обробка некоректных паведамленняў є часткай продукту, а не элементамі пазнейшага дапрацоўвання. Неабходна людзкая аправарэнне для тых крокоў, якія выкалічваюць грошы або зменяюць даны ў працэсе виробніцтва. Працэс кампайлявання не є гарантыяй полнага адпаведнення продукту выклікам бізнесу.

Матрыца рашэнняў: калі НЕ варта выкарыстоўваць мульті-агента

У стадії «Матрыцы адзьянакоў» неабходна прадзефінаваць вхідныя даны, адпаведальную особу за кожны крок і критэрыя завершэння пры змены коду. Аперацыйныя працавнікі должны магчыма ўвайсці зноў у кожны крок, выкарыстоўваючы вядомы пункт перапалоў, без неабязковасці здогадвацца пра схованы стан. Лепш выбіраць маленькія, тэставаныя елементы замест большых скрыптов. Калі крок не выконваецца, прычына неудачы должна вказываць на адну конкрэтную адпаведальнасць, а не на заплутаны процес. Неабязкова ўключыць людскія пераказы для тых крокоў, якія ведуць да выдаткаў грошаў або зміняюць даны ў працэсе виробніцтва. Компіляцыйныя налашчэння не є гарантыяй полнай адпрацоўкі бізнес-процэса.

Што далей

Для стадіі «Што далей» неабяжна ўзначыць вхідныя даны, адпаведальнага за крок і крэтырыя завершэння пры зміне коду. Аперацыйныя працавнікі должны магчымае перайсці на выкананне кроку з вядомага пункта контролю, не спрабоўваючы здогадвацца пра захаваны стан. Спрацоўвайце з гэтай стадіяй як з кантрактом межаў вхідных дадзеных і перакананых выходных рэзультатаў. Даць назвы артыфактам, узначыць перакананні ў успеху і адмовіцца ад беззвучнага частковага завершэння. Забезпечыце людскія празборы для тых крокаў, якія витрачаюць грошы або зменяюць даны ў працэсе. Компіляцыйныя налашчанні не ўзначаюць павнае завершэння бізнес-процесу. Для стадіі «Што далей» неабяжна ўзначыць вхідныя даны, адпаведальнага за крок і крэтырыя завершэння пры зміне коду. Аперацыйныя працавнікі должны магчымае перайсці на выкананне кроку з вядомага пункта контролю, не спрабоўваючы здагадвацца пра захаваны стан. Зберагачыце налашчанні парадульна ад коду прыкладнення. Файлы сераўіса, хранільнікі секрэтных дадзеных і флагі функцыйяў должны знаходзіцца ў аднам месцы, якое працавнікі можуць аудытаваць, не чытаючы весь ланцуг задач.

Продзеўжыць чытанне

Калі працуеце на стадыі «Продзеўжыць чытанне», спачатку запісайце умовы кантракту: неабходныя даны, сігнал успеху і тое, што выходзіць па частковай нявыполненасці. Такі список пераканае ў тым, што пазнейшыя змены коду будуць чыстымі. Запісуйце адночасна шлях успеху і шлях вярнення. Перапрыбуткі, людзкі контроль і обработка некоректных паведамленняў є частью продукту, а не пазнейшым дапрацоўкам. Зробіце пераканаючы пункт пасля дорогіх крокаў. Система вярнення не должна зноў выклікаць той самы кантакт з LLM, калі аператар перапрыбуе пазнейшы вузел.

Чек-ліст для эксплуатацыі

Стадыя чек-ліста для эксплуатацыі працюе найкраща, калі яе спрыямаць як мерыемую плошчу. Запісайце адна ідеальная транскрыпцыю, адзін прыклад нявыполненасці і прыметку па вярненню да пачатковага стану, перш чым расширваць сферу дзеяння.

Запісуйце часы выконання і кост токеноў або запытаў разам з функцыйнальнымі рэзултатамі. Відразы костаў з самага пачатку запобегае неспакойным рахункам, калі процес пераходзіць з дэмаверсіі ў спяльныя сераўы.

Зберагаюце стан графа ў простам і типаваным формате. Вкладаныя блобы маскуюць інфармацію пра тое, який вузел запісаў якое поле, і спакошуюць продовжэнне роботы пасля перерываў.

Калі дозволяе бюджет, дадзіце тэст на перакананне, які працюе з критычным шляхам у CI за дапамою фікстураў, а не реальных платных API.

Конфігурацыю трэба зберагаць паза кодам прыкладнай програмы. Файлы сераўнавання, хранільнікі секрэтных данных і флагі функцыяй должны знаходзіцца ў аднам месцы, куда аператары можуць аудытувать іх без неабяжнага чытання всего графа.

Зберагаюце стан графа ў простам і типаваным формате. Вкладаныя блобы маскуюць інфармацію пра тое, який вузел запісаў якое поле, і спакошуюць продовжэнне роботы пасля перерываў.

Перш чым апранаваць стек, заморозьце версіі, зафіксуйце ідеальны транскрыпт для критычнага шляху і паказваце крокі для атрыбутавання. У спакульных сераўнаваннях неабходны ліміты швайнаў, перакананні ў прыналежнасці та чысты власнік для змены секрэтных данных. Валіце надзейнасць працы над красавім разовым дэманстрацыйным прыкладам.

Запіска параграфу f4e352541695: не кластыць ключы прадаўцаў у репазітары, задаць максымальную кантэнцыю токена на сесію і зберагчыць транскрыпты праза фіксаты для ацэнкі, каб пазнейшыя замены моделей заставалі пораўнанневымі.

Калі працуеце над стадзіяй 0 запіскі па зміцнэнню, спачатку запісайце контракт: неабходныя вхідныя даны, сігнал успеху і тое, што выходзіць у разе частковага невыпання. Такі чэк-ліст дапамагае заставаць пазнейшыя змены коду чыстымі. Запісвайце час выканання і вартасць токена або запыту праза функцыйнае рэзультат. Відкрытая візуабілізацыя вартасцей запобегае неспакоўным рахункам, калі парадокс пераходзіць з дэмавайнага сераўера ў спакульнае сераўеры.

Дзялей 0/727 па зміцнэнню: вы мерзіце час выканання, класыя ошибак і витрату токена для гэтай запіскі, а потым вырашаеце, чы рашыцца застаўіць змену на адной фіксованай сэтке пытанняў, а не на адной лягкай інформацыі.

Этап 1 прыткага зміцнення работае наяўна, калі яго спрыяваць як меравальную паверхню. Зафіксавайце адна ідеальная транскрыпцыю, адзін прыклад неудачы і запіс пра вярнэнне да пачатковага стану перш чым расширваць сферу дзеяння. Запісуйце адночасна шлях успеху і шлях вярнэння. Перапрыбуткі, людзкія контралі і обработка некоректных паведамленняў є часткай продукту, а не наступным этапам дапрацоўкі.

Дзеянне прыткага зміцнення 1/727: вы меравайце час выканання, класію памылак і выкарыстоўванне токенав для гэтага запісу, а пасля, на аднойчынных крэтарыях, а не на асоціяціях, выявляйце, чы хацеце застаўіць змяну.

Для этапа 2 прыткага зміцнення, перш чым зменяць код, задаце вхідныя даны, адпаведальнага за крок і крэтарыі завершэння. Аперацыйныя працавнікі павінны магчымае перазваляць крок з вядомай точкі контролю, не спадзяваючыся на схованы стан. Спрыявайце гэтаму этапу як кантракту межа вхіднымі данымі і паверанымі выходнымі рэзультатамі. Даўце назвы артыфактам, задаце перакананні на успех і адмовіцеся ад тых падчасовых завершэнняў, якія не ўсе павераны.

Дзеянне паўжасткі 2/727: звярніце увагу на час выканання, класы памылак і витрату токенаў для гэтага зьязку, а пасля, на аднойчынай базе пытанняў, а не на асобістых спазырах, выявіце, чы хачаце застаўіць змяну.

Калі працуеце над 3-й стадзіяю паўжасткі, спачатку запісайце контракт: неабходныя даны, сігнал успеху і тое, што выканаецца у разе частковай памылки. Такі список дапамагае заставіць пазнейшыя змяны коду чыстымі. Зберагаеце канфігурацыю праза код аплікацыі. Файлы сяродавішча, хранільнікі секрэтных дадзенняў і флагі функций должны знаходзіцца ў аднам месцы, куда аператары можуць адбавіць аудыт без неабходнасці чытання всей структуры.

Дзеянне паўжасткі 3/727: звярніце увагу на час выканання, класы памылак і витрату токенаў для гэтага зьязку, а пасля, на аднойчынай базе пытанняў, а не на асобістых спазырах, выявіце, чы хачаце застаўіць змяну.

Этап 4 прыцелкі змяцнення работае наяўна, калі яго спрыяваць як вимерную паверхню. Запісаўце адна «золатая» транскрыпцыю, адин прыклад неудачы і запіс перадзвороту рэшэнняя пры расшырэнні масштаба. Валіце маленькія, тэставаныя елементы замест большых скрыптов. Калі якісьць крок не выйшла, неудача павінна адносіцца да адной адпаведальнасці, а не да заплутанага ланцоўка дзеяння.

Дакладнасць прыцелкі змяцнення 4/727: вимеравайце час выканання, класію памылак і витрату токенав для гэтай прыцелкі, а потым вырашайце, чы рашыцца застаўляць змяну, адпаведна фіксаванаму набору пытанняў, а не лячэнням.