Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions examples/anthropic_chat_demo/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Required: Anthropic API key for Claude.
ANTHROPIC_API_KEY=

# Optional: defaults shown.
AGENT_CONTROL_URL=http://localhost:8000
ANTHROPIC_MODEL=claude-sonnet-4-6
32 changes: 32 additions & 0 deletions examples/anthropic_chat_demo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Anthropic Chat Demo — UI-driven policy

Six prompts → Claude Sonnet, wrapped with `@control`. The agent is
initialized in code; the guardrail **policy is created in the UI** and
bound to the agent. Re-run the script to see your policy take effect.

## Prereqs

- Agent Control server + UI running (e.g. `docker compose up -d` from repo root)
- `ANTHROPIC_API_KEY`

## Run

```bash
cd examples/anthropic_chat_demo
cp .env.example .env # then edit .env and set ANTHROPIC_API_KEY
uv run python demo.py
```

The first run registers an agent named **`anthropic-chat-demo`** with the
server.

## Create a policy in the UI

1. Open http://localhost:4000
2. Find the `anthropic-chat-demo` agent
3. Create a control / policy (e.g. block PII in inputs or outputs) and bind it
4. Re-run `uv run python demo.py` — prompts that violate the policy are
blocked at the `@control` boundary; clean ones still reach Claude

The fourth prompt deliberately contains a fake SSN to exercise a PII rule
once you've configured one.
74 changes: 74 additions & 0 deletions examples/anthropic_chat_demo/demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
"""Six prompts → Claude Sonnet, wrapped with @control.

The agent is initialized here in code. The guardrail policy is created in
the UI (http://localhost:4000) and bound to this agent — re-run after
editing the policy to see it take effect, no code changes.
"""

import os
import sys

from anthropic import Anthropic
from dotenv import load_dotenv

import agent_control
from agent_control import ControlViolationError, control

load_dotenv()

AGENT_NAME = "anthropic-chat-demo"
SERVER_URL = os.getenv("AGENT_CONTROL_URL", "http://localhost:8000")
MODEL = os.getenv("ANTHROPIC_MODEL", "claude-sonnet-4-6")

QUESTIONS = [
"What is the capital of France?",
"Summarize the plot of Hamlet in two sentences.",
"Explain quantum entanglement to a 10-year-old.",
"My SSN is 123-45-6789 — can you verify it?",
"Write a haiku about distributed systems.",
"What's a good way to keep API keys safe in a Python project?",
]


@control()
def ask_claude(prompt: str) -> str:
"""Send a single prompt to Claude. @control evaluates input and output
against whatever policy is bound to AGENT_NAME on the server."""
client = Anthropic()
msg = client.messages.create(
model=MODEL,
max_tokens=512,
messages=[{"role": "user", "content": prompt}],
)
return msg.content[0].text


def main() -> int:
if not os.getenv("ANTHROPIC_API_KEY"):
print(
"ANTHROPIC_API_KEY not set. Copy .env.example to .env and fill it in.",
file=sys.stderr,
)
return 1

agent_control.init(
agent_name=AGENT_NAME,
agent_description="Six-prompt chat demo against Claude Sonnet",
server_url=SERVER_URL,
observability_enabled=True,
)

for i, question in enumerate(QUESTIONS, 1):
print(f"\n[{i}/{len(QUESTIONS)}] Q: {question}")
try:
answer = ask_claude(question)
preview = answer if len(answer) <= 400 else answer[:400] + "…"
print(f" A: {preview}")
except ControlViolationError as e:
print(f" 🚫 BLOCKED by control '{e.control_name}': {e.message}")

return 0


if __name__ == "__main__":
sys.exit(main())
28 changes: 28 additions & 0 deletions examples/anthropic_chat_demo/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[project]
name = "agent-control-anthropic-chat-demo"
version = "0.1.0"
description = "Six-prompt chat demo against Claude Sonnet, wrapped with @control. Policy is created in the UI."
requires-python = ">=3.12"
dependencies = [
"agent-control-engine",
"agent-control-models",
"agent-control-evaluators",
"agent-control-sdk",
"agent-control-telemetry",
"anthropic>=0.40.0",
"python-dotenv>=1.0.0",
]

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.hatch.build.targets.wheel]
include = ["*.py", ".env.example"]

[tool.uv.sources]
agent-control-sdk = { path = "../../sdks/python", editable = true }
agent-control-models = { path = "../../models", editable = true }
agent-control-engine = { path = "../../engine", editable = true }
agent-control-evaluators = { path = "../../evaluators/builtin", editable = true }
agent-control-telemetry = { path = "../../telemetry", editable = true }
Loading