Strona główna / Artykuły / Wskazówki praktyczne: Twój agent AI nie jest inteligentny. Oto jak stworzyć takiego, który

Wskazówki praktyczne: Twój agent AI nie jest inteligentny. Oto jak stworzyć takiego, który

Krok po kroku praktyczne wskazówki: Twój agent AI nie jest inteligentny. Oto jak stworzyć takiego, który: zawiera umowy, mechanizmy weryfikacji oraz gotowe miejsca na kod dla zespołów wdrażających ten wzorzec.

3392 słów

Niech to służy jako wersja przeznaczona dla operatorów, zawierająca zasady przedstawione w artykule „Twój agent AI nie jest inteligentny. Oto jak stworzyć takiego, który naprawdę myśli”: wyraźne etapy, uporządkowane sekcje kodu oraz notatki dotyczące napraw, które przetrwają przeniesienie obowiązków.

Spis treści

Etap spisu treści działa najlepiej, gdy traktuje się go jako mierzalną powierzchnię do analizy. Zapisz jeden idealny zapis rozmowy, jeden przypadek awarii oraz notatkę dotyczącą cofnięcia działań, zanim rozszerzysz zakres pracy. Wolno preferować małe, testowalne jednostki zamiast rozbudowanych skryptów. Gdy jakiś krok zawiedzie, awaria powinna wskazywać na konkretną odpowiedzialność, a nie na skomplikowany łańcuch operacji. Utrzymuj stan grafu w prostej formie i z określonym typem danych. Wplecione elementy ukrywają informację o tym, który węzeł zapisał dane do którego pola, i powodują przerwę w kontynuacji pracy po zakłóceniach.

Dlaczego większość agentów AI to po prostu zaawansowane łańcuchy zapytań

Najlepiej sprawdzają się etapy projektowania agentów AI, gdy traktuje się je jako mierzalną powierzchnię do analizy. Zapisz jeden idealny przykład działania, jeden przypadek niepowodzenia oraz notatkę dotyczącą cofnięcia działań, zanim rozszerzysz zakres projektu. Traktuj ten etap jako umowę pomiędzy danymi wejściowymi a zweryfikowanymi wynikami. Nadaj nazwy poszczególnym elementom, zdefiniuj kryteria sukcesu i odrzucaj ciche, częściowe ukończenie zadań. Określ budżet tokenów na jeden ruch i na całą sesję. Narzędzia agentowe intensywnie rozszerzają kontekst; sztywne limity zapobiegają temu, by demonstracje przerodziły się w nieoczekiwane rachunki.

Problem z zasadą „Po prostu użyj ReAct”

Problem z podejściem opartym wyłącznie na etapach rozwiązuje się najlepiej, gdy traktuje się je jako mierzalną powierzchnię do analizy. Zapisz jeden idealny przykład działania, jeden przypadek awarii oraz notatkę dotyczącą cofnięcia zmian, zanim rozszerzysz zakres pracy. Zapisuj czasy wykonywania operacji oraz koszt tokenów lub zapytań obok wyników funkcjonalnych. Wczesna widoczność kosztów zapobiega nieoczekiwanym rachunkom, gdy przechodzi się z środowiska demonstracyjnego do współdzielonych środowisk. Utrzymuj stan grafu w prostej formie i z określonym typem danych. Wtórne struktury danych ukrywają informację o tym, który węzeł zapisał dane do którego pola, co powoduje przerwę w kontynuacji pracy po zakłóceniach.

Architektura: cztery węzły, jeden pętla

Faza Architecture Four Nodes funkcjonuje najlepiej, gdy traktowana jest jako mierzalna powierzchnia. Zapisz jeden idealny przypadek użycia, jeden przypadek awarii oraz notatkę dotyczącą cofnięcia zmian, zanim rozszerzysz zakres projektu. Przechowuj konfigurację poza kodem aplikacji. Pliki środowiskowe, magazyny poufnych danych oraz flagi funkcjonalne powinny znajdować się w jednym miejscu, aby operatorzy mogli je sprawdzić bez konieczności przeglądania całego grafu. Utrzymuj stan grafu w prostej formie i z określonym typem danych. Wplecione struktury ukrywają informację o tym, który węzeł zapisał dane w danym polu, co utrudnia kontynuację pracy po przerwach.

