Skip to main content

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)

Source distribution for openverb-fellowship 0.1.0
File Size Uploaded
openverb_fellowship-0.1.0.tar.gz 20.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for openverb-fellowship 0.1.0
File Interpreter ABI Platform
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

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page