> ## Documentation Index
> Fetch the complete documentation index at: https://docs.responsibleailabs.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Policy Engine

> Declarative rules that translate RAIL scores into actions: block, warn, flag, or allow.

Evaluation tells you what the score is. The policy engine tells your application what to do about it: "if the score drops below 7.5, block the response."

There are two ways to apply a policy:

* **Application policy (recommended).** Configure enforcement mode, thresholds, dimension weights, compliance, and safe-regeneration once per [application](/concepts/applications) in the dashboard. It is enforced automatically for every evaluation made with that application's keys, with no per-request rules in your code. Inspect the live policy with [`GET /config`](/api-reference/config); each evaluation reports a [`policy_outcome`](/api-reference/evaluation).
* **SDK policy (local).** Declare rules in code and let the SDK act on them in your process. Useful for local-only logic or rules you do not want to manage centrally.

<Info>
  **Application policy:** [`GET /config`](/api-reference/config) | **Python SDK:** [`client.eval()` with `policy=`](/sdk/python/policy-engine) | **Sessions:** [RAILSession](/sdk/python/sessions)
</Info>

## Evaluation vs policy

|                 | Evaluation                            | Policy Engine                                   |
| --------------- | ------------------------------------- | ----------------------------------------------- |
| **Returns**     | Scores, confidence, explanations      | Action: `block` / `warn` / `flag` / `allow`     |
| **Role**        | Observation                           | Enforcement                                     |
| **When to use** | You want scores and decide what to do | You want the SDK to enforce rules automatically |

## How it works

```mermaid theme={null}
flowchart TD
    Eval["Evaluation result\n(scores per dimension)"] --> Engine["Policy Engine"]
    Engine --> R1{"Rule 1\nsafety &lt; 7?"}
    R1 -- yes --> Block["action: block"]
    R1 -- no --> R2{"Rule 2\nfairness &lt; 6?"}
    R2 -- yes --> Flag["action: flag"]
    R2 -- no --> R3{"Rule 3\nreliability &lt; 5?"}
    R3 -- yes --> Warn["action: warn"]
    R3 -- no --> Allow["action: allow"]
```

Rules are evaluated in priority order. The first matching rule determines the primary action. Lower-priority rules that also match append their actions as secondary, so no failure is silently dropped.

## Policy actions

| Action  | When to use                                     | Example                                              |
| ------- | ----------------------------------------------- | ---------------------------------------------------- |
| `block` | Response must not reach the user                | `safety < 5` on a customer-facing chatbot            |
| `warn`  | Response can proceed, caller should be notified | `reliability < 6` - response may contain uncertainty |
| `flag`  | Queue for async human review without blocking   | `fairness < 7` - flag for bias review                |
| `allow` | Explicitly pass (default for unmatched content) | Catch-all at the end of a rule list                  |

## Declaring a policy

A rule fires when that dimension scores **below** its `threshold` — `threshold` is the minimum needed to pass. For example, `Rule(dimension="safety", threshold=7.0, action="block")` blocks any response whose safety score is under 7.0.

<CodeGroup>
  ```python Python theme={null}
  from rail_score_sdk import RailScoreClient, Policy, Rule

  client = RailScoreClient(api_key="...")

  policy = Policy(rules=[
      Rule(dimension="safety",      threshold=7.0, action="block"),
      Rule(dimension="fairness",    threshold=6.0, action="flag"),
      Rule(dimension="reliability", threshold=5.0, action="warn"),
  ])

  result = client.eval(
      content="...",
      mode="basic",
      policy=policy,
  )

  print(result.policy_outcome.action)           # "block" | "warn" | "flag" | "allow"
  print(result.policy_outcome.triggered_rules)  # Which rules fired
  print(result.policy_outcome.blocked)          # True if action == "block"
  ```

  ```typescript JavaScript theme={null}
  import { RailScoreClient } from "@responsible-ai-labs/rail-score";

  const client = new RailScoreClient({ apiKey: "..." });

  const policy = {
    rules: [
      { dimension: "safety",      threshold: 7.0, action: "block" },
      { dimension: "fairness",    threshold: 6.0, action: "flag"  },
      { dimension: "reliability", threshold: 5.0, action: "warn"  },
    ],
  };

  const result = await client.eval({ content: "...", mode: "basic", policy });

  console.log(result.policyOutcome.action);
  console.log(result.policyOutcome.blocked);
  ```
</CodeGroup>

## Reusable policies

Define a policy once and attach it to the client so it applies to every `eval()` call automatically:

```python theme={null}
HEALTHCARE_POLICY = Policy(rules=[
    Rule(dimension="safety",       threshold=8.5, action="block"),
    Rule(dimension="reliability",  threshold=7.5, action="block"),
    Rule(dimension="privacy",      threshold=8.0, action="block"),
    Rule(dimension="transparency", threshold=6.0, action="warn"),
])

client = RailScoreClient(
    api_key="...",
    default_policy=HEALTHCARE_POLICY,
)

result = client.eval(content="...", mode="basic")  # Policy applies automatically

if result.policy_outcome.blocked:
    return "I'm unable to provide that information — please consult a healthcare professional."
```

## Session-level policies

A session tracks quality across an entire conversation. You can set a policy that triggers on aggregate conversation quality, which is useful for detecting gradual drift across many turns:

```python theme={null}
from rail_score_sdk import RailScoreClient, RAILSession, Policy, Rule

turn_policy = Policy(rules=[
    Rule(dimension="safety", threshold=5.0, action="block"),
])

session_policy = Policy(rules=[
    Rule(dimension="safety", threshold=7.0, action="flag", aggregate="avg"),
])

session = RAILSession(
    client=client,
    turn_policy=turn_policy,
    session_policy=session_policy,
)

for user_message in conversation:
    response = await generate_response(user_message)
    outcome = session.record(content=response, mode="basic")

    if outcome.turn_blocked:
        send_fallback()
    elif outcome.session_flagged:
        notify_human_reviewer(session.session_id)
    else:
        send(response)
```

## Real-world policy examples

<Accordion title="Healthcare chatbot">
  ```
  block when safety       < 8.5
  block when reliability   < 7.5
  block when privacy       < 8.0
  warn  when transparency  < 6.0
  ```
</Accordion>

<Accordion title="Hiring assistant">
  ```
  block when fairness    < 8.0
  flag  when inclusivity < 7.0
  warn  when safety      < 6.0
  ```
</Accordion>

<Accordion title="Customer support bot">
  ```
  block when safety      < 7.0
  warn  when reliability < 5.0
  flag  when user_impact < 6.0
  ```
</Accordion>

## What's next

<CardGroup cols={2}>
  <Card title="Python: Policy Engine" icon="python" href="/sdk/python/policy-engine">
    Full API for Policy, Rule, and policy callbacks.
  </Card>

  <Card title="Python: Sessions" icon="python" href="/sdk/python/sessions">
    RAILSession lifecycle and aggregate policies.
  </Card>

  <Card title="Concepts: Middleware" icon="layer-group" href="/concepts/middleware">
    Combine policies with provider wrappers for zero-boilerplate enforcement.
  </Card>

  <Card title="Concepts: Evaluation" icon="magnifying-glass" href="/concepts/evaluation">
    Understanding scores before applying policy rules.
  </Card>
</CardGroup>
