Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Agentic OpenAI Starter

A typed Python foundation for a public library of versioned prompts, reusable Pydantic AI tools, and observable OpenAI agents. Every agent created by the included factory is instrumented with Pydantic Logfire, so agent runs, model requests, and tool calls appear in one trace.

Repository layout

agentic-openai-starter/
├── agents/
│   ├── __init__.py
│   ├── base.py                  # Agent factory and Logfire lifecycle
│   └── py.typed
├── prompts/
│   ├── catalog/
│   │   └── browser-researcher/
│   │       └── 1.0.0.md        # Immutable system-prompt version
│   ├── __init__.py
│   ├── registry.py              # Safe lookup, SemVer selection, SHA-256 identity
│   └── py.typed
├── skills/
│   ├── __init__.py
│   ├── browser.py               # Typed async Pydantic AI tool
│   └── py.typed
├── tests/
│   ├── test_agent.py
│   ├── test_browser.py
│   ├── test_prompt_registry.py
│   └── test_runtime.py
├── .env.example
├── .gitignore
├── LICENSE
├── main.py                      # Async CLI entrypoint
├── pyproject.toml
└── uv.lock

The boundaries are intentional: prompt content is data, skills are reusable functions with injected dependencies, agent modules compose prompts and skills, and entrypoints own resource lifetimes. Adding dozens of agents does not require a global agent registry or import-time network clients.

Bootstrap and run

Install uv first, then run these commands from the copied repository directory:

git init -b main
uv sync --all-groups

cp .env.example .env.local
chmod 600 .env.local
read -rsp "OpenAI API key: " OPENAI_API_KEY; echo
export OPENAI_API_KEY
python3 - <<'PY'
import os
from pathlib import Path

path = Path(".env.local")
lines = [
    line
    for line in path.read_text(encoding="utf-8").splitlines()
    if not line.startswith("OPENAI_API_KEY=")
]
lines.insert(0, f"OPENAI_API_KEY={os.environ['OPENAI_API_KEY']}")
path.write_text("\n".join(lines) + "\n", encoding="utf-8")
PY
unset OPENAI_API_KEY

uv run logfire auth
uv run logfire projects new agentic-openai-starter

uv run python main.py

The Logfire commands open a browser for authentication and create the local .logfire/ credentials used by logfire.configure(). Both .logfire/ and .env.local are ignored by Git. If your organization already has the project, select it instead:

uv run logfire projects use agentic-openai-starter --org your-organization

The default request tells the model to open https://example.com. A successful run prints the answer in the terminal and produces one Logfire trace containing the named browser_research_agent run, OpenAI model request, and browse_url tool call. Open the project URL shown by:

uv run logfire whoami

To run a different request, pass it as one quoted argument. Add every destination host to BROWSER_ALLOWED_HOSTS in .env.local before using it:

uv run python main.py "Open https://example.com and summarize the page in one sentence."

Configuration

Variable Required Default Purpose
OPENAI_API_KEY yes none OpenAI project API key; never logged or committed
AGENT_MODEL no openai:gpt-5.6-luna Pydantic AI provider-qualified model name
APP_ENVIRONMENT no development Logfire environment attribute
LOGFIRE_SERVICE_NAME no agentic-openai-starter Service name shown in Logfire
BROWSER_ALLOWED_HOSTS no example.com,www.example.com Comma-separated exact HTTPS host allowlist

The browser skill does not follow redirects, accept embedded URL credentials, use nonstandard ports, or read non-text responses. It stops after 100 KB. Those defaults reduce SSRF and unbounded-context risk; extend them deliberately rather than disabling them globally.

Add or version a prompt

Prompt identity is the directory name plus a MAJOR.MINOR.PATCH Markdown filename:

prompts/catalog/release-notes/1.0.0.md
prompts/catalog/release-notes/1.1.0.md

Use lowercase kebab-case for the name. Never edit a released prompt file in place: add a new version. PromptRegistry.load("release-notes") selects 1.1.0; PromptRegistry.load("release-notes", "1.0.0") pins the old behavior. Every resolved prompt carries a SHA-256 digest into the agent metadata, making a trace attributable to exact prompt content.

Create an agent with the new prompt by calling the existing factory:

from pydantic_ai import Agent

from agents.base import build_agent
from skills.browser import BrowserSession

release_notes_agent: Agent[BrowserSession, str] = build_agent(
    prompt_name="release-notes",
    prompt_version="1.1.0",
)

For a distinct production agent, add another factory under agents/, call configure_observability() before constructing it, and give it a unique Pydantic AI name so Logfire traces remain searchable.

Add a skill

A skill is a typed function. Use RunContext[DependencyType] when it needs clients, configuration, identity, or other runtime resources; keep construction and cleanup in the entrypoint. Pydantic AI derives the JSON tool schema from the function signature and docstring.

from dataclasses import dataclass

from pydantic_ai import RunContext


@dataclass(frozen=True, slots=True)
class AccountDependencies:
    account_id: str


async def get_account_id(ctx: RunContext[AccountDependencies]) -> str:
    """Return the authenticated account identifier."""
    return ctx.deps.account_id

Export the function from skills/__init__.py, register it in the target agent's tools=[...], and add tests that invoke its real dependency boundary. Avoid module-level clients: injected dependencies make tools async-safe, testable, and reusable across agents.

Quality gates

Run the complete local gate before opening a pull request:

uv lock --check
uv run ruff check .
uv run ruff format --check .
uv run mypy
uv run pytest -q
uv build

Logfire's Pydantic AI integration records prompt and completion content by default. Review the Logfire integration and content controls before sending sensitive data. OpenAI model names and availability change over time; the current catalog is documented in the OpenAI model guide.

License

MIT. See LICENSE.

About

Typed, observable Pydantic AI agents with versioned prompts, reusable tools, OpenAI, and Logfire.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages