The choke point
Lesson 1 said the rule belongs in the loop between deciding and running. This lesson puts it there, which is the single idea the whole toolkit is built on.
The kernel is the object that owns the gate. It takes a policy engine, and it gives you one method to call before any tool runs.
from agent_control_plane import AgentKernel, PolicyEngine
policy = PolicyEngine()
policy.add_constraint("support", ["lookup_order", "issue_refund"])
kernel = AgentKernel(policy_engine=policy)
print(kernel.intercept_tool_execution("support", "lookup_order", {"order_id": "A17"}))None is the answer you want. The method returns nothing at all when the call is allowed, which reads strangely the first time and is the convention the rest of the toolkit follows.
What a refusal looks like
verdict = kernel.intercept_tool_execution("support", "delete_order", {"order_id": "A17"})
for key in sorted(verdict):
print(key, "=", verdict[key])A dictionary instead of None, saying what was refused and why. The keys are sorted here because a dictionary printed whole has no promised order, and a lesson whose output changes between runs is a lesson that cannot be checked.
mute is the toolkit's name for the shape of this refusal: the tool produces nothing, and the agent is told the call failed rather than being told a lie about it succeeding.
The gate in the loop
Now put it back in the loop from lesson 1. The only new line is the one that asks the kernel first.
from pretend_agent_governance import PretendAgent, TOOLS
agent = PretendAgent()
for message in ["Where is order A17?", "please delete order A17"]:
tool, args = agent.decide(message)
verdict = kernel.intercept_tool_execution("support", tool, args)
if verdict is None:
print(tool, "ran:", TOOLS[tool](**args))
else:
print(tool, "refused:", verdict["error"])The deletion from lesson 1 does not happen now. The agent still decided to do it, the message still said what it said, and the order is still there.
BLOCKED: on your terminal. That goes to standard error, which is why it is not part of the captured output above.That last step is worth sitting with. intercept_tool_execution does not reach into your program and stop anything. It returns an answer, and your loop is what honours it. Lesson 27 comes back to what that means when the agent is written by someone else.
- Add
issue_refundto a message and watch it pass, since it is on the allow-list. - Delete the
add_constraintline entirely and run it again. Lesson 5 explains what you see. - Change
if verdict is Nonetoif verdictand watch a gate that is wired in backwards let everything through.
This is what real progress feels like.