Skip to content

Commit d88656f

Browse files
committed
Clean README with trading demo focus
1 parent a2c35e1 commit d88656f

1 file changed

Lines changed: 40 additions & 196 deletions

File tree

README.md

Lines changed: 40 additions & 196 deletions
Original file line numberDiff line numberDiff line change
@@ -1,239 +1,83 @@
1-
# Execution Guard — Exactly-Once Execution for AI Agents
1+
# SafeAgent / Execution Guard
22

3-
A missing layer for safe side effects in agent systems.
3+
## Prevent duplicate or incorrect execution when retries happen
44

5-
Retries can duplicate irreversible actions:
6-
payments, emails, trades, and external API mutations.
5+
A missing execution boundary for AI agents, trading bots, automations, and workflow systems.
76

8-
Execution Guard ensures a side effect runs exactly once — even under retries.
7+
When a system hits:
8+
- timeout
9+
- partial failure
10+
- retry
11+
- uncertain completion
912

10-
> Even if your system runs it twice, it executes once.
13+
…it often does not know whether the action already happened.
1114

12-
---
13-
14-
## Quickstart (runs in ~10 seconds)
15-
16-
Run the same request twice — it executes once.
17-
18-
Install:
19-
20-
```bash
21-
pip install safeagent-exec-guard
22-
```
23-
24-
Minimal local example (SQLite):
25-
26-
```python
27-
from safeagent_exec_guard.sqlite_store import SQLiteExecutionStore
28-
29-
store = SQLiteExecutionStore("safeagent.db")
30-
store.init_db()
31-
32-
def send_payment(request_id: str):
33-
action = "send_payment"
34-
35-
if store.insert_if_not_exists(request_id, action):
36-
print("executing side effect")
37-
result = {"status": "sent", "receipt_id": "rcpt_12345"}
38-
store.complete(request_id, result)
39-
print("result:", result)
40-
else:
41-
print("duplicate blocked")
15+
That is how you get:
16+
- duplicate trades
17+
- duplicate payments
18+
- duplicate emails
19+
- duplicate API mutations
4220

43-
send_payment("req_123")
44-
send_payment("req_123")
45-
```
46-
47-
Expected output:
21+
SafeAgent adds a durable execution boundary around real side effects so retries can be reconciled instead of replayed.
4822

49-
```text
50-
executing side effect
51-
result: {'status': 'sent', 'receipt_id': 'rcpt_12345'}
52-
duplicate blocked
53-
```
23+
> Most systems can retry. Very few can decide when a retry is still correct.
5424
5525
---
5626

57-
## Demo
58-
59-
A retry without protection can execute the same irreversible action twice.
60-
61-
Execution Guard guarantees the side effect runs once — even if the agent retries.
62-
63-
### Same request ID. Same retry. Different result.
27+
## Demo — duplicate trade prevented after uncertain completion
6428

65-
Without an execution boundary, retries can re-run irreversible side effects.
66-
With Execution Guard, the second attempt returns the original receipt instead of executing again.
29+
![SafeAgent Trading Demo](assets/safeagent_trading_demo_v2.gif)
6730

68-
![Execution Guard Demo](assets/execution_guard_demo.gif)
31+
Without SafeAgent:
32+
- retry replays the action
33+
- duplicate trade executes
6934

70-
SafeAgent is a reference implementation of the Execution Guard pattern.
35+
With SafeAgent:
36+
- retry resolves against existing execution
37+
- duplicate is blocked
7138

7239
---
7340

74-
## Install
41+
## Quickstart
7542

76-
```bash
7743
pip install safeagent-exec-guard
78-
```
79-
80-
---
81-
82-
## Quick start (SQLite)
83-
84-
Use SQLite for local development, demos, and single-process workflows.
8544

