Skip to content

Commit 77c81a0

Browse files
authored
feat: add correlated audit events to Safe Agent API
Add structured audit events correlated by request_id for allowed and denied tool executions, with tests and documentation after all CI gates passed.
1 parent 0169a65 commit 77c81a0

5 files changed

Lines changed: 109 additions & 23 deletions

File tree

README.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
| Status | Projeto executável | Qualidade |
1414
|---|---|---|
15-
| `v0.3` | **Safe Agent API** | GitHub Actions · pytest · CodeQL · Docker · secret scan |
15+
| `v0.4` | **Safe Agent API** | GitHub Actions · pytest · CodeQL · Docker · audit correlation |
1616

1717
`agentic-ai` · `guardrails` · `tool-calling` · `RAG` · `MCP` · `evals` · `observability` · `security`
1818

@@ -55,14 +55,16 @@ A API implementa uma camada determinística de policy antes da execução de too
5555
| `send_notification` | exige aprovação humana |
5656
| `delete_record` | bloqueada no exemplo |
5757

58+
O fluxo `POST /v1/run-demo` também retorna um **audit event estruturado**. O `correlation_id` é derivado do `request_id`, permitindo ligar decisão, execução e diagnóstico operacional sem armazenar conversa privada.
59+
5860
Endpoints:
5961

6062
- `GET /health`
6163
- `POST /v1/tool-check`
6264
- `POST /v1/run-demo`
6365
- documentação OpenAPI em `/docs`
6466

65-
Os testes verificam same-tenant access, cross-tenant denial, approval gate e bloqueio de ação destrutiva.
67+
Os testes verificam same-tenant access, cross-tenant denial, approval gate, bloqueio destrutivo e correlação de auditoria para ações permitidas e negadas.
6668

6769
## Modelo de produção
6870

@@ -75,7 +77,8 @@ USE CASE
7577
→ RAG / MEMORY
7678
→ EVALS
7779
→ HUMAN APPROVAL
78-
→ DEPLOY
80+
→ EXECUTION
81+
→ AUDIT + CORRELATION
7982
→ TRACE
8083
→ INCIDENT RESPONSE
8184
→ IMPROVE
@@ -91,8 +94,10 @@ flowchart TB
9194
O --> T[Tool Registry]
9295
T --> A[Approval Gate]
9396
A --> S[Business Systems]
97+
S --> AU[Audit Event + Correlation ID]
9498
O --> H[Human Handoff]
9599
O --> X[Tracing / Evals / Metrics]
100+
AU --> X
96101
```
97102

98103
## Checklist essencial
@@ -115,6 +120,7 @@ flowchart TB
115120
- [ ] task success, tool selection e argumentos avaliados;
116121
- [ ] testes de segurança/autorização;
117122
- [ ] tracing, custo, latência e taxa de erro observáveis;
123+
- [ ] audit event correlacionado por execução crítica;
118124
- [ ] handoff humano e kill switch disponíveis.
119125

120126
## Conteúdo técnico

examples/safe-agent-api/README.md

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,32 @@
11
# Safe Agent API
22

3-
Projeto executável do **AI Agent Production Checklist**. Ele demonstra uma camada determinística de autorização para tools de agentes sem depender de LLM ou API key.
3+
Projeto executável do **AI Agent Production Checklist**. Demonstra uma camada determinística de autorização, aprovação e auditoria para tools de agentes sem depender de LLM ou API key.
44

55
## O que demonstra
66

77
- tenant isolation;
88
- least privilege;
99
- approval gate humano;
1010
- bloqueio de tool destrutiva;
11+
- audit event estruturado;
12+
- `request_id` propagado como `correlation_id`;
1113
- contratos Pydantic;
1214
- API FastAPI com OpenAPI automática;
13-
- testes de segurança com pytest.
15+
- testes de segurança com pytest;
16+
- execução via Docker.
17+
18+
## Rodar com Docker
19+
20+
```bash
21+
git clone https://github.com/Videirafo/AI-Agent-Production-Checklist.git
22+
cd AI-Agent-Production-Checklist/examples/safe-agent-api
23+
docker compose up --build
24+
```
25+
26+
Abra:
27+
28+
- API docs: `http://127.0.0.1:8000/docs`
29+
- health: `http://127.0.0.1:8000/health`
1430

1531
## Clonar e abrir no VS Code
1632

@@ -20,15 +36,13 @@ cd AI-Agent-Production-Checklist/examples/safe-agent-api
2036
code .
2137
```
2238

23-
Crie o ambiente e instale:
24-
2539
### Windows PowerShell
2640

2741
```powershell
2842
py -m venv .venv
2943
.\.venv\Scripts\Activate.ps1
3044
python -m pip install -e ".[dev]"
31-
fastapi dev
45+
fastapi dev app/main.py
3246
```
3347

3448
### Linux/macOS
@@ -37,21 +51,16 @@ fastapi dev
3751
python3 -m venv .venv
3852
source .venv/bin/activate
3953
python -m pip install -e ".[dev]"
40-
fastapi dev
54+
fastapi dev app/main.py
4155
```
4256

43-
Abra:
44-
45-
- API docs: `http://127.0.0.1:8000/docs`
46-
- health: `http://127.0.0.1:8000/health`
47-
4857
## Executar testes
4958

5059
```bash
5160
pytest
5261
```
5362

54-
## Exemplo de política
63+
## Políticas
5564

5665
| Tool | Regra |
5766
|---|---|
@@ -61,20 +70,23 @@ pytest
6170

6271
A autorização é executada **fora do prompt/modelo**. Um LLM pode sugerir uma ação, mas não concede a si mesmo permissão para executá-la.
6372

