fix: SkillOpt Epoch 2 — pydanticai decision intelligence

Edits accepted and merged:
- Run method decision table (when to use run/run_sync/run_stream/run_stream_events/iter)
- Graph API comparison table (BaseNode vs GraphBuilder trade-offs)
- Error handling quick-pick with exception table and recovery patterns

Version bumped from 1.0.2 to 1.0.3.

All 3 validation tasks passed with no regressions (6/6, 10/10, 8/8 rubric items).

Signed-off-by: Magnus Hedemark <magnus919@pm.me>
This commit is contained in:
Magnus Hedemark
2026-07-09 05:48:35 -04:00
parent 3cfdcf8677
commit 9ff931ddf5
+50 -1
View File
@@ -9,7 +9,7 @@ description: >-
license: MIT
metadata:
source: https://pydantic.dev/docs/ai/overview/
version: "1.0.1"
version: "1.0.3"
compatibility: Python 3.10+; requires pydantic-ai or pydantic-ai-slim package
---
@@ -109,6 +109,55 @@ class ProcessNode(BaseNode[MyState]):
```
→ See `references/graph.md` for both BaseNode and GraphBuilder APIs, parallel execution, and join/reducer patterns.
### When to use which run method
| When you need… | Use | Key behavior |
|---|---|---|
| A single answer, sync code | `run_sync()` | Blocks until complete, returns `RunResult` |
| A single answer, async code | `run()` | Async, returns `RunResult` |
| Stream text as it's generated | `run_stream()` | Async context manager, yields `stream_text()` / `stream_output()` |
| See granular events (tool calls, part starts, deltas) | `run_stream_events()` | Yields `AgentStreamEvent` types — `FunctionToolCallEvent`, `PartStartEvent`, `FinalResultEvent` |
| Manual control over each graph step | `iter()` | Iterate over agent's internal graph nodes (`UserPromptNode``ModelRequestNode``CallToolsNode`) |
| Tool calls to execute during streaming | `run_stream_events()` or `run(event_stream_handler=...)` | `run_stream()` stops at the first output that matches `output_type` and does NOT execute subsequent tool calls |
*Details for each run method in `references/core-agents.md`.*
### Graph API: BaseNode vs GraphBuilder
| Factor | BaseNode (class-based) | GraphBuilder (function-based) |
|---|---|---|
| Style | Subclass `BaseNode[StateT]`, implement `async run()` | Decorate async functions with `@g.step` |
| State mutation | Via `ctx.state` inside `run()` method | Via `ctx.state` inside step function |
| Parallelism | Manual fork/join logic | Built-in `.map()` per-element fan-out and `.broadcast()` same-input-to-multiple |
| Joins / aggregation | Manual aggregation in return types | Built-in reducers: `reduce_list_append`, `reduce_sum`, `reduce_dict_update`, etc. |
| Edge declaration | Inferred from `run()` return type annotation | Explicit via `g.edge_from(source).to(target)` |
| When to use | Complex node logic, OO patterns, conditional edge logic | Simple linear flows, parallel data processing, concise syntax |
*Both APIs in `references/graph.md`.*
### Error handling quick-pick
```python
from pydantic_ai import UnexpectedModelBehavior, capture_run_messages
with capture_run_messages() as messages:
try:
result = agent.run_sync('Query')
except UnexpectedModelBehavior as e:
cause = e.__cause__ # Often ModelRetry('reason')
print(f"Root cause: {cause}")
print("Full conversation:", messages) # Inspect every message
# Common recovery: raise ModelRetry from tools with clear instructions
```
| Exception | Meaning | Recovery |
|---|---|---|
| `UnexpectedModelBehavior` | Retry limit exceeded or model gave unexpected response | Inspect `e.__cause__`, check messages, adjust instructions or tool retries |
| `ModelRetry` (raised from tools) | Tool wants model to retry with different args | Let it propagate — PydanticAI handles it automatically up to `retries` limit |
| `ModelAPIError` | Provider returned 4xx/5xx | Check API key, rate limits, model availability |
| `UsageLimitExceeded` | Token/request budget exhausted | Increase `UsageLimits` or optimize prompt |
| `HookTimeoutError` | A lifecycle hook timed out | Increase hook timeout or optimize hook logic |
## Key CLI Commands
```bash