A research pipeline exploring three-layer agentic orchestration — where declarative structure ends and imperative judgment begins.
Type: Application Skill Loadout: PAUL, AEGIS Stack: Python 3.11 · pydantic-ai · pydantic-graph · SQLModel · SQLite · Typer · Rich
TriLayer is a solo research tool built to answer one architectural question: at what point does a declarative graph specification require imperative runtime judgment to recover?
Three layers:
- Planner — given a research topic, produces a typed
GraphSpec(nodes, edges, typed data flows) - Executor — pydantic-graph's runtime, extended with mutation capabilities: detects weak node output, inserts nodes, rewires edges, logs every deviation
- Node Agents — execute narrow research tasks (arXiv search, paper summarization) via pydantic-ai subagents
HumanInput is a first-class node type — execution blocks and prompts the user when the Executor can't resolve ambiguity autonomously. Runs persist to SQLite and are resumable across processes.
Test domain: academic paper research — given a query, find relevant arXiv papers, extract key contributions, produce a structured summary. The GraphMutation log is the primary research artifact.
| Layer | Choice |
|---|---|
| Agent framework | pydantic-ai |
| Graph runtime | pydantic-graph |
| Database | SQLite + SQLModel |
| CLI | Typer |
| Terminal UI | Rich |
| Testing | pytest + pytest-asyncio |
| Config | python-dotenv |
| arXiv integration | arxiv (Python library) |
| Entity | Purpose |
|---|---|
GraphSpec |
Immutable Planner output — original declared intent |
ExecutionGraph |
Mutable clone per run — what actually executes |
Node |
AgentNode | HumanInputNode |
Edge |
Typed payload between nodes (explicit wiring, declared by Planner or injected by Executor) |
ExecutionRun |
Ties GraphSpec → ExecutionGraph, tracks status |
NodeExecution |
Per-node state (status, input, output, retries) |
HumanInputRequest |
Blocking clarification tied to a NodeExecution |
GraphMutation |
Executor deviation log — primary research artifact |
trilayer run <topic> Planner → GraphSpec → Executor loop
trilayer resume <run-id> Resume a suspended HumanInput run
trilayer inspect <run-id> Rich display: graph, node statuses, mutations
trilayer runs List all ExecutionRuns with status
GraphSpec is immutable after the Planner produces it. The Executor (pydantic-graph's runtime, extended) clones it into a mutable ExecutionGraph and works there.
Adaptation trigger: LLM judgment is the primary mechanism — the Executor asks the LLM whether the node output is sufficient to proceed. A fast-path threshold (e.g. < 3 results) fires before invoking LLM judgment to avoid unnecessary inference.
When the Executor adapts, it logs a GraphMutation with a structured enum reason (INSUFFICIENT_RESULTS, LOW_CONFIDENCE, AMBIGUOUS_QUERY, HUMAN_CLARIFICATION_REQUIRED) plus before/after graph snapshots. The delta between the original GraphSpec and the final ExecutionGraph is the answer to the research question.
Edge payloads are typed — the Planner declares all data flows upfront; the Executor must wire new edges explicitly when inserting nodes, making every deviation traceable.
| Phase | Name | Outcome |
|---|---|---|
| 1 | Three-Layer Wiring | All three layers connected, real arXiv data flows through typed edges |
| 2 | Executor Adaptation | Executor detects weak output, inserts nodes, mutation log populated |
| 3 | HumanInput | Blocking clarification works end-to-end |
| 4 | State Persistence | Runs survive process restart, resume works |
| 5 | Rich Visualization | inspect renders full run breakdown |
- GraphSpec is immutable: Executor works on a mutable
ExecutionGraphclone — preserves original Planner intent for research comparison. - Typed edge payloads: Planner declares all data flows; Executor wires edges explicitly when mutating. Deviation is traceable.
- HumanInput is blocking (terminal): Simpler to implement; valid for research purposes.
- No FastAPI: Typer + Rich sufficient for a local research tool.
- GraphMutation as primary artifact: Records what changed, where, and why — designed to be inspectable across runs.
- arXiv as primary integration: Free, no auth, stable Python library.
- Executor is pydantic-graph's runtime, extended: Not a separate orchestrator — pydantic-graph's runner augmented with mutation logic. pydantic-ai handles LLM inference within Node Agents.
- Adaptation trigger — LLM judgment primary, threshold fallback: Fast-path threshold fires first; LLM judgment invoked when threshold alone is insufficient.
GraphMutation.reasonis a structured enum: Enables programmatic analysis of mutation patterns across runs.
Last updated: 2026-04-06