Przejdź do głównej zawartości

Claude Agent SDK: własni agenci na pętli Claude Code

Claude Agent SDK to biblioteka dla Pythona i TypeScriptu, która uruchamia binarkę Claude Code jako podproces i udostępnia kodowi aplikacji jej pętlę agenta: wbudowane narzędzia, reguły uprawnień, hooki, subagentów, serwery MCP i wznawialne sesje. Pasuje do botów, które potrzebują własnych narzędzi w procesie, zabezpieczeń w kodzie albo typowanych wyników, których jedno wywołanie claude -p nie wyrazi.

CI w twoim zespole robi się czerwone dwadzieścia razy w tygodniu i za każdym razem ktoś otwiera log na 4000 linii, przewija kaskadę błędów pochodnych i decyduje, czy to prawdziwa regresja, niestabilny test, czy zepsuty runner. Krok z claude -p już był: dał prozę, z którą nikt nic nie zrobił, raz powołał się na plik, którego nie ma, a raz poprosił o uprawnienie, którego nie miał kto zatwierdzić. Ta strona buduje wersję, która tego nie robi.

  • Bota do triage’u CI w około 130 liniach Pythona (z odpowiednikiem w TypeScripcie), który klasyfikuje nieudany przebieg GitHub Actions i cytuje swoje dowody
  • Konfigurację uprawnień, w której bot jest tylko do odczytu z konstrukcji, a nie z polecenia: bez Bash, bez Edit, z odczytami ograniczonymi do checkoutu
  • Jedno własne narzędzie w procesie, jeden hook PreToolUse, jednego subagenta i kontrakt w JSON Schema, każde z nazwanym zadaniem
  • Deterministyczną kontrolę, która odrzuca werdykt, jeśli jego dowodów nie ma w logu ani w repozytorium, oraz pętlę ewaluacyjną mierzącą bota na oznaczonych historycznych awariach
  • Regułę decyzyjną: kiedy SDK się opłaca, a kiedy wystarczy claude -p albo Routines (rutyny w chmurze)

Zacznij od najmniejszej rzeczy, która działa. Większość botów w CI nigdy nie potrzebuje SDK, a SDK wnosi do pipeline’u zależność, podproces i klucz API.

PotrzebujeszUżyjDlaczego
Jeden prompt, jedna odpowiedź JSON, w kroku powłokiclaude -p z --output-format json, --json-schema i --max-budget-usdZero kodu do utrzymania. Zobacz automatyzację zadań w trybie headless
Przegląd PR albo odpowiadanie na @claude na GitHubieanthropics/claude-code-action@v1Akcja sama obsługuje checkout, komentarze i tokeny. Zobacz CI/CD z Claude Code
Przebieg z harmonogramu albo webhooka w chmurze AnthropicRoutinesŻadnego runnera do utrzymania
Własne narzędzia w twoim języku, zabezpieczenia w kodzie, sterowanie wieloma turami, typowane wyniki, strumieniowanie do własnego UIAgent SDKWszystko poniżej tej tabeli
Agent hostowany przez Anthropic, bez procesu, który musisz uruchamiaćClaude Managed AgentsAnthropic uruchamia pętlę i sandbox; płacisz za tokeny modelu plus 0,08 USD za godzinę sesji
Claude API z własną pętlą narzędzi, bez narzędzi Claude CodeKliencki SDK Claude APIAgent SDK to Claude Code jako biblioteka; kliencki SDK to surowe API

SDK istnieje tylko dla Pythona i TypeScriptu. Z każdego innego języka przegląd Agent SDK każe uruchamiać CLI jako podproces z -p i --output-format json. To samo zadanie napisane równolegle w Codex SDK i Cursor SDK znajdziesz w porównaniu trzech SDK do sterowania agentami z kodu.

Okno terminala
# Python 3.10+. Wheel zawiera CLI Claude Code; nie trzeba nic więcej instalować.
pip install claude-agent-sdk==0.2.160

Żeby użyć systemowego claude zamiast dołączonego, przekaż cli_path="/path/to/claude" w ClaudeAgentOptions.

Uwierzytelniasz się kluczem API z Claude Console, wyeksportowanym jako ANTHROPIC_API_KEY w procesie, który uruchamia agenta. SDK nie wczytuje za ciebie plików .env. U dostawców chmurowych ustaw CLAUDE_CODE_USE_BEDROCK=1, CLAUDE_CODE_USE_VERTEX=1 (Agent Platform w Google Cloud) albo CLAUDE_CODE_USE_FOUNDRY=1 razem z poświadczeniami danego dostawcy. Przegląd Agent SDK mówi też, że zewnętrzni deweloperzy nie mogą bez wcześniejszej zgody oferować logowania przez claude.ai ani jego limitów w produktach zbudowanych na SDK, więc produkt, który udostępniasz innym, uwierzytelnia się kluczem API.

Każde wywołanie SDK uruchamia binarkę claude, wysyła twój prompt strumieniem JSON i zwraca typowane komunikaty: SystemMessage, AssistantMessage (tekst i bloki wywołań narzędzi), UserMessage (wyniki narzędzi) oraz jeden ResultMessage na koniec każdej tury. To na ResultMessage reaguje twój kod. Zawiera subtype (success, error_max_turns, error_max_budget_usd, error_max_structured_output_retries albo error_during_execution), is_error, result, structured_output, total_cost_usd, num_turns, session_id i permission_denials.

W obu językach są dwa punkty wejścia:

  • query() to jedno wywołanie: prompt na wejściu, strumień komunikatów na wyjściu. Nadaje się do zadań wsadowych, w których znasz całe wejście z góry.
  • ClaudeSDKClient (Python) trzyma otwarte połączenie, więc możesz wysyłać kolejne wiadomości, przerwać turę przez interrupt() i zmienić tryb uprawnień w trakcie sesji. README Pythona przedstawia go jako punkt wejścia dla własnych narzędzi i hooków, dlatego przykład poniżej go używa. W TypeScripcie query() zwraca obiekt Query, który robi to samo, gdy jako prompt przekażesz asynchroniczny iterable.

Trzy ustawienia domyślne zaskakują ludzi przychodzących z CLI:

  1. Bez promptu systemowego Claude Code, dopóki o niego nie poprosisz. Gdy pominiesz system_prompt, oba SDK uruchamiają CLI z pustym promptem systemowym (sprawdzone w kodzie 0.2.160 i 0.3.283). Dla agenta programistycznego przekaż {"type": "preset", "preset": "claude_code", "append": "..."}.
  2. Wczytuje się każdy plik ustawień, dopóki tego nie zawęzisz. Nieustawione setting_sources wczytuje ustawienia użytkownika, projektu i lokalne, więc własny ~/.claude/settings.json runnera przecieka do przebiegu. Przekaż ["project"] tylko wtedy, gdy kod w cwd jest zaufany; lista musi zawierać "project", żeby wczytał się CLAUDE.md. Gdy cwd to commit kontrybutora, przekaż []: ustawienia projektu mogą definiować hooki, a hooki uruchamiają polecenia powłoki z sekretami joba w środowisku.
  3. Sesje SDK nie startują w trybie auto. Od v2.1.283 (kanał latest) auto mode jest trybem startowym sesji interaktywnych, ale claude -p i Agent SDK nadal startują w domyślnym, ręcznym trybie. Ustaw permission_mode jawnie.