64-
## Teste manual
73+
## Audit + correlation
74+
75+
`POST /v1/run-demo` recebe um `request_id`. A resposta inclui um `audit_event` cujo `correlation_id` usa o mesmo identificador. Assim uma decisão permitida ou negada pode ser ligada à execução e ao diagnóstico operacional.
6576

66-
POST `/v1/tool-check`:
77+
Exemplo:
6778

6879
```json
6980
{
81+
"request_id": "req-demo-002",
7082
"actor_tenant_id": "alpha",
7183
"resource_tenant_id": "alpha",
7284
"tool": "send_notification",
73-
"human_approved": false
85+
"human_approved": true
7486
}
7587
```
7688

77-
O resultado deve indicar `human_approval_required`.
89+
A resposta registra `allowed`, `executed`, `reason`, tenants, tool e `human_approved`, sem armazenar prompt ou conteúdo de conversa.
7890

7991
## Fazer sua branch
8092

examples/safe-agent-api/app/main.py

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
from fastapi import FastAPI
22

3-
from app.models import RunDemoRequest, RunDemoResponse, ToolCheckRequest, ToolDecision
3+
from app.models import (
4+
AuditEvent,
5+
RunDemoRequest,
6+
RunDemoResponse,
7+
ToolCheckRequest,
8+
ToolDecision,
9+
)
410
from app.policy import evaluate_tool
511

612
app = FastAPI(
713
title="Safe Agent API",
8-
version="0.1.0",
9-
description="Deterministic policy layer for agent tool execution demos.",
14+
version="0.2.0",
15+
description="Deterministic policy and audit layer for agent tool execution demos.",
1016
)
1117

1218

@@ -20,6 +26,23 @@ def tool_check(request: ToolCheckRequest) -> ToolDecision:
2026
return evaluate_tool(request)
2127

2228

29+
def build_audit_event(
30+
request: RunDemoRequest,
31+
decision: ToolDecision,
32+
executed: bool,
33+
) -> AuditEvent:
34+
return AuditEvent(
35+
correlation_id=request.request_id,
36+
actor_tenant_id=request.actor_tenant_id,
37+
resource_tenant_id=request.resource_tenant_id,
38+
tool=request.tool,
39+
allowed=decision.allowed,
40+
executed=executed,
41+
reason=decision.reason,
42+
human_approved=request.human_approved,
43+
)
44+
45+
2346
@app.post("/v1/run-demo", response_model=RunDemoResponse)
2447
def run_demo(request: RunDemoRequest) -> RunDemoResponse:
2548
decision = evaluate_tool(request)
@@ -29,6 +52,7 @@ def run_demo(request: RunDemoRequest) -> RunDemoResponse:
2952
request_id=request.request_id,
3053
decision=decision,
3154
executed=False,
55+
audit_event=build_audit_event(request, decision, executed=False),
3256
)
3357

3458
simulated_result = {
@@ -41,4 +65,5 @@ def run_demo(request: RunDemoRequest) -> RunDemoResponse:
4165
decision=decision,
4266
executed=True,
4367
result=simulated_result,
68+
audit_event=build_audit_event(request, decision, executed=True),
4469
)

examples/safe-agent-api/app/models.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,21 @@ class RunDemoRequest(ToolCheckRequest):
2222
request_id: str = Field(min_length=1)
2323

2424

25+
class AuditEvent(BaseModel):
26+
event_type: Literal["tool_execution_decision"] = "tool_execution_decision"
27+
correlation_id: str
28+
actor_tenant_id: str
29+
resource_tenant_id: str
30+
tool: ToolName
31+
allowed: bool
32+
executed: bool
33+
reason: str
34+
human_approved: bool
35+
36+
2537
class RunDemoResponse(BaseModel):
2638
request_id: str
2739
decision: ToolDecision
2840
executed: bool
2941
result: str | None = None
42+
audit_event: AuditEvent

examples/safe-agent-api/tests/test_api.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def test_notification_requires_human_approval() -> None:
5858
assert body["requires_human_approval"] is True
5959

6060

61-
def test_destructive_tool_is_disabled() -> None:
61+
def test_destructive_tool_denial_is_correlated_in_audit_event() -> None:
6262
response = client.post(
6363
"/v1/run-demo",
6464
json={
@@ -72,3 +72,33 @@ def test_destructive_tool_is_disabled() -> None:
7272
body = response.json()
7373
assert body["executed"] is False
7474
assert body["decision"]["reason"] == "destructive_tool_disabled_in_demo"
75+
assert body["audit_event"]["correlation_id"] == "req-demo-001"
76+
assert body["audit_event"]["allowed"] is False
77+
assert body["audit_event"]["executed"] is False
78+
79+
80+
def test_approved_notification_execution_is_audited() -> None:
81+
response = client.post(
82+
"/v1/run-demo",
83+
json={
84+
"request_id": "req-demo-002",
85+
"actor_tenant_id": "alpha",
86+
"resource_tenant_id": "alpha",
87+
"tool": "send_notification",
88+
"human_approved": True,
89+
},
90+
)
91+
body = response.json()
92+
assert body["executed"] is True
93+
assert body["result"] == "demo_notification_sent"
94+
assert body["audit_event"] == {
95+
"event_type": "tool_execution_decision",
96+
"correlation_id": "req-demo-002",
97+
"actor_tenant_id": "alpha",
98+
"resource_tenant_id": "alpha",
99+
"tool": "send_notification",
100+
"allowed": True,
101+
"executed": True,
102+
"reason": "policy_allowed",
103+
"human_approved": True,
104+
}

0 commit comments

Comments
 (0)