Chapter 1: Your First Policy¶
In this chapter you will write a YAML policy file that blocks dangerous agent actions and then evaluate it with a few lines of Python.
What you'll learn:
| Section | Topic |
|---|---|
| What is a policy? | The core idea behind policy-as-code |
| Write the YAML | Create your first policy file |
| Evaluate with Python | Load and test the policy |
| Try it yourself | Exercises to reinforce what you learned |
What is a policy?¶
A policy is a set of rules that decide whether an agent's action is allowed or denied. Think of it like a checklist a security guard follows:
- Agent wants to delete a database โ check the list โ not allowed
- Agent wants to send an email โ check the list โ not allowed
- Agent wants to search documents โ check the list โ nothing says no โ allowed
With policy-as-code, those rules live in a YAML file that anyone can read, review, and version-control โ not buried inside application logic.
Step 1: Write the YAML policy¶
Create a file called 01_first_policy.yaml:
version: "1.0"
name: my-first-policy
description: A simple policy that blocks dangerous agent actions
rules:
- name: block-delete-database
condition:
field: tool_name
operator: eq
value: delete_database
action: deny
priority: 100
message: "Deleting databases is not allowed"
- name: block-send-email
condition:
field: tool_name
operator: eq
value: send_email
action: deny
priority: 90
message: "Sending emails requires approval"
defaults:
action: allow
max_tool_calls: 10
Let's break that down piece by piece.
The header¶
version: "1.0"
name: my-first-policy
description: A simple policy that blocks dangerous agent actions
Every policy needs a version, a name, and a short description. These help you identify the policy later when you have many of them.
A single rule¶
- name: block-delete-database
condition:
field: tool_name
operator: eq
value: delete_database
action: deny
priority: 100
message: "Deleting databases is not allowed"
| Field | Meaning |
|---|---|
name | A human-readable label for this rule |
condition.field | Which piece of context to check โ here, the name of the tool the agent wants to use |
condition.operator | How to compare โ eq means "equals" |
condition.value | The value to compare against |
action | What to do when the condition matches โ deny blocks the action |
priority | Higher numbers are checked first |
message | Explanation shown when the rule triggers |
Reading it out loud: "If the tool name equals delete_database, deny the action."
Defaults¶
If none of the rules match, the default action applies. Here we set it to allow โ meaning anything not explicitly blocked is permitted.
Step 2: Evaluate with Python¶
Now we use PolicyEvaluator to load the YAML file and check agent actions against it.
from agent_os.policies import PolicyEvaluator
from agent_os.policies.schema import PolicyDocument
# 1. Create an evaluator and load a specific policy file
evaluator = PolicyEvaluator()
policy = PolicyDocument.from_yaml("01_first_policy.yaml")
evaluator.policies.append(policy)
# 2. Simulate an agent action โ a dictionary describing what the agent wants to do
context = {"tool_name": "delete_database"}
# 3. Evaluate the context against the loaded policy
decision = evaluator.evaluate(context)
# 4. Check the result
print(decision.allowed) # False
print(decision.reason) # "Deleting databases is not allowed"
The four steps are always the same: create โ load โ evaluate โ act on the decision.
Full example output¶
Run the complete example:
============================================================
Chapter 1: Your First Policy
============================================================
๐ซ Agent tries to delete a database
Tool: delete_database
Result: DENIED
Reason: Deleting databases is not allowed
๐ซ Agent tries to send an email
Tool: send_email
Result: DENIED
Reason: Sending emails requires approval
โ
Agent tries to search documents
Tool: search_documents
Result: ALLOWED
Reason: No rules matched; default action applied
============================================================
Done. 3 actions evaluated.
============================================================
Notice how search_documents was allowed โ no rule mentions it, so the default allow action kicks in.
How does it work?¶
Here is what happens inside evaluator.evaluate(context):
context = {"tool_name": "search_documents"}
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Rule: block-delete-database โ
โ tool_name == delete_database? โโโโถ No, skip
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Rule: block-send-email โ
โ tool_name == send_email? โโโโถ No, skip
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
No rule matched
โ
โผ
Apply default โ ALLOW โ
Rules are checked in priority order (highest first). The first rule that matches determines the decision. If nothing matches, the default action applies.
Try it yourself¶
- Add a new rule that blocks
restart_serverand re-run the example. - Change the default from
allowtodeny. What happens tosearch_documentsnow? - Add a rule that explicitly allows
search_documentswith actionallowโ does it still work when the default isdeny?
What's missing?¶
This policy works, but it's not production-ready yet. Right now every agent shares the same rules โ a read-only assistant and a powerful admin are treated identically. In the next chapter we fix that by giving each role its own policy.
Next: Chapter 2 โ Capability Scoping โ give different agents different permissions.