Bot uruchamia się po nieudanym workflow CI, czyta log z nieudanych kroków i kod z checkoutu, a potem zapisuje verdict.json. Kolejny krok workflow zamienia ten plik w komentarz pod PR. Sam agent nie może ani zapisywać plików, ani uruchamiać poleceń.

  1. Napisz kontrakt przed promptem. Werdykt to JSON Schema: category z zamkniętej listy, culprit_file, niepusta tablica evidence, confidence i summary. Przekazany jako output_format sprawia, że CLI waliduje końcową odpowiedź i ponawia próbę, aż będzie zgodna. Jeśli się nie da, tura kończy się error_max_structured_output_retries, a nie wolnym tekstem, który późniejszy krok musiałby parsować.

  2. Usuń narzędzia, których bot nigdy nie może mieć. tools=["Read", "Grep", "Glob", "Agent"] to kompletny zestaw wbudowanych narzędzi tej sesji. Bash, Edit i Write nie są ograniczone; dla modelu po prostu nie istnieją. allowed_tools to coś innego: tylko automatycznie zatwierdza i nic nie mówi o dostępności.

  3. Zatwierdzaj trybem, nie listą. permission_mode="dontAsk" odrzuca wszystko, czego nie zatwierdza żadna reguła, i nigdy nie czeka na pytanie. Claude Code zatwierdza odczyty plików i Grep wewnątrz katalogu roboczego bez żadnej reguły, więc Read zostaje poza allowed_tools. To samo dotyczy Agent, który nigdy nie pyta przed uruchomieniem, więc jedyną regułą allow jest własne narzędzie do logu. Gołe Read jako reguła allow zatwierdziłoby odczyty w dowolnym miejscu runnera, także poza checkoutem.

  4. Daj agentowi wąskie narzędzie zamiast powłoki. get_failed_log to funkcja w Pythonie wystawiona jako narzędzie MCP w procesie. Agent może pobrać log z nieudanych kroków tego przebiegu i nic więcej. Nie uruchomi gh z innymi argumentami, bo nie ma Bash.

  5. Regułę, którą model mógłby pominąć, wymuś hookiem. Odczyty wewnątrz checkoutu są zatwierdzane automatycznie, więc hook PreToolUse odrzuca każde Read, Grep i Glob, którego ścieżka, filtr glob albo wzorzec Glob wygląda jak .env* albo secrets/. Hooki są pierwsze w kolejności oceny uprawnień, a odmowa z hooka obowiązuje w każdym trybie. Hook to zabezpieczenie awaryjne, a nie właściwa ochrona: Grep bez ścieżki przeszukuje cały cwd i hook nie ma czego dopasować. Prawdziwą ochroną jest checkout, w którym nie ma żadnych sekretów.

  6. Trzymaj log poza głównym kontekstem dzięki subagentowi. Subagent log-reader na tańszym aliasie haiku czyta do 3000 linii logu i zwraca tylko pierwszy prawdziwy błąd z 20 liniami kontekstu. Główny agent wydaje kontekst na kod, nie na kaskadę błędów.

  7. Ogranicz przebieg i sprawdź odpowiedź. max_turns=30 i max_budget_usd=2.0 ograniczają koszt zagubionego przebiegu. Po otrzymaniu wyniku zwykły Python sprawdza każdy dowód w logu i w checkoucie, a jeśli któryś nie ma pokrycia, obniża werdykt do unknown.

