[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