bogdanraduta commited on
Commit
4165a01
·
verified ·
1 Parent(s): 9c8e219

Add inference_contract

Browse files
inference_contract/INFERENCE.md ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Inference contract - FlowX Sentinel Gate
2
+
3
+ This is the **frozen inference contract** for `flowxai/sentinel-gate`: the exact system
4
+ prompt, user-turn format, decode settings, and output schema the weights were trained
5
+ against. Do not edit the prompt or schema; the LoRA was trained on them verbatim.
6
+
7
+ Prompt version: `sentinel_sys_v1`.
8
+
9
+ Files in this directory:
10
+
11
+ - [`prompt_sentinel_sys_v1.txt`](./prompt_sentinel_sys_v1.txt) - the system prompt, verbatim.
12
+ - [`schema_sentinel_v1.json`](./schema_sentinel_v1.json) - JSON Schema for the oracle output.
13
+
14
+ ---
15
+
16
+ ## System prompt (verbatim)
17
+
18
+ The exact two-line system prompt is in
19
+ [`prompt_sentinel_sys_v1.txt`](./prompt_sentinel_sys_v1.txt):
20
+
21
+ ```
22
+ You are an escalation gate for regulated decisions.
23
+ Determine: ESCALATE or DECIDE? Output ONLY JSON.
24
+ ```
25
+
26
+ ## User-turn format
27
+
28
+ One case per turn. The case JSON carries the domain facts plus the applicable
29
+ `policy_schema` (a `PDP...` policy id), then a fixed trailing question:
30
+
31
+ ```
32
+ Case:
33
+ <case JSON: domain facts + "policy_schema": "PDP...">
34
+
35
+ Decide: ESCALATE or DECIDE?
36
+ ```
37
+
38
+ The `Case:\n` prefix and the trailing `\n\nDecide: ESCALATE or DECIDE?` line are part of
39
+ the contract - keep them exactly. The model was trained with these delimiters framing the
40
+ case object.
41
+
42
+ ## Decode settings
43
+
44
+ | Setting | Value | Why |
45
+ |---|---|---|
46
+ | `enable_thinking` | **`False`** | Qwen3-4B is a thinking model, but the adapter was trained on pure JSON with no thinking block. The default template yields empty/degraded output. Set this at `apply_chat_template`. |
47
+ | `temperature` | **`0`** (greedy) | Deterministic decisions; the gate must be reproducible for audit. |
48
+ | `max_new_tokens` | **~1200** | The oracle JSON (category block + reasoning + audit trail) can run long, especially for BOUNDARY_CONDITION and EXTERNAL_DEPENDENCY. Truncation is the main cause of invalid JSON. |
49
+
50
+ ## The six escalation categories
51
+
52
+ Present as `escalation_category` when `action` is `ESCALATE` (it is `null` for `DECIDE`):
53
+
54
+ 1. `MISSING_REQUIRED_DOCUMENTATION` - a hard precondition document/evidence is absent.
55
+ 2. `POLICY_VIOLATION` - a policy/regulation rule is triggered and blocks auto-release.
56
+ 3. `BOUNDARY_CONDITION` - the case sits near a policy threshold; the edge needs a human read.
57
+ 4. `INSUFFICIENT_CONFIDENCE` - the facts do not resolve the decision to an actionable degree.
58
+ 5. `CONFLICTING_SIGNALS` - two or more trusted sources disagree materially.
59
+ 6. `EXTERNAL_DEPENDENCY` - the decision is blocked awaiting an outside result (screening, ruling).
60
+
61
+ Each category emits a category-specific block under a distinct key
62
+ (`policy_violations`, `missing_preconditions`, `boundary_analysis`, `confidence_factors`,
63
+ `conflicting_signals`, `external_dependency`). See `schema_sentinel_v1.json`.
64
+
65
+ ## Deterministic JSON repair (deploy with it)
66
+
67
+ Raw JSON validity from the model is **0.89** on the held-out set. The deployed pipeline
68
+ pairs the model with a **deterministic JSON repair step**: parse the raw output; if it
69
+ fails, apply structural fixes (close unterminated strings/brackets, strip any trailing
70
+ prose after the final `}`, drop a leading thinking artifact if one leaks) and re-parse, then
71
+ validate against `schema_sentinel_v1.json`. On a repair failure, retry the decode once. Do
72
+ not rely on raw output being parseable; treat the repair step as part of the contract.
73
+
74
+ ## What to gate on
75
+
76
+ - **Gate on the `action` field (ESCALATE vs DECIDE).** This is the decision the model is
77
+ for, and it is **perfect on the held-out set** (n=71): zero missed escalations
78
+ (false-negative rate 0.000) and zero over-escalation (false-positive rate 0.000). Wire
79
+ your automate-vs-route branch off `action` alone.
80
+ - **Treat `escalation_category` as a routing hint, not ground truth.** Category accuracy on
81
+ true-escalate is **~0.61**; the categories legitimately overlap for some cases (e.g. a
82
+ boundary case that is also a policy edge). Use it to pick a specialist queue, but do not
83
+ make correctness-critical branches depend on it, and let a human re-label at intake.
84
+ - `confidence_score` is calibrated per the training oracle; apply the threshold your risk
85
+ posture requires. It is advisory, not a second gate.
86
+
87
+ ## Minimal wiring (MLX)
88
+
89
+ ```python
90
+ from mlx_lm import load, generate
91
+
92
+ SYSTEM = open("prompt_sentinel_sys_v1.txt").read()
93
+ model, tok = load("flowxai/sentinel-gate-mlx-int4")
94
+
95
+ case_json = "<case JSON with domain facts + policy_schema>"
96
+ user = f"Case:\n{case_json}\n\nDecide: ESCALATE or DECIDE?"
97
+
98
+ prompt = tok.apply_chat_template(
99
+ [{"role": "system", "content": SYSTEM},
100
+ {"role": "user", "content": user}],
101
+ add_generation_prompt=True, enable_thinking=False,
102
+ )
103
+ raw = generate(model, tok, prompt=prompt, max_tokens=1200, verbose=False)
104
+ # then: deterministic JSON repair -> validate against schema_sentinel_v1.json
105
+ ```
inference_contract/prompt_sentinel_sys_v1.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ You are an escalation gate for regulated decisions.
2
+ Determine: ESCALATE or DECIDE? Output ONLY JSON.
inference_contract/schema_sentinel_v1.json ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://huggingface.co/flowxai/sentinel-gate/inference_contract/schema_sentinel_v1.json",
4
+ "title": "FlowX Sentinel Gate output (schema_sentinel_v1)",
5
+ "description": "The single JSON object the Sentinel Gate emits for one regulated-decision case. The gate decides ESCALATE (route to a human) vs DECIDE (safe to automate). When action=ESCALATE, escalation_category is one of six ids and a category-specific block is present; when action=DECIDE, escalation_category is null. Prompt version sentinel_sys_v1. Note: additionalProperties is intentionally true because the category-specific block key varies by category (policy_violations, missing_preconditions, boundary_analysis, confidence_factors, conflicting_signals, external_dependency) and DECIDE cases carry decision/rationale keys.",
6
+ "type": "object",
7
+ "additionalProperties": true,
8
+ "required": [
9
+ "action",
10
+ "escalation_category",
11
+ "confidence_score",
12
+ "audit_trail"
13
+ ],
14
+ "properties": {
15
+ "action": {
16
+ "type": "string",
17
+ "description": "The gate decision. ESCALATE routes the case to a human; DECIDE marks it safe to automate. This is the field to gate on (perfect on the held-out set).",
18
+ "enum": ["ESCALATE", "DECIDE"]
19
+ },
20
+ "escalation_category": {
21
+ "description": "The human-routing label for an escalation, or null when action=DECIDE. A secondary routing hint (~0.61 accuracy on true-escalate), not the gate.",
22
+ "type": ["string", "null"],
23
+ "enum": [
24
+ "MISSING_REQUIRED_DOCUMENTATION",
25
+ "POLICY_VIOLATION",
26
+ "BOUNDARY_CONDITION",
27
+ "INSUFFICIENT_CONFIDENCE",
28
+ "CONFLICTING_SIGNALS",
29
+ "EXTERNAL_DEPENDENCY",
30
+ null
31
+ ]
32
+ },
33
+ "confidence_score": {
34
+ "type": "number",
35
+ "description": "Calibrated confidence in the decision, 0.0-1.0. For ESCALATE this is typically the confidence that the case is safe to automate (low), so a low score supports escalation; for DECIDE it is the confidence in the auto-decision (high).",
36
+ "minimum": 0.0,
37
+ "maximum": 1.0
38
+ },
39
+ "confidence_reasoning": {
40
+ "type": "string",
41
+ "description": "One to three sentences explaining the confidence_score and why the case was escalated or auto-decided."
42
+ },
43
+ "human_action_required": {
44
+ "type": "string",
45
+ "description": "For ESCALATE: the concrete next step a human owner must take (who does what). For DECIDE: the string \"NONE\"."
46
+ },
47
+ "audit_trail": {
48
+ "type": "array",
49
+ "description": "Ordered, append-only log of the reasoning steps and policy gates evaluated, for compliance review.",
50
+ "items": { "type": "string" },
51
+ "minItems": 1
52
+ },
53
+ "policy_violations": {
54
+ "type": "object",
55
+ "description": "Category-specific block for POLICY_VIOLATION. Keyed by violation id; each entry names the policy, regulation, restriction, and consequence.",
56
+ "additionalProperties": true
57
+ },
58
+ "missing_preconditions": {
59
+ "type": "object",
60
+ "description": "Category-specific block for MISSING_REQUIRED_DOCUMENTATION. Keyed by the missing precondition; each entry names required_by, regulation, severity, and reason.",
61
+ "additionalProperties": true
62
+ },
63
+ "boundary_analysis": {
64
+ "type": "object",
65
+ "description": "Category-specific block for BOUNDARY_CONDITION. Names the policy_threshold, the shipment/case value, distance_from_threshold, and an assessment of the edge case.",
66
+ "additionalProperties": true
67
+ },
68
+ "confidence_factors": {
69
+ "type": "object",
70
+ "description": "Category-specific block for INSUFFICIENT_CONFIDENCE. Lists ambiguous_signals and why_uncertain.",
71
+ "additionalProperties": true
72
+ },
73
+ "conflicting_signals": {
74
+ "type": "array",
75
+ "description": "Category-specific block for CONFLICTING_SIGNALS. The competing sources/values that disagree.",
76
+ "items": { "type": "object", "additionalProperties": true }
77
+ },
78
+ "external_dependency": {
79
+ "type": "object",
80
+ "description": "Category-specific block for EXTERNAL_DEPENDENCY. Names what the decision is awaiting and the blocking_gate.",
81
+ "additionalProperties": true
82
+ },
83
+ "escalation_path": {
84
+ "type": "string",
85
+ "description": "Optional routing hint naming the specialist queue or workflow that should own the escalation."
86
+ },
87
+ "policy_gates_passed": {
88
+ "type": "array",
89
+ "description": "Optional list of policy gates that were checked and passed before the decision (present on some ESCALATE edge cases and on DECIDE cases).",
90
+ "items": { "type": "string" }
91
+ },
92
+ "decision": {
93
+ "type": "string",
94
+ "description": "For DECIDE cases: the automated outcome selected (e.g. ROUTE_APPROVED)."
95
+ },
96
+ "selected_route": {
97
+ "type": "string",
98
+ "description": "For DECIDE cases where a route/option is chosen: the selected option."
99
+ },
100
+ "rationale": {
101
+ "type": "string",
102
+ "description": "For DECIDE cases: the plain rationale for auto-deciding (some records use confidence_reasoning for this)."
103
+ }
104
+ },
105
+ "allOf": [
106
+ {
107
+ "if": { "properties": { "action": { "const": "DECIDE" } } },
108
+ "then": { "properties": { "escalation_category": { "type": "null" } } }
109
+ },
110
+ {
111
+ "if": { "properties": { "action": { "const": "ESCALATE" } } },
112
+ "then": {
113
+ "properties": {
114
+ "escalation_category": {
115
+ "type": "string",
116
+ "enum": [
117
+ "MISSING_REQUIRED_DOCUMENTATION",
118
+ "POLICY_VIOLATION",
119
+ "BOUNDARY_CONDITION",
120
+ "INSUFFICIENT_CONFIDENCE",
121
+ "CONFLICTING_SIGNALS",
122
+ "EXTERNAL_DEPENDENCY"
123
+ ]
124
+ }
125
+ }
126
+ }
127
+ }
128
+ ]
129
+ }