# .github/scripts/triage.py: explains why a CI run failed. Read-only by construction.
import json
import os
import re
import sys
from pathlib import Path
import anyio
from claude_agent_sdk import (
AgentDefinition,
ClaudeAgentOptions,
ClaudeSDKClient,
HookMatcher,
ResultMessage,
create_sdk_mcp_server,
tool,
)
RUN_ID = os.environ["RUN_ID"]
REPO = Path(os.environ.get("TARGET_DIR", ".")).resolve() # the untrusted checkout: data, never code
async def failed_log() -> str:
proc = await anyio.run_process(["gh", "run", "view", RUN_ID, "--log-failed"])
return proc.stdout.decode(errors="replace")
# 1. A custom tool: the agent gets the failed-step log, not a shell.
@tool("get_failed_log", "Log of the failed steps in the CI run under triage", {"max_lines": int})
async def get_failed_log(args):
lines = (await failed_log()).splitlines()
return {"content": [{"type": "text", "text": "\n".join(lines[-args["max_lines"]:])}]}
# 2. A hook: runs on every matching call, whatever the model decides.
async def deny_secret_reads(input_data, tool_use_id, context):
tool_input = input_data["tool_input"]
keys = ["file_path", "path", "glob"] + (["pattern"] if input_data["tool_name"] == "Glob" else [])
# A Grep over the whole cwd has no path to match: the checkout itself must hold no secrets.
if any(re.search(r"(^|/)(\.env[^/]*|secrets?)(/|$)", str(tool_input.get(k) or "")) for k in keys):
return {
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Triage never reads secret files.",
}
}
return {}
# 3. The verdict contract.
VERDICT = {
"type": "object",
"properties": {
"category": {"enum": ["test-regression", "flaky-test", "build-config", "infrastructure", "unknown"]},
"culprit_file": {"type": ["string", "null"]},
"evidence": {"type": "array", "items": {"type": "string"}, "minItems": 1},
"confidence": {"enum": ["high", "medium", "low"]},
"summary": {"type": "string"},
},
"required": ["category", "culprit_file", "evidence", "confidence", "summary"],
"additionalProperties": False,
}
options = ClaudeAgentOptions(
system_prompt={"type": "preset", "preset": "claude_code",
"append": "You are a CI triage bot. You never change files."},
cwd=REPO,
tools=["Read", "Grep", "Glob", "Agent"], # the entire built-in toolset: no Bash, Edit or Write
allowed_tools=["mcp__ci__get_failed_log"], # reads inside cwd and Agent calls need no rule
permission_mode="dontAsk", # anything not approved is denied, never prompted
setting_sources=[], # load NO settings from the untrusted checkout: its hooks would run with your API key
mcp_servers={"ci": create_sdk_mcp_server(name="ci", version="1.0.0", tools=[get_failed_log])},
strict_mcp_config=True, # ignore any .mcp.json the checked-out branch brings along
agents={
"log-reader": AgentDefinition(
description="Reads the CI failure log and returns only the first real error.",
prompt=(
"Call get_failed_log with max_lines=3000. Return the first error that is not a "
"consequence of an earlier one, quoted verbatim with 20 lines of context, and the "
"test or step name. Do not speculate about causes."
),
tools=["mcp__ci__get_failed_log"],
model="haiku",
)
},
hooks={"PreToolUse": [HookMatcher(matcher="Read|Grep|Glob", hooks=[deny_secret_reads])]},
output_format={"type": "json_schema", "schema": VERDICT},
max_turns=30,
max_budget_usd=2.0,
)
PROMPT = (
"CI run failed. Use the log-reader subagent to get the first real error, then find the code it "
"points at. Classify the failure. Every evidence item must be a verbatim log line or a "
"path:line you opened. If you cannot tie the error to a file, answer unknown; do not guess."
)
# 4. Deterministic check: every evidence item must exist in the log or in the checkout.
MIN_EVIDENCE = 20 # "Error" or "FAIL" appear in every failed log and prove nothing
def grounded(item: str, log: str) -> bool:
item = item.strip()
if len(item) >= MIN_EVIDENCE and item in log:
return True
m = re.fullmatch(r"([\w./-]+):(\d+)", item)
if not m:
return False
path = (REPO / m[1]).resolve()
if not (path.is_file() and path.is_relative_to(REPO)):
return False
return int(m[2]) <= len(path.read_text(errors="replace").splitlines())
async def main() -> int:
async with ClaudeSDKClient(options=options) as client:
await client.query(PROMPT)
async for msg in client.receive_response():
if not isinstance(msg, ResultMessage):
continue
print(f"subtype={msg.subtype} cost_usd={msg.total_cost_usd} turns={msg.num_turns}", file=sys.stderr)
if msg.is_error or msg.structured_output is None:
return 1 # no verdict, no comment
verdict = msg.structured_output
log = await failed_log()
if not all(grounded(e, log) for e in verdict["evidence"]):
verdict |= {"category": "unknown", "confidence": "low"}
Path("verdict.json").write_text(json.dumps(verdict, indent=2))
return 0
return 1
if __name__ == "__main__":
sys.exit(anyio.run(main))

Nazwa narzędzia MCP ma postać mcp__<server>__<tool>, gdzie <server> to klucz w mcp_servers (ci), a nie name przekazane do create_sdk_mcp_server. Pomyl klucz, a allowed_tools zatwierdzi narzędzie, które nie istnieje, i dontAsk po cichu je odrzuci.

Workflow odpala się, gdy twój workflow CI kończy się porażką. Uruchamia skrypt triage z gałęzi domyślnej, robi checkout nieudanego commita do osobnego katalogu untrusted/, który agent tylko czyta, i publikuje werdykt tylko wtedy, gdy skrypt go wyprodukował.

