How to Use ToolPolicyContext for Policy Decisions in AISuite
ToolPolicyContext is the data structure that AISuite passes to every tool policy before executing a tool call, exposing fields like tool_name, tool_arguments, and agent_state so your policy can return a boolean or a ToolPolicyDecision to allow or deny the operation.
When building AI agents with AISuite, controlling which tools the LLM can invoke is critical for security and compliance. The framework provides a robust policy evaluation mechanism centered around the ToolPolicyContext class defined in aisuite/agents/types.py, which aggregates all relevant metadata about an impending tool execution. By leveraging this context object, developers can implement fine-grained access controls that inspect everything from the tool's arguments to the current conversation state.
What Is ToolPolicyContext?
In the AISuite architecture, ToolPolicyContext serves as the single source of truth for policy decisions. According to the type definitions in aisuite/agents/types.py, this dataclass encapsulates the complete execution context available to a policy at decision time.
The context exposes the following fields:
- tool_name: The identifier of the tool being invoked.
- tool_metadata: Static configuration from the tool definition, including description, parameters, and custom policy tags.
- tool_arguments: The actual arguments generated by the LLM for this specific call.
- agent_state: A snapshot of the current agent context, including conversation history and variables.
- request_id: A unique identifier for the LLM request that triggered the tool call.
- user_id: An optional identifier for the end-user, enabling multi-tenant policy enforcement.
How Policy Evaluation Works
When a tool call is imminent, AISuite constructs a ToolPolicyContext instance and routes it through the internal _evaluate_tool_policy function in aisuite/utils/tools.py. As implemented in andrewyng/aisuite, this evaluation follows a strict precedence:
- No policy configured: The tool executes immediately without checks.
- Callable policy: The function receives the context and must return a
bool(True allows, False denies). - Object-based policy: The object’s
evaluate(self, context: ToolPolicyContext)method is called, which may return either a boolean or aToolPolicyDecisionobject.
If the policy denies the call, AISuite inserts the error message "Tool call denied by policy" into the LLM response instead of executing the tool. Every decision is recorded as a policy event in last_policy_events, enabling downstream tracing and audit trails.
Implementing Tool Policies with ToolPolicyContext
Simple Callable Policies
The fastest way to restrict tool access is a plain function that inspects the tool_name field. This approach works well for allowlists or simple denials.
import aisuite as ai
def allow_only_read_file(context: ai.ToolPolicyContext) -> bool:
"""Allow only the read_file tool; reject all others."""
return context.tool_name == "read_file"
# Apply when creating a runner
runner = ai.AgentRunner(
agent=my_agent,
tool_policy=allow_only_read_file,
)
Rich Decisions with ToolPolicyDecision
For policies requiring explanatory messages or complex logic, implement a class that returns ToolPolicyDecision. This is defined alongside ToolPolicyContext in aisuite/agents/types.py.
import aisuite as ai
class ApprovalPolicy:
"""Requires human approval for tools tagged as sensitive."""
def __init__(self, callback):
self.callback = callback
def evaluate(self, context: ai.ToolPolicyContext) -> ai.ToolPolicyDecision:
# Check metadata for policy tags
if context.tool_metadata.get("policy") == "requires_approval":
approved = self.callback(context)
return ai.ToolPolicyDecision(
allowed=approved,
reason="approved by user" if approved else "approval denied"
)
return ai.ToolPolicyDecision(allowed=True)
# Usage
client = ai.Client(tool_policy=ApprovalPolicy(my_approval_fn))
Inspecting Agent State and Arguments
Sophisticated policies can enforce role-based access control by examining agent_state or validate inputs via tool_arguments.
import aisuite as ai
def admin_only_shell(context: ai.ToolPolicyContext) -> bool:
"""Block shell commands unless the user has admin role."""
if context.tool_name == "exec_shell":
user_role = context.agent_state.get("user_role", "guest")
return user_role == "admin"
return True
client = ai.Client(tool_policy=admin_only_shell)
Core Implementation Files
Understanding the source layout helps when debugging policy behavior or extending the framework:
aisuite/agents/types.py: DefinesToolPolicyContext,ToolPolicyDecision, and the policy protocol interfaces.aisuite/agents/policies.py: Contains concrete policy implementations likeAllowToolsPolicyandRequireApprovalPolicy.aisuite/utils/tools.py: Houses the_evaluate_tool_policyfunction and the logic for recording policy events.examples/cli/create_demo_trace.py: Demonstrates a demo policy that denies shell commands for safety testing.tests/agents/test_tool_policy.py: Validates the evaluation flow and context passing.tests/agents/test_tool_metadata_policy.py: Verifies that tool metadata is correctly propagated into the policy context.
Summary
ToolPolicyContextaggregates tool metadata, arguments, agent state, and request IDs into a single object passed to every policy check.- Policies can be simple callables returning booleans or class-based implementations returning
ToolPolicyDecisionobjects with explicit reasons. - The evaluation logic in
aisuite/utils/tools.pyautomatically records all decisions as policy events for auditing. - When denied, AISuite returns the specific error message
"Tool call denied by policy"to the LLM instead of executing the tool. - Access
agent_statefor conversation-aware policies andtool_metadatafor configuration-driven rules.
Frequently Asked Questions
What fields does ToolPolicyContext expose?
ToolPolicyContext provides tool_name, tool_metadata, tool_arguments, agent_state, request_id, and optional user_id. These fields are defined in aisuite/agents/types.py and populated by the framework before each policy check.
Can a policy return a custom denial message?
Yes. Instead of returning a boolean, return a ToolPolicyDecision object with allowed=False and a custom reason string. AISuite includes this reason in the last_policy_events log, though the LLM receives the standard "Tool call denied by policy" message.
How do I access the conversation history inside a policy?
Inspect the agent_state field of the ToolPolicyContext. This dictionary contains the current agent context, including conversation history and any variables set by the agent, allowing you to make decisions based on the full interaction state.
Where are policy decisions logged?
Each evaluation result is stored as a policy event in last_policy_events, accessible on the runner or client instance. This audit trail captures the decision, timestamp, and reason, which is essential for debugging and compliance monitoring.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →