openverb-fellowship
The Fellowship pattern for Python AI agents.
Register, execute, and audit verbs with zero friction.
Built on the openverb core protocol.
pip install openverb-fellowship
"In clarity, they found control. In control, they found scale. In scale, they found order."
— The Fellowship of the Verbs
The Problem
AI systems hide their capabilities. Actions are scattered across prompts, routes, and handlers.
A developer asks: "What can my AI actually do?" — and there's no clear answer.
The Solution
Declare every action as a verb. Named. Structured. Visible. Controllable.
from openverb_fellowship import Fellowship
fellowship = Fellowship("my_app")
@fellowship.verb(
name="job.create",
description="Create a new job record",
params={
"title": {"type": "string", "description": "Job title", "required": True},
"client_id": {"type": "string", "description": "Client ID", "required": True},
},
returns={"id": {"type": "string", "description": "New job ID"}},
)
def create_job(params):
job = db.jobs.create(title=params["title"], client_id=params["client_id"])
return {"verb": "job.create", "status": "success", "data": {"id": job.id}}
# See everything your AI can do
fellowship.list_verbs()
# ["job.create"]
# Execute with validation
result = fellowship.execute({"verb": "job.create", "params": {"title": "Fix roof", "client_id": "c1"}})
# {"verb": "job.create", "status": "success", "data": {"id": "job_123"}}
Quick Start
1. Define your Fellowship
from openverb_fellowship import Fellowship, FellowshipExecutor, ai_safe_policy
fellowship = Fellowship("tradie_app", description="Job management AI verbs")
@fellowship.verb(
name="job.create",
description="Create a new job",
params={
"title": {"type": "string", "description": "Job title", "required": True},
"client_id": {"type": "string", "description": "Client ID", "required": True},
},
returns={"id": {"type": "string", "description": "New job ID"}},
category="jobs",
)
def create_job(params):
return {"verb": "job.create", "status": "success", "data": {"id": "job_abc"}}
@fellowship.verb(
name="invoice.generate",
description="Generate a PDF invoice for a job",
params={"job_id": {"type": "string", "description": "Job ID", "required": True}},
returns={"url": {"type": "string", "description": "PDF download URL"}},
category="invoices",
)
def generate_invoice(params):
url = pdf_service.generate(params["job_id"])
return {"verb": "invoice.generate", "status": "success", "data": {"url": url}}
2. Execute with policy
executor = FellowshipExecutor(fellowship, policy=ai_safe_policy())
result = executor.execute({
"verb": "job.create",
"params": {"title": "Fix roof", "client_id": "c1"}
})
# {"verb": "job.create", "status": "success", "data": {"id": "job_abc"}, "duration_ms": 2.1, ...}
3. Connect to your AI model
from openverb_fellowship import generate_system_prompt, parse_ai_response, format_receipt
# Generate the system prompt
system_prompt = generate_system_prompt(fellowship)
# Call your model
response = openai.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": "Create a job to fix the roof for client C1"}
]
)
# Parse and execute
parsed = parse_ai_response(response.choices[0].message.content)
if parsed["success"]:
receipt = executor.execute_batch(parsed["actions"])
print(format_receipt(receipt))
The @verb Decorator
@fellowship.verb(
name="user.notify", # required — dot-separated namespace.action
description="...", # required — what it does
params={ # optional — input schema
"user_id": {"type": "string", "required": True, "description": "..."},
"message": {"type": "string", "required": True, "description": "..."},
},
returns={ # optional — output schema
"sent": {"type": "boolean", "description": "Whether the notification was sent"},
},
destructive=False, # marks the verb as dangerous in prompts/policy
requires_confirmation=False, # marks the verb as needing explicit user approval
category="users", # optional — groups verbs in prompts
)
def notify_user(params):
# params is a dict of the declared params
# return an ActionResult dict
return {"verb": "user.notify", "status": "success", "data": {"sent": True}}
Programmatic registration (no decorator)
fellowship.register(
"map.geocode",
handler=geocode_address,
description="Geocode a street address to coordinates",
params={
"address": {"type": "string", "required": True, "description": "Street address"},
},
returns={
"lat": {"type": "number", "description": "Latitude"},
"lng": {"type": "number", "description": "Longitude"},
},
)
Executor
from openverb_fellowship import FellowshipExecutor, format_receipt
executor = FellowshipExecutor(
fellowship,
policy=ai_safe_policy(), # policy config
dry_run=False, # if True, skips all execution
stop_on_failure=False, # if True, halts batch on first failure
on_before=lambda action: log.info(f"Executing {action['verb']}"),
on_after=lambda action, result: audit_db.write(result),
)
# Single action
result = executor.execute({"verb": "job.create", "params": {"title": "T", "client_id": "c1"}})
# Batch
receipt = executor.execute_batch([
{"verb": "job.create", "params": {"title": "Job A", "client_id": "c1"}},
{"verb": "invoice.generate", "params": {"job_id": "job_abc"}},
])
print(format_receipt(receipt))
# Fellowship Receipt — 2026-04-26T...
# Status: ✓ SUCCESS
# Summary: 2 succeeded
# Executed:
# ✓ job.create (1.2ms)
# ✓ invoice.generate (3.4ms)
# Total: 4.6ms
Policy
Control exactly what AI is allowed to do.
from openverb_fellowship import ai_safe_policy, read_only_policy, deny_list_policy, create_policy
# Built-in presets
ai_safe_policy() # requires_confirmation for destructive verbs
permissive_policy() # allow everything (trusted envs only)
read_only_policy(allowed_verbs={"job.list", "job.get"})
deny_list_policy(["db.reset", "user.delete"], reason="Disabled in this environment")
# Fluent builder
policy = (
create_policy()
.deny("db.reset", "Requires manual intervention")
.require_confirmation("invoice.generate", "Invoices need approval")
.allow("job.list")
.build()
)
Policy decisions: "allow" | "deny" | "require_confirmation"
When require_confirmation fires, the result has skipped=True and skip_reason — your UI can surface this to the user before retrying.
Prompt Generator
from openverb_fellowship import generate_system_prompt, to_tool_call_schemas
# Full system prompt (markdown, all verbs)
prompt = generate_system_prompt(fellowship)
# Category filter
prompt = generate_system_prompt(fellowship, categories=["jobs"])
# XML style (recommended for Claude)
prompt = generate_system_prompt(fellowship, style="xml")
# Compact — verb names only, no schema (for token efficiency)
prompt = generate_system_prompt(fellowship, include_schema=False, include_example=False, style="plain")
# OpenAI / Anthropic tool-call schemas
tools = to_tool_call_schemas(fellowship)
tools = to_tool_call_schemas(fellowship, verb_names=["job.create", "invoice.generate"])
Inspection
fellowship.list_verbs()
# ["job.create", "job.delete", "job.list", "invoice.generate"]
fellowship.get_verb("job.create")
# {"name": "job.create", "description": "...", "params": {...}, ...}
fellowship.get_verbs_by_category("jobs")
fellowship.get_destructive_verbs()
fellowship.get_verbs_requiring_confirmation()
print(fellowship.describe())
# Fellowship: tradie_app
# Verbs: 4
# job.create
# Create a new job
# job.delete [destructive, requires-confirmation]
# Delete a job permanently
# ...
# Export the VerbLibrary as JSON (compatible with all openverb tools)
print(fellowship.to_json())
# Use with core openverb utilities directly
from openverb import build_registry, validate_action
registry = build_registry(fellowship.library)
Loading an External Library
# Load from an existing openverb.*.json file
fellowship.load_library("openverb.ide.json")
# Then register handlers for the loaded verbs
fellowship.add_handler("create_file", my_create_file_fn)
Architecture
openverb-fellowship
├── registry.py — Fellowship class, @verb decorator, VerbLibrary construction
├── executor.py — FellowshipExecutor: validate → policy → execute → receipt
├── policy.py — allow/deny/require_confirmation rules + fluent builder
└── prompt.py — system prompt generator + tool-call schema export
Core dependency: openverb (load_library, build_registry, validate_action, create_executor)
The Fellowship produces a valid VerbLibrary dict that works with all openverb core utilities.
All types (Verb, Action, ActionResult, VerbHandler) come from the core — nothing is duplicated.
License
MIT — © 2026 OpenVerb Contributors
openverb.org/the-fellowship-of-the-verbs
Metadata
Release files for openverb-fellowship 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| openverb_fellowship-0.1.0.tar.gz | 20.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| openverb_fellowship-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 37.6 kB
Release files / openverb_fellowship-0.1.0.tar.gz
| Download URL | openverb_fellowship-0.1.0.tar.gz |
|---|---|
| Size | 20.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4cb921cd6090d8e53c8d2f1c07002b0a53ff1298deac03e9ed243263e39b8fba
|
|
BLAKE2b-256 checksum How to use checksums |
597c3709dd88b089ed2205b94e25f06a9347edb45853c2e31099db7ffb383f3c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.3
|
Release files / openverb_fellowship-0.1.0-py3-none-any.whl
| Download URL | openverb_fellowship-0.1.0-py3-none-any.whl |
|---|---|
| Size | 17.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
006e6887c5972aabd48a3414eea9c9a68f8fef8ecc0c6ba0d4fdf4dd652ea206
|
|
BLAKE2b-256 checksum How to use checksums |
005e4fbb2640ec8a38491a69d4cc482f3ec28a9dbd20ffd5455d86e6d0d69921
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.3
|