.github/workflows/ci-triage.yml
name: ci-triage
on:
workflow_run:
workflows: [CI]
types: [completed]
permissions:
actions: read
contents: read
pull-requests: write
jobs:
triage:
if: github.event.workflow_run.conclusion == 'failure'
runs-on: ubuntu-latest
timeout-minutes: 15
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
steps:
# Trusted code: the triage script comes from the default branch, never from the failing commit.
- uses: actions/checkout@v7
with:
ref: ${{ github.event.repository.default_branch }}
persist-credentials: false
# Untrusted data: the failing commit, checked out beside it and only ever read by the agent.
- uses: actions/checkout@v7
with:
ref: ${{ github.event.workflow_run.head_sha }}
path: untrusted
persist-credentials: false
- uses: actions/setup-python@v7
with:
python-version: '3.12'
- run: pip install claude-agent-sdk==0.2.160
- name: Triage
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
RUN_ID: ${{ github.event.workflow_run.id }}
TARGET_DIR: untrusted
run: python .github/scripts/triage.py
- name: Comment on the pull request
if: github.event.workflow_run.pull_requests[0] != null
run: |
jq -r '"**CI triage:** \(.category) (\(.confidence) confidence)\n\n\(.summary)\n\n" +
(.evidence | map("- `" + . + "`") | join("\n"))' verdict.json > comment.md
gh pr comment ${{ github.event.workflow_run.pull_requests[0].number }} --body-file comment.md

Dla pull requestów z forków workflow_run.pull_requests jest puste, więc ten krok się pomija; jeśli chcesz tam skomentować, znajdź PR przez gh pr list --search <head_sha> --state open --json number.

workflow_run działa z plikiem workflow i sekretami z gałęzi domyślnej, a nieudany commit może pochodzić od dowolnego kontrybutora. Dlatego nic z tego commita nigdy się nie wykonuje: skrypt pochodzi z gałęzi domyślnej, setting_sources=[] nie ładuje hooków z .claude/settings.json ani .mcp.json z commita, a agent nie dostaje Bash, narzędzi zapisu ani poświadczeń w .git/config. Uruchomienie skryptu albo załadowanie ustawień z nieudanego commita przy ANTHROPIC_API_KEY w środowisku pozwoliłoby dowolnemu pull requestowi wykraść klucz. Zanim poluzujesz którekolwiek z tych ograniczeń, przeczytaj model zagrożeń dla agentów.

Opcje na siebie zachodzą i większość błędów w uprawnieniach bierze się z traktowania jednej jak drugiej. Claude Code ocenia każde wywołanie narzędzia w tej kolejności: hooki, reguły deny, reguły ask, tryb uprawnień, reguły allow, a na końcu twój callback can_use_tool.

Opcja (Python / TypeScript)Co robiTypowy błąd
tools / toolsUstala bazowy zestaw wbudowanych narzędzi. [] wyłącza wszystkiePozostawienie jej nieustawionej i próba ograniczania przez allowed_tools
allowed_tools / allowedToolsReguły allow: wymienione wywołania są zatwierdzane automatyczniePrzekonanie, że ukrywa niewymienione narzędzia. README: „nie usuwa narzędzi z zestawu Claude’a”
disallowed_tools / disallowedToolsReguły deny. Goła nazwa (Bash) usuwa narzędzie; reguła z zakresem (Bash(rm *)) blokuje pasujące wywołania w każdym trybieOczekiwanie, że Bash(rm *) złapie /bin/rm; reguła dopasowuje polecenie w zapisanej postaci
permission_mode / permissionModedefault, acceptEdits, plan, dontAsk, auto, bypassPermissionsbypassPermissions w CI. TypeScript wymaga do niego dodatkowo allowDangerouslySkipPermissions: true
hooks / hooksTwoja funkcja działa przed regułami. Odmowa obowiązuje w każdym trybie; zgoda nie pomija późniejszych reguł deny ani askZałożenie, że zgoda z hooka przebija regułę deny
can_use_tool / canUseToolRozstrzyga wywołania, których nic wcześniej nie rozstrzygnęło, zamiast interaktywnego pytaniaOczekiwanie, że widzi każde wywołanie. Nigdy nie widzi wywołań zatwierdzonych wcześniej, a dontAsk całkiem go pomija

