Conditions on the arguments themselves
Lesson 15 read the world around the call. This lesson reads the call, which is where the refund cap finally becomes a policy rule rather than an if in your own code.
A condition's first field is a path, not just a name. Prefix it with args. and it reads from the arguments the agent chose.
from agent_control_plane import PolicyEngine
from agent_control_plane.policy_engine import ConditionalPermission, Condition
policy = PolicyEngine()
policy.add_conditional_permission("support", ConditionalPermission(
tool_name="issue_refund",
conditions=[Condition("args.amount", "lte", 100)]))
policy.set_agent_context("support", {})
for amount in [40, 4000]:
print(amount, policy.check_violation("support", "issue_refund", {"amount": amount}))The cap from lesson 8 without writing a validator function, and on the interception path this time rather than the request path. This is the version that a tool-calling agent actually passes through.
set_agent_context is still needed even with an empty dictionary, because the engine looks the role's context up while building what the condition reads.
Deeper paths
Dots go as far down as the arguments do.
policy.add_conditional_permission("support", ConditionalPermission(
tool_name="send_email",
conditions=[Condition("args.message.priority", "ne", "urgent")]))
policy.set_agent_context("support", {})
for priority in ["normal", "urgent"]:
args = {"message": {"priority": priority, "body": "hello"}}
print(f"{priority:7}", policy.check_violation("support", "send_email", args))Each dot is a dictionary lookup. If any step is missing the path yields nothing, and a condition reading nothing is false, so the permission is refused.
Both at once
Conditions in one permission can mix the two sources freely: something about the world, and something about this particular call.
policy.add_conditional_permission("support", ConditionalPermission(
tool_name="issue_refund",
conditions=[Condition("customer_status", "eq", "verified"),
Condition("args.amount", "lte", 100)]))
policy.set_agent_context("support", {"customer_status": "verified"})
for amount in [40, 4000]:
print(amount, policy.check_violation("support", "issue_refund", {"amount": amount}))A verified customer, a small amount, allowed. A verified customer asking for four thousand, refused. That single rule is most of what a refund policy needs to say.
- Swap
lteforltand test the boundary at exactly 100. - Read
args.order_idwithinagainst a list of two order ids. - Ask for a refund with no
amountkey at all and confirm it is refused.
Little by little, you're building something great.