START --> Planner --> Executor <--> Replanner --> Reporter --> END

Zarządzanie stanem: filar całego systemu

Faza State Management The Backbone funkcjonuje najlepiej, gdy jest traktowana jako mierzalna powierzchnia do analizy. Zapisz jeden idealny przepis działania, jeden przypadek awarii oraz notatkę dotyczącą cofnięcia działań, zanim rozszerzysz zakres. Zdokumentuj zarówno prawidłowy przebieg działania, jak i ścieżkę przywracania do normalnego stanu. Próby ponownych działań, kontrolne punkty ludzkie oraz obsługa wiadomości nieprzyjętych stanowią część produktu, a nie elementy dodawane później. Utrzymuj stan grafu w prostej formie i z określonym typem danych. Wplecione struktury ukrywają informację o tym, który węzeł zapisał dane do którego pola, co utrudnia kontynuację działania po przerwach. Faza State Management The Backbone funkcjonuje najlepiej, gdy jest traktowana jako mierzalna powierzchnia do analizy. Zapisz jeden idealny przepis działania, jeden przypadek awarii oraz notatkę dotyczącą cofnięcia działań, zanim rozszerzysz zakres. Traktuj tę fazę jako umowę pomiędzy danymi wejściowymi a zweryfikowanymi danymi wyjściowymi. Nadaj nazwy poszczególnym elementom, zdefiniuj kryteria sukcesu i odrzucaj ciche, częściowe ukończenie zadań.

import operator
from typing import Annotated, TypedDict

from pydantic import BaseModel, Field

class StrategyState(TypedDict, total=False):
    """Global state that flows through the LangGraph nodes."""
    query: str
    plan: list[dict]
    scratchpad: Annotated[list[dict], operator.add]
    current_step: int
    final_report: str
    replan_count: int

Schematy ustrukturyzowanych danych wyjściowych: PlanStep i Plan

Dla etapu PlanStep w schematach strukturyzowanego wyjścia należy zdefiniować dane wejściowe, osobę odpowiedzialną za dany etap oraz kryteria zakończenia przed modyfikacją kodu. Operatorzy powinni móc ponownie uruchomić dany etap na podstawie znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Należy rejestrować czasy wykonywania oraz koszt tokenów lub zapytań obok wyników funkcjonalnych. Wczesna widoczność kosztów zapobiega niespodziewanym rachunkom, gdy ścieżka przechodzi z środowiska demonstracyjnego do współdzielonych środowisk. Konieczne jest ludzkie zatwierdzenie dla operacji, które generują wydatki lub zmieniają dane produkcyjne. Połączenia realizowane w czasie kompilacji nie gwarantują pełnej kompletności biznesowej.

AVAILABLE_TOOLS_TEXT = """
- get_metrics(ticker, metric?): Return stock metrics. 'metric' is optional
  (P/E, EPS, Revenue, Market Cap, Sector).
- search_news(ticker): Return recent news headlines for a ticker.
- compare_metrics(tickers: list, metric): Compare one metric across
  multiple tickers.
"""

class PlanStep(BaseModel):
    """A single executable step inside an analysis plan."""
    step_id: int = Field(description="Sequential step number")
    tool: str = Field(
        description=f"Tool to use. Must be one of:\n{AVAILABLE_TOOLS_TEXT}"
    )
    args: dict = Field(description="Arguments for the tool call")
    purpose: str = Field(description="Why this step is needed")

class Plan(BaseModel):
    """The full plan generated by the planner node."""
    goal: str = Field(description="The overall analysis goal")
    steps: list[PlanStep] = Field(
        description="Ordered list of steps to execute"
    )
class ReplanDecision(BaseModel):
    """Output of the replanner node."""

reasoning: str = Field(
        description="Analysis of current progress and findings"
    )
    should_replan: bool = Field(
        description="Whether the plan needs modification"
    )
    updated_steps: list[PlanStep] = Field(
        default_factory=list,
        description="Remaining steps if replan is needed. Empty if no changes.",
    )

System narzędzi: trzy narzędzia, jeden rejestr

Dla trzeciego etapu Systemu Narzędziowego należy zdefiniować dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia przed modyfikacją kodu. Operatorzy powinni móc ponownie uruchomić dany krok na podstawie znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Konfigurację należy przechowywać poza kodem aplikacji. Pliki środowiskowe, magazyny poufnych danych oraz flagi funkcjonalne powinny znajdować się w jednym miejscu, które operatorzy mogą sprawdzić bez konieczności czytania całej struktury. Autoryzacja powinna odbywać się przy bramie wejściowej, a ponowna autoryzacja – na poziomie przetwarzania danych. Sam token nośny nie stanowi granicy między poszczególnymi użytkownikami.

The ToolRegistry

Dla etapu The ToolRegistry należy zdefiniować dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia przed zmianą kodu. Operatorzy powinni móc ponownie uruchomić ten krok od znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Należy udokumentować zarówno prawidłowy przebieg działania, jak i ścieżkę naprawczą. Próby ponownych działań, kontrolne punkty ludzkie oraz obsługa wiadomości błędnych stanowią część produktu, a nie elementy dodawane później. Należy się autoryzować przy bramce wejściowej, a ponownie uzyskiwać uprawnienia na poziomie warstwy danych. Sam token nośny nie stanowi granicy między poszczególnymi użytkownikami. Dla etapu The ToolRegistry należy zdefiniować dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia przed zmianą kodu. Operatorzy powinni móc ponownie uruchomić ten krok od znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Traktuj ten etap jako umowę pomiędzy danymi wejściowymi a zweryfikowanymi wynikami. Należy nadać nazwy poszczególnym elementom, zdefiniować kryteria sukcesu oraz odrzucić przypadki cichego, częściowego ukończenia zadania.

from langchain_core.tools import BaseTool
from typing import Iterable, Mapping, Any

class ToolRegistry:
    """Namespace-aware container for LangChain tools."""
    def __init__(self) -> None:
        self._tools_by_toolset: dict[str, dict[str, BaseTool]] = {}
    def add_tools(self, toolset: str, tools: Iterable[BaseTool]) -> None:
        bucket = self._tools_by_toolset.setdefault(toolset, {})
        bucket.update({t.name: t for t in tools})
    def get_tools(self, toolset: str) -> tuple[BaseTool, ...]:
        return tuple(self._tools_by_toolset.get(toolset, {}).values())
    def invoke(
        self, toolset: str, tool_name: str, tool_args: Mapping[str, Any]
    ) -> Any:
        t = self._tools_by_toolset.get(toolset, {}).get(tool_name)
        if t is None:
            raise ValueError(
                f"Unknown tool '{tool_name}' in toolset '{toolset}'"
            )
        return t.invoke(dict(tool_args))

Węzeł Planisty: Myśl przed działaniem

Gdy przechodzisz przez etap myślenia w ramach Węzła Planisty, najpierw zapisz umowę: wymagane dane wejściowe, sygnał sukcesu oraz to, co się dzieje w przypadku częściowego niepowodzenia. Taka lista kontrolna zapewnia uczciwość późniejszych zmian w kodzie. Obok wyników funkcjonalnych zapisz czas trwania oraz koszt tokena lub zapytania. Wczesna widoczność kosztów zapobiega niespodziewanym rachunkom, gdy ścieżka przechodzi z wersji demonstracyjnej do środowisk współdzielonych. Ustal punkty kontrolne po kosztownych krokach. Narzędzie do kontynuacji pracy nie powinno ponownie naliczać opłaty za tę samą wywołanie LLM, gdy operator próbuje ponownie uruchomić późniejszy węzeł.

def planner_node(state: StrategyState) -> dict:
    """Create a step-by-step research plan using structured output."""
    planner = model.with_structured_output(Plan)

prompt = PLAN_PROMPT.format(
        available_tools=AVAILABLE_TOOLS_TEXT,
        ticker_choices=ticker_choices_text(),
        metric_choices=metric_choices_text(),
        query=state["query"],
    )
    plan: Plan = planner.invoke(prompt)
    steps = [s.model_dump() for s in plan.steps]
    return {"plan": steps, "current_step": 0}

PLAN_PROMPT: Gdzie znajdują się zasady bezpieczeństwa

Gdy pracujesz na etapie PLANPROMPT Where the Guardrails, najpierw zapisz umowę: wymagane dane wejściowe, sygnał sukcesu oraz to, co się dzieje w przypadku częściowego awarii. Taka lista kontrolna zapewnia uczciwość późniejszych zmian w kodzie. Przechowuj konfigurację poza kodem aplikacji. Pliki środowiskowe, magazyny tajnych danych oraz flagi funkcjonalne powinny znajdować się w jednym miejscu, które operatorzy mogą sprawdzić bez konieczności czytania całej struktury. Zachowuj w pamięci podręcznej stabilne instrukcje systemu oraz schematy narzędzi. Ponowne wysyłanie identycznego prefiksu jest częstą przyczyną marnotrawstwa zasobów.

PLAN_PROMPT = """\
You are a financial research planner. Given a user's analysis request,
create a step-by-step research plan using the available tools.

Available tools:
{available_tools}
Rules:
- Use only the tools listed above.
- Every plan step must be executable with one of those tools.
- When a tool accepts 'ticker' or 'tickers', use only these exact values:
  {ticker_choices}
- When a tool accepts 'metric', use one of these exact values:
  {metric_choices}
- There are no other tools available. Final synthesis is handled separately.
Create an efficient plan. Group related lookups. Aim for 4-8 steps.
User request: {query}"""

Węzeł Wykonawczy: Krok po kroku

Gdy pracujesz nad etapem The Executor Node One, najpierw zapisz umowę: wymagane dane wejściowe, sygnał sukcesu oraz to, co dzieje się w przypadku częściowego niepowodzenia. Taka lista kontrolna zapewnia uczciwość późniejszych zmian w kodzie. Zdokumentuj zarówno ścieżkę prawidłowego działania, jak i ścieżkę naprawczą. Próby ponowne, kontrola przez ludzi oraz obsługa wiadomości błędnych stanowią część produktu, a nie elementy dodawane później. Ustal punkty kontrolne po kosztownych krokach. Narzędzie do kontynuacji pracy nie powinno ponownie naliczać opłat za tę samą wywołanie LLM, gdy operator próbuje ponownie uruchomić późniejszy węzeł. Gdy pracujesz nad etapem The Executor Node One, najpierw zapisz umowę: wymagane dane wejściowe, sygnał sukcesu oraz to, co dzieje się w przypadku częściowego niepowodzenia. Taka lista kontrolna zapewnia uczciwość późniejszych zmian w kodzie. Traktuj ten etap jako umowę pomiędzy danymi wejściowymi a zweryfikowanymi wynikami. Nadaj nazwy poszczególnym elementom, zdefiniuj kryteria sukcesu i odrzucaj ciche, częściowe ukończenie zadań.

MAX_STEPS = 12  # Safety limit on total steps

def executor_node(state: StrategyState) -> dict:
    """Execute the next pending step from the plan."""
    plan = state.get("plan", [])
    current_step = state.get("current_step", 0)
    if current_step >= len(plan):
        return {}
    if current_step >= MAX_STEPS:
        return {"current_step": len(plan)}
    step = plan[current_step]
    tool_name = step["tool"]
    tool_args = step["args"]
    try:
        result = str(
            TOOL_REGISTRY.invoke(
                AgentName.EXECUTOR.value, tool_name, tool_args
            )
        )
        status = "Error" if result.startswith("Error:") else "Success"
    except Exception as exc:
        result = f"Error: {exc}"
        status = "Error"
    entry = {
        "step": current_step + 1,
        "tool": tool_name,
        "args": tool_args,
        "result": result,
        "status": status,
    }
    return {
        "scratchpad": [entry],
        "current_step": current_step + 1,
    }

The Replanner Node: Gdzie następuje samokorekta