Dla bota bez nadzoru niezawodny jest wzorzec z przykładu: zmniejsz tools, wybierz dontAsk, zatwierdź tylko dodatkowe narzędzia potrzebne do zadania, a każdą regułę „nigdy” umieść w hooku albo w regule deny. Dla bota, przy którym człowiek jest osiągalny (np. zatwierdzający na Slacku), użyj trybu default z callbackiem can_use_tool, który przekazuje prośbę dalej i czeka na odpowiedź.

Hooki różnią się między SDK. SDK TypeScriptu przyjmuje pełny zestaw zdarzeń Claude Code; SDK Pythona przyjmuje podzbiór (PreToolUse, PostToolUse, PostToolUseFailure, UserPromptSubmit, Stop, SubagentStart, SubagentStop, PreCompact, Notification i PermissionRequest w 0.2.160), bez SessionStart i SessionEnd. Jeśli twój projekt zależy od hooka sesji, pisz w TypeScripcie. Hooki w postaci poleceń powłoki, współdzielone z pracą interaktywną, opisuje przewodnik po hookach Claude Code.

Każdy ResultMessage niesie session_id. Przekaż go z powrotem jako resume, żeby kontynuować z pełnym kontekstem, a dodaj fork_session=True, żeby odgałęzić nową sesję z nowym ID bez zmiany oryginału. To otwiera wzorzec dwufazowy: sesja triage’u tylko do odczytu, potem jej fork w trybie acceptEdits, który przygotowuje poprawkę na gałęzi i korzysta ze wszystkiego, co triage już przeczytał.

Haczyk leży w tym, gdzie mieszkają sesje. Claude Code zapisuje transkrypty w ~/.claude/projects/<encoded-cwd>/<session-id>.jsonl na maszynie, która je utworzyła, więc sesja z jednego joba GitHub Actions znika, gdy następny job startuje na świeżym runnerze. Masz trzy wyjścia:

  • Uruchom obie fazy w jednym jobie.
  • Wgraj plik .jsonl jako artefakt i odtwórz go w ~/.claude/projects/ przed wywołaniem resume.
  • Podłącz adapter session_store (Python) albo sessionStore (TypeScript), który kopiuje transkrypty do twojego magazynu, i wznawiaj z tym samym cwd.

Przy przebiegach „odpal i zapomnij”, których nikt nie będzie wznawiał, persistSession: false w TypeScripcie w ogóle pomija zapis transkryptu. W Pythonie ustaw zamiast tego CLAUDE_CODE_SKIP_PROMPT_HISTORY w opcji env (dokumentacja sesji Agent SDK).

Bot jest przydatny tylko wtedy, gdy jego werdykty trafiają częściej niż zmęczony człowiek przeglądający log, a ty musisz to wiedzieć bez czytania każdego werdyktu. Kontrolę robią cztery warstwy i każda zawodzi głośno.

  1. Schemat. CLI waliduje werdykt względem JSON Schema. Przebieg, który nie potrafi go wyprodukować, kończy się niezerowym kodem i niczego nie publikuje.
  2. Ugruntowanie. grounded() sprawdza, czy każdy dowód ma co najmniej 20 znaków i występuje dosłownie w logu, albo jest istniejącym path:line wewnątrz checkoutu. Wymyślony plik albo sparafrazowana linia logu zamienia werdykt w unknown z niską pewnością. To zwykły kod, więc możesz go przetestować jednostkowo.
  3. Limity budżetu i tur. Zagubiony przebieg kończy się error_max_budget_usd albo error_max_turns zamiast przepalać pieniądze; koszt każdego przebiegu trafia z total_cost_usd do logu joba.
  4. Zestaw ewaluacyjny. Oznacz 20 historycznych nieudanych przebiegów ich znaną przyczyną, puść na nich bota i porównaj. Uruchom to lokalnie, z własnym kluczem API:
