본문으로 건너뛰기

Runner 이벤트

PlanRunner.run_stream()Plan을 dataclass event stream으로 바꿉니다. Runner는 의도적으로 transport-neutral합니다. HTTP, session, gateway retry, SSE, DB row, UI state를 알지 않고, product adapter가 call_tool(tool_name, args) 함수를 제공한 뒤 각 event를 어디로 보낼지 결정합니다.

Runner event는 실행 경로를 관측 가능하게 만들어야 할 때 사용합니다.

  • 실행 중인 plan의 UI progress
  • 어떤 step에서 실패했는지 설명하는 log
  • stage별 latency가 있는 Quality Lab 결과
  • 이후 scrub/promotion 가능한 trace learning record
  • executor error를 숨기지 않는 structured failure response

Minimal Usage

from dataclasses import asdict

from graph_tool_call.plan import PlanRunner

def call_tool(tool_name: str, args: dict):
return executor.execute(tool_name, args)

runner = PlanRunner(call_tool)

for event in runner.run_stream(
plan,
input_context={"siteNo": "10001"},
trace_metadata={"collection_id": "orders", "case_id": "order-detail-001"},
):
send_sse(asdict(event))

input_context${input.foo}${user_input.foo} binding 모두에 값을 공급합니다. trace_metadata는 모든 event에 복사되므로 작고 scrub된 값만 넣습니다.

Event Lifecycle

기본 runner mode는 linear execution이며 첫 hard failure에서 abort합니다.

plan.started
step.started
step.completed
step.started
step.completed
plan.completed

on_error="abort" mode에서 binding 또는 tool call이 실패하면 다음 흐름입니다.

plan.started
step.started
step.failed
plan.aborted

Recovery mode는 기존 contract를 깨지 않고 event를 추가합니다.

Event언제 발생하나Adapter가 할 일
step.retryingretry 가능한 step이 실패했고 다음 attempt가 실행될 때retry 상태를 표시하고 run을 유지
step.skippedrecover mode가 소비되지 않는 실패 step을 skip할 때degraded progress로 표시하고 run을 유지
plan.repairedrepairer가 대체 plan을 만들 때필요하면 화면의 plan id/path 갱신
binding.repaired오래된 ${sN.path} binding이 다른 path로 복구될 때낮은 confidence의 repair evidence로 저장
args.coerced실행 전 type cast 또는 enum folding이 발생할 때coercion을 diagnostics에 노출

v1 runner는 on_error="recover"PlanRepairer를 설정한 경우를 제외하면 fan-out, conditional, automatic replanning을 수행하지 않습니다.

Event Types

Type주요 fieldSuccess path
plan.startedplan_id, goal, step_count첫 event
step.startedstep_id, tool, args_resolved, index, total각 tool call 직전
step.completedduration_ms, output_preview, output_sizetool call 성공 후
plan.completedoutput, total_duration_ms, trace_steps최종 성공 event
step.failederror, duration_msabort 전 실패 step
plan.abortedfailed_step, error, trace_steps최종 실패 event

모든 event는 아래 field를 가질 수 있습니다.

Field의미
typerouting에 쓰는 stable event type
stage현재는 runner
plan_id현재 plan id
step_id해당될 때 현재 step id
tool해당될 때 tool name
graph_tool_call_versionengine version
trace_metadataadapter가 제공한 safe metadata

Payload Examples

{
"type": "step.started",
"stage": "runner",
"plan_id": "plan-42",
"step_id": "s1",
"tool": "getOrderDetail",
"args_resolved": {"orderNo": "ORD-1001"},
"index": 1,
"total": 2,
"graph_tool_call_version": "0.31.0",
"trace_metadata": {"collection_id": "orders-api", "quality_case_id": "case-001"}
}

Adapter Contract

Adapter는 runner 경계를 좁게 유지해야 합니다.

Runner 입력Adapter 책임
Planplan retrieval 또는 synthesis
input_context사용자 입력 field, 추출 entity, collection default
trace_metadatacollection id, case id, safe auth readiness, request id
call_toolHTTP 실행, auth header, host allowlist, mutation policy

raw token, cookie, email, phone number, raw user id, full request/response body는 trace_metadata에 넣지 않습니다. 제품에서 audit-grade payload 저장이 필요하면 별도 보안 system에 두고 runner event에는 reference id만 남깁니다.

SSE And UI Mapping

제품 UI에는 안정적인 envelope으로 event를 forwarding하는 편이 좋습니다.

{
"type": "runner.event",
"run_id": "ql-run-20260725-001",
"event": {"type": "step.completed", "step_id": "s1"}
}

권장 UI grouping은 다음입니다.

UI StateEvent Condition
Runningplan.started, step.started, step.retrying
Partial progressstep.completed, step.skipped, args.coerced
Needs attentionbinding.repaired, plan.repaired
Successplan.completed
Failedstep.failed, plan.aborted

Persisting Events

장기 저장에는 payload 없이 동작을 설명할 수 있는 field만 남깁니다.

  • event type
  • plan id와 step id
  • tool name
  • duration
  • failure reason
  • retry 또는 repair state
  • scrub된 output preview
  • trace metadata

Quality Lab과 trace learning은 같은 event stream을 사용하고, 거기서 compact record를 derive해야 합니다. 그래야 debugging, evaluation, learning이 하나의 source of truth에 묶입니다.

Troubleshooting

증상가능한 원인확인할 것
plan.abortedkind=binding필요한 producer 값이 없음PlanStep.args binding과 upstream output_preview
step.failedkind=tooladapter executor가 예외를 던짐HTTP status, auth readiness, executor log
step.completed가 없음첫 step이 output 전에 실패함step.failed.errorfailed_step
args.coerced가 없음validate_argsoff이거나 tools map이 없음PlanRunner(..., tools=..., validate_args="coerce") 설정
retry가 발생하지 않음step이 retryable이 아니거나 RetryPolicy가 없음PlanStep.retryable=True 또는 RetryPolicy(retry_all=True)

관련 문서