The Replanner Node – w tym modelu etap realizacji działa najlepiej, gdy traktuje się go jako powierzchnię poddającą się pomiarom. Zapisz jeden idealny przykład działania, jeden przypadek awarii oraz notatkę dotyczącą cofnięcia działań, zanim rozszerzysz zakres projektu. Zarejestruj czasy wykonywania operacji oraz koszt tokenów lub zapytań obok wyników funkcjonalnych. Wczesna widoczność kosztów zapobiega nieoczekiwanym rachunkom, gdy proces przechodzi z środowiska demonstracyjnego do współdzielonych środowisk. Utrzymuj stan grafu w prostej formie i z określonym typem danych. Wложone struktury danych ukrywają informację o tym, który węzeł zapisał dane w danym polu, co powoduje przerwanie kontynuacji działania po zakłóceniach.

MAX_REPLANS = 2  # Prevent infinite replanning

def replanner_node(state: StrategyState) -> dict:
    """Review progress and optionally modify the remaining plan."""
    plan = state.get("plan", [])
    current_step = state.get("current_step", 0)
    replan_count = state.get("replan_count", 0)
    scratchpad = state.get("scratchpad", [])
    remaining = plan[current_step:]
    if len(remaining) = MAX_REPLANS:
        return {}
    scratchpad_text = "\n".join(
        f"Step {e['step']}: {format_tool_call(e['tool'], e['args'])} "
        f"-> [{e['status']}] {e['result'][:150]}..."
        for e in scratchpad
    )
    remaining_text = "\n".join(
        f"Step {s['step_id']}: {format_tool_call(s['tool'], s['args'])} "
        f"- {s['purpose']}"
        for s in remaining
    )
    replanner = model.with_structured_output(ReplanDecision)
    prompt = REPLAN_PROMPT.format(
        goal=state["query"],
        scratchpad=scratchpad_text,
        remaining_steps=remaining_text,
    )
    decision: ReplanDecision = replanner.invoke(prompt)
    if decision.should_replan and decision.updated_steps:
        new_steps = plan[:current_step] + [
            s.model_dump() for s in decision.updated_steps
        ]
        return {"plan": new_steps, "replan_count": replan_count + 1}
    return {"replan_count": replan_count + 1}

REPLAN_PROMPT

Etap REPLANPROMPT działa najlepiej, gdy traktuje się go jako mierzalną powierzchnię. Zapisz jeden idealny przepis działania, jeden przypadek awarii oraz notatkę dotyczącą cofnięcia zmian, zanim rozszerzysz zakres pracy. Trzymaj konfigurację poza kodem aplikacji. Pliki środowiskowe, magazyny poufnych danych oraz flagi funkcjonalne powinny znajdować się w jednym miejscu, które operatorzy mogą sprawdzić bez konieczności przeglądania całej struktury. Ustal limit tokenów na rundę i sesję. Narzędzia typu agentic intensywnie rozszerzają kontekst; sztywne ograniczenia zapobiegają temu, by demonstracje przerodziły się w niespodziewane rachunki.

REPLAN_PROMPT = """\
You are a financial research planner reviewing progress on a research task.

Original goal: {goal}
Completed steps and findings so far:
{scratchpad}
Remaining steps in the plan:
{remaining_steps}
Based on the findings so far, should the remaining plan change?
If an expected tool failed or revealed something unexpected, add a step
to investigate.
If a step is now redundant, remove it.
Use only the available executable tools already shown in the plan.
Do not add recommendation, summary, or report-writing steps."""

Łączenie grafu: montaż LangGraph

Faza Wiring the Graph LangGraph funkcjonuje najlepiej, gdy jest traktowana jako mierzalna powierzchnia. Zapisz jeden idealny przepływ działania, jeden przypadek awarii oraz notatkę dotyczącą cofnięcia działań, zanim rozszerzysz zakres. Zdokumentuj zarówno pomyślny, jak i alternatywny scenariusz naprawczy. Próby ponownych działań, kontrolne punkty ludzkie oraz obsługa wiadomości nieodebranych stanowią część produktu, a nie elementy dodawane później. Utrzymuj stan grafu w prostej formie i z określonym typem danych. Wplecione struktury ukrywają informację o tym, który węzeł zapisał dane do którego pola, co utrudnia kontynuację działania po przerwach. Faza Wiring the Graph LangGraph funkcjonuje najlepiej, gdy jest traktowana jako mierzalna powierzchnia. Zapisz jeden idealny przepływ działania, jeden przypadek awarii oraz notatkę dotyczącą cofnięcia działań, zanim rozszerzysz zakres. Traktuj tę fazę jako umowę pomiędzy danymi wejściowymi a zweryfikowanymi wynikami. Nadaj nazwy poszczególnym elementom, zdefiniuj kryteria sukcesu i odrzucaj ciche, częściowe ukończenie zadań.