Okno terminala
# evals/triage-cases.tsv holds lines of: <run id> <TAB> <expected category>
# Each case gets its own worktree; triage.py always comes from your current branch.
while IFS=$'\t' read -r run expected; do
sha=$(gh run view "$run" --json headSha -q .headSha)
# Fork heads and force-pushed commits are often missing from a local clone.
git fetch --quiet origin "$sha" || { echo -e "$run\t$expected\tskip\tFETCH-FAIL"; continue; }
git worktree add --quiet --detach "/tmp/case-$run" "$sha"
rm -f verdict.json
if RUN_ID="$run" TARGET_DIR="/tmp/case-$run" python .github/scripts/triage.py 2>>costs.log; then
got=$(jq -r .category verdict.json)
else
got=no-verdict
fi
git worktree remove --force "/tmp/case-$run"
echo -e "$run\t$expected\t$got\t$([ "$got" = "$expected" ] && echo PASS || echo FAIL)"
done < evals/triage-cases.tsv | tee results.tsv
awk -F'\t' '$4=="PASS"{p++} END{print p+0"/"NR" passed"}' results.tsv

Powtarzaj ewaluację przy każdej zmianie promptu, schematu, modelu subagenta albo wersji SDK i trzymaj wyniki razem ze zmianą. Jeśli już używasz promptfoo, jego provider anthropic:claude-agent-sdk uruchamia SDK jako testowany system w ten sam sposób.

Kto zatwierdza: tech lead odpowiedzialny za CI ustala próg trafności, od którego komentarze bota są wiarygodne, a dopóki ewaluacja go nie przekroczy, komentarz pozostaje doradczy i nigdy nie blokuje merge’a. Gdy werdykt okaże się błędny na produkcji, dopisz ten przebieg do triage-cases.tsv. To nawyk dowodów zamiast diffów zastosowany do agenta: czytasz tabelę ewaluacji i wynik ugruntowania, a nie transkrypt agenta.

Wklej je do Claude Code w repozytorium, w którym ma działać bot.

ObjawPrzyczynaWyjście
Agent zachowuje się jak zwykły chatbot i ignoruje konwencje narzędziPominięty system_prompt, więc CLI wystartowało z pustym promptemPrzekaż preset claude_code z append
Reguły z CLAUDE.md są ignorowane w CI albo pojawiają się prywatne ustawienia deweloperasetting_sources nie zawiera "project" albo jest nieustawione i wczytuje ustawienia użytkownika runneraDla zaufanych checkoutów ustaw jawnie ["project"]; dla niezaufanych zostaw [] i przekaż reguły w system_prompt
Narzędzie z allowed_tools nigdy nie jest wywołane; permission_denials je wymieniaNazwa narzędzia MCP używa name serwera, a nie jego klucza w mcp_serversZmień na mcp__<key>__<tool>
Przebieg wisi albo każdy zapis jest odrzucany w CINie ma człowieka, który odpowie na pytania trybu domyślnegoUżyj dontAsk i zatwierdź to, czego potrzebuje zadanie, albo dostarcz can_use_tool
subtype to error_max_structured_output_retriesSchemat jest ostrzejszy, niż pozwalają dowody, np. culprit_file wymagany jako string, gdy go nie maDopuść null albo kategorię unknown, jak w przykładzie
resume w kolejnym jobie zgłasza nieznaną sesjęTranskrypty są lokalne dla runnera, który je utworzyłTen sam job, odtworzony artefakt .jsonl albo magazyn sesji
TypeScript: „Claude Code native binary not found at …”; Python: CLINotFoundErrorBrak działającej binarki CLI: w TypeScripcie pominięto zależności opcjonalne, więc brak binarki dla platformyInstaluj bez --omit=optional albo ustaw pathToClaudeCodeExecutable (TypeScript) lub cli_path (Python)
Gałąź kontrybutora dodaje serwer MCP, który bot potem wczytujeProjektowy .mcp.json wczytuje się w sesjach SDK bez pytaniastrict_mcp_config=True i serwery przekazywane wyłącznie w kodzie
Narzędzie do logu zawodzi (np. brak gh na runnerze)Środowisko, nie agentPrompt każe agentowi odpowiedzieć unknown; przebieg testowy z 2026-09-26 bez gh zrobił dokładnie to zamiast zgadywać. Napraw runner i uruchom ponownie

Model stojący za aliasem haiku i aktualne ceny znajdziesz w centrum wiedzy o modelach. Serwery MCP inne niż te działające w procesie opisuje konfiguracja MCP w Claude Code. Odpowiednikiem dla Codexa jest praca z Codex SDK, a dla Cursora Cursor SDK.