Chapter 2: Capability Scoping¶
In Chapter 1 you created a single policy that applies to every agent. In practice, different agents need different permissions โ a reader agent should only search and read, while an admin agent can do more.
This chapter shows how to scope capabilities by assigning each role its own policy file.
What you'll learn:
| Section | Topic |
|---|---|
| The idea | Why different agents need different rules |
| Two policy files | A restrictive reader policy and a permissive admin policy |
| Evaluate both roles | Load the right policy for the right agent |
| Try it yourself | Exercises |
The idea¶
Imagine a company with two types of AI assistants:
| Role | Allowed actions | Blocked actions |
|---|---|---|
| Reader | Search documents | Send email, write files, delete databases |
| Admin | Search, send email, write files | Delete databases |
The key principle is least privilege โ every agent gets the minimum permissions it needs to do its job and nothing more.
The simplest way to implement this: one YAML policy file per role.
Step 1: Create a policy per role¶
Reader policy (02_reader_policy.yaml)¶
version: "1.0"
name: reader-policy
description: Restrictive policy for read-only agents
rules:
- name: block-delete-database
condition:
field: tool_name
operator: eq
value: delete_database
action: deny
priority: 100
message: "Reader agents cannot delete databases"
- name: block-send-email
condition:
field: tool_name
operator: eq
value: send_email
action: deny
priority: 90
message: "Reader agents cannot send emails"
- name: block-write-file
condition:
field: tool_name
operator: eq
value: write_file
action: deny
priority: 80
message: "Reader agents cannot write files"
defaults:
action: allow
max_tool_calls: 10
Three rules, each blocking a specific tool. Everything else is allowed by default, so search_documents passes through.
Admin policy (02_admin_policy.yaml)¶
version: "1.0"
name: admin-policy
description: Permissive policy for admin agents
rules:
- name: block-delete-database
condition:
field: tool_name
operator: eq
value: delete_database
action: deny
priority: 100
message: "Nobody is allowed to delete databases"
defaults:
action: allow
max_tool_calls: 50
Only one rule โ even admins cannot delete a database. Everything else is allowed. Notice the admin also gets a higher max_tool_calls limit (50 vs 10).
Comparing the two¶
| Action | Reader | Admin |
|---|---|---|
search_documents | allowed | allowed |
send_email | denied | allowed |
write_file | denied | allowed |
delete_database | denied | denied |
Step 2: Evaluate both roles¶
To scope capabilities, you load the correct policy file for the agent's role:
from agent_os.policies import PolicyEvaluator
from agent_os.policies.schema import PolicyDocument
def load_single_policy(filename: str) -> PolicyEvaluator:
"""Create an evaluator loaded with one specific policy file."""
evaluator = PolicyEvaluator()
policy = PolicyDocument.from_yaml(filename)
evaluator.policies.append(policy)
return evaluator
# Each role gets its own evaluator
reader_evaluator = load_single_policy("02_reader_policy.yaml")
admin_evaluator = load_single_policy("02_admin_policy.yaml")
# Same action, different result depending on the role
reader_decision = reader_evaluator.evaluate({"tool_name": "send_email"})
admin_decision = admin_evaluator.evaluate({"tool_name": "send_email"})
print(reader_decision.allowed) # False โ reader can't send email
print(admin_decision.allowed) # True โ admin can
Full example output¶
================================================================
Chapter 2: Capability Scoping
================================================================
Action Reader Admin
-------------------------------------------------------
search_documents โ
allowed โ
allowed
send_email ๐ซ denied โ
allowed
write_file ๐ซ denied โ
allowed
delete_database ๐ซ denied ๐ซ denied
================================================================
Same actions, different permissions per role.
================================================================
Same four actions, but the reader is blocked from three of them while the admin is only blocked from one.
When to use this pattern¶
Capability scoping works well when:
- You have distinct agent roles with clearly different permission levels
- You want to review and audit each role's permissions in a separate file
- You want to add a new role by simply adding a new YAML file
In a real application, your code would pick the right policy file based on the agent's identity โ for example by looking up the agent's role from a configuration or trust credential.
Try it yourself¶
- Create a
moderator-policy.yamlthat can send emails and search, but cannot write files or delete databases. Add it to the script and compare all three roles. - What happens if you load both policy files into the same evaluator? Which rules win? (Hint: think about priority numbers.)
- Change the reader's default from
allowtodeny. Now the reader is blocked from everything except actions you explicitly allow. Add a rule that allowssearch_documents.
What's missing?¶
We now have per-role permissions, but nothing prevents an agent from calling the same tool over and over. A reader agent could fire search_documents a thousand times in a second โ burning tokens, overloading APIs, and running up costs. In production, you need rate limits.
Previous: Chapter 1 โ Your First Policy Next: Chapter 3 โ Rate Limiting โ prevent agents from doing too many things too fast.