Use an agent in a workflow
An agent — a prompt + LLM provider/model + typed ports + tools — is projected
as an ordinary node type, agent.<slug>, kind agent
(libs/engine-core/src/engine/agents.ts, agentToNodeMeta). Once it's loaded
on the instance, wiring it into a graph is no different from wiring any other
node: same palette, same connections: grammar, same static validation. This
recipe covers that wiring. It assumes the agent itself already exists — either
contributed by a plugin's contributes.agents or a legacy instance-local
agents/*.agent.yaml (see
Author an LLM agent) — and that
you already know the basics of connecting ports (see
Wire ports and
The dataflow model).
Don't confuse an agent node with the editor's "✨ Assistant IA" panel, which uses an LLM to draft or edit the workflow graph itself — a different feature, covered in Use the AI assistant.
Find the agent's node type id and ports
- Open the workflow editor's palette and look under the Agents IA
category (
libs/engine-core/src/engine/agents.ts:category: "Agents IA") — every loaded agent appears there, alongside built-in and plugin nodes (see Use the visual editor). If you're editing YAML by hand instead, callGET /api/catalogorGET /api/agentson the running instance to list the same node type ids. - The id is
agent.<slug(name)>: the agent'sname, lower-cased, accents stripped, non-alphanumerics collapsed to-(apps/api/src/plugins.ts,slug; the legacy instance-local path derives it from the.agent.yamlfilename instead —agentNodeTypeIdinlibs/engine-core/src/engine/agents.ts). "Revue gériatrique de l'ordonnance" becomesagent.revue-geriatrique-de-l-ordonnance(external-plugins/geriatrie/plugin.yaml). - Read the port names off the palette entry's hover text or the catalog
entry's
description, which is generated asAgent <provider>/<model>. Entrées (<name>: <Type>, …) → sorties (<name>: <Type>, …).plusOutils : <label>, …when the agent has tools. An agent can declare more than one named input and output port, each independently typed and optionally a list — it isn't always a singleinput/outputpair. Compare two real agents:agent.revue-geriatrique-de-l-ordonnance— one input port literally namedinput(typePrescription), one output namedoutput(typeDeprescribingAssessment): the older single-port shorthand (input:/output:as plain type names) normalizes to ports named exactlyinput/output(libs/shared/src/plugin/manifest.ts,AgentContribSchemapreprocess).agent.extraction-de-symptomes-transmission-ide(external-plugins/demo-softway/plugin.yaml) — one input namedtransmission(String), one output namedsymptoms(String,list: true): the named-ports form, so you wire.transmissionand.symptoms, not.input/.output.
Add the node
In the visual editor, click the agent's entry in the Agents IA palette group — it's added to the canvas and selected, exactly like any other node (
apps/config-plane/app/components/WorkflowEditor.tsx, the paletteonClick/addNode).In YAML, add an entry under
nodes:withtype: agent.<slug>and any node id you like:nodes: review_assessment: type: agent.revue-geriatrique-de-l-ordonnanceIf the agent's plugin isn't installed on this instance yet, install it first — see Declare and install plugins.
Wire its ports
An agent node's ports are checked for connection compatibility the same way
as any node's — output type must be assignable to input type, required
inputs must end up connected or given a fixed value. Nothing here is
agent-specific; follow Wire ports for split-pin fields,
params⇄ports, and transform.break/transform.make/transform.convert for
type mismatches. Wire each named port by name, from the upstream node's
matching output to the agent's input, and from the agent's output onward:
# content/clinical-demo/workflows/deprescription-geriatrique.workflow.yaml
nodes:
load_prescription: { type: order.load-prescription }
review_assessment: { type: agent.revue-geriatrique-de-l-ordonnance }
choose_candidate: { type: agent.selection-du-candidat-prioritaire }
taper_plan: { type: agent.plan-de-sevrage-medicamenteux }
connections:
- { from: trigger.prescriptionRef, to: load_prescription.prescriptionRef }
- { from: load_prescription.prescription, to: review_assessment.input }
- { from: review_assessment.output, to: choose_candidate.input }
- { from: choose_candidate.output, to: taper_plan.input }
Chain several agents
Feeding one agent's output straight into the next agent's input is ordinary
fan-out/convergence wiring — nothing special beyond matching types. The full
chain above runs five agents back to back, each consuming the previous one's
typed output
(content/clinical-demo/workflows/deprescription-geriatrique.workflow.yaml); the same
output can also fan out to more than one downstream agent, as taper_plan's
output does to both counseling and follow_up in that file.
Ambient context reaches an agent's tools automatically
An agent node itself declares no context requirement
(agentToNodeMeta: context: []) — you don't need to add anything to the
workflow's context: list just because you added an agent node. But the
ambient context (patient, encounter, …) the workflow already carries is
passed through to every tool the agent calls at run time
(libs/engine-core/src/engine/catalog.ts: the agent's run builds each tool's
invoke with the node's own context) — so a tool like
patient.load-demographics still resolves the right patient without you
wiring anything extra into the agent node.
Validate before running
meridian-plugin validate does not know about agent nodes — it reports any
agent.* node as an unknown type, because the CLI only registers metadata
from the manifests you pass it explicitly
(libs/plugin-cli/src/commands/validate.ts). For a workflow that includes
agent nodes, check it on a running instance instead, where the catalog
already includes every loaded agent — see
Validate a workflow
and Simulate a workflow.
Try it without paying for LLM calls
Once the graph has at least one agent node, the editor's simulation panel
shows an agents réels ("real agents") checkbox
(apps/config-plane/app/components/WorkflowEditor.tsx, SimulatePanel,
hasAgents). Leave it unchecked to run the simulation with a deterministic
mock in place of every agent — useful for checking the graph's shape and
wiring — or check it to exercise the real prompt/tool loop against a
provider, which is billed. See
Simulate a workflow for the rest of that flow.
Run it for real
Executing an agent node needs the instance's agent port bound to a runner
adapter (normally @posos/llm) with a matching provider API key in the
environment — otherwise the run fails explicitly rather than falling back to
a mock (external-plugins/llm/adapters/runner.ts). That binding is instance
configuration, not something this workflow declares:
ports:
agent: { adapter: llm }
See Author an LLM agent for the full binding and the per-provider environment variable names, and Configure ports and adapters for wiring instance ports in general. Once it's bound, kick off and follow the run like any other workflow — see Run and resolve and Monitor executions.
If a run fails with "pas de réponse finale après N tours" (the agent
exhausted its maxIterations without producing a result), that's a property
of the agent's own definition, not of how you wired it here — see
Author an LLM agent to adjust
it.
Without a pre-registered agent: agent.dynamic and agent.inline
Two core nodes (palette group Agents) cover the cases where the agent is not — or not yet — a registered one:
- Agent (choisi au runtime) (
agent.dynamic): theagentinput names a REGISTERED agent (name oragent.<slug>id) and can be wired, so the graph decides at runtime which agent runs;inputscarries the agent's input record (Any), and the whole output record is emitted onresult(Any— its shape depends on the agent picked, so wire it into atransform.formator anotherAnyconsumer). The inspector offers a selector when the input is not wired. - Agent (défini dans le nœud) (
agent.inline): the full agent definition — provider, model, prompt, max iterations, tools, typed inputs/outputs — lives in the node's config; the typed fields become the node's ports, so wiring and validation work exactly as with a registered agent. Use it for a one-off agent without declaring it first; promote it to a registered agent (plugin or Agents page) when it earns reuse.
Both execute through the same runner and tool wiring as registered agent nodes — simulation (mock or real) and provider keys behave identically.
A registered agent's definition is validated by its schema when it loads; an inline one has no such pass, so the node checks what it can when it runs: a provider outside the supported list and an unknown type name on an input or output field fail with an explicit message, rather than reaching the runner as garbage. Declaring at least one typed output and a model is likewise required.
Related
- Author an LLM agent —
declaring the agent itself: ports, tools, prompt,
maxIterations, and the runner adapter binding. - Wire ports — the general connection mechanics used above (split-pin, params⇄ports, type adapters).
- The dataflow model — typed ports and validation, the concepts this recipe assumes.
- Validate a workflow — its Limitations section explains why agent nodes need a running instance to check, not the CLI.
- Simulate a workflow — running the graph against sample data, mock or real agents.
- Run and resolve and Monitor executions — following an agent-containing run to completion.
- Use the visual editor — the palette and canvas mechanics used to add and select nodes.
- Use the AI assistant — the LLM-assisted graph editor feature, distinct from an agent node.
- Expose an agent as an API — the same agents invoked over HTTP, plain JSON or OpenAI-compatible.
- Declare and install plugins — getting an agent's owning plugin onto the instance in the first place.
- Configure ports and adapters
and Environment variables
— binding the
agentport and its provider API keys.