결과 검증 루프
VerificationLoop는 Agentic executor가 만든 응답 후보를 FinalResponse와 output guardrail보다 먼저 평가합니다. 완전한 opt-in 기능이므로 [verification.<workflow>]이나 코드퍼스트 verification= 인자가 없으면 기존 호출 순서, 결과, trace가 바뀌지 않습니다.
rubric과 판정 기준은 SDK가 아니라 Registry evaluator 프로젝트가 소유합니다. SDK는 strict request/decision schema, durable 수명, 수정 상한, 도구 차단과 관측 계약만 강제합니다. CLI scaffold에서는 app/evaluator_policy.py가 prompt seed의 버전·baseline·renderer를, prompts/outcome-evaluator-system-prompt.template.md가 prompt 본문을 소유합니다. 실제 Registry 반영은 여전히 운영자의 수동 작업입니다.
TOML로 적용
섹션 제목: “TOML로 적용”운영 ORCH는 애플리케이션 코드를 바꾸지 않고 workflow 이름에 맞는 table을 추가합니다.
[agents]# Registry에서 운영자가 먼저 등록한 evaluator Agent ID입니다.outcome_evaluator = "registry:support-outcome-evaluator"
[verification.work]mode = "observe"evaluator = "outcome_evaluator"policy_id = "support-answer-quality-v1"agentic_programs = ["support"]max_revisions = 1revision = "continue_without_tools"on_exhausted = "fail"work는 rules_workflow(name="work", ...)의 이름입니다. agentic_programs는 같은 workflow가 실제로 도달할 수 있는 [agentic.*] 이름만 허용합니다. evaluator alias는 [agents]에 있어야 하고 WorkMemory 업무 결과로 취급되지 않습니다. policy_id는 checkpoint와 evaluator 요청 크기를 안정적으로 제한하기 위해 최대 256자입니다.
최초 배포는 observe를 권장합니다. 승격과 즉시 복귀는 mode만 enforce, observe, off로 바꿔 수행할 수 있습니다.
코드퍼스트 적용
섹션 제목: “코드퍼스트 적용”라이브러리나 테스트에서 정책을 코드로 소유해야 할 때만 VerificationLoop.agent()를 전달합니다.
from llamon_agent.orchestrator import ( RuleSource, VerificationLoop, rules_workflow,)
workflow = rules_workflow( name="work", source=RuleSource.request(), verification=VerificationLoop.agent( evaluator="outcome_evaluator", policy_id="support-answer-quality-v1", agentic_programs=["support"], mode="observe", max_revisions=1, ),)같은 workflow에 TOML과 코드퍼스트 설정을 동시에 두면 부팅 단계에서 거부합니다. v1에는 .verify_with() 체이닝 API가 없습니다.
전용 evaluator scaffold
섹션 제목: “전용 evaluator scaffold”outcome-evaluator는 기존 기본값을 바꾸지 않는 Registry 템플릿입니다. --template을
명시해야 생성되고, memory는 묻지 않고 off로 고정합니다.
uv run llamon agent quality-evaluator \ --template outcome-evaluator \ --runtime-source registry \ --memory off \ --yes생성 프로젝트는 MCP, A2A child와 deterministic tool을 비우고 Agent Card의 streaming capability를
false로 선언합니다. response_format={"type": "json_object"}는 Registry 모델에 보내는
best-effort 힌트이므로 Anthropic 등 일부 provider가 무시할 수 있습니다. 계약을 실제로 강제하는
경계는 provider 기능이 아니라 VerificationEvaluatorAdapter입니다.
from llamon_agent import VerificationEvaluatorAdapterfrom app.evaluator_policy import BASELINE_CRITERION_IDS
evaluator_agent = VerificationEvaluatorAdapter( runtime_agent, required_criteria=BASELINE_CRITERION_IDS,)
app = await create_server( # ... agent=evaluator_agent, upstream_parts_policy="replace",)Adapter는 dispatch, invoke, stream 세 진입점에서 모델을 호출하기 전에 입력을 검사합니다.
stream도 감싼 runtime의 dispatch를 한 번만 호출해 검증된 최종 결과 하나만 보냅니다.
a2a_data에 DataPart가 정확히 하나가 아니거나 raw FilePart가 하나라도 있으면
모델을 호출하지 않습니다.
공개 request 계약
섹션 제목: “공개 request 계약”ORCH producer와 evaluator가 같은 경계를 공유할 수 있도록 schema 상수와 validator를 공개합니다. 모두 lazy export이므로 최상위 package import만으로 verification 모듈이 로드되지는 않습니다.
from llamon_agent import ( VERIFICATION_DECISION_SCHEMA, VERIFICATION_REQUEST_SCHEMA, validate_verification_request,)
safe_copy = validate_verification_request(inbound_data_part)validate_verification_request()는 알 수 없는 field와 암묵적 형 변환을 거부하고 깊은 복사본을
반환합니다. 주요 한도는 다음과 같습니다.
policyId1 ~ 256자,agenticProgram1 ~ 128자- 요청·후보 text 각각 최대 2,000자, data/file projection 각각 최대 12개
taskState는None또는 최대 64자 문자열- candidate/evidence digest는 소문자 SHA-256 64자리
- evidence 최대 16개, ID 128자, text summary 500자, schema 최대 12개·각 256자
- projection은 JSON 직렬화 가능해야 하며 최대 깊이 5, 문자열 1,000자, list 20개, object 40개
- canonical JSON 전체 최대 24 KiB
ORCH의 verification_request()도 반환 직전에 같은 validator를 호출합니다. 크기 상한을
맞추려고 text와 evidence를 비우는 _bound_request()의 축소 결과도 정상 계약으로 허용합니다.
Adapter 조합 제약
섹션 제목: “Adapter 조합 제약”최종 A2A 응답에는 decision DataPart가 정확히 하나여야 합니다. 따라서
ResponseContractSummaryAdapter처럼 DataPart를 추가하는 adapter와 조합할 수 없고,
upstream_parts_policy="append"도 거부됩니다. DataPart를 추가하지 않는 guardrail은
replace 정책에서만 조합할 수 있습니다. 이 제한은 문서 관례에 그치지 않고 adapter chain과
서버 정책을 연결할 때도 검사합니다.
요청이나 판정이 잘못되면 adapter는 원문을 되돌려 보내지 않고 각각
VERIFICATION_EVALUATOR_INVALID_REQUEST, VERIFICATION_EVALUATOR_INVALID_DECISION이라는
고정 text-only 결과를 반환합니다. ORCH consumer는 빈/잘못된 decision DataPart를
비재시도 VERIFICATION_EVALUATOR_INVALID로 분류합니다. 감싼 runtime의 예외나 failed task는
VERIFICATION_EVALUATOR_FAILED이고, timeout/unavailable만 기존 정책에 따라 재시도할 수
있습니다. observe에서는 오류를 기록하고 원 후보를 통과시키며, enforce에서는 typed
failure와 provisional WorkMemory 격리를 적용합니다.
Prompt seed 운영
섹션 제목: “Prompt seed 운영”생성된 app/evaluator_policy.py는 protocol version, baseline criterion ID·설명과 renderer만
담습니다. 긴 prompt 본문은 prompts/outcome-evaluator-system-prompt.template.md에 둡니다.
두 파일을 생성 프로젝트가 함께 소유하므로 evaluator마다 기준과 Registry prompt version을
검토할 수 있고, 운영 코드에 prompt 본문을 섞지 않아도 됩니다. 다섯 baseline은
request_alignment, evidence_grounding,
internal_consistency, completeness_and_actionability, safety_and_privacy입니다.
# criterion은 app/evaluator_policy.py에서 수정# prompt 본문은 prompts/outcome-evaluator-system-prompt.template.md에서 수정python -m app.evaluator_policy --writepython -m app.evaluator_policy --checkuv run pytest -q그 다음 seed를 Registry에 새 prompt version으로 수동 업로드하고, .env의
OUTCOME_EVALUATOR_MODEL_ID와 OUTCOME_EVALUATOR_PROMPT_ID를 실제 ID로 바꾼 뒤
policyVariant를 올립니다. prompt ID는 업로드 뒤에 생기므로 scaffold는 이 값을 자동으로
묻지 않습니다. 기존 ID를 생성할 때 넣으려면 --configure-env를 명시하세요.
SDK는 업로드 후 Registry prompt drift를 감지할 수 없습니다. prompt의 관리 마커는 권고이며
adapter가 실질적 보안·출력 경계입니다.
evaluator 응답 계약
섹션 제목: “evaluator 응답 계약”evaluator는 DataPart 하나만 반환해야 하며 다음 필드를 정확히 포함해야 합니다.
{ "schema": "llamon.orchestrator.verification-decision.v1", "decision": "pass", "summary": "필수 근거를 포함해 답변했습니다.", "criteria": [ { "id": "grounded-answer", "passed": true, "feedback": "인용한 근거와 결론이 일치합니다." } ]}decision은 pass | revise | review 중 하나입니다. 알 수 없는 schema, decision, 최상위 필드, criterion 필드, 누락 필드는 fail-fast로 처리합니다. criteria는 1~32개이며 각 항목은 비어 있지 않은 고유 id, boolean passed, 최대 500자의 feedback을 가져야 합니다. pass는 모든 criterion이 통과할 때만 가능하고 revise·review에는 실패 criterion이 하나 이상 있어야 합니다. 사람 판단이 필요하면 관련 criterion을 passed=false로 두고 review를 선택하도록 prompt에 명시합니다. evaluator의 상세 chain-of-thought는 계약이나 trace에 넣지 않습니다.
ORCH consumer의 VerificationDecision.from_dict()는 기존 v1 수용 범위를 유지합니다. 위 다섯
baseline을 각각 정확히 한 번 요구하는 규칙은 required_criteria=를 지정한 evaluator
producer의 추가 hardening이며, 기존 consumer가 다른 유효 criterion 집합을 읽는 범위를
좁히지 않습니다.
evaluator 요청은 24 KiB 이하의 bounded projection입니다.
- 현재 요청과 후보 text는 길이를 제한하고 URL·긴 base64 run을 제거합니다.
- DataPart는 raw·OCR·content·bytes·credential 계열 키를 제거하고 건수·깊이를 제한합니다.
- 파일 원문이나 base64는 보내지 않고 안전한 metadata reference만 보냅니다.
- 이전 결과는 bounded summary, schema와 digest 형태의 근거만 보냅니다.
- evaluator 호출에서는 WorkMemory 자동 주입을 끕니다.
모드별 동작
섹션 제목: “모드별 동작”| mode | evaluator 호출 | 응답 제어 |
|---|---|---|
off | 없음 | 기존 후보를 그대로 진행하고 검증 trace도 남기지 않음 |
observe | 있음 | pass·revise·review·오류를 기록하되 원래 후보를 진행 |
enforce | 있음 | pass만 진행; 나머지는 제한적 수정 또는 typed failure |
enforce에서 revise를 받으면 side effect가 없었던 AgenticExecutor 실행만 수정할 수 있습니다. evaluator feedback은 llamon.orchestrator.verification-feedback.v1 DataPart로 같은 controller state에 추가됩니다. 기존 observation은 보존하지만 available_tools=[], tools_enabled=false로 한 번만 더 controller를 호출합니다. controller가 도구를 요청하면 child를 부르기 전에 VERIFICATION_REVISION_TOOL_BLOCKED로 종료합니다.
수정 후보는 evaluator를 한 번 더 통과해야 합니다. 두 번째 판정이 pass가 아니면 VERIFICATION_EXHAUSTED입니다. review는 VERIFICATION_REVIEW_REQUIRED, side-effecting 실행의 revise는 VERIFICATION_REVISION_UNSAFE로 종료합니다. VerificationLoop는 기존 HumanReviewPort, result_gate, output guardrail을 대체하지 않습니다.
실행 수명과 재개
섹션 제목: “실행 수명과 재개”검증을 적용한 실행 순서는 다음과 같습니다.
select→ execute→ provisional Completed→ verify→ finalize→ output guardrail→ WorkMemory accept→ emit검증 전에는 AgenticExecutor 완료 state와 WorkMemory 후보를 확정하지 않습니다. pass 또는 observe 진행 뒤 FinalResponse와 output guardrail까지 성공해야 executor state가 accept_completed()되고 WorkMemory가 committed로 이동합니다. 검증 실패나 output guardrail 차단은 완료 state를 rollback하고 provisional 기억을 quarantine합니다.
verify checkpoint는 별도 state version과 다음 snapshot을 보존합니다.
policy_id와 canonical policy digest- evaluator alias와 실제 target snapshot
- candidate digest
- evaluator 호출 횟수와 수정 횟수
evaluator timeout이나 일시 unavailable은 retryable failure로 checkpoint를 남깁니다. 같은 conversation으로 재개하면 Agentic child를 다시 실행하지 않고 후보를 복구해 평가를 이어 갑니다. 최초 target resolver 장애 때 concrete target을 얻지 못했다면 복구 후 처음 확인한 target을 snapshot으로 채택하고, 이미 concrete snapshot이 있던 실행에서 target이 바뀐 경우에만 drift failure로 종료합니다.
수정 controller 호출은 성공한 최종 답변을 state에 저장할 때 revision budget도 함께 확정합니다. 호출 도중 프로세스가 중단되면 기존 후보와 budget 0 상태로 안전하게 재개하며, 수정 답변 저장 직후 workflow checkpoint 갱신 전에 중단된 경우에는 executor의 revision counter와 candidate digest를 한 단계만 reconciliation한 뒤 재평가합니다. 정책, 이미 확정된 evaluator target 또는 그 밖의 후보 state가 예상 범위를 벗어나면 새 기준으로 자동 재평가하지 않고 drift failure로 종료합니다. 기존 select, execute, finalize checkpoint 형식은 계속 읽을 수 있습니다.
evaluator의 input_required, invalid schema, 비재시도 오류는 evaluator 오류입니다. 업무 사용자에게 evaluator 질문을 전달하지 않습니다.
SDK Runtime Loop와 오프라인 개선 경계
섹션 제목: “SDK Runtime Loop와 오프라인 개선 경계”아래 네 요소가 모두 같은 수준의 SDK 공개 API인 것은 아닙니다. 실제 SDK runtime 기능은 AgenticExecutor의 bounded Agent Loop와 opt-in VerificationLoop입니다. A2A Event / Turn cycle은 요청 수신, checkpoint 재개와 응답 emit을 한눈에 설명하기 위한 개념 경계이고, Hill Climbing은 wheel runtime이 아니라 이 SDK 저장소에서 운영자가 실행하는 maintainer 도구입니다.
작은 화면에서는 좌우로 스크롤하세요. runtime 기능과 설명용·오프라인 경계의 차이는 전체 화면에서 볼 수 있습니다.전체 화면에서 보기 ↗
- A2A Event / Turn cycle (설명용 경계) — 한 요청이나 재개를 받아 최종 event를 한 번 emit하는 흐름을 설명합니다. 이 이름의 별도 public class나 Python event loop를 제공한다는 뜻은 아닙니다.
- Agent Loop (SDK runtime) —
AgenticExecutor가 controller 단계와 도구 호출 상한 안에서 observation을 누적하며, final action이 나오면 provisional 후보를 만듭니다. VerificationLoop(SDK opt-in) — evaluator가 후보를 판정하고,enforce에서는 같은 state를 도구 없이 최대 한 번 수정합니다.- Hill Climbing (repository maintainer tool) — trace export, clustering, baseline/candidate replay와 개선 제안까지만 offline 자동화합니다. wheel runtime 기능이 아니며 Registry draft, 배포, canary, 승격과 rollback은 수동입니다.
관측성과 승격
섹션 제목: “관측성과 승격”기존 trace backend가 활성화되어 있으면 evaluator 호출마다 orchestrator.verification child span과 boolean verification_passed score를 기록합니다. Langfuse나 다른 backend가 비활성화되어 있으면 완전한 no-op이며 업무 실행에 영향을 주지 않습니다.
전용 evaluator 서비스 내부에는 별도의 outcome-evaluation evaluator observation이 생깁니다.
여기에는 검증된 policyId, 고정 stage=verification, contractValid, decision,
criteria 개수만 기록하며 후보·evidence·feedback 원문은 기록하지 않습니다. scaffold의 root
observability는 serviceRole=outcome-evaluator와 policyVariant를 제공합니다.
verification_passed score는 ORCH tracing만 기록하고 evaluator 서비스는 중복 생성하지
않습니다.
scaffold는 TRACE_BACKEND=none으로 시작하고 테스트에서는 NullBackend를 강제합니다.
따라서 로컬 .env에 운영 인증값이 있어도 pytest가 실제 trace를 보내지 않습니다. 배포에서
backend와 인증값을 명시적으로 설정한 경우에만 evaluator observation을 전송합니다.
turn span과 child span에는 다음 bounded metadata가 기록됩니다.
verification_policyverification_modeverification_decisionverification_attemptsverification_criteriaverification_revisedverification_evaluatoragentic_programroute_decisioncandidate_digestroute_decision은 기존 routing trace가 같은 turn span에 기록합니다. child span output에는 분석 도구가 직접 읽을 수 있는 VerificationEvent v1 객체와 호환용 flattened 필드가 함께 기록됩니다.
원문 저장 여부는 기존 capture_content 정책을 따릅니다. false이면 evaluator summary와 criterion feedback은 길이만 남긴 redacted marker로 바뀌고 score comment와 replay request는 저장하지 않습니다. true일 때도 검증 이벤트에는 bounded summary, 짧은 criterion feedback, digest와 안전하게 투영한 replay request만 둡니다.
verification_evaluator에는 alias가 아니라 checkpoint와 동일한 evaluator target snapshot을 기록합니다.
권장 rollout은 observe → 일부 enforce canary → 확대입니다. 예를 들어 eligible turn 200건, route별 human label 50건 이상을 모은 뒤 evaluator 오류율, 불필요한 revise/review, 수정 표본의 사람 평가와 p95 latency를 확인합니다. 기준을 넘으면 SDK를 롤백하지 않고 즉시 observe나 off로 복귀합니다.
오프라인 Hill Climbing
섹션 제목: “오프라인 Hill Climbing”SDK 저장소의 maintainer 도구는 Langfuse에서 내보낸 VerificationEvent v1 JSON/JSONL을 정규화하고, 실패·수정·사람 불일치 사례를 묶어 수동 개선 제안을 만듭니다. 현재 full event output과 초기 v1의 flattened verification 필드를 모두 읽습니다.
uv run python scripts/verification_hillclimb.py \ --input ./verification-events.jsonl \ --output-dir ./verification-proposal \ --target-agent support-outcome-evaluator \ --current-version v1 \ --current-prompt ./current-evaluator-prompt.txt별도 baseline과 candidate ORCH endpoint가 준비되어 있으면 동일한 bounded replay 요청을 두 endpoint에 보낼 수 있습니다. replay request는 trace의 content capture가 활성화된 경우에만 export에 포함됩니다.
uv run python scripts/verification_hillclimb.py \ --input ./verification-events.jsonl \ --output-dir ./verification-proposal \ --target-agent support-outcome-evaluator \ --current-version v1 \ --baseline-url https://baseline.example/a2a \ --candidate-url https://candidate.example/a2a산출물은 proposal.md, metrics.json, regression-cases.jsonl, candidate-prompt.patch입니다. 도구는 Registry API를 호출하거나 prompt·alias·배포·canary를 변경하지 않습니다. 운영자가 근거와 diff를 검토해 Registry draft/version 생성, candidate 배포, replay, canary, 승격과 롤백을 모두 수동으로 수행해야 합니다.