8645
```python
8746
from safeagent_exec_guard.sqlite_store import SQLiteExecutionStore
8847

8948
store = SQLiteExecutionStore("safeagent.db")
9049
store.init_db()
9150

92-
request_id = "payment_123"
93-
action = "send_payment"
94-
95-
def do_side_effect():
96-
print("Executing payment...")
97-
return {"status": "sent", "receipt_id": "rcpt_12345"}
98-
99-
if store.insert_if_not_exists(request_id, action):
100-
result = do_side_effect()
101-
store.complete(request_id, result)
102-
print("Executed:", result)
103-
else:
104-
print("Duplicate request detected — execution blocked")
105-
106-
```
107-
See also: Wrap a side effect
108-
109-
## Postgres (production)
110-
111-
Use Postgres for distributed workers, production services, and multi-instance agent systems.
112-
113-
### Start Postgres with Docker
114-
115-
```bash
116-
docker run --name safeagent-postgres \
117-
-e POSTGRES_PASSWORD=postgres \
118-
-e POSTGRES_USER=postgres \
119-
-e POSTGRES_DB=postgres \
120-
-p 5432:5432 \
121-
-d postgres:16
122-
```
123-
124-
### Example
125-
126-
```python
127-
import os
128-
from safeagent_exec_guard.postgres_store import PostgresExecutionStore
129-
130-
dsn = os.getenv(
131-
"POSTGRES_DSN",
132-
"postgresql://postgres:postgres@localhost:5432/postgres"
133-
)
134-
135-
store = PostgresExecutionStore(dsn)
136-
store.init_db()
137-
138-
request_id = "payment_123"
139-
action = "send_payment"
140-
141-
def do_side_effect():
142-
print("Executing payment...")
143-
return {"status": "sent", "receipt_id": "rcpt_12345"}
144-
145-
if store.insert_if_not_exists(request_id, action):
146-
result = do_side_effect()
147-
store.complete(request_id, result)
148-
print("Executed:", result)
149-
else:
150-
print("Duplicate request detected — execution blocked")
151-
```
152-
153-
### Reset the demo table
154-
155-
```bash
156-
docker exec -it safeagent-postgres psql -U postgres -d postgres -c "TRUNCATE TABLE execution_requests;"
157-
```
158-
159-
---
160-
161-
## How it works
51+
def send_payment(request_id: str):
52+
action = "send_payment"
16253

163-
```python
164-
if store.insert_if_not_exists(request_id, action):
165-
result = do_side_effect()
166-
store.complete(request_id, result)
167-
else:
168-
print("Duplicate request detected — execution blocked")
54+
if store.insert_if_not_exists(request_id, action):
55+
result = {"status": "sent"}
56+
store.complete(request_id, result)
57+
print("executed")
58+
else:
59+
print("duplicate blocked")
16960
```
17061

171-
A request is identified once. Every retry resolves against that same execution record.
172-
17362
---
17463

17564
## Mental model
17665

17766
Without SafeAgent:
178-
179-
1. Request is sent.
180-
2. Timeout or uncertainty happens.
181-
3. The system retries.
182-
4. The side effect runs again.
67+
retry → replay → duplicate
18368

18469
With SafeAgent:
185-
186-
1. Request is sent with a `request_id`.
187-
2. Execution is recorded durably.
188-
3. The side effect runs once.
189-
4. Retries are blocked for the same request.
70+
retry → resolve → safe
19071

19172
---
19273

19374
## Where this matters
19475

195-
- Payments
196-
- Trading systems
197-
- Background jobs
198-
- Webhooks
199-
- External API mutations
200-
- AI agent tool calls
201-
- Ticketing and messaging workflows
202-
203-
Any system where running the same side effect twice is unacceptable.
204-
205-
---
206-
207-
## Why this exists
208-
209-
Retries do not mean “nothing happened.”
210-
211-
They mean “we do not know what happened.”
212-
213-
Most systems retry anyway.
214-
215-
That is fine for reads.
216-
217-
It is dangerous for side effects.
218-
219-
That is how you get duplicate payments, duplicate emails, duplicate trades, and repeated external mutations.
220-
221-
Execution Guard adds a safety boundary at the side-effect layer:
222-
223-
- record the execution attempt
224-
- execute once
225-
- resolve retries safely
226-
227-
---
228-
229-
## Case studies
230-
231-
- [Trading Bot Case Study](docs/TRADING_BOT_CASE_STUDY.md)
232-
233-
## Backends
234-
235-
- SQLite → local / single process
236-
- Postgres → distributed / production
76+
- trading
77+
- payments
78+
- APIs
79+
- agents
80+
- workflows
23781

23882
---
23983

0 commit comments

Comments
 (0)