refactor the docs of agenton

This commit is contained in:
盐粒 Yanli
2026-05-08 01:34:08 +08:00
parent 5a425f1525
commit a523d071ca
4 changed files with 307 additions and 88 deletions

View File

@@ -1,3 +1,4 @@
# Dify Agent
Agenton documentation lives in [`docs/agenton/`](docs/agenton/).
Agenton documentation lives in [`docs/agenton/guide/`](docs/agenton/guide/) and
[`docs/agenton/api/`](docs/agenton/api/).

View File

@@ -1,88 +1,6 @@
# Agenton configuration and sessions
# Agenton documentation
Agenton composes shared `Layer` instances into a named graph. Treat layer
instances as reusable capability definitions: config and dependency declarations
belong on the layer class or instance, while per-session runtime values belong
on the `LayerControl` created for that layer in a `CompositorSession`.
## Config, runtime state, and runtime handles
- **Config** is serializable graph input. Config-constructible layers declare a
`type_id` and a Pydantic `config_type`; builders validate node config before
calling `Layer.from_config(validated_config)`.
- **Runtime state** is serializable per-layer/per-session state. Layers declare a
Pydantic `runtime_state_type`; session snapshots persist this model with
`model_dump(mode="json")`.
- **Runtime handles** are live Python objects such as clients, open files, or
process handles. Layers declare a Pydantic `runtime_handles_type` with
`arbitrary_types_allowed=True`. Handles are never serialized; resume hooks
should rehydrate them from runtime state.
`Layer.__init_subclass__` infers `deps_type`, `config_type`,
`runtime_state_type`, and `runtime_handles_type` from generic base arguments
when possible. For example, `PlainLayer[NoLayerDeps, MyConfig, MyState,
MyHandles]` automatically installs those Pydantic schemas. Omitted schema slots
default to `EmptyLayerConfig`, `EmptyRuntimeState`, and `EmptyRuntimeHandles`.
Lifecycle hooks can annotate controls as `LayerControl[MyState, MyHandles]` to
get static checking and IDE completion for runtime state and handles.
## Registry and builder
Register config-constructible layers manually:
```python
registry = LayerRegistry()
registry.register_layer(PromptLayer) # uses PromptLayer.type_id == "plain.prompt"
```
Use `CompositorBuilder` to mix serializable config nodes with live instances:
```python
compositor = (
CompositorBuilder(registry)
.add_config(
{
"layers": [
{
"name": "prompt",
"type": "plain.prompt",
"config": {"prefix": "Hi", "user": "Answer with examples."},
}
]
}
)
.add_instance(name="profile", layer=ObjectLayer(profile))
.build()
)
```
Use `.add_instance()` for layers that require Python objects or callables, such
as `ObjectLayer`, `ToolsLayer`, and dynamic tool layers.
## System prompts and user prompts
Layers expose three prompt surfaces:
- `prefix_prompts`: system prompt fragments collected in layer order.
- `suffix_prompts`: system prompt fragments collected in reverse layer order.
- `user_prompts`: user-message fragments collected in layer order.
`PromptLayer` accepts `prefix`, `user`, and `suffix` config fields. For
pydantic-ai, `PYDANTIC_AI_TRANSFORMERS` maps `compositor.prompts` to system
prompt functions and `compositor.user_prompts` to values suitable for
`Agent.run(user_prompt=...)`.
## Session snapshot and restore
`Compositor.snapshot_session(session)` serializes non-active sessions, including
layer lifecycle state and runtime state. It rejects active sessions because live
handles cannot be snapshotted safely. Restore with
`Compositor.session_from_snapshot(snapshot)`; restored controls validate runtime
state with each layer schema and initialize empty runtime handles. Suspended
sessions resume through `on_context_resume`, where handles should be hydrated
from the restored runtime state.
Create sessions with `Compositor.new_session()` or
`Compositor.session_from_snapshot()`. `Compositor.enter()` validates that every
session control uses the target layer's runtime state and handle schemas before
any lifecycle hook runs.
- [User guide](guide/) explains how to compose layers, register config-backed
plugins, use system/user prompts, and snapshot sessions.
- [API reference](api/) lists the public Agenton classes, methods, and extension
points.

View File

