mirror of
https://github.com/pydantic/pydantic-ai-harness.git
synced 2026-07-21 02:45:34 +00:00
Guide agents building harness capabilities (#247)
This commit is contained in:
@@ -1,43 +1,63 @@
|
||||
# Pydantic AI Harness
|
||||
|
||||
## Local setup
|
||||
## Repository purpose
|
||||
|
||||
On first session, if `CLAUDE.local.md` does not exist, create it with the following content:
|
||||
`pydantic-ai-harness` is the first-party capability library for Pydantic AI.
|
||||
|
||||
```markdown
|
||||
This file is checked into the repository but ignored, any changes made stay local.
|
||||
Pydantic AI core owns the primitive runtime: agent loop semantics, normalized
|
||||
messages, model/provider/profile behavior, tool execution semantics, durable
|
||||
execution primitives, and generic capability hooks.
|
||||
|
||||
Use this file only to persist information about the specific workstation you are used on.
|
||||
Harness owns optional, batteries-included compositions built from those
|
||||
primitives: coding-agent tools, guardrails, memory, context management, repo
|
||||
tools, verification loops, skills, planning, sub-agents, and other reusable
|
||||
agent behaviors.
|
||||
|
||||
Capabilities branch is checked out at: <absolute path to the capabilities branch checkout on this system>
|
||||
```
|
||||
|
||||
Fill in the absolute path where the `capabilities` branch of `pydantic-ai` is checked out on your system. If unknown, ask the user.
|
||||
When a change needs new core semantics, stop and propose the Pydantic AI core
|
||||
change instead of reimplementing core behavior in harness.
|
||||
|
||||
## Vocabulary
|
||||
|
||||
- **Capability**: an `AbstractCapability` subclass that bundles tools, hooks, instructions, and model settings into a reusable unit. This is the core abstraction of pydantic-ai-harness
|
||||
- **Capability**: an `AbstractCapability` subclass that bundles tools, hooks, instructions, and model settings into a reusable unit. This is the core abstraction of pydantic-ai-harness.
|
||||
- **Hook**: a lifecycle method on `AbstractCapability` that intercepts agent graph execution (e.g. `before_model_request`, `wrap_run`, `after_tool_execute`)
|
||||
- **Toolset**: a collection of tools that a capability can provide to the agent
|
||||
- **Guard**: a type of capability that validates inputs/outputs or controls tool access (e.g. `InputGuardrail`, `CostGuard`)
|
||||
- **Harness**: this package -- a collection of pre-made capabilities for pydantic-ai
|
||||
- **Harness**: this package -- a collection of pre-made capabilities for Pydantic AI.
|
||||
- **AICA**: AI Code Assistant -- the automated agent that implements issues, reviews plans, and handles PR feedback
|
||||
- **Ralph loop**: the state-machine-based workflow that drives AICA through phases (TRIAGE -> GOALS -> PLAN -> CODE -> VERIFY -> REVIEW -> PUBLISH)
|
||||
- **DDD+ protocol**: classification system for PR review comments (do, dismiss, discuss, waiting, done)
|
||||
|
||||
## AICA preflight
|
||||
|
||||
Before implementing or reviewing a capability change:
|
||||
|
||||
1. Read `agent_docs/index.md`.
|
||||
2. Read the linked `agent_docs/` guide for the task.
|
||||
3. Read the public Pydantic AI docs for every integration point you touch:
|
||||
- capabilities: <https://pydantic.dev/docs/ai/core-concepts/capabilities/>
|
||||
- hooks: <https://pydantic.dev/docs/ai/core-concepts/hooks/>
|
||||
- toolsets: <https://pydantic.dev/docs/ai/tools-toolsets/toolsets/>
|
||||
- advanced tools: <https://pydantic.dev/docs/ai/tools-toolsets/tools-advanced/>
|
||||
- agents: <https://pydantic.dev/docs/ai/core-concepts/agent/>
|
||||
- testing: <https://pydantic.dev/docs/ai/guides/testing/>
|
||||
4. Inspect the installed `pydantic_ai` package source for exact hook/toolset
|
||||
signatures when needed. Do not assume a contributor's local checkout layout.
|
||||
5. Use `pydantic_ai_harness.code_mode` as the exemplar for capability shape,
|
||||
docs, tests, and public exports until another capability becomes a better
|
||||
example.
|
||||
|
||||
## Capabilities API reference
|
||||
|
||||
When implementing a new capability, reference these docs in the pydantic-ai repo (path in `CLAUDE.local.md`):
|
||||
When implementing a new capability, reference these docs:
|
||||
|
||||
- `docs/capabilities.md` -- main capabilities documentation, usage patterns, built-in capabilities
|
||||
- `docs/hooks.md` -- lifecycle hooks reference, hook ordering, all hook categories
|
||||
- `docs/extensibility.md` -- publishing capabilities as packages, spec serialization
|
||||
- `docs/toolsets.md` -- toolset abstraction, building tools for capabilities
|
||||
- `docs/tools-advanced.md` -- tool hooks, prepare_tools, tool validation
|
||||
- `docs/agent.md` -- agent configuration, instructions, model settings
|
||||
- `pydantic_ai_slim/pydantic_ai/capabilities/abstract.py` -- the `AbstractCapability` base class (all hook methods)
|
||||
- `pydantic_ai_slim/pydantic_ai/capabilities/hooks.py` -- decorator-based `Hooks` capability
|
||||
- `pydantic_ai_slim/pydantic_ai/capabilities/combined.py` -- `CombinedCapability` for composition
|
||||
- <https://pydantic.dev/docs/ai/core-concepts/capabilities/> -- main capabilities documentation, usage patterns, built-in capabilities
|
||||
- <https://pydantic.dev/docs/ai/core-concepts/hooks/> -- lifecycle hooks reference, hook ordering, all hook categories
|
||||
- <https://pydantic.dev/docs/ai/guides/extensibility/> -- publishing capabilities as packages, spec serialization
|
||||
- <https://pydantic.dev/docs/ai/tools-toolsets/toolsets/> -- toolset abstraction, building tools for capabilities
|
||||
- <https://pydantic.dev/docs/ai/tools-toolsets/tools-advanced/> -- tool hooks, prepare tools, tool validation
|
||||
- <https://pydantic.dev/docs/ai/core-concepts/agent/> -- agent configuration, instructions, model settings
|
||||
- Installed `pydantic_ai.capabilities` source -- `AbstractCapability`, hook signatures, and composition behavior
|
||||
- Installed `pydantic_ai.toolsets` source -- `AbstractToolset`, `WrapperToolset`, and `ToolsetTool`
|
||||
|
||||
## Coding standards
|
||||
|
||||
@@ -84,12 +104,19 @@ tests/
|
||||
test_<capability>.py
|
||||
```
|
||||
|
||||
Do not add placeholder template files for new capabilities. Start from the
|
||||
existing `CodeMode` package shape, then delete what the new capability does not
|
||||
need.
|
||||
|
||||
## Testing patterns
|
||||
|
||||
- Use `pydantic_ai.models.TestModel` for all tests (no real API calls)
|
||||
- `ALLOW_MODEL_REQUESTS = False` is set globally in `conftest.py`
|
||||
- Tests use `pytest-anyio` for async support
|
||||
- Each capability test class follows: `TestCapabilityName` with methods `test_<scenario>`
|
||||
- Prefer tests through `Agent(..., capabilities=[...])` when that is the public
|
||||
behavior. Use direct `Toolset`/`RunContext` tests for lower-level lifecycle,
|
||||
schema, retry, or wrapper behavior that is hard to isolate through `Agent`.
|
||||
|
||||
## Contributing rules for AICAs
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
# Capability Authoring
|
||||
|
||||
Harness capabilities should be small, composable batteries built on Pydantic AI
|
||||
primitives.
|
||||
|
||||
## Choose The Abstraction
|
||||
|
||||
- Use `AbstractCapability` when the feature contributes instructions, model
|
||||
settings, toolsets, native tools, or lifecycle hooks.
|
||||
- Use a `WrapperToolset` when the feature changes how an existing toolset is
|
||||
presented or called.
|
||||
- Use a leaf `AbstractToolset` when the feature owns a new collection of tools.
|
||||
- Use hooks when behavior belongs at a specific point in the agent lifecycle.
|
||||
- Use capability ordering only when composition semantics require it. Keep the
|
||||
reason visible in the code or docstring.
|
||||
|
||||
If the feature changes provider wire behavior, normalized message structure,
|
||||
tool execution semantics, output selection, or durable execution primitives, it
|
||||
probably belongs in Pydantic AI core first.
|
||||
|
||||
## Public Shape
|
||||
|
||||
Each capability package should normally have:
|
||||
|
||||
- `__init__.py` with public exports
|
||||
- `_capability.py` for the public capability class
|
||||
- `_toolset.py` only if the capability needs toolset behavior
|
||||
- `README.md` with focused usage docs
|
||||
- mirrored tests under `tests/<capability>/`
|
||||
|
||||
The root `pydantic_ai_harness/__init__.py` should re-export stable public
|
||||
capabilities. Keep implementation helpers private unless users need them.
|
||||
|
||||
## API Design
|
||||
|
||||
- Prefer a small dataclass capability with typed fields.
|
||||
- Name fields by the user concept, not the implementation mechanism.
|
||||
- Accept the most generic useful input types.
|
||||
- Avoid `Any` in new public signatures.
|
||||
- Avoid casts. Fix the type shape instead.
|
||||
- Keep defaults conservative and easy to explain.
|
||||
- Do not add package dependencies without a clear issue and package-manager
|
||||
command.
|
||||
|
||||
## Composition Checks
|
||||
|
||||
Before treating a capability as done, check how it composes with:
|
||||
|
||||
- other capabilities in the same `Agent(..., capabilities=[...])`
|
||||
- toolsets and wrapper toolsets
|
||||
- `ToolSearch`
|
||||
- deferred tools and approval flows
|
||||
- provider-native versus local fallback tools
|
||||
- streaming/event behavior when the capability emits or wraps events
|
||||
- durable execution when the capability affects tool calls, context,
|
||||
serialization, retries, or lifecycle ordering
|
||||
|
||||
`CodeMode` is a useful reference for wrapper-toolset composition, tool
|
||||
selection, `ToolSearch` interaction, public docs, and test depth.
|
||||
|
||||
## Docs
|
||||
|
||||
Each user-facing capability needs docs close to the code. Explain:
|
||||
|
||||
- what problem it solves
|
||||
- minimal usage
|
||||
- key options
|
||||
- how it composes with relevant Pydantic AI features
|
||||
- important safety or execution constraints
|
||||
|
||||
Keep examples runnable with the declared extras.
|
||||
@@ -0,0 +1,43 @@
|
||||
# Core Boundary
|
||||
|
||||
Harness should extend Pydantic AI through public primitives. It should not
|
||||
quietly create a second runtime.
|
||||
|
||||
## Belongs In Pydantic AI Core
|
||||
|
||||
Start in Pydantic AI core when the change needs:
|
||||
|
||||
- new agent loop semantics
|
||||
- new normalized message parts or message-history behavior
|
||||
- provider API compatibility or provider wire mapping
|
||||
- model/profile/provider capability facts
|
||||
- generic tool execution semantics
|
||||
- output-mode semantics
|
||||
- durable execution primitives
|
||||
- generic capability hooks or hook ordering changes
|
||||
- MCP protocol behavior that should apply to all users
|
||||
|
||||
Harness can depend on these once core exposes the right primitive.
|
||||
|
||||
## Belongs In Harness
|
||||
|
||||
Harness is the right home for:
|
||||
|
||||
- reusable capability compositions
|
||||
- coding-agent tools and repo workflows
|
||||
- guardrails built on hooks
|
||||
- memory and persistence policies
|
||||
- context management policies
|
||||
- tool-output handling policies
|
||||
- planning, skills, task tracking, and sub-agent compositions
|
||||
- opinionated defaults that are useful but not fundamental to every Pydantic AI
|
||||
user
|
||||
|
||||
## Decision Rule
|
||||
|
||||
If a feature would be hard for a third-party capability package to implement
|
||||
correctly through public Pydantic AI APIs, do not work around that in harness.
|
||||
Identify the missing core primitive and propose that change first.
|
||||
|
||||
If a feature is mostly a policy decision over existing hooks/toolsets/messages,
|
||||
build it in harness.
|
||||
@@ -0,0 +1,45 @@
|
||||
# Harness Agent Docs
|
||||
|
||||
Use these guides when building or reviewing `pydantic-ai-harness` changes.
|
||||
|
||||
## Required Reads
|
||||
|
||||
For any code change:
|
||||
|
||||
1. `AGENTS.md`
|
||||
2. This file
|
||||
3. The guide below that matches the task
|
||||
4. The public Pydantic AI docs for the integration points you touch
|
||||
|
||||
## Task Routing
|
||||
|
||||
- New or changed capability API: `capability-authoring.md`
|
||||
- New or changed tests: `testing-capabilities.md`
|
||||
- Unsure whether behavior belongs in harness or Pydantic AI core: `core-boundary.md`
|
||||
- Review, pre-PR check, or final self-check: `review-checklist.md`
|
||||
|
||||
## Exemplar
|
||||
|
||||
Use `pydantic_ai_harness.code_mode` as the current exemplar for capability
|
||||
shape:
|
||||
|
||||
- public re-export from `pydantic_ai_harness/__init__.py`
|
||||
- package-level re-export from `pydantic_ai_harness/code_mode/__init__.py`
|
||||
- public capability class in `_capability.py`
|
||||
- implementation toolset in `_toolset.py`
|
||||
- capability README next to implementation
|
||||
- mirrored tests under `tests/code_mode/`
|
||||
|
||||
Do not copy `CodeMode` mechanically. Use it to understand package shape,
|
||||
testing depth, docs placement, and how a harness capability composes with
|
||||
Pydantic AI toolsets.
|
||||
|
||||
## Pydantic AI References
|
||||
|
||||
- Capabilities: <https://pydantic.dev/docs/ai/core-concepts/capabilities/>
|
||||
- Hooks: <https://pydantic.dev/docs/ai/core-concepts/hooks/>
|
||||
- Toolsets: <https://pydantic.dev/docs/ai/tools-toolsets/toolsets/>
|
||||
- Advanced tools: <https://pydantic.dev/docs/ai/tools-toolsets/tools-advanced/>
|
||||
- Agents: <https://pydantic.dev/docs/ai/core-concepts/agent/>
|
||||
- Testing: <https://pydantic.dev/docs/ai/guides/testing/>
|
||||
- Extensibility: <https://pydantic.dev/docs/ai/guides/extensibility/>
|
||||
@@ -0,0 +1,36 @@
|
||||
# Review Checklist
|
||||
|
||||
Use this before opening a PR or reviewing a capability change.
|
||||
|
||||
## Product Fit
|
||||
|
||||
- The capability has a clear user or dogfooding need.
|
||||
- The behavior belongs in harness, not Pydantic AI core.
|
||||
- The public API is small and named around user concepts.
|
||||
- The capability composes with relevant existing capabilities.
|
||||
|
||||
## Implementation
|
||||
|
||||
- Public exports are intentional.
|
||||
- Private helpers stay private.
|
||||
- Types are precise; new public signatures do not use `Any`.
|
||||
- No casts are used to paper over type design.
|
||||
- The implementation uses Pydantic AI hooks/toolsets instead of duplicating core
|
||||
runtime behavior.
|
||||
- Capability ordering is justified when present.
|
||||
- Dependency changes were made through `uv` and have a clear reason.
|
||||
|
||||
## Tests
|
||||
|
||||
- Tests cover the public `Agent(..., capabilities=[...])` path where possible.
|
||||
- Lower-level tests cover lifecycle, schemas, retries, and metadata when needed.
|
||||
- Error paths and important option combinations are covered.
|
||||
- Relevant protocol-shaped output is snapshotted.
|
||||
- `make lint`, `make typecheck`, and `make test` pass before handoff.
|
||||
|
||||
## Docs
|
||||
|
||||
- Capability README or root README is updated for user-facing behavior.
|
||||
- Examples match declared extras.
|
||||
- Docs explain composition constraints and safety implications.
|
||||
- The PR links an issue.
|
||||
@@ -0,0 +1,52 @@
|
||||
# Testing Capabilities
|
||||
|
||||
Harness tests should exercise the behavior users rely on, not only private
|
||||
helpers.
|
||||
|
||||
## Default Shape
|
||||
|
||||
- Use `pydantic_ai.models.TestModel` for model behavior.
|
||||
- Keep real provider calls out of tests.
|
||||
- Prefer `Agent(..., capabilities=[...])` tests for public behavior.
|
||||
- Mirror source packages under `tests/<capability>/`.
|
||||
- Use `pytest-anyio` for async capability/toolset behavior.
|
||||
|
||||
## Lower-Level Tests
|
||||
|
||||
Direct toolset tests are appropriate when you need to inspect:
|
||||
|
||||
- listed tools and schemas
|
||||
- wrapper-toolset lifecycle
|
||||
- retry behavior
|
||||
- metadata and synthetic tool-call records
|
||||
- `RunContext` or `ToolManager` interactions
|
||||
- edge cases that are awkward to force through a full agent run
|
||||
|
||||
Use the `CodeMode` tests as the current reference for direct `RunContext` and
|
||||
`ToolManager` setup.
|
||||
|
||||
## Coverage
|
||||
|
||||
The project enforces 100% branch coverage with `make testcov`. Tests for a new
|
||||
capability should cover:
|
||||
|
||||
- default configuration
|
||||
- important option combinations
|
||||
- failure and retry paths
|
||||
- composition with relevant Pydantic AI features
|
||||
- docs examples when examples are executable
|
||||
|
||||
Use snapshots when behavior is protocol-shaped: messages, event streams,
|
||||
schemas, telemetry spans, or structured tool metadata.
|
||||
|
||||
## Commands
|
||||
|
||||
Run focused checks first, then broaden:
|
||||
|
||||
```bash
|
||||
uv run pytest tests/<capability>
|
||||
make lint
|
||||
make typecheck
|
||||
make test
|
||||
make testcov
|
||||
```
|
||||
Reference in New Issue
Block a user