Agent Governance Toolkitagent-governance-toolkit 4.1.0 · Python 3.10+
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
23 small wins to finish your pathNext lesson

The flight recorder

Lesson 19 found the empty slot. This lesson fills it with the toolkit's audit log, which writes to SQLite and is built to be shown to somebody who does not take your word for things.

It is called a flight recorder, and it takes a path to a database file that it creates if it is not there.

Example
import os, tempfile
from agent_control_plane import AgentKernel, PolicyEngine, FlightRecorder

db = os.path.join(tempfile.mkdtemp(), "audit.db")
recorder = FlightRecorder(db_path=db)

policy = PolicyEngine()
policy.add_constraint("support", ["lookup_order", "issue_refund"])
kernel = AgentKernel(policy_engine=policy, audit_logger=recorder)
print("recording to a fresh database:", os.path.exists(db))

A temporary directory keeps the course tidy. In a real service this is a path you keep, back up and never let the agent's own tools write to.

Three calls

Example
from pretend_agent_governance import PretendAgent, TOOLS

agent = PretendAgent()
for message in ["Where is order A17?", "refund 40 on A17", "delete order A17"]:
    tool, args = agent.decide(message)
    verdict = kernel.intercept_tool_execution("support", tool, args, input_prompt=message)
    if verdict is None:
        TOOLS[tool](**args)
print("done")

input_prompt is the fourth argument to the gate and it is optional. Pass it. It stores the sentence that led to the call, which is the difference between a log saying a refund happened and a log saying why anybody thought it should.

Reading it back

Writes are buffered, so ask for them to be flushed before reading. Lesson 21 explains why that is not an accident.

Example
recorder.flush()
for row in sorted(recorder.query_logs(), key=lambda r: r["tool_name"]):
    print(f"{row['tool_name']:14} {row['policy_verdict']:8} {row['violation_reason']}")

Three rows: two allowed, one blocked with the reason beside it. The rows are sorted by tool name here because query_logs returns them newest first by timestamp, and timestamps from the same millisecond do not sort predictably.

Each row also carries the arguments, the prompt, a trace id, a timestamp and two hashes. The hashes are lesson 22.

Try it yourself
  • Drop the input_prompt argument and see what the column holds instead.
  • Print row['tool_args'] and notice it comes back as text rather than a dictionary.
  • Point a second FlightRecorder at the same file and read the rows the first one wrote.

Every expert started right here.