Skip to main content
Capture selected production invocations for later evaluation. Define a rule in lifecycle/evals_improvement.py and register a store. Harnest runs capture in the background after the response. This is separate from Simulations and live evaluation, where “live” means ADK’s real-time inference mode, not production traffic.
Harnest does not detect personal data. Without your own redactor, captures retain structure only and omit input, output, tool arguments, and tool results. Register a redactor only when you have a policy for the content you intend to store.

Select invocations

lifecycle/evals_improvement.py
The rule takes exactly one CaptureEvent argument and returns True to capture the invocation. It can be synchronous or asynchronous. Only one capture rule is allowed. sample_rate is optional and must be between 0 and 1. It additionally captures that share of traffic rejected by the rule. The example keeps failures plus a 5% sample of other invocations. Records identify the selection reason as rule or sample. Both @lifecycle.evals.capture and @lifecycle.evals.redact must live in lifecycle/evals_improvement.py; Harnest refuses them in other files. Deleting this file turns capture off without changing the rest of the application.

What the rule sees

tool_error is a best guess: it detects ADK results with an error field and LangGraph tool messages with status="error". Use explicit checks in your rule when your tools have a different failure contract. user_id, session_id, and metadata are never stored in capture records.

Register a store

The rule’s store="captures" names a @lifecycle.storage.custom("captures") provider:
lifecycle/storage.py
LocalCaptureStore(directory, retention_days=30) writes one JSON Lines file per UTC day. It deletes old days on startup and when a new day begins. Use it locally or on a single instance with a persistent volume. For another backend, return a value implementing harnest.eval_capture.CaptureStore:

Choose what content to keep

A redactor takes exactly one CaptureRecord and must return a CaptureRecord. Only one redactor is allowed. If it returns anything else, Harnest drops the capture. The "redacted" marker means your policy ran; it does not certify that all personal data was removed.

Example with Presidio

Presidio’s analyzer detects supported personal-data entities, and its anonymizer replaces detected spans. Install presidio-analyzer and presidio-anonymizer in your agent environment and provision the NLP model required by your analyzer configuration before serving. Add this to the same lifecycle/evals_improvement.py file as the rule. This example applies Presidio to input and output text and discards tool arguments and results, whose schemas may contain sensitive values beyond free text.
lifecycle/evals_improvement.py
Automated detection is not guaranteed to catch everything. Review the languages, recognizers, and domain-specific sensitive data your application handles before retaining content. Leave capture structure-only if that policy is not ready.

Stored record format

CaptureRecord serializes with schemaVersion: 1 and these fields: When content is "omitted", inputText and outputText are empty strings, and each tool’s arguments and result are null. Tool names and failure flags remain available.

Configure sampling and disable capture

Harnest selects the sample rate in this order:
  1. The authored sample_rate on @lifecycle.evals.capture.
  2. HARNEST_EVAL_CAPTURE_SAMPLE_RATE.
  3. 0, which disables sampling of rejected traffic.
Omit the authored sample_rate if operators should control it through the environment. A zero rate still captures invocations accepted by the rule. Set HARNEST_EVAL_CAPTURE=off to disable capture without changing code. false, 0, no, and disabled also switch it off. Both environment variables can be set under spec.environment in config.yaml:
config.yaml
The off switch disables capture regardless of the configured sample rate.

Failure handling and validation

Capture runs in the background after the response. Errors in the rule, redactor, or store are logged as eval.capture.failed and never reach users. Successful captures log eval.capture.stored. Neither event contains content. Compilation rejects:
  • Either decorator outside lifecycle/evals_improvement.py.
  • A rule or redactor with the wrong number of arguments.
  • Duplicate capture rules or redactors.
  • A missing named store or a provider value that does not implement CaptureStore.

Coverage and limitations

Capture covers Harnest’s own routes, including /responses, streaming, and AG-UI. It does not cover ADK’s native /run or /run_sse routes. Playground evaluation runs are never captured. There is no harnest evals pull command yet. Use your store’s list method to read captured records.