precedent-ai 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- precedent_ai-0.2.1/LICENSE +21 -0
- precedent_ai-0.2.1/PKG-INFO +541 -0
- precedent_ai-0.2.1/README.md +501 -0
- precedent_ai-0.2.1/precedent/__init__.py +12 -0
- precedent_ai-0.2.1/precedent/calibrate.py +58 -0
- precedent_ai-0.2.1/precedent/cli.py +679 -0
- precedent_ai-0.2.1/precedent/config.py +122 -0
- precedent_ai-0.2.1/precedent/drift.py +135 -0
- precedent_ai-0.2.1/precedent/evals.py +64 -0
- precedent_ai-0.2.1/precedent/export.py +151 -0
- precedent_ai-0.2.1/precedent/guard.py +219 -0
- precedent_ai-0.2.1/precedent/ingest/__init__.py +42 -0
- precedent_ai-0.2.1/precedent/ingest/episodes.py +110 -0
- precedent_ai-0.2.1/precedent/ingest/jsonl.py +54 -0
- precedent_ai-0.2.1/precedent/ingest/redact.py +57 -0
- precedent_ai-0.2.1/precedent/ingest/slack.py +71 -0
- precedent_ai-0.2.1/precedent/ingest/whatsapp.py +94 -0
- precedent_ai-0.2.1/precedent/kb.py +280 -0
- precedent_ai-0.2.1/precedent/llm.py +317 -0
- precedent_ai-0.2.1/precedent/loop.py +320 -0
- precedent_ai-0.2.1/precedent/mcp.py +166 -0
- precedent_ai-0.2.1/precedent/models.py +316 -0
- precedent_ai-0.2.1/precedent/packs.py +100 -0
- precedent_ai-0.2.1/precedent/py.typed +0 -0
- precedent_ai-0.2.1/precedent/reasoner.py +316 -0
- precedent_ai-0.2.1/precedent/retrieve.py +149 -0
- precedent_ai-0.2.1/precedent/runtime.py +163 -0
- precedent_ai-0.2.1/precedent/store.py +337 -0
- precedent_ai-0.2.1/precedent_ai.egg-info/PKG-INFO +541 -0
- precedent_ai-0.2.1/precedent_ai.egg-info/SOURCES.txt +43 -0
- precedent_ai-0.2.1/precedent_ai.egg-info/dependency_links.txt +1 -0
- precedent_ai-0.2.1/precedent_ai.egg-info/entry_points.txt +2 -0
- precedent_ai-0.2.1/precedent_ai.egg-info/requires.txt +10 -0
- precedent_ai-0.2.1/precedent_ai.egg-info/top_level.txt +1 -0
- precedent_ai-0.2.1/pyproject.toml +78 -0
- precedent_ai-0.2.1/setup.cfg +4 -0
- precedent_ai-0.2.1/tests/test_guard.py +251 -0
- precedent_ai-0.2.1/tests/test_hardening.py +424 -0
- precedent_ai-0.2.1/tests/test_ingest.py +83 -0
- precedent_ai-0.2.1/tests/test_interfaces.py +100 -0
- precedent_ai-0.2.1/tests/test_live.py +67 -0
- precedent_ai-0.2.1/tests/test_loop.py +142 -0
- precedent_ai-0.2.1/tests/test_providers.py +62 -0
- precedent_ai-0.2.1/tests/test_runtime.py +123 -0
- precedent_ai-0.2.1/tests/test_security.py +172 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Muhammad Burhan Mujahid
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,541 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: precedent-ai
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: Human-approved operating rules and deterministic action gates for AI agents, mined from how your team already works (WhatsApp, Slack), with drift detection and regression evals.
|
|
5
|
+
Author: Muhammad Burhan Mujahid
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/burhan-mujahid/precedent
|
|
8
|
+
Project-URL: Documentation, https://github.com/burhan-mujahid/precedent#readme
|
|
9
|
+
Project-URL: Changelog, https://github.com/burhan-mujahid/precedent/blob/main/CHANGELOG.md
|
|
10
|
+
Project-URL: Issues, https://github.com/burhan-mujahid/precedent/issues
|
|
11
|
+
Project-URL: Security, https://github.com/burhan-mujahid/precedent/blob/main/SECURITY.md
|
|
12
|
+
Keywords: ai-agents,llm,llm-agents,agentic-ai,guardrails,human-in-the-loop,knowledge-base,sop,business-rules,policy,governance,mcp,model-context-protocol,agent-skills,context-engineering,prompt-injection,evals,whatsapp,slack,langgraph,claude,gemini
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Classifier: Environment :: Console
|
|
22
|
+
Classifier: Intended Audience :: Information Technology
|
|
23
|
+
Classifier: Operating System :: OS Independent
|
|
24
|
+
Classifier: Topic :: Office/Business
|
|
25
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
26
|
+
Classifier: Topic :: Communications :: Chat
|
|
27
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
28
|
+
Requires-Python: >=3.10
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Requires-Dist: PyYAML>=6.0
|
|
32
|
+
Provides-Extra: certs
|
|
33
|
+
Requires-Dist: certifi; extra == "certs"
|
|
34
|
+
Provides-Extra: dev
|
|
35
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
36
|
+
Requires-Dist: ruff>=0.6; extra == "dev"
|
|
37
|
+
Requires-Dist: mypy>=1.11; extra == "dev"
|
|
38
|
+
Requires-Dist: types-PyYAML; extra == "dev"
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
|
|
41
|
+
<p align="center">
|
|
42
|
+
<img src="docs/assets/banner.svg" alt="Precedent: turn how your team already works into rules your AI agents follow, and keep those rules true." width="100%">
|
|
43
|
+
</p>
|
|
44
|
+
|
|
45
|
+
<p align="center">
|
|
46
|
+
<a href="https://github.com/burhan-mujahid/precedent/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/burhan-mujahid/precedent/actions/workflows/ci.yml/badge.svg"></a>
|
|
47
|
+
<img alt="Python 3.10 to 3.13" src="https://img.shields.io/badge/python-3.10%20%E2%80%93%203.13-3776ab?logo=python&logoColor=white">
|
|
48
|
+
<img alt="One runtime dependency" src="https://img.shields.io/badge/dependencies-1%20(PyYAML)-2ea44f">
|
|
49
|
+
<img alt="Typed" src="https://img.shields.io/badge/typed-mypy%20strict-informational">
|
|
50
|
+
<a href="https://modelcontextprotocol.io"><img alt="MCP server" src="https://img.shields.io/badge/MCP-server-8a63d2"></a>
|
|
51
|
+
<a href="SECURITY.md"><img alt="Security policy" src="https://img.shields.io/badge/security-policy-critical"></a>
|
|
52
|
+
<a href="LICENSE"><img alt="MIT license" src="https://img.shields.io/badge/license-MIT-yellow"></a>
|
|
53
|
+
</p>
|
|
54
|
+
|
|
55
|
+
<p align="center">
|
|
56
|
+
<a href="https://burhan-mujahid.github.io/precedent/"><b>Website</b></a> ·
|
|
57
|
+
<a href="#-quick-start">Quick start</a> ·
|
|
58
|
+
<a href="#-how-it-works">How it works</a> ·
|
|
59
|
+
<a href="#-plug-it-into-your-agent">Integrations</a> ·
|
|
60
|
+
<a href="#-results-with-real-models">Live results</a> ·
|
|
61
|
+
<a href="#-safety-by-design">Safety</a> ·
|
|
62
|
+
<a href="docs/DESIGN.md">Design</a> ·
|
|
63
|
+
<a href="docs/DEMO.md">Demo guide</a>
|
|
64
|
+
</p>
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
> **Your AI agent doesn't know how *your* business makes decisions.**
|
|
69
|
+
> When to refund. Who approves a discount. What to say about allergies. What *never* to do.
|
|
70
|
+
> Precedent learns those rules from the conversations your team is already having, has a person
|
|
71
|
+
> approve every one, and puts a deterministic gate in front of every action your agents take.
|
|
72
|
+
|
|
73
|
+
<p align="center">
|
|
74
|
+
<img src="docs/assets/demo.svg" alt="Terminal: three situations get three outcomes - ACT on a late delivery, NEEDS APPROVAL on a damaged cake refund, ESCALATE on a supplier bank-details change" width="88%">
|
|
75
|
+
</p>
|
|
76
|
+
|
|
77
|
+
<p align="center"><sub>Real output from <code>bash examples/demo.sh</code> (offline, no API key). Long lines shortened to fit.</sub></p>
|
|
78
|
+
|
|
79
|
+
> [!NOTE]
|
|
80
|
+
> **New in 0.2: halting a run that's already going.** Approving the start of an action isn't enough. Agents now re-check before every step and after Done, and Precedent **halts** the run when a step goes beyond what was approved, a rule changes mid-run, or a decision is reused or stale. Each halt leaves a record of which rule, which run, and who halted it. [How it works](#-the-execution-gate-halting-a-run-thats-already-going) · prompted by [Gary Ko](https://www.linkedin.com/in/gary-ko-976863210/).
|
|
81
|
+
|
|
82
|
+
## 🧭 The problem
|
|
83
|
+
|
|
84
|
+
Every agent deployment hits the same wall: the agent is capable, but it doesn't know the house rules.
|
|
85
|
+
|
|
86
|
+
| The usual fix | Why it breaks |
|
|
87
|
+
|---|---|
|
|
88
|
+
| 📝 **"Write a knowledge base"** | It describes how the founder *thinks* the business runs. It takes weeks, and it's stale weeks later. |
|
|
89
|
+
| 🔌 **"Let the agent learn from the tools"** | CRMs and inboxes show *what* happened, never *why*, and can't tell a precedent from a mistake. |
|
|
90
|
+
| 🤖 **"Let the agent figure it out"** | Models are poorly calibrated about when *not* to act. That is the one decision you can't hand them. |
|
|
91
|
+
|
|
92
|
+
The real process lives in the moments when someone asks a colleague ***"how do we handle X?"*** and gets an answer. Precedent treats those moments as the source, a person as the authority, and the agent's uncertainty as the signal for what to learn next.
|
|
93
|
+
|
|
94
|
+
## ✨ What you get
|
|
95
|
+
|
|
96
|
+
<table>
|
|
97
|
+
<tr>
|
|
98
|
+
<td width="50%" valign="top">
|
|
99
|
+
|
|
100
|
+
### ⛏️ Rules mined, not written
|
|
101
|
+
Point it at a WhatsApp export, a Slack export or JSONL. It finds the Q&A threads and drafts one rule per answer, **each linked to the exact messages it came from**. Fixing a draft takes minutes; writing from a blank page takes weeks.
|
|
102
|
+
|
|
103
|
+
</td>
|
|
104
|
+
<td width="50%" valign="top">
|
|
105
|
+
|
|
106
|
+
### ✅ A person approves everything
|
|
107
|
+
There is exactly **one door** into the rulebook: an approved proposal. Models draft; people decide. Every change is versioned, attributed and replayable.
|
|
108
|
+
|
|
109
|
+
</td>
|
|
110
|
+
</tr>
|
|
111
|
+
<tr>
|
|
112
|
+
<td valign="top">
|
|
113
|
+
|
|
114
|
+
### 🚦 A gate before every action, and while it runs
|
|
115
|
+
Agents call `consult_rulebook` before acting. **Five deterministic gates outside the model** return one of three outcomes: act, ask a human to approve, or escalate. Then `check_action` re-checks **before each step and after Done**, and **halts** the run if the step goes beyond what was approved.
|
|
116
|
+
|
|
117
|
+
</td>
|
|
118
|
+
<td valign="top">
|
|
119
|
+
|
|
120
|
+
### 🔁 Every escalation becomes a rule
|
|
121
|
+
Gaps are clustered into a weekly digest. A human answers once; the answer becomes a draft rule **and** a regression test, so the same question never escalates twice.
|
|
122
|
+
|
|
123
|
+
</td>
|
|
124
|
+
</tr>
|
|
125
|
+
<tr>
|
|
126
|
+
<td valign="top">
|
|
127
|
+
|
|
128
|
+
### 📉 Drift caught early
|
|
129
|
+
Rules that are overridden, unused, past their review date or duplicated get flagged, with a drafted fix, before they cause damage.
|
|
130
|
+
|
|
131
|
+
</td>
|
|
132
|
+
<td valign="top">
|
|
133
|
+
|
|
134
|
+
### 🗂️ Plain files, in git
|
|
135
|
+
YAML rules, JSONL logs. No database, no vector store, no server. Review rule changes in pull requests and gate merges with `precedent eval` in CI.
|
|
136
|
+
|
|
137
|
+
</td>
|
|
138
|
+
</tr>
|
|
139
|
+
</table>
|
|
140
|
+
|
|
141
|
+
## 🚀 Quick start
|
|
142
|
+
|
|
143
|
+
No API key needed. Python 3.10+.
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
pip install "git+https://github.com/burhan-mujahid/precedent@v0.2.1"
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**See the whole loop in about 20 seconds** on a sample bakery WhatsApp group:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
git clone https://github.com/burhan-mujahid/precedent && cd precedent
|
|
153
|
+
pip install -e .
|
|
154
|
+
bash examples/demo.sh # offline
|
|
155
|
+
bash examples/demo.sh --live # with GEMINI_API_KEY / MOONSHOT_API_KEY / ANTHROPIC_API_KEY / OPENAI_API_KEY set
|
|
156
|
+
bash examples/demo.sh --pause # step by step, for presenting
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**Or on your own team's chat:**
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
precedent init my-rulebook --pack support # packs: general support ops sales finance hr oncall
|
|
163
|
+
cd my-rulebook
|
|
164
|
+
precedent ingest ~/Downloads/"WhatsApp Chat with Ops.txt"
|
|
165
|
+
precedent review # approve / edit / reject drafts, with the source messages shown
|
|
166
|
+
precedent ask "Customer says the birthday cake arrived damaged"
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
```text
|
|
170
|
+
[NEEDS APPROVAL] D-20261109T1154-1eadcb90d0 confidence 0.88 risk high
|
|
171
|
+
answer : Ask for a photo first. If the damage is clear we remake it for free or refund in full, their choice. Don't argue about it, it's never worth the review.
|
|
172
|
+
rules : R-003 v1
|
|
173
|
+
why : reasoner: matched R-003 on: arrived, cake, customer, damaged
|
|
174
|
+
why : high-risk action: a human must approve
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
> [!TIP]
|
|
178
|
+
> WhatsApp: open the chat → ⋮ → More → **Export chat** → Without media. Android and iOS formats in all common locales work, including Urdu and other right-to-left chats.
|
|
179
|
+
|
|
180
|
+
## 🔍 How it works
|
|
181
|
+
|
|
182
|
+
```mermaid
|
|
183
|
+
flowchart LR
|
|
184
|
+
subgraph sources ["Where the knowledge already is"]
|
|
185
|
+
A["💬 WhatsApp / Slack / JSONL"]
|
|
186
|
+
B["📝 Session notes"]
|
|
187
|
+
C["✋ Human overrides"]
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
A -- "mine Q&A threads" --> P["📋 Proposals<br/>(drafts with evidence)"]
|
|
191
|
+
B -- "precedent wrap" --> P
|
|
192
|
+
C -- "precedent feedback" --> P
|
|
193
|
+
G["🕳️ Gaps"] -- "a person answers once" --> P
|
|
194
|
+
|
|
195
|
+
P -- "a person approves" --> R[("📚 Rulebook<br/>versioned YAML, in git")]
|
|
196
|
+
|
|
197
|
+
AG["🤖 Agent"] -- "consult_rulebook" --> GATES{"🚦 5 gates"}
|
|
198
|
+
R --> GATES
|
|
199
|
+
|
|
200
|
+
GATES --> ACT["🟢 ACT<br/>cites rule versions"]
|
|
201
|
+
GATES --> APP["🟡 NEEDS APPROVAL<br/>high-risk: a human signs off"]
|
|
202
|
+
GATES --> ESC["🔴 ESCALATE<br/>logged as a gap"]
|
|
203
|
+
ESC --> G
|
|
204
|
+
|
|
205
|
+
ACT -- "each step + after Done" --> CHK{"🛑 check_action"}
|
|
206
|
+
APP -- "after sign-off" --> CHK
|
|
207
|
+
CHK --> CONT["continue"]
|
|
208
|
+
CHK --> HALT["HALT<br/>halt record · run stopped"]
|
|
209
|
+
HALT -- "a person answers once" --> G
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### The five gates
|
|
213
|
+
|
|
214
|
+
Every `consult_rulebook` call goes through these, in order. The first failure wins, and **every failure opens a gap** for a human to answer.
|
|
215
|
+
|
|
216
|
+
| # | Gate | Escalates when | Why it's outside the model |
|
|
217
|
+
|:-:|---|---|---|
|
|
218
|
+
| 1 | **Coverage** | no approved rule mentions enough of the situation (IDF-weighted) | the model never sees an off-topic rulebook it could stretch to fit |
|
|
219
|
+
| 2 | **Judgement** | the model says the rules don't settle it, or two rules conflict | an honest "I'm not sure" is a feature, not a failure |
|
|
220
|
+
| 3 | **Grounding** | the model cites a rule it wasn't given, or its answer uses a number found in neither the rule nor the situation | invented rules and prompt-injected amounts never reach the agent |
|
|
221
|
+
| 4 | **Confidence** | confidence is below the threshold for the rule's risk tier | thresholds are yours to set, and `precedent calibrate` sets them from evidence |
|
|
222
|
+
| 5 | **Risk** | the rule is high-risk (money, legal, health, personal data, irreversible) | → **NEEDS APPROVAL**: the agent prepares, a person signs off |
|
|
223
|
+
|
|
224
|
+
### 🛑 The execution gate: halting a run that's already going
|
|
225
|
+
|
|
226
|
+
The five gates decide whether an action may **start**. They can't stop one that's already running, and they never see what actually happened. So there is a second gate. While acting, the agent calls `check_action` (or `kb.check`, or `precedent check`) with its decision id before each step that changes something, and once more after Done. Every call returns **continue** or **halt**. None of the checks calls a model.
|
|
227
|
+
|
|
228
|
+
| # | Check | Halts when | Reason |
|
|
229
|
+
|:-:|---|---|---|
|
|
230
|
+
| 1 | **Known** | the agent cites a decision the rulebook never issued | `unknown_decision` |
|
|
231
|
+
| 2 | **Sticky** | this decision, or any decision in the same run, was halted before | `halted` |
|
|
232
|
+
| 3 | **Single use** | the decision already passed its after-Done check | `already_done` |
|
|
233
|
+
| 4 | **Authorised** | the decision was ESCALATE, or NEEDS APPROVAL with no sign-off recorded | `not_authorized`, `no_sign_off` |
|
|
234
|
+
| 5 | **Fresh** | the decision, or its sign-off, is older than `runtime.decision_ttl_minutes` (60) | `expired` |
|
|
235
|
+
| 6 | **Rules unchanged** | a cited rule was retired, re-versioned or edited since the decision | `rule_changed` |
|
|
236
|
+
| 7 | **In scope** | the step uses an amount, or a high-risk word (bank, refund, delete…), that's in neither the decision nor its rules | `out_of_scope` |
|
|
237
|
+
|
|
238
|
+
Every halt is a **halt record** in `logs/events.jsonl`: the decision, the run, the cited rule versions, the stage (`during` or `after`), the reason, and **who created it**. That's `precedent:check` for the checks above, or the person or probe that called `precedent halt`. Halts are final. Once a run is halted, every later check in it halts too, so an agent can't retry its way past one. Out-of-scope and external halts also open a gap, so a person's answer becomes a rule fix and a regression test.
|
|
239
|
+
|
|
240
|
+
```text
|
|
241
|
+
> check: Refunding 5000 to the customer's card
|
|
242
|
+
[HALT] H-20261109T1428-e1f2fb8921 D-20261109T1428-2ed8117143 during run run-cake
|
|
243
|
+
why : out_of_scope: the step uses 5000, which the decision did not authorise
|
|
244
|
+
gap : G-20261109T1428-80363d1ecc
|
|
245
|
+
|
|
246
|
+
> check: Refunded the cake in full
|
|
247
|
+
[HALT] H-20261109T1428-e1f2fb8921 D-20261109T1428-2ed8117143 after run run-cake
|
|
248
|
+
why : halted: H-20261109T1428-e1f2fb8921 (out_of_scope) already stopped this decision; a person starts a new run
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
**Bring your own monitor.** If something outside Precedent halts a run, such as an execution monitor or a person watching, it calls `report_outcome` with `verdict: "halt"` (or `precedent halt D-… "why" --fix "what should happen"`). Precedent records the halt, stops the rest of the run, opens a gap and drafts the rule fix. Approving the fix adds a regression case.
|
|
252
|
+
|
|
253
|
+
## 🔌 Plug it into your agent
|
|
254
|
+
|
|
255
|
+
Precedent isn't an agent framework or a memory store. It's the **decision layer underneath** whatever you already use.
|
|
256
|
+
|
|
257
|
+
<details open>
|
|
258
|
+
<summary><b>🐍 Python: any framework</b></summary>
|
|
259
|
+
|
|
260
|
+
```python
|
|
261
|
+
from precedent import Precedent
|
|
262
|
+
|
|
263
|
+
kb = Precedent.open("./my-rulebook")
|
|
264
|
+
d = kb.resolve("Customer wants a refund, order arrived 3 days late", context={"order_value": 4500},
|
|
265
|
+
run_id="run-42")
|
|
266
|
+
|
|
267
|
+
if d.outcome == "answer": # act; d.cited == [{"id": "R-004", "version": 2, "sha": "f1733b9bf477"}]
|
|
268
|
+
...
|
|
269
|
+
elif d.outcome == "needs_approval": # covered, but high risk: a human signs off (kb.sign_off(d.id, "owner"))
|
|
270
|
+
...
|
|
271
|
+
else: # "escalate": a gap was logged and will come back as a rule
|
|
272
|
+
...
|
|
273
|
+
|
|
274
|
+
# While acting: before each step, and once after Done
|
|
275
|
+
if kb.check(d.id, "Refunding 4500 to the original card").halted:
|
|
276
|
+
stop_the_run()
|
|
277
|
+
kb.check(d.id, "Refunded 4500", stage="after")
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
For function-calling APIs, `kb.tool_spec()` gives a JSON Schema tool definition and `kb.handle_tool_call(args)` runs it. The tool call **never raises**: any error comes back as `escalate` with "Do NOT act". `kb.check_tool_spec()` and `kb.handle_check_call(args)` do the same for the execution gate, and any error there comes back as `halt`. Ready-made snippets: [Anthropic tool use](examples/integrations/anthropic_tool_use.py) · [LangGraph](examples/integrations/langgraph_tool.py).
|
|
281
|
+
|
|
282
|
+
</details>
|
|
283
|
+
|
|
284
|
+
<details>
|
|
285
|
+
<summary><b>🧩 MCP: Claude Desktop / Code, Cursor, Hermes, any MCP client</b></summary>
|
|
286
|
+
|
|
287
|
+
```json
|
|
288
|
+
{ "mcpServers": { "precedent": { "command": "precedent", "args": ["mcp", "--root", "/path/to/my-rulebook"] } } }
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
| Tool | What it does |
|
|
292
|
+
|---|---|
|
|
293
|
+
| `consult_rulebook` | the runtime gate: answer / needs_approval / escalate, with cited rule versions and the approved wording |
|
|
294
|
+
| `check_action` | the execution gate, before each step and after Done: continue / halt |
|
|
295
|
+
| `report_outcome` | confirm (becomes a regression test), override (drafts a rule fix for human review), or halt (an outside monitor stopped the run: halt record + gap + draft fix) |
|
|
296
|
+
|
|
297
|
+
Agents can't sign off their own NEEDS APPROVAL decisions: there is no sign-off tool over MCP.
|
|
298
|
+
| `search_rules` | read-only lookup of approved rules |
|
|
299
|
+
| `open_gaps` | what agents couldn't handle, waiting for a human |
|
|
300
|
+
|
|
301
|
+
</details>
|
|
302
|
+
|
|
303
|
+
<details>
|
|
304
|
+
<summary><b>⚙️ Shell, n8n, Make, cron: anything that can run a command</b></summary>
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
precedent ask "Supplier emailed saying their bank account has changed" --json
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
Prints the full decision as JSON. The exit code is `0` only when the agent may act on its own and `3` otherwise, so a script can't proceed by accident.
|
|
311
|
+
|
|
312
|
+
</details>
|
|
313
|
+
|
|
314
|
+
<details>
|
|
315
|
+
<summary><b>📦 Static exports: Agent Skills, AGENTS.md, Parlant</b></summary>
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
precedent export skill # an Agent Skills folder (agentskills.io): Claude, Codex, Cursor, Gemini CLI, Hermes...
|
|
319
|
+
precedent export agents # an AGENTS.md / CLAUDE.md block
|
|
320
|
+
precedent export parlant # a script that loads every approved rule as a Parlant guideline
|
|
321
|
+
precedent export json | markdown
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Static files can't run the gates, so every export tells the agent to escalate when no rule clearly fits and never to act alone on high-risk rules. For the full gates, use MCP or the Python API.
|
|
325
|
+
|
|
326
|
+
</details>
|
|
327
|
+
|
|
328
|
+
<details>
|
|
329
|
+
<summary><b>🛡️ CI for your rulebook repo</b></summary>
|
|
330
|
+
|
|
331
|
+
Every pull request that changes a rule must keep all regression cases passing and introduce no critical drift. Copy [`examples/integrations/rulebook-ci.yml`](examples/integrations/rulebook-ci.yml):
|
|
332
|
+
|
|
333
|
+
```yaml
|
|
334
|
+
- run: precedent eval --min-pass 1.0
|
|
335
|
+
- run: precedent health --strict
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
</details>
|
|
339
|
+
|
|
340
|
+
## 🧪 Results with real models
|
|
341
|
+
|
|
342
|
+
The same sample chat and five situations, run through Gemini, Kimi and the offline baseline. Nothing below is edited; full transcripts and token counts are in [docs/LIVE-RESULTS.md](docs/LIVE-RESULTS.md).
|
|
343
|
+
|
|
344
|
+
| Situation | Gemini `gemini-3.8-flash` | Kimi `kimi-k3` | Offline baseline |
|
|
345
|
+
|---|:-:|:-:|:-:|
|
|
346
|
+
| Brownies safe for a nut allergy? | 🟡 approval | 🟡 approval | 🟡 approval |
|
|
347
|
+
| Delivery 1.5 hours late | 🟢 act | 🟡 approval¹ | 🟢 act |
|
|
348
|
+
| Cancel an order for the day after tomorrow | 🟡 approval | 🟡 approval | 🔴 escalate² |
|
|
349
|
+
| Birthday cake arrived damaged | 🟡 approval | 🟡 approval | 🟡 approval |
|
|
350
|
+
| Supplier says their bank details changed | 🔴 escalate | 🔴 escalate | 🔴 escalate |
|
|
351
|
+
|
|
352
|
+
<sub>¹ Kimi rated "message the customer and add a free cookie box" as high risk when drafting; a reviewer settles that once, at approval. ² The keyword baseline matched only "cancel" and "order" (confidence 0.52, under the 0.55 approval floor), so a person decides. The models worked out that "the day after tomorrow" is more than 24 hours away.</sub>
|
|
353
|
+
|
|
354
|
+
Ingest of the 9 Q&A threads: **Gemini 9.4 s**, **Kimi 57 s**, offline 0.3 s. Each decision is one model call (none when the coverage gate escalates first).
|
|
355
|
+
|
|
356
|
+
## 🗓️ The weekly loop: about 30 minutes for a founder
|
|
357
|
+
|
|
358
|
+
```bash
|
|
359
|
+
precedent cycle
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
One digest, in order of leverage:
|
|
363
|
+
|
|
364
|
+
1. **Answer these.** Escalations clustered by similarity (*"7 cases about supplier bank details"*). One answer per cluster becomes a draft rule.
|
|
365
|
+
2. **Review these drafts.** From chat mining, overrides, session wraps and drift checks.
|
|
366
|
+
3. **Rulebook health.** Stale, overridden, unused and duplicate rules, and the escalation rate.
|
|
367
|
+
4. **Failing regression cases**, if a change broke something that used to work.
|
|
368
|
+
|
|
369
|
+
<details>
|
|
370
|
+
<summary><b>All commands</b></summary>
|
|
371
|
+
|
|
372
|
+
| Command | What it does |
|
|
373
|
+
|---|---|
|
|
374
|
+
| `precedent init DIR --pack support` | create a rulebook (`precedent packs` lists packs) |
|
|
375
|
+
| `precedent ingest FILE` | mine WhatsApp / Slack / JSONL into rule drafts |
|
|
376
|
+
| `precedent review` | the only way into the rulebook: walk the drafts and approve / edit / reject each (`list`, `show`, `approve`, `reject` for scripts) |
|
|
377
|
+
| `precedent ask "…" [--run ID] [--json]` | the runtime gate |
|
|
378
|
+
| `precedent signoff D-…` | a person approves a NEEDS APPROVAL decision so it may run |
|
|
379
|
+
| `precedent check D-… "step" [--after] [--json]` | the execution gate: continue (exit 0) or halt (exit 3) |
|
|
380
|
+
| `precedent halt D-… "why" [--fix "…"]` | a person or monitor halts a running action; `--fix` drafts a rule fix |
|
|
381
|
+
| `precedent halts [--run ID]` | every halt: decision, run, rule versions, stage, reason, who created it |
|
|
382
|
+
| `precedent gaps answer G-… "what we do"` | turn an escalation (and similar open ones) into a draft rule |
|
|
383
|
+
| `precedent feedback D-… --confirm` | a correct decision becomes a regression case |
|
|
384
|
+
| `precedent feedback D-… --override "…"` | a wrong decision opens a gap and drafts a fix to the rule it cited |
|
|
385
|
+
| `precedent wrap notes.md` | end-of-session wrap: decisions in your notes become proposals |
|
|
386
|
+
| `precedent cycle` | the weekly digest |
|
|
387
|
+
| `precedent health --propose --strict` | drift report; drafts deprecate / reconfirm proposals; non-zero exit in CI |
|
|
388
|
+
| `precedent eval --min-pass 1.0` | replay every regression case against the current rulebook |
|
|
389
|
+
| `precedent calibrate --target 0.95` | recommend confidence thresholds from evidence |
|
|
390
|
+
| `precedent replay --as-of 2026-10-01 "…"` | what would the rulebook have said on that date? |
|
|
391
|
+
| `precedent rules [list \| show \| history \| reconfirm]` | browse rules and their version history |
|
|
392
|
+
| `precedent export skill \| agents \| parlant \| json \| markdown` | compile for other tools |
|
|
393
|
+
| `precedent doctor` | check the model setup end to end |
|
|
394
|
+
| `precedent mcp` | run the MCP server on stdio |
|
|
395
|
+
|
|
396
|
+
Every command takes `--root DIR` (or `PRECEDENT_ROOT`). Set `PRECEDENT_USER` so approvals are attributed.
|
|
397
|
+
|
|
398
|
+
</details>
|
|
399
|
+
|
|
400
|
+
## 🤖 Using a model
|
|
401
|
+
|
|
402
|
+
The offline reasoner is a deterministic keyword baseline for tests and demos. For real use, pick a provider in `precedent.yaml`, export its key, and run `precedent doctor`. It lists the models your key can use, makes a test call, and runs one real decision.
|
|
403
|
+
|
|
404
|
+
| Provider | `provider.kind` | Key env var | Notes |
|
|
405
|
+
|---|---|---|---|
|
|
406
|
+
| Google Gemini | `gemini` | `GEMINI_API_KEY` | Gemini 3 models reason first, so the preset uses `max_tokens: 8000`, `reasoning_effort: low` |
|
|
407
|
+
| Kimi (Moonshot) | `kimi` | `MOONSHOT_API_KEY` | key from platform.moonshot.ai (a Kimi Coding key won't work) |
|
|
408
|
+
| Anthropic | `anthropic` | `ANTHROPIC_API_KEY` | native Messages API |
|
|
409
|
+
| OpenAI | `openai` | `OPENAI_API_KEY` | |
|
|
410
|
+
| OpenRouter / Groq | `openrouter` / `groq` | `OPENROUTER_API_KEY` / `GROQ_API_KEY` | |
|
|
411
|
+
| Ollama (local) | `ollama` | none | nothing leaves your machine |
|
|
412
|
+
| Anything OpenAI-compatible | `openai` + `base_url` | `api_key_env` | vLLM, LM Studio, Azure, Together… |
|
|
413
|
+
|
|
414
|
+
```yaml
|
|
415
|
+
provider:
|
|
416
|
+
kind: gemini
|
|
417
|
+
model: <a current Flash model: see `precedent doctor --filter flash`>
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
Model names change often, so Precedent never hard-codes one. If a model rejects `temperature` or `reasoning_effort`, the client drops it and retries. Mining runs `ingest.workers` (default 4) model calls in parallel and prints the calls and tokens it used.
|
|
421
|
+
|
|
422
|
+
> [!NOTE]
|
|
423
|
+
> On python.org builds of Python for macOS, HTTPS fails until you run *Install Certificates.command*. Or install the extra: `pip install "precedent-ai[certs] @ git+https://github.com/burhan-mujahid/precedent"`.
|
|
424
|
+
|
|
425
|
+
<details>
|
|
426
|
+
<summary><b>Full configuration (<code>precedent.yaml</code>) with defaults</b></summary>
|
|
427
|
+
|
|
428
|
+
```yaml
|
|
429
|
+
policy:
|
|
430
|
+
min_confidence: { low: 0.55, medium: 0.70, high: 1.01 } # > 1.0 = never act alone
|
|
431
|
+
approval_floor: 0.55 # high-risk matches below this escalate instead of going to sign-off
|
|
432
|
+
stale_penalty: 0.8 # confidence multiplier for rules past their review date
|
|
433
|
+
runtime:
|
|
434
|
+
redact: true # strip personal data and secrets before the model and the log
|
|
435
|
+
max_situation_chars: 4000
|
|
436
|
+
decision_ttl_minutes: 60 # how long a decision (or its sign-off) authorises action
|
|
437
|
+
retrieval: { top_k: 5, min_coverage: 0.30 }
|
|
438
|
+
rules: { review_days: 90 }
|
|
439
|
+
drift: { unused_days: 60, override_limit: 2, duplicate_similarity: 0.6 }
|
|
440
|
+
ingest: { redact: true, episode_window_hours: 12, episode_max_replies: 8, workers: 4 }
|
|
441
|
+
risk: { high_terms: [], medium_terms: [] } # domain packs fill these in
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
Safety settings are range-checked on load, so a typo like `0,7` stops with an error instead of silently opening a gate.
|
|
445
|
+
|
|
446
|
+
</details>
|
|
447
|
+
|
|
448
|
+
## 🔐 Safety by design
|
|
449
|
+
|
|
450
|
+
Precedent's job is deciding when an agent may act alone, so it's built to **fail closed**: when anything is in doubt, a person decides.
|
|
451
|
+
|
|
452
|
+
- 🧱 **The model is untrusted.** It can only cite rules it was shown. An answer with a number found in neither the rule nor the situation (a prompt-injected *"refund 5000"*) escalates. Model errors escalate. Agents also receive the approved rule text word for word.
|
|
453
|
+
- 🙈 **Privacy floor.** API keys, tokens, passwords, private keys, emails, phone numbers, card and IBAN numbers, IPs and URL query strings are redacted **before anything is stored or sent to a model**, both at ingest and at runtime. Use `ollama` and nothing leaves your machine.
|
|
454
|
+
- 🛑 **Halts are deterministic and final.** The execution gate halts a step that goes beyond its decision, a decision reused after Done or past its lifetime, and any run whose rules changed underneath it. A halted run stays halted.
|
|
455
|
+
- 🧾 **Audit trail.** Every decision records the id, version and content hash of each rule it relied on. Logs are append-only, and any past decision can be replayed against the rulebook as it stood that day.
|
|
456
|
+
- 🔒 **Hardened storage.** Ids are validated before they become file names, approvals take a cross-process lock, API keys only travel over HTTPS, and inputs and responses are size-capped.
|
|
457
|
+
- 🧬 **Supply chain.** One runtime dependency. CI runs `pip-audit`, Actions are pinned to commit SHAs, releases ship with signed build provenance.
|
|
458
|
+
|
|
459
|
+
The full threat model, what is **not** defended, and how to report a vulnerability: **[SECURITY.md](SECURITY.md)**.
|
|
460
|
+
|
|
461
|
+
## 🗺️ How it compares
|
|
462
|
+
|
|
463
|
+
The pieces exist separately; the combination doesn't.
|
|
464
|
+
|
|
465
|
+
| | Where rules come from | Who decides what's true | Gate before acting | Escalation → rule → test | Drift detection |
|
|
466
|
+
|---|---|---|:-:|:-:|---|
|
|
467
|
+
| Behaviour engines (e.g. Parlant) | written by developers | developers | guideline matching | – | conflict checks |
|
|
468
|
+
| Memory layers (Mem0, Zep/Graphiti, Letta) | conversations with users | the system | – | – | temporal invalidation |
|
|
469
|
+
| Self-improving agents (ACE, Hermes) | the agent's own attempts | the agent | – | – | playbook curation |
|
|
470
|
+
| LLM Wiki pattern | your documents | the LLM writes, people read | – | – | lint |
|
|
471
|
+
| Support SaaS (e.g. Sierra) | ticket resolutions | reviewers | inside a closed product | support only | – |
|
|
472
|
+
| **Precedent** | **your team's own Q&A + escalations** | **a person approves every change** | **✅ 5 deterministic gates, plus halt during and after** | **✅ automatic** | **stale · overridden · unused · duplicate · coverage** |
|
|
473
|
+
|
|
474
|
+
Precedent can export to Parlant, so it complements rather than competes. Details and related research: [docs/DESIGN.md §9](docs/DESIGN.md#9-related-work-and-how-precedent-differs).
|
|
475
|
+
|
|
476
|
+
## 🏗️ Project layout
|
|
477
|
+
|
|
478
|
+
```text
|
|
479
|
+
precedent/
|
|
480
|
+
ingest/ WhatsApp, Slack and JSONL parsers, redaction, Q&A episode detection
|
|
481
|
+
retrieve.py dependency-free BM25 + coverage scoring
|
|
482
|
+
reasoner.py extract / decide / wrap: model-backed and deterministic offline versions
|
|
483
|
+
runtime.py the five gates
|
|
484
|
+
guard.py the execution gate: check during and after an action, halt records, sign-off
|
|
485
|
+
loop.py mining, review, gaps, feedback, session wrap
|
|
486
|
+
drift.py health checks evals.py regression replay
|
|
487
|
+
calibrate.py evidence-based thresholds export.py Skill / AGENTS.md / Parlant / JSON
|
|
488
|
+
mcp.py stdio MCP server packs.py domain packs cli.py
|
|
489
|
+
skill/precedent/ use Precedent from Claude or any Agent Skills client
|
|
490
|
+
examples/ sample WhatsApp export, end-to-end demo, integration snippets
|
|
491
|
+
docs/ DESIGN.md · LIVE-RESULTS.md · DEMO.md
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
## 📍 Status and limits
|
|
495
|
+
|
|
496
|
+
**v0.2.1, alpha.** 139 tests, run by CI on Linux (Python 3.10 to 3.13), macOS and Windows; ruff and strict mypy clean. Known limits:
|
|
497
|
+
|
|
498
|
+
- **The offline reasoner is lexical.** It's a deterministic baseline for tests and demos; real deployments should use a model for `extract` and `decide`.
|
|
499
|
+
- **Retrieval is BM25.** It suits rulebooks of tens to low thousands of rules written in the team's own words. Stemming is English-only; Urdu and Roman Urdu chats parse fine, but rules match on exact words. An embedding retriever can drop in behind the same one-method interface.
|
|
500
|
+
- **Calibration needs evidence.** Thresholds are only recommended once a risk tier has enough regression cases.
|
|
501
|
+
- **No built-in authentication.** Whoever can write the rulebook folder can approve rules. Keep it in a git repo with reviewed pull requests ([SECURITY.md](SECURITY.md)).
|
|
502
|
+
|
|
503
|
+
**Roadmap:** direct connectors (Slack, WhatsApp Business, Gmail, helpdesks) · an optional embedding retriever · agreement across several model samples as a confidence signal · a static review UI · multi-reviewer quorum for high-risk rules.
|
|
504
|
+
|
|
505
|
+
## 🤝 Contributing
|
|
506
|
+
|
|
507
|
+
Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md). Good first issues: more chat formats (Teams, Discord, Telegram, email), domain packs, an embedding retriever, a review UI.
|
|
508
|
+
|
|
509
|
+
```bash
|
|
510
|
+
pip install -e ".[dev]" && pytest -q && ruff check . && mypy
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
Live provider tests (skipped without keys): `GEMINI_API_KEY=… MOONSHOT_API_KEY=… pytest -m live`.
|
|
514
|
+
|
|
515
|
+
## 🙏 Acknowledgements
|
|
516
|
+
|
|
517
|
+
The execution gate (`check_action`, halt records, sign-off, `report_outcome` halts) exists because [Gary Ko](https://www.linkedin.com/in/gary-ko-976863210/) pushed on the first release. He pointed out that act, needs approval and escalate only gate the *next* action: none of them is a HALT. He asked which rule, in which run, actually halted an in-progress action, and who created the artifact for that halt. In v0.1.0 the honest answer was "nothing does". Now every halt has a record that answers all three. Thank you, Gary.
|
|
518
|
+
|
|
519
|
+
## 📚 Citing
|
|
520
|
+
|
|
521
|
+
If Precedent helps your research or product, please cite it. GitHub's **"Cite this repository"** button (from [CITATION.cff](CITATION.cff)) gives APA and BibTeX:
|
|
522
|
+
|
|
523
|
+
```bibtex
|
|
524
|
+
@software{mujahid_precedent_2026,
|
|
525
|
+
author = {Mujahid, Muhammad Burhan},
|
|
526
|
+
title = {Precedent: human-approved operating rules and deterministic action gates for AI agents},
|
|
527
|
+
year = {2026},
|
|
528
|
+
version = {0.2.1},
|
|
529
|
+
url = {https://github.com/burhan-mujahid/precedent}
|
|
530
|
+
}
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
## 📬 Contact
|
|
534
|
+
|
|
535
|
+
**Muhammad Burhan Mujahid**: [burhanmujahid552@gmail.com](mailto:burhanmujahid552@gmail.com) · [@burhan-mujahid](https://github.com/burhan-mujahid)
|
|
536
|
+
|
|
537
|
+
Questions and ideas: [Discussions](https://github.com/burhan-mujahid/precedent/discussions). Bugs: [Issues](https://github.com/burhan-mujahid/precedent/issues). Security reports: [privately](SECURITY.md), please.
|
|
538
|
+
|
|
539
|
+
## 📄 License
|
|
540
|
+
|
|
541
|
+
[MIT](LICENSE) © Muhammad Burhan Mujahid
|