from langgraph.graph import StateGraph, END
from enum import Enum

class AgentName(Enum):
    PLANNER = "planner"
    EXECUTOR = "executor"
    REPLANNER = "replanner"
    REPORT = "report"

def build_graph():
    """Build and compile the LangGraph planning-agent workflow."""
    workflow = StateGraph(StrategyState)
    workflow.add_node(AgentName.PLANNER.value, planner_node)
    workflow.add_node(AgentName.EXECUTOR.value, executor_node)
    workflow.add_node(AgentName.REPLANNER.value, replanner_node)
    workflow.add_node(AgentName.REPORT.value, report_node)
    workflow.set_entry_point(AgentName.PLANNER.value)
    workflow.add_edge(AgentName.PLANNER.value, AgentName.EXECUTOR.value)
    workflow.add_edge(AgentName.EXECUTOR.value, AgentName.REPLANNER.value)
    workflow.add_conditional_edges(
        AgentName.REPLANNER.value,
        should_continue_execution,
        {
            AgentName.EXECUTOR.value: AgentName.EXECUTOR.value,
            AgentName.REPORT.value: AgentName.REPORT.value,
        },
    )
    workflow.add_edge(AgentName.REPORT.value, END)
    return workflow.compile()

def should_continue_execution(state: StrategyState) -> str:
    """Return the next node name after re-planning."""
    if state.get("current_step", 0) >= len(state.get("plan", [])):
        return AgentName.REPORT.value
    return AgentName.EXECUTOR.value

Prawdziwy przykład wykonywania: śledzenie jednego zapytania

W następnym etapie rzeczywistego przykładu wykonania należy zdefiniować dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia przed modyfikacją kodu. Operatorzy powinni móc ponownie uruchomić dany krok na podstawie znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Należy rejestrować czasy wykonywania oraz koszt tokenów lub zapytań obok wyników funkcjonalnych. Wczesna widoczność kosztów zapobiega nieoczekiwanym rachunkom, gdy ścieżka przechodzi z środowiska demonstracyjnego do współdzielonych środowisk. Konieczne jest ludzkie zatwierdzenie w przypadkach, gdy dochodzi do wydatków lub zmian w danych produkcyjnych. Podłączenia realizowane w czasie kompilacji nie gwarantują pełnej kompletności rozwiązania biznesowego.

{
  "metric": "P/E",
  "values": {
    "NVDA": 58.3,
    "AMD": 102.5
  }
}

Dalej z tym

W fazie „Gdzie to pójdzie dalej” należy zdefiniować dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia przed modyfikacją kodu. Operatorzy powinni móc ponownie uruchomić dany krok na podstawie znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Konfigurację należy przechowywać poza kodem aplikacji. Pliki środowiskowe, magazyny tajnych danych oraz flagi funkcjonalne powinny znajdować się w jednym miejscu, które operatorzy mogą sprawdzić bez konieczności czytania całej struktury. Zatwierdzenie przez człowieka powinno być wymagane dla operacji, które wiążą się z wydawaniem pieniędzy lub modyfikacją danych produkcyjnych. Połączenia realizowane w czasie kompilacji nie równają się pełnej kompletności biznesowej.

Ostateczne uwagi