@@ -0,0 +1,183 @@
# Agenton API reference
This page summarizes the public Agenton API. Import paths are shown for the
symbols commonly used by layer authors and compositor callers.
## Layers: `agenton.layers`
### `Layer[DepsT, PromptT, UserPromptT, ToolT, ConfigT, RuntimeStateT, RuntimeHandlesT]`
Framework-neutral base class for prompt/tool layers.
Class attributes:
- `type_id: str | None`: registry id for config-backed plugin layers.
- `config_type: type[BaseModel]`: Pydantic schema for serialized layer config.
- `runtime_state_type: type[BaseModel]`: Pydantic schema for snapshot-safe
per-session state.
- `runtime_handles_type: type[BaseModel]`: Pydantic schema for live runtime
handles; use `arbitrary_types_allowed=True` for client/process objects.
- `deps_type: type[LayerDeps]`: inferred from the layer generic base or declared
explicitly.
Construction and dependency APIs:
- `from_config(config: ConfigT) -> Self`: create a layer from schema-validated
config. The default implementation raises `TypeError`.
- `dependency_names() -> frozenset[str]`: dependency fields declared by
`deps_type`.
- `bind_deps(deps: Mapping[str, Layer | None]) -> None`: bind graph dependencies.
- `new_control(state=LifecycleState.NEW, runtime_state=None) -> LayerControl`: create
a schema-validated per-session control.
Lifecycle hooks:
- `on_context_create(control)`
- `on_context_resume(control)`
- `on_context_suspend(control)`
- `on_context_delete(control)`
- `enter(control)` / `lifecycle_enter(control)`: async context manager entry
surface. Override `enter()` only when a layer needs to wrap extra resources.
Prompt/tool authoring surfaces:
- `prefix_prompts -> Sequence[PromptT]`
- `suffix_prompts -> Sequence[PromptT]`
- `user_prompts -> Sequence[UserPromptT]`
- `tools -> Sequence[ToolT]`
Aggregation adapters implemented by typed layer families:
- `wrap_prompt(prompt: PromptT) -> object`
- `wrap_user_prompt(prompt: UserPromptT) -> object`
- `wrap_tool(tool: ToolT) -> object`
### `LayerControl[RuntimeStateT, RuntimeHandlesT]`
Per-layer, per-session lifecycle control.
Fields:
- `state: LifecycleState`
- `exit_intent: ExitIntent`
- `runtime_state: RuntimeStateT`
- `runtime_handles: RuntimeHandlesT`
Methods:
- `suspend_on_exit() -> None`
- `delete_on_exit() -> None`
`runtime_state` is serialized in session snapshots. `runtime_handles` is never
serialized and should be rehydrated from runtime state in resume hooks.
### Schema defaults and lifecycle enums
- `EmptyLayerConfig`
- `EmptyRuntimeState`
- `EmptyRuntimeHandles`
- `LifecycleState`: `NEW`, `ACTIVE`, `SUSPENDED`, `CLOSED`
- `ExitIntent`: `DELETE`, `SUSPEND`
### Typed layer families: `agenton.layers.types`
- `PlainLayer[DepsT, ConfigT, RuntimeStateT, RuntimeHandlesT]`
- `PydanticAILayer[DepsT, AgentDepsT, ConfigT, RuntimeStateT, RuntimeHandlesT]`
Tagged aggregate item types:
- `PlainPromptType`, `PlainUserPromptType`, `PlainToolType`
- `PydanticAIPromptType`, `PydanticAIUserPromptType`, `PydanticAIToolType`
- `AllPromptTypes`, `AllUserPromptTypes`, `AllToolTypes`
## Compositor: `agenton.compositor`
### Config models
- `LayerNodeConfig`: `name`, `type`, `config`, `deps`, `metadata`
- `CompositorConfig`: `schema_version`, `layers`
Config nodes are pure serializable graph input. Use live instances for Python
objects and callables.
### Registry
`LayerRegistry` manually registers config-backed layer classes.
- `register_layer(layer_type, type_id=None) -> None`
- `resolve(type_id) -> LayerDescriptor`
- `descriptors() -> Mapping[str, LayerDescriptor]`
`LayerDescriptor` exposes `type_id`, `layer_type`, `config_type`,
`runtime_state_type`, and `runtime_handles_type`.
### Builder
`CompositorBuilder(registry)` mixes config-backed nodes and live instances.
- `add_config(config) -> Self`
- `add_config_layer(name, type, config=None, deps=None) -> Self`
- `add_instance(name, layer, deps=None) -> Self`
- `build(prompt_transformer=None, user_prompt_transformer=None, tool_transformer=None) -> Compositor`
### Compositor
`Compositor[PromptT, ToolT, LayerPromptT, LayerToolT, UserPromptT, LayerUserPromptT]`
owns the ordered layer graph.
Construction:
- `Compositor(layers=..., deps_name_mapping=..., ...)`
- `Compositor.from_config(conf, registry=..., ...)`
Aggregation properties:
- `prompts -> list[PromptT]`: prefix prompts in layer order, suffix prompts in
reverse layer order, then optional `prompt_transformer`.
- `user_prompts -> list[UserPromptT]`: user prompts in layer order, then optional
`user_prompt_transformer`.
- `tools -> list[ToolT]`: tools in layer order, then optional `tool_transformer`.
Session APIs:
- `new_session() -> CompositorSession`
- `enter(session=None) -> AsyncIterator[CompositorSession]`
- `snapshot_session(session) -> CompositorSessionSnapshot`
- `session_from_snapshot(snapshot) -> CompositorSession`
### Sessions and snapshots
`CompositorSession` owns ordered layer controls.
- `suspend_on_exit() -> None`
- `delete_on_exit() -> None`
- `layer(name) -> LayerControl`
Snapshot models:
- `LayerSessionSnapshot`: `name`, `state`, `runtime_state`
- `CompositorSessionSnapshot`: `schema_version`, `layers`
Snapshots reject active sessions and exclude `runtime_handles` and `exit_intent`.
## Collection layers and transformers
### Plain layers: `agenton_collections.layers.plain`
- `PromptLayer`: config-backed layer with `PromptLayerConfig(prefix, user,
suffix)` and `type_id = "plain.prompt"`.
- `ObjectLayer`: instance-only layer for Python objects.
- `ToolsLayer`: instance-only layer for callables.
- `DynamicToolsLayer`: instance-only layer for object-bound callables.
### Pydantic AI bridge
`agenton_collections.layers.pydantic_ai.PydanticAIBridgeLayer` exposes
pydantic-ai system prompts, user prompts, and tools while depending on an
`ObjectLayer` for `RunContext.deps`.
`agenton_collections.transformers.PYDANTIC_AI_TRANSFORMERS` provides:
- `prompt_transformer`: maps `compositor.prompts` to pydantic-ai system prompt functions.
- `user_prompt_transformer`: maps `compositor.user_prompts` to pydantic-ai `UserContent`.
- `tool_transformer`: maps `compositor.tools` to pydantic-ai tools.

