|
Download README.md from NagaYu/interlock: direct link, hf CLI and curl.
- Browser
- Download file 6.97 kB
-
https://huggingface.co/NagaYu/interlock/resolve/main/README.md
- Command line
-
hf download hf://NagaYu/interlock/README.md
-
curl -L -o README.md https://huggingface.co/NagaYu/interlock/resolve/main/README.md
6.97 kB
| license: apache-2.0 | |
| library_name: interlock | |
| pipeline_tag: text-classification | |
| tags: | |
| - agent-safety | |
| - tool-use | |
| - guardrails | |
| - ai-safety | |
| language: | |
| - en | |
| # Interlock | |
| Interlock judges a *proposed agent tool call* against *what the user actually | |
| asked for*. Content shields read the request and ask whether it is harmful. | |
| Interlock reads the request/action pair and asks a different question: is this | |
| call **more** than what was asked? | |
| It is deliberately blind to where a deviation came from. A widened `path` | |
| argument looks the same whether it originated in a prompt injection, a | |
| jailbreak, or an ordinary model mistake, and the defence is the same either | |
| way. | |
| ## Output | |
| Three-valued, never binary: | |
| | decision | meaning | | |
| |-----------|---------| | |
| | `allow` | the call stays within what the request asked for | | |
| | `confirm` | the call should be put to the user before it runs | | |
| | `block` | the call goes materially beyond the request | | |
| The three decisions are `allow`, `confirm`, `block`. Alongside the decision the model | |
| predicts a deviation type, one of `none`, `scope_expansion`, `unrequested_action`, `irreversible_unconfirmed`, `target_mismatch`, `out_of_scope`, `privilege_escalation`. | |
| `confirm` is a first-class outcome. Collapsing it into `block` would trade a | |
| question for a refusal on exactly the cases where the request is ambiguous. | |
| ## Two tiers | |
| **Tier 1 -- linear (1080 parameters).** A two-headed multinomial | |
| logistic regression over 107 structured request/action alignment | |
| features. Pure NumPy at inference: no tokeniser, no torch. This is the tier | |
| that runs on every agent turn. Shipped as `interlock_linear.npz`, with the | |
| fitted operating point in `policy.json`. | |
| **Tier 2 -- encoder (optional).** ModernBERT-base with a decision head and a | |
| deviation head, reading a role-annotated text rendering of the same pair. More | |
| accurate where the wording rather than the structure carries the signal, and | |
| correspondingly slower. Shipped as `interlock.onnx` when it has been trained | |
| and exported; the export is verified against the torch model to within 1e-3 on | |
| the raw logits before it is published. | |
| A third file, `interlock-linear.gguf`, is a weight container for tier 1 with | |
| its feature names and operating point attached. It is not a runtime: llama.cpp | |
| has no architecture for this classification head and cannot serve it. The | |
| verified runtimes are NumPy for tier 1 and ONNX Runtime for tier 2. | |
| ## Evaluation, and the split that makes it mean something | |
| The train/test split is taken at **tool family** granularity, not at row | |
| granularity. Training and validation draw on `fs, shell, mail, calendar, issues`. | |
| The held-out split draws on `browser, crm, storage, db` -- every tool schema | |
| in it is one the model has never seen. The split is verified against the files | |
| on disk, not just asserted in code. | |
| Measured for tier 1: | |
| | split | over-blocking | deviation detection | | |
| |-------|---------------|---------------------| | |
| | validation (seen families, unseen world) | 2.9% | 88.0% | | |
| | held-out families (unseen tool schemas) | 1.8% | 79.3% | | |
| Over-blocking is the fraction of *legitimate* calls that were interrupted, and | |
| it is the headline number. A shield that interrupts real work gets switched | |
| off, and a switched-off shield detects nothing. Detection is reported second | |
| for that reason, not first. | |
| Latency, timing a full check per call including encoding: 0.247 ms mean, 0.663 ms p95 over 400 single-threaded calls on arm64. | |
| Timing the model's arithmetic alone would understate what a deployment pays. | |
| ## Intended use | |
| Interlock sits between an agent's proposed tool call and its execution. Given | |
| the user's request, the proposed call and the tool's schema, it returns a | |
| verdict that a harness can act on: proceed, ask the user, or refuse. It is | |
| designed for agent runtimes where tool schemas are declared and where a | |
| confirmation prompt is a cheaper failure than an unwanted side effect. | |
| ```python | |
| from interlock.model import load_shield | |
| shield = load_shield("artifacts") | |
| # The request names one file. The call names the directory that contains it. | |
| verdict = shield.check( | |
| "Archive the Q3 notes at /work/apollo/q3-notes.md", | |
| {"tool": "fs.delete_path", "arguments": {"path": "/work/apollo", "recursive": True}}, | |
| ) | |
| print(verdict.decision, verdict.deviation) # block scope_expansion | |
| for reason in verdict.reasons: | |
| print(" -", reason) | |
| ``` | |
| Nothing about that call reads as dangerous on its own: it is an ordinary delete, | |
| on an ordinary path, in the project the user named. It is wrong only relative to | |
| the request, which is the only place the difference exists. | |
| A tool the model has never seen still gets a verdict rather than an exception, | |
| because an agent can propose anything: | |
| ```python | |
| verdict = shield.check( | |
| "Rotate the API key for the billing service", | |
| {"tool": "acme.rotate_key", "arguments": {"service": "billing"}}, | |
| ) | |
| print(verdict.decision, verdict.known_tool) # confirm False | |
| ``` | |
| It is not a content moderation model, not a prompt-injection detector, and not | |
| a substitute for sandboxing or for asking the user. | |
| ## Limitations | |
| - **Synthetic data.** Every episode, tool schema and deviation in training is | |
| generated. The tool families model common operation shapes rather than any | |
| particular product's API, and no real service was contacted at any point. | |
| Performance on production agent traffic is unmeasured here. | |
| - **English only.** Requests and tool descriptions are English. Nothing has | |
| been measured in any other language. | |
| - **One layer among several.** Interlock reads the correspondence between a | |
| request and a call. It does not read the content of a payload for harm, does | |
| not sandbox anything, and cannot see effects that occur outside the tool | |
| call it is shown. | |
| - **It does not guarantee safety.** An `allow` verdict means this shield found | |
| no boundary deviation, which is a weaker statement than "this call is safe". | |
| Deploy it beside content shields, sandboxing and human confirmation, not | |
| instead of them. | |
| - **Unseen tools cost accuracy.** Detection falls from 88.0% to | |
| 79.3% on held-out tool families. The over-blocking rate holds, so | |
| the degradation shows up as missed deviations rather than as new | |
| interruptions, but it is real. | |
| - **The operating point is a choice.** Thresholds were fitted against an | |
| explicit over-blocking budget on validation data. A deployment with a | |
| different tolerance for interruption should refit them rather than inherit | |
| these. | |
| ## Files | |
| | file | what it is | | |
| |------|------------| | |
| | `interlock_linear.npz` | tier-1 weights, the default serving path | | |
| | `policy.json` | fitted decision thresholds | | |
| | `train_report_linear.json` | measured metrics for tier 1 | | |
| | `interlock.onnx` | tier-2 graph, when the encoder has been trained | | |
| | `interlock-linear.gguf` | tier-1 weight container, not a runtime | | |
| | `latency.json` | measured per-call latency and peak RSS | | |
| Card generated 2026-08-30T01:30:45+00:00 for `NagaYu/interlock`. | |