W fazie Ostatecznych Uwag należy zdefiniować dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia przed zmianą kodu. Operatorzy powinni móc ponownie uruchomić ten krok od znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Należy udokumentować zarówno prawidłowy przebieg procesu, jak i ścieżkę naprawczą. Próby ponownych działań, kontrola przez ludzi oraz obsługa wiadomości błędowych stanowią część produktu, a nie elementy dodawane później. Konieczne jest uzyskanie zatwierdzenia człowieka w przypadkach, gdy dochodzi do wydawania pieniędzy lub zmiany danych produkcyjnych. Połączenia realizowane w czasie kompilacji nie równają się kompletności rozwiązania biznesowego. W fazie Ostatecznych Uwag należy zdefiniować dane wejściowe, osobę odpowiedzialną za dany krok oraz kryteria zakończenia przed zmianą kodu. Operatorzy powinni móc ponownie uruchomić ten krok od znanego punktu kontrolnego, bez konieczności zgadywania ukrytego stanu. Traktuj tę fazę jako umowę pomiędzy danymi wejściowymi a zweryfikowanymi wynikami. Nazwij poszczególne elementy, zdefiniuj kryteria sukcesu i odrzuć ciche, częściowe ukończenie zadania.

Ciągnijmy naukę razem

Gdy przechodzisz przez etap „Let’s Keep Learning”, najpierw zapisz umowę: wymagane dane wejściowe, sygnał sukcesu oraz to, co dzieje się w przypadku częściowego niepowodzenia. Taka lista kontrolna zapewnia uczciwość późniejszych zmian w kodzie. Zapisz czas trwania oraz koszt tokenów lub zapytań obok wyników funkcjonalnych. Wczesna widoczność kosztów zapobiega niespodziewanym rachunkom, gdy ścieżka przechodzi z środowiska demonstracyjnego do współdzielonych środowisk. Ustal punkty kontrolne po kosztownych krokach. Narzędzie do kontynuacji pracy nie powinno ponownie naliczać opłat za tę samą wywołanie LLM, gdy operator próbuje ponownie uruchomić późniejszy węzeł.

Wiadomość od naszego założyciela

Gdy przechodzisz przez etap wiadomości A z naszej platformy, najpierw zapisz umowę: wymagane dane wejściowe, sygnał sukcesu oraz to, co dzieje się w przypadku częściowego niepowodzenia. Taka lista kontrolna zapewnia uczciwość późniejszych zmian w kodzie.

Lista kontrolna operacyjna

Gdy przechodzisz przez etap listy kontrolnej operacyjnej, najpierw zapisz umowę: wymagane dane wejściowe, sygnał sukcesu oraz to, co dzieje się w przypadku częściowego niepowodzenia. Taka lista kontrolna zapewnia uczciwość późniejszych zmian w kodzie.

Niech lepsze będą małe, testowalne jednostki niż rozbudowane skrypty. Gdy jakiś krok się nie powiedzie, błąd powinien wskazywać na konkretną odpowiedzialność, a nie na skomplikowany łańcuch operacji.

Punkt kontrolny po kosztownych krokach. Przy wznowieniu pracy nie powinno dochodzić do ponownego naliczania opłat za tę samą wywołanie LLM, gdy operator próbuje ponownie uruchomić późniejszy węzeł.

Zamrożone wersje zależności oraz zapis digestu obrazu, który uruchomił demonstrację. Reprodukowalność jest ważniejsza od lokalnej wiedzy specjalistów.

Traktuj tę fazę jako umowę pomiędzy danymi wejściowymi a zweryfikowanymi wynikami. Nadaj nazwy artefaktom, zdefiniuj kryteria sukcesu i odrzuć ciche, częściowe ukończenie zadań.

Punkt kontrolny po kosztownych krokach. Przy wznowieniu pracy nie powinno dochodzić do ponownego naliczania opłat za tę samą wywołanie LLM, gdy operator próbuje ponownie uruchomić późniejszy węzeł.

Zanim przejdziesz do wyższej wersji stacku, zamroź wersje oprogramowania, utwórz dokładny zapis działań dla kluczowej ścieżki oraz potwierdź kroki odwracające zmiany. Środowiska współdzielone wymagają ograniczeń szybkości, weryfikacji dostępności oraz jasno określonego właściciela odpowiedzialnego za rotację haseł. Wolisz nudną niezawodność od pomysłowych, jednorazowych demonstracji.

Uwagi dotyczące fea74fe7fb83: unikaj przechowywania kluczy dostawcy w repozytorium, ustaw ograniczenie liczby tokenów na sesję oraz przechowuj transkrypcje obok plików testowych, aby późniejsze zmiany modeli pozostały porównywalne.