[Discuss] Define the canonical agent definition (informed by other agent frameworks)
## Goal
Agree on the **canonical "agent definition"** for the AI module 2.0 — i.e. *what fields/primitives make up an agent* as a declarative structure — informed by how the leading agent frameworks define an agent. The output of this discussion feeds directly into the entity work in #3586504 (which fields live on the entity), the structured input/output work in #3586501 / #3586502, and the runner/control split in #3586505 / #3586510.
## Background
The [META] (#3586513) lifts the *structure* of an agent into the AI module and makes the *runner* swappable, with a Drupal runner (#3586506) and a Symfony AI runner (#3586507). For the runner abstraction to be meaningful — and for our definition to be portable to / interoperable with external runtimes such as Symfony AI — the **agent definition itself** should be deliberately designed rather than inherited 1-to-1 from today's `ai_agents` entity.
#3586504 proposes a pragmatic 1-to-1 move for 2.0 (system prompt, title, description, default information tools, status, text I/O by default). This issue is the **discussion layer underneath that**: it surveys other frameworks and asks which primitives are genuinely *part of the agent definition* (portable, runner-agnostic) versus which are *runtime/control concerns* that belong on the runner or control-plugin layer (#3586505, #3586508, #3586510).
This is a `discussion` issue — no code is expected here; the goal is a decision recorded in **## Decision** that the implementation issues can reference.
## Proposed approach
### What the frameworks agree on
Research across eight current frameworks (OpenAI Agents SDK, Symfony AI, Google ADK, LangChain/LangGraph, CrewAI, Microsoft Agent Framework/AutoGen, Pydantic AI, Anthropic Claude Agent SDK) shows a fairly stable core. Mapping their real field names:
| Concept | OpenAI Agents SDK | Symfony AI | Google ADK | LangGraph | CrewAI | MS Agent Framework | Pydantic AI | Claude Agent SDK |
|---|---|---|---|---|---|---|---|---|
| Identifier | `name` | (via processors) | `name` | `name` | `role` | `name` | `name` | (file/`name`) |
| Instructions / system prompt | `instructions` | `Message::forSystem` | `instruction` | `prompt` | `role`+`goal`+`backstory` | `instructions` | `instructions`/`system_prompt` | `prompt` |
| Description (for delegation) | `handoff_description` | — | `description` | — | — | — | — | `description` |
| Model | `model` | `$model` | `model` | `model` | `llm` | `client` | `model` | `model` |
| Model settings | `model_settings` | (platform opts) | `generate_content_config` | (on model) | — | — | `model_settings` | — |
| Tools | `tools` (+`mcp_servers`) | `Toolbox`/`#[AsTool]` | `tools` | `tools` | `tools` | `tools` | `tools`/`toolsets` | `tools` |
| Structured **input** | (via context) | (input processors) | `input_schema` | `context_schema` | (task) | — | `deps_type` | — |
| Structured **output** | `output_type` | `response_format` | `output_schema` | `response_format` | (task `output_pydantic`) | (response format) | `output_type` | — |
| Sub-agents / delegation | `handoffs` | `Subagent` tool | `sub_agents` | (graph compose) | `allow_delegation` | (teams) | (agent delegation) | subagents |
| Memory | (sessions) | `MemoryInputProcessor` | session state / `output_key` | `checkpointer`/`store` | `memory` | `model_context` | (history/deps) | (per-subagent ctx) |
| Guardrails | `input/output_guardrails` | (processors) | callbacks | hooks | (task) | middleware | output validators | tool restriction |
**Near-universal definition fields:** identifier, instructions/system prompt, model (+ settings), tools.
**Common but not universal:** description-for-delegation, structured output schema, sub-agents/delegation, structured input schema.
**Framework-specific / not part of the static definition:** memory, guardrails — almost every framework treats these as runtime/middleware concerns rather than flat definition fields.
### Two structural philosophies to choose between
- **Flat field list** (OpenAI SDK, ADK, Pydantic AI, CrewAI): the agent is a struct of named fields. Easy to express as a Drupal config entity + schema. Matches #3586504's direction.
- **Composition / processor pipeline** (Symfony AI): only `platform`, `model`, `inputProcessors`, `outputProcessors` are real constructor args; name, instructions, tools, memory and output schema are *attached* via processors and the message bag. This is the biggest divergence and is directly relevant because we ship a **Symfony AI runner** (#3586507).
The recommendation for discussion: define the AI-module agent as a **flat, declarative entity** (portable, serialisable, runner-agnostic), and treat the Symfony processor pipeline as a runner-side *projection* of that definition rather than letting Symfony's shape leak into our entity.
### Proposed canonical fields for the AI-module agent definition
Runner-agnostic core (belongs on the entity):
- **id / machine name**
- **label** (title) and **description** — description doubling as the "when to delegate to this agent" hint (ADK/OpenAI/Claude all use a description for this)
- **system prompt** (+ existing `secured_system_prompt` concept)
- **input definition(s)** — default text; structured input per #3586501
- **output definition(s)** — default text; structured output per #3586502
- **tools** — referencing the Tool API (#3586503), incl. *default information tools*
- **status** (enabled/disabled)
- optionally **sub-agent / handoff references** — see open questions
Deferred to the runner / control-plugin layer (NOT on the static definition):
- model & model settings (a runner concern — the Drupal runner may bind a provider/model; other runtimes choose their own)
- memory, guardrails, masquerade/permissions, max loops, tool usage limits, orchestration/triage flags (→ #3586505, #3586508, #3586510)
## Stakeholders *(optional)*
* @michaellander
* @harivansh
## Open questions
* Is **model / model settings** part of the agent *definition*, or strictly a runner binding? Frameworks split on this (definition field in most; a Platform concern in Symfony).
* Are **sub-agents / delegation** a first-class field of the definition (`handoffs`/`sub_agents`), or modelled as a *tool* (Symfony's `Subagent`, CrewAI delegation)? This affects whether orchestration/triage stays an entity flag or moves to the runner.
* Should the canonical definition track an **interoperability manifest** — e.g. expose / import from an A2A *Agent Card* (`/.well-known/agent-card.json`: name, description, version, capabilities, I/O modalities, `skills`) — so Drupal agents are portable to external runtimes? Or is that out of scope for 2.0?
* How far do we lean on **MCP** for the tool side of the definition vs. our own Tool API (#3586503)?
* Do we need a distinct **structured input schema** field at all for 2.0, or is text-in/structured-out (per #3586504) enough to start?
## Resources
* Parent meta: #3586513
* Entity move: #3586504 · Structured input: #3586501 · Structured output: #3586502 · Tool API switch: #3586503
* Runner abstraction: #3586505 · Drupal runner: #3586506 · Symfony AI runner: #3586507 · Control plugins: #3586510
* Prior Symfony mapping research: #3567006
* OpenAI Agents SDK — https://openai.github.io/openai-agents-python/ref/agent/
* Symfony AI (Agent component) — https://symfony.com/doc/current/ai/components/agent.html
* Google ADK (LlmAgent) — https://google.github.io/adk-docs/agents/llm-agents/
* LangGraph `create_react_agent` — https://reference.langchain.com/python/langgraph.prebuilt/chat_agent_executor/create_react_agent
* CrewAI Agent — https://docs.crewai.com/en/concepts/agents
* Pydantic AI Agent — https://ai.pydantic.dev/agent/
* Anthropic Claude Agent SDK (subagents) — https://docs.claude.com/en/docs/agent-sdk/subagents
* A2A protocol / Agent Card — https://a2a-protocol.org/latest/specification/
## Decision
<!-- Fill in before closing: summarise what was decided and the key reason. Leave empty until resolved. -->
issue