Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.lasscyber.com/llms.txt

Use this file to discover all available pages before exploring further.

The Agnes Analyzer (also called the combined analyzer or execution engine) is the single endpoint most production integrations should call:
POST /api/v1/analyze/
It runs a customer-defined policy that tells Agnes which analyzers to run, in what order, and what to do with their output. This page is the spec for that policy, with worked examples drawn from the shipped default-inbound policy. If you only need a working example, the Quickstart and the SDK PolicyBuilder get you there fastest.

The endpoint

MethodPOST
Path/api/v1/analyze/
AuthAuthorization: Bearer ak_…
Body{ "prompt": "...", "policy_slug": "...", "policy_id": "...", "sdp_policy_id": "...", "yara_policy_id": "..." }
Either policy_slug or policy_id selects the policy. If neither is set, the tenant’s is_default inbound policy runs. The optional sdp_policy_id and yara_policy_id overrides let a single policy template work against multiple SDP / YARA rule sets. The response carries the per-analyzer outputs, aggregated metrics, the overall status, and the request ID. See Interpreting results for the shape.

What a policy is

A policy is a JSON document with five top-level fields:
FieldTypePurpose
namestringHuman-readable display name.
slugstringStable identifier for SDK lookups (e.g. default-inbound).
available_analyzersarrayWhich analyzers may run, with parameter values.
execution_planarray of stepsOrdered list of steps. Each step is sequential or asynchronous.
termination_conditionsarrayPer-analyzer rules for when to short-circuit.
Policies live in Postgres (table-backed PolicyOrm) and are tenant-scoped. Built-in policies (default-inbound, default-outbound, default-permissive) ship as fixtures and are cloned into a new tenant on first use.

Walking through default-inbound

The shipped inbound default is the canonical example. Annotated:
{
  "name": "Default Inbound",
  "slug": "default-inbound",
  "description": "Strong default protection for user-supplied input...",
  "is_default": true,

  "available_analyzers": [
    {
      "name": "adversarial_detection_analyzer",
      "params": { "model_id": "meta-llama/Llama-Prompt-Guard-2-22M" }
    },
    {
      "name": "safety_moderation_analyzer",
      "params": { "model_id": "google/shieldgemma-2b" }
    },
    { "name": "dlp_analyzer",  "params": {} },
    { "name": "url_analyzer",  "params": {} },
    { "name": "yara_analyzer", "params": {} }
  ],

  "execution_plan": [
    { "type": "sequential",   "analyzers": ["adversarial_detection_analyzer"] },
    { "type": "sequential",   "analyzers": ["safety_moderation_analyzer"] },
    { "type": "asynchronous", "analyzers": ["dlp_analyzer", "url_analyzer", "yara_analyzer"] }
  ],

  "termination_conditions": [
    {
      "analyzer_name": "adversarial_detection_analyzer",
      "output_match": "INJECTION/JAILBREAK",
      "thresholds": [
        { "metric_name": "score", "operator": ">=", "value": 0.85,
          "action_on_met": "terminate_immediately" }
      ],
      "logical_operator": "AND",
      "on_match_action": "terminate_immediately"
    },
    {
      "analyzer_name": "safety_moderation_analyzer",
      "output_match": "UNSAFE",
      "on_match_action": "terminate_immediately"
    },
    {
      "analyzer_name": "dlp_analyzer",
      "thresholds": [
        { "metric_name": "findings_count", "operator": ">", "value": 0,
          "action_on_met": "terminate_immediately" }
      ],
      "on_match_action": "proceed_to_next_step"
    },
    {
      "analyzer_name": "url_analyzer",
      "thresholds": [
        { "metric_name": "unsafe_urls_count", "operator": ">", "value": 0,
          "action_on_met": "terminate_immediately" }
      ],
      "on_match_action": "proceed_to_next_step"
    },
    {
      "analyzer_name": "yara_analyzer",
      "thresholds": [
        { "metric_name": "matches_found", "operator": ">", "value": 0,
          "action_on_met": "terminate_immediately" }
      ],
      "on_match_action": "proceed_to_next_step"
    }
  ],

  "default_telemetry": true
}
What that means at runtime:
  1. Step 1 runs the prompt-injection classifier in isolation. If the model labels the input INJECTION/JAILBREAK and the score is >= 0.85, the run terminates immediately with a “blocked” decision.
  2. Step 2 runs the safety judge. Any unsafe verdict terminates the run.
  3. Step 3 runs SDP, URL risk, and YARA in parallel. Any one of them — even the cheapest YARA hit — terminates the run.
The first two steps are where you spend GPU time, so they run sequentially with explicit early-exit. The cheap analyzers in step 3 race against each other so the latency floor is the slowest of the three rather than their sum.

Execution semantics

Steps

Each step has a type:
  • sequential — runs the analyzers in the listed order. The first analyzer that errors stops the step with overall_status = "ERROR".
  • asynchronous — runs the analyzers concurrently with asyncio.gather. Every analyzer in the group runs to completion (or error) before the step finishes.

Termination rules

After each successful analyzer output, the engine evaluates that analyzer’s termination rules. A rule has up to three signals:
  • output_match — a regex against the structured output of the analyzer (e.g. INJECTION/JAILBREAK, Toxic, MALWARE, EMAIL_ADDRESS).
  • thresholds — a comparison on a metric (>, >=, ==, <, <=) with a constant value.
  • logical_operatorAND or OR connecting output_match with the thresholds.
Each rule has an on_match_action:
  • terminate_immediately — stop the pipeline; the run’s overall_status becomes TERMINATED_EARLY. The decision returned to your code is “blocked”.
  • proceed_to_next_step — continue, but flag the analyzer.

Errors

InfrastructureAnalyzerError (raised when an upstream is unhealthy, e.g. the model service is unreachable) bubbles up as analyzer_unavailable (HTTP 503) with Retry-After. SDKs retry these automatically with exponential backoff. Other analyzer errors mark that analyzer’s status ERROR and the overall run as ERROR. The response still includes per-analyzer details so you can see which analyzer failed.

Building a policy

You have three ways to author a policy:

1. Dashboard policy editor

The schema-driven editor at agnes.lasscyber.com/protection/policies walks the analyzer catalog and generates the right inputs for each analyzer’s parameters, metrics, and termination rules. This is the recommended path for non-engineers.

2. SDK PolicyBuilder

Both SDKs ship a fluent builder that translates canonical SDK-facing analyzer names to the server keys at build() time:
from agnes import Agnes, PolicyBuilder

policy = (
    PolicyBuilder("inbound-strict", slug="inbound-strict")
    .prompt_injection_jailbreak(threshold=0.85)
    .safe_responsible_ai(block_on=["harassment", "self_harm"])
    .sensitive_data(sdp_policy="default-pii")
    .url_risk()
    .yara()
    .terminate_on_any_block()
    .build()
)

agnes = Agnes()
agnes.policies.create(policy)

3. Raw JSON via API

For automation pipelines, POST /api/v1/policies/ accepts the MultiAnalyzerConfig shape directly. See the auto-generated API reference for the schema.

Direction: inbound vs outbound

Agnes does not enforce a particular direction; you decide which policy runs on inputs versus outputs. The shipped defaults are:
  • default-inbound — strict, blocks on adversarial / unsafe / sensitive data findings.
  • default-outbound — slightly different thresholds, weighted toward catching hallucinated PII and unsafe responses.
  • default-permissive — runs the same analyzers but only flags; never terminates. Useful for shadow rollouts.
The guard() helper in both SDKs auto-flips default-inbounddefault-outbound when you call check_output. Policies you create yourself can use any slug naming; the SDK’s flip heuristic only fires for the default- prefix.

Performance and cost levers

  • Order steps cheap-to-expensive. Termination on a YARA hit costs a fraction of a millisecond; on a ShieldGemma run it’s tens of milliseconds.
  • Use the smaller models when latency matters. Llama-Prompt-Guard-2-22M is materially faster than the 86M variant; shieldgemma-2b is cheaper than 9b. The catalog labels each option with size and context limits.
  • Disable analyzers you do not need. Removing an analyzer from available_analyzers removes its cost entirely; setting an always-false threshold leaves the analyzer running but suppresses its termination signal.
  • Shadow first, enforce later. Clone a policy, set every termination to proceed_to_next_step, and run it for a week. The Analysis log tells you what would have blocked. Promote when you have signal.

Next