View File

@@ -0,0 +1,117 @@
# Agenton user guide
Agenton composes shared `Layer` instances into a named graph. Treat layer
instances as reusable capability definitions: config and dependency declarations
belong on the layer class or instance, while per-session runtime values belong
on the `LayerControl` created for that layer in a `CompositorSession`.
## Config, runtime state, and runtime handles
- **Config** is serializable graph input. Config-constructible layers declare a
`type_id` and a Pydantic `config_type`; builders validate node config before
calling `Layer.from_config(validated_config)`.
- **Runtime state** is serializable per-layer/per-session state. Layers declare a
Pydantic `runtime_state_type`; session snapshots persist this model with
`model_dump(mode="json")`.
- **Runtime handles** are live Python objects such as clients, open files, or
process handles. Layers declare a Pydantic `runtime_handles_type` with
`arbitrary_types_allowed=True`. Handles are never serialized; resume hooks
should rehydrate them from runtime state.
## Define a config-backed layer
Use a Pydantic model for config and pass it through the typed layer family so
`Layer.__init_subclass__` can infer the schema:
```python
class GreetingConfig(BaseModel):
prefix: str
model_config = ConfigDict(extra="forbid")
@dataclass
class GreetingLayer(PlainLayer[NoLayerDeps, GreetingConfig]):
type_id = "example.greeting"
prefix: str
@classmethod
def from_config(cls, config: GreetingConfig) -> Self:
return cls(prefix=config.prefix)
@property
def prefix_prompts(self) -> list[str]:
return [self.prefix]
```
Omitted schema slots default to `EmptyLayerConfig`, `EmptyRuntimeState`, and
`EmptyRuntimeHandles`. Lifecycle hooks can annotate controls as
`LayerControl[MyState, MyHandles]` to get static checking and IDE completion for
runtime state and handles.
## Register layers and build a compositor
Register config-constructible layers manually:
```python
registry = LayerRegistry()
registry.register_layer(PromptLayer) # uses PromptLayer.type_id == "plain.prompt"
```
Use `CompositorBuilder` to mix serializable config nodes with live instances:
```python
compositor = (
CompositorBuilder(registry)
.add_config(
{
"layers": [
{
"name": "prompt",
"type": "plain.prompt",
"config": {"prefix": "Hi", "user": "Answer with examples."},
}
]
}
)
.add_instance(name="profile", layer=ObjectLayer(profile))
.build()
)
```
Use `.add_instance()` for layers that require Python objects or callables, such
as `ObjectLayer`, `ToolsLayer`, and dynamic tool layers.
## System prompts and user prompts
Layers expose three prompt surfaces:
- `prefix_prompts`: system prompt fragments collected in layer order.
- `suffix_prompts`: system prompt fragments collected in reverse layer order.
- `user_prompts`: user-message fragments collected in layer order.
`PromptLayer` accepts `prefix`, `user`, and `suffix` config fields. For
pydantic-ai, `PYDANTIC_AI_TRANSFORMERS` maps `compositor.prompts` to system
prompt functions and `compositor.user_prompts` to values suitable for
`Agent.run(user_prompt=...)`.
## Session snapshot and restore
`Compositor.snapshot_session(session)` serializes non-active sessions, including
layer lifecycle state and runtime state. It rejects active sessions because live
handles cannot be snapshotted safely. Restore with
`Compositor.session_from_snapshot(snapshot)`; restored controls validate runtime
state with each layer schema and initialize empty runtime handles. Suspended
sessions resume through `on_context_resume`, where handles should be hydrated
from the restored runtime state.
Create sessions with `Compositor.new_session()` or
`Compositor.session_from_snapshot()`. `Compositor.enter()` validates that every
session control uses the target layer's runtime state and handle schemas before
any lifecycle hook runs.
See also:
- `examples/agenton/basics.py`
- `examples/agenton/pydantic_ai_bridge.py`
- `examples/agenton/session_snapshot.py`