Refine agent cancellation guideline
Lead with the observable contract (ctx.Done = graceful suspend, return is *SuspendedError, framework shields downstream calls) and mention agent.ErrSuspendForCheckpoint as the recommended cancel cause for graceful-stop intent. Drop the leak of the WithoutCancel mechanism — readers need the contract, not the strategy. Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
This commit is contained in:
@@ -43,25 +43,32 @@ type Tool interface {
|
|||||||
|
|
||||||
## Cancellation semantics
|
## Cancellation semantics
|
||||||
|
|
||||||
`ctx.Done()` is a **graceful-suspend signal**, not a hard abort. When the
|
`ctx.Done()` is a **graceful-suspend signal**, not a hard abort. When
|
||||||
caller cancels `ctx`, `Run`/`RunStreamed`/`Resume`/`Restore` finish their
|
the caller cancels `ctx`, `Run`/`RunStreamed`/`Resume`/`Restore` let
|
||||||
in-flight LLM call and tool, persist a checkpoint via the configured
|
the in-flight LLM call and tool finish, persist a checkpoint via the
|
||||||
`Checkpointer`, and return `*SuspendedError`. Internally `coreLoop`
|
configured `Checkpointer`, and return `*SuspendedError`. The framework
|
||||||
shadows the incoming ctx with `context.WithoutCancel(ctx)` and uses the
|
shields downstream calls (LLM, tools, hooks, guardrails, save) from
|
||||||
shadow for every downstream call so the cancel never kills work
|
the cancellation so they complete naturally; the cancel is only
|
||||||
in-progress; only the at-boundary check observes the original ctx.
|
observed at the next safe boundary.
|
||||||
|
|
||||||
|
Use `agent.ErrSuspendForCheckpoint` as the cancel cause when the
|
||||||
|
intent is graceful suspend — supervisors that distinguish a
|
||||||
|
graceful-stop request from infrastructure-level causes (lease loss,
|
||||||
|
heartbeat failure) inspect `context.Cause(ctx)` to dispatch.
|
||||||
|
|
||||||
Implications:
|
Implications:
|
||||||
|
|
||||||
- A `context.WithTimeout` becomes a "max wall-clock budget then suspend"
|
- A `context.WithTimeout` becomes a "max wall-clock budget then
|
||||||
— strictly better than today's "deadline kills work outright."
|
suspend" — strictly better than the alternative where the deadline
|
||||||
|
kills work outright.
|
||||||
- There is no in-process hard-abort path. Callers that genuinely need
|
- There is no in-process hard-abort path. Callers that genuinely need
|
||||||
to kill a run terminate the process; stale recovery handles the row.
|
to kill a run terminate the process; stale recovery handles the row.
|
||||||
- Tools that need their own deadline must derive it themselves
|
- Tools receive a non-cancellable ctx; if a tool needs a hard deadline
|
||||||
(`context.WithTimeout(ctx, ...)` *inside* the tool body).
|
it must derive its own with `context.WithTimeout(ctx, ...)` inside
|
||||||
|
the tool body.
|
||||||
|
|
||||||
The supervisor (`pkg/probo/agent_run_handler.go`) maps a SIGTERM-driven
|
The supervisor (`pkg/probo/agent_run_handler.go`) maps a SIGTERM-driven
|
||||||
shutdown broadcast onto a per-run `cancelRun(ErrSuspendForCheckpoint)`,
|
shutdown broadcast onto a per-run `cancelRun(agent.ErrSuspendForCheckpoint)`,
|
||||||
so the same contract drives both the public Go API and the worker
|
so the same contract drives both the public Go API and the worker
|
||||||
infrastructure path.
|
infrastructure path.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user