diagnose-agent-latency · git:20260812.ca33479 · 2026-08-12 · sha256 0fedd18b245887a3
diagnose-agent-latency git:20260812.ca33479A
Immutable. This exact content is served forever at /api/v1/blob/0fedd18b245887a3.
--- name: diagnose-agent-latency description: Diagnose and optimize Canvas Agent turn latency with the development-only Agent DevTools or Langfuse traces. Use when a Canvas chat feels slow, TTFA/TTFT/first-render regresses, a run spends unexpected time in Canvas Host, Engine, Pi, tools, or Renderer, or a performance change needs a before/after trace comparison. --- # Diagnose Agent Latency Run a measurement-first loop. Attribute elapsed time to an owner before changing code, change one bottleneck at a time, then repeat the same workload to prove the result. ## 1. Establish a Reproducible Run Work only in development mode. Observability is intentionally absent from packaged production builds. 1. Record the prompt, scope, model/provider, runtime owner, attachment count, canvas node count, and whether the run is cold or warm. 2. Keep those inputs fixed across comparisons. Do not compare different models, scopes, prompts, or context sizes as if they were the same workload. 3. Run one warm-up and then at least three measured turns. Use the median for a local conclusion; use a larger Langfuse cohort when claiming a broad win. 4. Keep the raw `runId` for every sample. Enable the local trace subscriber: ```bash PULSE_CANVAS_AGENT_OBSERVABILITY=1 pnpm --filter canvas-workspace dev ``` Also enable `canvas-agent-debug-trace` in Settings → Experimental, then reload the renderer. `CANVAS_AGENT_DEBUG_TRACE=1` enables main-process detail capture for scripted runs, but the renderer DevTools route still requires the experimental flag. Open `#/debug` or use the Debug Trace card on an assistant message. Use Langfuse when the task needs cross-run filters, cohorts, percentiles, or a shared remote trace. Add `LANGFUSE_PUBLIC_KEY`, `LANGFUSE_SECRET_KEY`, and optionally `LANGFUSE_BASE_URL`. Never put credentials in source or renderer state. Read `../../knowledge/langfuse-observability.md` before changing the exporter or interpreting its trace model. The built harness launches production output, so it cannot activate this development-only DevTools plugin. Use `canvas-harness` for surrounding UI smoke checks, but use the development launch above for latency traces. ## 2. Read the Timeline Correctly Treat the lanes as ownership boundaries: - **Canvas Host**: queue, scope activation, context preparation, runtime dispatch, and response processing. - **Engine** or **Pi**: runtime execution. Attribute using the runtime label on the run, not the feature flag the operator expected. - **Generation**: one provider/model call. Tool-using turns can have several. - **Tool**: a named tool interval inside the runtime. - **Renderer**: the first committed UI content milestone. Interpret the summary metrics as follows: - `Total`: request start through host completion. - `TTFA`: first stream activity, which may be a tool call rather than text. - `TTFT`: first user-visible text. It can legitimately be later than TTFA. - `First render`: first assistant content committed by the renderer. - `Bottleneck`: longest exclusive duration, not proof of root cause by itself. TTFA and TTFT are point milestones inside runtime execution. Do not add them to phase durations. Compare milestone deltas instead: ```text pre-runtime = runtime start - request start activity wait = TTFA - runtime start text wait = TTFT - TTFA render lag = first render - TTFT post-runtime = total - runtime end ``` If a timestamp is missing, report it as missing. Do not turn it into zero. ## 3. Localize the Bottleneck Use this order: 1. Large queue, scope, or context phase: inspect Canvas Host work before the runtime. Check context size, canvas reads, synchronous work, and duplicated preparation. 2. Large activity wait with one generation: suspect provider latency, network, model queueing, or delayed runtime callbacks. Compare the same prompt across providers only as a separately named experiment. 3. Small TTFA but large text wait: inspect tool-first behavior, multiple model iterations, clarification, or reasoning before the first text. Do not label this as renderer slowness. 4. Long runtime after TTFT: inspect long generations, serial tools, repeated model calls, or work that needlessly blocks turn completion. 5. Large render lag: inspect stream batching, main-to-renderer IPC, React scheduling, and Markdown/render cost. 6. Large post-runtime tail: inspect response processing, persistence, trace serialization, and completion notifications. Correlate the timeline with tool names and generation boundaries. A large runtime bar only says the runtime owns the interval; its children explain what happened inside it. ## 4. Map Evidence to Code Start from the owner indicated by the trace: - Event contract and bus: `src/shared/agent-observability.ts`, `src/plugins/main/agent-observability-bus.ts` - Canvas Host phases: `src/main/agent/observability/host-run.ts`, `src/main/agent/service.ts`, `src/main/agent/canvas-agent.ts` - Runtime selection: `src/main/agent/segment-execution.ts` - Engine children: `src/main/agent/observability/engine-plugin.ts` - Pi generations: `src/main/agent/observability/pi-generation-events.ts`, `src/main/agent/backends/pi-agent-harness-backend.ts` - Renderer milestone: `src/main/agent/observability/renderer-mark.ts` and the chat stream renderer that publishes it - Local model/UI: `src/plugins/renderer/devtools/performance-model.ts`, `AgentDebugPage.tsx`, `ChatDebugTrace.tsx` - Remote exporter: `src/plugins/main/langfuse-observability.ts` Do not optimize `packages/engine/src/core/loop.ts` merely because Canvas uses Engine. First prove the slow interval is Engine-owned and not Canvas Host, Pi, provider, tool, or Renderer time. ## 5. Run a Controlled Optimization Experiment 1. State one falsifiable hypothesis tied to one measured interval. 2. Choose the smallest code change that should move that interval. 3. Add or update a timing/behavior regression test when changing event boundaries, first-activity detection, runtime attribution, or renderer marks. 4. Run focused tests plus `node scripts/harness/run-harness-check.mjs`. 5. Repeat the fixed workload with the same model and context. 6. Compare medians and report the absolute and percentage change. Call out any regression in TTFA, TTFT, first render, total, errors, or output quality. 7. Revert or reject an optimization that only moves time between labels, suppresses instrumentation, drops work, or changes the workload. For a performance-sensitive implementation, finish with the validation level selected by `../validate-canvas-change/SKILL.md`. ## 6. Report the Result Return a compact diagnosis with: ```text Workload: prompt/scope/model/runtime/context, cold or warm Samples: run IDs and count Baseline: median Total, TTFA, TTFT, First render Bottleneck: owner + interval + supporting child events Hypothesis: causal mechanism, with confidence and alternatives Experiment: one proposed or implemented change Result: before/after absolute and percentage deltas Validation: tests and trace comparison actually run Privacy: whether only timing metadata or payload capture was enabled ``` Separate observation from inference. One trace can justify a targeted next experiment, but not a general performance claim.