AI Process Model and Notation — Specification v0.1 (Draft)¶
Status: Draft
Date: 2026-06-21
Authors: Kshetra Studio
Namespace: http://apmn.kshetra.studio/ns/1.0
License: Apache 2.0
Canonical URL: https://apmn.kshetra.studio/spec/v0.1 (GitHub source: this file)
1. Introduction¶
AI Process Model and Notation (APMN) is an open specification for representing business processes that involve AI agents, large language models (LLMs), retrieval systems, MCP tools, and human participants.
APMN is designed as a superset of BPMN 2.0:
- Every APMN document is a valid BPMN 2.0 document
- APMN extensions use the apmn: XML namespace inside <bpmn:extensionElements>
- APMN also defines a canonical YAML serialisation as the primary authoring format
- Existing BPMN tools open APMN files without modification (extensions are skipped gracefully)
1.1 Design Principles¶
- Business-readable — a business analyst should be able to read and author APMN diagrams without writing code
- Probabilistic-first — routing can be based on confidence scores and LLM reasoning, not only hard-coded conditions
- AI-native — agents, RAG, MCP tools, vector stores, and memory are first-class node types, not afterthoughts
- Vendor-neutral — APMN compiles to any target runtime (Orkes Conductor, Google Agent Development Kit, LangGraph, AWS Bedrock, Temporal, etc.)
- Observable by default — observability events are part of the notation, not an external concern
- Backward compatible — all BPMN 2.0 node types are valid APMN
1.2 Resources¶
| Resource | URL |
|---|---|
| Spec (this document) | https://apmn.kshetra.studio/spec/v0.1 |
| JSON Schema | https://apmn.kshetra.studio/schema/v0.1/apmn-schema.yaml |
| GitHub | https://github.com/kshetra-studio/apmn |
| Reference compiler | TwinTrack — BPMN → APMN → target |
| APMN Modeller | apmn-modeler.kshetra.studio |
2. Serialisation Formats¶
2.1 YAML (primary)¶
APMN YAML is the recommended authoring format. It is human-readable, diff-friendly, and version-control friendly.
# APMN YAML document
apmn_version: "0.1"
xmlns_apmn: "http://apmn.kshetra.studio/ns/1.0"
process:
id: string # required, unique process identifier
name: string # required, human-readable name
description: string # optional
targets: [string] # optional — compiler targets (orkes, google_adk, langraph, bedrock)
nodes: [Node] # ordered list of all nodes
flows: [Flow] # sequence flows connecting nodes
Machine-readable schema: schema/apmn-schema.yaml (JSON Schema 2020-12 expressed in YAML).
2.2 BPMN 2.0 XML with APMN extensions (secondary)¶
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:apmn="http://apmn.kshetra.studio/ns/1.0">
<bpmn:process id="my_process">
<bpmn:serviceTask id="task_1" name="Verify Insurance">
<bpmn:extensionElements>
<apmn:agentTask model="gemini-2.0-flash"
confidence_threshold="0.95"/>
</bpmn:extensionElements>
</bpmn:serviceTask>
</bpmn:process>
</bpmn:definitions>
3. Node Types¶
3.1 Inherited from BPMN 2.0 (unchanged semantics)¶
| APMN Node Type | BPMN 2.0 Equivalent |
|---|---|
serviceTask |
bpmn:serviceTask |
scriptTask |
bpmn:scriptTask |
userTask |
bpmn:userTask |
manualTask |
bpmn:manualTask |
businessRuleTask |
bpmn:businessRuleTask |
sendTask |
bpmn:sendTask |
receiveTask |
bpmn:receiveTask |
exclusiveGateway |
bpmn:exclusiveGateway |
parallelGateway |
bpmn:parallelGateway |
startEvent |
bpmn:startEvent |
endEvent |
bpmn:endEvent |
timerEvent |
bpmn:intermediateCatchEvent + timerEventDefinition |
3.2 New APMN Task Types¶
Implementation notes in each section describe what a compiler must generate and what post-transform work the implementer must complete. Compiler support status for TwinTrack v0.1 is noted where applicable.
3.2.1 agentTask¶
An LLM-driven task. The agent receives a system prompt, optional context, and a list of available tools, and produces a result.
- id: string # required
type: agentTask # required
name: string # required
model: string # required — e.g. "gemini-2.0-flash", "claude-sonnet-4-6"
system_prompt: string # optional — overrides default instruction
tools: [string] # optional — MCP tool URIs or named tool references
temperature: float # optional, default 0.0
max_tokens: int # optional
confidence_threshold: float # optional, 0.0–1.0 — output routed to confidenceGate
documentation: string # optional
Compiler targets — implementation notes:
| Target | What the compiler generates | What you must complete |
|---|---|---|
| Orkes Conductor | A SIMPLE task definition JSON with taskType: SIMPLE and inputParameters. |
Deploy a Conductor Worker — an external process (any language) that polls GET /api/v1/tasks/{taskName} on your Orkes instance, calls your LLM, and POSTs the result to POST /api/v1/tasks. TwinTrack generates the task definition only; the worker is your code. |
| Google ADK | An LlmAgent instance configured with the specified model and a Runner. |
Supply GOOGLE_API_KEY (Gemini API) or set GOOGLE_CLOUD_PROJECT + GOOGLE_CLOUD_LOCATION for Vertex AI. The generated agent is a standalone Python module you run locally or deploy to Agent Engine. |
| LangGraph | A node function with an LLM call stub. | Wire the node into your StateGraph and supply the model client. |
3.2.2 ragTask¶
A retrieval-augmented generation task. Queries a vector store and augments a prompt with retrieved context before an LLM call.
- id: string
type: ragTask
name: string
vector_store: string # required — store ID or URI
query_from: string # required — input field to use as query
top_k: int # optional, default 5
similarity_threshold: float # optional, default 0.7
model: string # optional — LLM for synthesis step
documentation: string
Compiler targets — implementation notes:
| Target | What the compiler generates | What you must complete |
|---|---|---|
| Orkes Conductor | A SIMPLE task stub. |
Implement the worker: query your vector store (Pinecone, Weaviate, pgvector, etc.) using query_from as the query, inject retrieved context into the LLM prompt, POST result back to Conductor. |
| Google ADK | An LlmAgent with a retrieval tool stub. |
Wire your vector store client into the retrieval tool. Google provides Vertex AI Search as a managed option; for external stores implement a FunctionTool wrapper. |
3.2.3 mcpToolTask¶
Invokes a specific tool on an MCP (Model Context Protocol) server.
- id: string
type: mcpToolTask
name: string
server: string # required — MCP server URI (e.g. "https://mcp.example.com")
tool: string # required — tool name on the server
input_schema: object # optional — JSON Schema for tool input
documentation: string
Compiler targets — implementation notes:
| Target | What the compiler generates | What you must complete |
|---|---|---|
| Orkes Conductor | An HTTP task pointing to the MCP server's tool endpoint (POST <server>/tools/<tool>). |
Ensure the MCP server is reachable from your Orkes workers. Supply any auth headers via Orkes task configuration. |
| Google ADK | An MCPToolset integration stub referencing the server URL. |
The MCP server must be running and accessible. Provide credentials if the server requires auth. |
3.2.4 humanInLoopTask¶
A task where an AI agent produces a proposal that a human must approve, reject, or modify before the flow continues.
- id: string
type: humanInLoopTask
name: string
confidence_threshold: float # optional — auto-route to human if agent confidence < threshold
timeout: string # optional — ISO 8601 duration (e.g. "PT48H")
escalate_to: string # optional — node ID if timeout exceeded
documentation: string
Compiler targets — implementation notes:
| Target | What the compiler generates | What you must complete |
|---|---|---|
| Orkes Conductor | A HUMAN task definition. Orkes pauses the workflow at this task and waits. |
Build the human-facing UI (web app, email link, Slack bot, etc.) that calls POST /api/v1/tasks with the task outcome when the human responds. Without this, the workflow will pause indefinitely. If timeout is set, pair with an escapeGate to handle the timed-out path. |
| Google ADK | A before_tool_callback stub that yields control. |
Implement the callback to surface the agent's proposal to a human and await their input. The mechanism is application-specific (HTTP callback, queue, etc.). |
⚠️ Best practice: Always set
timeoutand connect anescapeGateto handle the timeout path. An unattendedhumanInLoopTaskwith no timeout is a workflow deadlock risk.
3.2.5 agentHandoff¶
Delegates control to a specialist sub-agent. Used in multi-agent orchestration.
- id: string
type: agentHandoff
name: string
target_agent: string # required — agent ID or URI
context_fields: [string] # optional — which workflow fields to pass to the sub-agent
documentation: string
Compiler targets — implementation notes:
| Target | What the compiler generates | What you must complete |
|---|---|---|
| Orkes Conductor | A SUB_WORKFLOW task referencing target_agent as the sub-workflow name. |
The sub-workflow must be registered on your Orkes instance separately. Supply input variable mappings. |
| Google ADK | A sub-LlmAgent call stub inside a SequentialAgent. |
Define the sub-agent and wire it into the pipeline. |
3.2.6 memoryTask¶
Reads from or writes to an agent memory store (session-scoped or long-term).
- id: string
type: memoryTask
name: string
store: string # required — "session" | "long_term" | store URI
operation: string # required — "read" | "write" | "append"
key: string # required — memory key
value_from: string # required for write — input field
documentation: string
Compiler targets — implementation notes:
| Target | What the compiler generates | What you must complete |
|---|---|---|
| Orkes Conductor | A SIMPLE task stub. |
Implement the worker with your memory backend (Redis, DynamoDB, a vector store, etc.). |
| Google ADK | A FunctionTool stub with read/write scaffolding. |
Wire your persistence layer (Firestore, Memorystore, etc.) into the generated function. |
Note: TwinTrack v0.1 generates stubs for
memoryTask. Full implementation is the implementer's responsibility.
3.2.7 vectorTask¶
Embeds content into, or queries, a vector store directly — as a standalone task, distinct from ragTask which combines retrieval with LLM synthesis.
- id: string
type: vectorTask
name: string
store: string # required — vector store ID or URI
operation: string # required — "embed" | "query" | "delete"
embedding_model: string # optional — embedding model to use
documentation: string
Compiler targets — implementation notes:
| Target | What the compiler generates | What you must complete |
|---|---|---|
| Orkes Conductor | A SIMPLE task stub. |
Implement the worker: call your embedding API (OpenAI, Vertex AI, etc.) and upsert/query your vector store. |
| Google ADK | A FunctionTool stub. |
Wire the Vertex AI Embeddings API or your chosen embedding service. |
Note: TwinTrack v0.1 generates stubs for
vectorTask. Full implementation is the implementer's responsibility.
3.2.8 observeEvent¶
Emits a trace, span, or log event to an observability platform. Does not block the flow — it is a fire-and-forget side-effect.
- id: string
type: observeEvent
name: string
platform: string # required — "langfuse" | "phoenix" | "otel" | "datadog"
trace_name: string # optional
span_name: string # optional
attributes: object # optional — key-value pairs to attach
Compiler targets — implementation notes:
| Target | What the compiler generates | What you must complete |
|---|---|---|
| Orkes Conductor | A SIMPLE task stub (async, fire-and-forget recommended). |
Implement the worker to POST to your observability platform. For LangFuse: POST /api/v1/trace. For OTEL: use the OTLP exporter. |
| Google ADK | A FunctionTool stub that logs to Cloud Logging by default. |
Replace with your preferred platform's SDK call if not using Cloud Logging. |
4. Gate Types¶
Gates are routing constructs. Unlike BPMN gateways, APMN gates carry AI-specific routing semantics (confidence, reasoning, semantic similarity). All gates compile to conditional branching constructs in target runtimes.
4.1 confidenceGate¶
Routes flow based on a numeric confidence score (0.0–1.0) produced by an upstream agentTask. Replaces the binary exclusiveGateway for AI-native routing.
- id: string
type: confidenceGate
name: string
source: string # required — "taskId.confidence"
routes:
high: string # target node ID for confidence ≥ high_threshold
medium: string # target node ID for confidence in [low_threshold, high_threshold)
low: string # target node ID for confidence < low_threshold
default: string # fallback if source field is absent
high_threshold: float # optional, default 0.85
low_threshold: float # optional, default 0.60
Compiler targets:
| Target | Generated construct | Notes |
|---|---|---|
| Orkes Conductor | SWITCH task evaluating ${taskId.output.confidence} |
TwinTrack v0.1 generates a basic SWITCH; confidence expression wiring is a post-transform step. |
| Google ADK | Conditional branch on agent output field | Wire confidence output from upstream LlmAgent. |
⚠️ Best practice: Always route the
lowpath to ahumanInLoopTask, never directly to anendEvent.
4.2 reasoningGate¶
Routes by parsing the LLM's chain-of-thought or structured output from an upstream agentTask. No hard-coded numeric condition required.
- id: string
type: reasoningGate
name: string
source: string # required — "taskId.reasoning" or "taskId.category"
routes:
- match: string # value to match against source field
target: string # target node ID
default: string
Compiler targets:
| Target | Generated construct | Notes |
|---|---|---|
| Orkes Conductor | SWITCH task on parsed output field |
Requires agentTask to produce structured output (JSON with category or intent field). TwinTrack v0.1 generates a basic SWITCH stub. |
| Google ADK | Conditional on LlmAgent output schema field |
Use output_schema on the upstream agentTask to enforce a structured response. |
4.3 modelVersionGate¶
Routes a percentage of traffic to different model versions. Enables canary rollout and A/B testing of model upgrades without redeploying the workflow.
- id: string
type: modelVersionGate
name: string
routes:
- model: string
weight: float # 0.0–1.0, weights must sum to 1.0
target: string
Compiler targets:
| Target | Generated construct | Notes |
|---|---|---|
| Orkes Conductor | SWITCH on a random-seed variable injected at workflow start |
TwinTrack v0.1 generates a basic SWITCH stub. Full weighted routing requires a pre-task that sets a model_version variable. |
| Google ADK | Conditional on a version selector input | Inject model_version as a workflow input parameter. |
4.4 semanticGate¶
Routes by computing semantic similarity between the process input and a set of intent cluster descriptions using vector embeddings. No keyword rules — intent is inferred.
- id: string
type: semanticGate
name: string
embedding_model: string # optional, default "text-embedding-004"
intents:
- label: string
description: string # natural-language description of this route
target: string
default: string
Compiler targets:
| Target | Generated construct | Notes |
|---|---|---|
| Orkes Conductor | SWITCH on a cluster label from a preceding vectorTask or ragTask |
TwinTrack v0.1 generates a basic SWITCH stub. Wire the cluster label from your embedding step. |
| Google ADK | Conditional on a semantic classifier tool result | Implement a FunctionTool that embeds the input and returns the closest intent label. |
Note: TwinTrack v0.1 generates stubs for
semanticGate. The embedding call is the implementer's responsibility.
4.5 mcpGate¶
Calls an MCP tool and routes based on the tool's return value. Commonly used for compliance checks and external validation steps.
- id: string
type: mcpGate
name: string
server: string
tool: string
routes:
- result_match: string # value to match against tool result
target: string
default: string
Compiler targets:
| Target | Generated construct | Notes |
|---|---|---|
| Orkes Conductor | HTTP task + SWITCH on the tool result field |
Wire result_match against the HTTP response body field. TwinTrack v0.1 generates a basic SWITCH stub. |
| Google ADK | MCPToolset call + conditional on result |
Ensure the MCP server is accessible from your ADK runtime. |
4.6 escapeGate¶
An automatic safety-net gate. Activates if any upstream node fails, exceeds a timeout, or falls below a confidence floor. Routes to a human task or fallback agent.
- id: string
type: escapeGate
name: string
watches: [string] # node IDs to monitor
triggers:
on_failure: string # target node ID
on_timeout: string
on_low_confidence: string
confidence_floor: float # optional, default 0.5
Compiler targets:
| Target | Generated construct | Notes |
|---|---|---|
| Orkes Conductor | DO_WHILE with error handler + SWITCH on condition |
TwinTrack v0.1 generates a SWITCH stub. Full DO_WHILE retry + error handler pattern is a post-transform step. Pair with Orkes task-level retryCount and timeoutSeconds. |
| Google ADK | Error callback handler on the watched agent | Implement on_error / after_agent_callback in the generated LlmAgent. |
⚠️ Spec constraint: An unconnected
escapeGate(no outgoing flows) is a spec violation. Theon_failurepath must always route to ahumanInLoopTaskor another agent — never directly to anendEvent.
5. Sequence Flows¶
Sequence flows follow BPMN 2.0 conventions with an optional condition field.
flows:
- id: string
source: string # source node ID
target: string # target node ID
name: string # optional — label shown on diagram
condition: string # optional — condition expression
6. Swimlanes and Pools¶
APMN uses BPMN 2.0 pools and lanes with two additional lane types:
New lane types:
- agent — contains agentTask, ragTask, mcpToolTask nodes
- tool — contains mcpToolTask, vectorTask nodes
- system — automated serviceTask, scriptTask nodes
7. Compiler Contract¶
Any tool claiming APMN compliance must:
- Accept APMN YAML as defined in this spec
- Accept BPMN 2.0 XML with
apmn:extension elements - Translate all BPMN 2.0 node types correctly
- Translate all APMN v0.1 node types to at least one target runtime
- Produce a
POST_TRANSFORM.mdlisting all touchpoints requiring manual completion - Reject documents that reference undefined node types
8. Versioning¶
APMN follows semantic versioning. This document describes APMN v0.1.
Breaking changes increment the minor version until v1.0, after which semver applies strictly.
The apmn_version field in YAML documents must match a supported spec version.
Appendix A — Compiler Target Mapping¶
A.1 TwinTrack v0.1 Support Status¶
✅ Full — generates ready-to-deploy output (worker implementation still required per Section 3)
⚠️ Stub — generates scaffolding with TODO markers; logic must be completed post-transform
❌ Not yet — not implemented in TwinTrack v0.1
| APMN Node | Orkes Conductor | Google ADK | Notes |
|---|---|---|---|
agentTask |
✅ SIMPLE task |
✅ LlmAgent |
Worker/agent implementation required |
ragTask |
⚠️ SIMPLE stub |
⚠️ LlmAgent + retrieval stub |
Vector store wiring required |
mcpToolTask |
✅ HTTP task |
⚠️ MCPToolset stub |
Server URL + auth required |
humanInLoopTask |
✅ HUMAN task |
⚠️ Callback stub | Human UI / callback handler required |
agentHandoff |
⚠️ SUB_WORKFLOW stub |
⚠️ Sub-agent stub | Sub-agent/sub-workflow must be registered separately |
memoryTask |
⚠️ SIMPLE stub |
⚠️ FunctionTool stub |
Persistence backend required |
vectorTask |
⚠️ SIMPLE stub |
⚠️ FunctionTool stub |
Embedding service required |
observeEvent |
⚠️ SIMPLE stub |
⚠️ FunctionTool stub |
Observability platform client required |
confidenceGate |
⚠️ SWITCH (basic) |
✅ Conditional | Confidence expression wiring is post-transform for Orkes |
reasoningGate |
⚠️ SWITCH (basic) |
✅ Conditional | Structured output from upstream agent required |
modelVersionGate |
⚠️ SWITCH (basic) |
⚠️ Conditional stub | Version selector injection required |
semanticGate |
⚠️ SWITCH (basic) |
⚠️ Conditional stub | Embedding step required |
mcpGate |
⚠️ HTTP + SWITCH stub |
⚠️ MCPToolset + conditional stub |
MCP server required |
escapeGate |
⚠️ SWITCH stub |
⚠️ Error callback stub | DO_WHILE + error handler wiring is post-transform for Orkes |
parallelGateway |
✅ FORK_JOIN |
✅ ParallelAgent |
Fully supported |
exclusiveGateway |
✅ SWITCH |
✅ Conditional | Standard BPMN gateway |
timerEvent |
✅ WAIT task |
⚠️ Stub | Duration wiring required |
A.2 Runtime Mapping Reference¶
| APMN Node | Orkes Conductor | Google ADK (v2.x) | LangGraph |
|---|---|---|---|
agentTask |
SIMPLE worker task |
LlmAgent |
node fn + LLM call |
ragTask |
SIMPLE worker task |
LlmAgent + retrieval FunctionTool |
node fn |
mcpToolTask |
HTTP task |
MCPToolset |
node fn |
humanInLoopTask |
HUMAN task |
before_tool_callback / interrupt |
interrupt node |
agentHandoff |
SUB_WORKFLOW |
Sub-LlmAgent in SequentialAgent |
send_message |
memoryTask |
SIMPLE worker task |
FunctionTool (read/write) |
node fn |
vectorTask |
SIMPLE worker task |
FunctionTool (embed/query) |
node fn |
observeEvent |
SIMPLE (async) |
FunctionTool / Cloud Logging |
passthrough node |
confidenceGate |
SWITCH on confidence field |
Conditional on LlmAgent output |
conditional edge |
reasoningGate |
SWITCH on structured output field |
Conditional on output schema field | conditional edge |
modelVersionGate |
SWITCH on version variable |
Conditional on version input | conditional edge |
semanticGate |
SWITCH on cluster label |
Conditional on classifier result | conditional edge |
mcpGate |
HTTP + SWITCH on result |
MCPToolset call + conditional |
conditional edge |
escapeGate |
DO_WHILE + SWITCH on error/timeout |
on_error / after_agent_callback |
error edge |
parallelGateway |
FORK_JOIN |
ParallelAgent |
parallel edges |
Google ADK note: The
@tooldecorator was removed in google-adk ≥ 2.0. All tool definitions useFunctionToolor are passed as plain Python callables toLlmAgent(tools=[...]).MCPToolsetis the standard integration for MCP servers. See Google ADK documentation for current API.