You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Flink-Agents currently provides Python and Java APIs. On top of these, we want to offer a declarative YAML API.
Compared with the programmatic APIs, the YAML API has the following advantages:
Human-friendly. Low barrier to entry; agents can be templated and easily customized via parameter substitution.
Coding-agent-friendly. A fixed schema saves tokens and enables strict schema validation. It also cleanly separates configuration changes (declared parameters) from logic changes (Action code).
Agent Declaration
This section describes how to declare agents with the YAML API, including:
Declaring a single agent in a YAML file.
Declaring multiple agents in a single YAML file.
Declaring shared Resources and Actions alongside agents, and referencing them from the same file or from other YAML files.
How to declare a single agent
An agent typically consists of three kinds of components: Events, Resources, and Actions.
Events: The unit of communication that flows inside an agent. Events are the signals that drive the whole workflow, and each event carries a type field that identifies its kind.
Actions: Code snippets triggered by an event that emit new events. Actions hold the main business logic.
Resources: Reusable, stateful components that actions can invoke at runtime — for example ChatModel, Tool, Prompt, and VectorStore.
Declaring an agent usually means declaring its Resources and Actions. The YAML below declares an agent named Example Agent:
description (optional): A short description of what the agent does or how it works.
namespace (optional): Corresponds to a Java package or a Python module.
When namespace is set on the agent, function may be omitted on Actions and Tools — the framework will derive the fully-qualified function path from namespace and name. This keeps YAML files more readable.
The fully-qualified name of the method backing this function tool.
When the agent declares a namespace:
function may be just the method name; the framework will join namespace and function into a fully-qualified name.
function may also be omitted entirely; the tool's name will be treated as the method name, and the framework will join namespace and name into a fully-qualified name.
If both function and namespace are absent, an error is raised.
type (optional): The implementation language of the function, java or python.
Multiple agents may be declared in a single YAML file:
agents:
- name: Agent1description: The first agent# resources# actions
- name: Agent2description: The second agent# resources# actions
How to declare and reuse common Resources and Actions
Shared Resources and Actions can be declared at the same level as agents in a YAML file. Such shared components can be referenced by agents declared in the same file or in other YAML files.
Declaring and using shared Resources
agents:
- name: Agent1description: The first agentchat_model_setups:
- name: my_llmclazz: ollamaconnection: my_connectionthinking: false
- name: Agent2description: The second agentchat_model_setups:
- name: my_llmclazz: ollamaconnection: my_connectionthinking: true# common resources for reusechat_model_connections:
- name: my_connectionclazz: ollamabase_url: http://localhost:11434
Shared Resources are registered with the AgentsExecutionEnvironment. Any agent running in the same AgentsExecutionEnvironment can use them directly.
Note: Composing a single agent across multiple YAML files is not supported. If the YAML files parsed together contain duplicate agent names, or duplicate shared Resource or Action names, an exception is thrown.
Declaring and using shared Actions
agents:
- name: Agent1description: The first agentactions:
- action1
- name: my_actionfunction: $module.my_actionon: [input]
- name: Agent2description: The second agentactions:
- action1# common actions for reuseactions:
- name: action1function: $module.action1type: pythonon: [input]
- name: action2function: $module.action2type: pythonon: [chat_response]
Agent Building
AgentsExecutionEnvironment exposes a load_yaml / loadYaml method that loads and parses one or more YAML files. The method:
Registers shared Resources into the environment.
Registers every declared agent into the environment.
classAgentsExecutionEnvironment:
_resources: Dict[ResourceType, Dict[str, Any]]
_agents: Dict[str, Agent]
defload_yaml(self, paths: List[Path|str]) ->None:
"""Load and parse YAML files; all declared agents are registered into self._agents."""
The method may be called multiple times. Results from successive calls are merged; an error is raised if a duplicate agent name, shared Resource, or shared Action is detected.
Usage: callers can look up agents registered in the environment by name.
To help users and coding agents understand and use the YAML API — and to make YAML files validatable — we provide a formal specification of the API. Because Flink-Agents exposes APIs in multiple languages, we describe the specification with JSON Schema, a language-neutral format.
The specification is codegen'd from the Python and Java data structures, so it never needs to be written by hand. This brings two benefits:
A well-defined interface contract, easy for LLMs and other external systems to consume.
Cross-language coverage, which allows us to verify API compatibility across language implementations.
JSON Schema
The JSON Schema below is illustrative — the final specification will continue to evolve alongside the YAML API.
JSON Schema describes the YAML API specification in a language-neutral way. In Python it maps to a Pydantic BaseModel, and in Java to a POJO.
Python — Pydantic BaseModel:
classEventAttribute(BaseModel):
"""Attribute spec under an event schema."""model_config=ConfigDict(extra="forbid")
type: strrequired: bool=FalseclassEventSchema(BaseModel):
"""Advisory schema for a unified event's ``attributes`` map."""model_config=ConfigDict(extra="forbid")
name: strattributes: Dict[str, EventAttribute] =Field(default_factory=dict)
strict: bool=FalseclassPromptMessage(BaseModel):
"""One message in a multi-turn prompt template."""model_config=ConfigDict(extra="forbid")
role: MessageRole=MessageRole.USERcontent: strclassPromptSpec(BaseModel):
"""Declarative prompt: either a single ``text`` template or a list of role-tagged ``messages``. Exactly one of the two fields must be set. """model_config=ConfigDict(extra="forbid")
name: strtext: str|None=Nonemessages: List[PromptMessage] |None=None@model_validator(mode="after")def_require_exactly_one(self) ->"PromptSpec":
if (self.textisNone) == (self.messagesisNone):
msg="prompt must define exactly one of 'text' or 'messages'"raiseValueError(msg)
returnselfclassToolSpec(BaseModel):
"""Points ``function:`` at a module attribute that is a callable tool. When ``function:`` is omitted, the loader falls back to ``<module>.<name>``. """model_config=ConfigDict(extra="forbid")
name: strfunction: str|None=NoneclassDescriptorSpec(BaseModel):
"""Schema for any ResourceDescriptor-backed resource (chat model setups, chat model connections, embedding model setups, embedding model connections, vector stores, MCP servers). ``clazz`` is required and must be a fully-qualified class path; all other fields beyond ``name``/``clazz`` are passed through verbatim as ``ResourceDescriptor`` keyword arguments. Using ``extra="allow"`` so callers can supply provider-specific options without needing a field for each one. """model_config=ConfigDict(extra="allow")
name: strclazz: strdefto_descriptor(self) ->ResourceDescriptor:
"""Build the ``ResourceDescriptor`` this spec describes."""kwargs=dict(self.model_extraor {})
returnResourceDescriptor(clazz=self.clazz, **kwargs)
classActionSpec(BaseModel):
"""An action references a user function and the event types it listens to. When ``function:`` is omitted, the loader falls back to ``<module>.<name>``. """model_config=ConfigDict(extra="forbid")
name: strfunction: str|None=Noneon: List[str]
config: Dict[str, Any] |None=NoneclassAgentDocument(BaseModel):
"""Top-level YAML document. Flat shape: ``name``/``description``/``module`` are top-level fields (no ``agent:`` wrapper); each resource category is its own top-level list (no ``resources:`` wrapper). """model_config=ConfigDict(extra="forbid")
name: str|None=Nonedescription: str|None=Nonemodule: str|None=Noneevents: List[EventSchema] =Field(default_factory=list)
chat_model_connections: List[DescriptorSpec] =Field(default_factory=list)
chat_model_setups: List[DescriptorSpec] =Field(default_factory=list)
embedding_model_connections: List[DescriptorSpec] =Field(default_factory=list)
embedding_model_setups: List[DescriptorSpec] =Field(default_factory=list)
vector_stores: List[DescriptorSpec] =Field(default_factory=list)
mcp_servers: List[DescriptorSpec] =Field(default_factory=list)
prompts: List[PromptSpec] =Field(default_factory=list)
tools: List[ToolSpec] =Field(default_factory=list)
actions: List[ActionSpec] =Field(default_factory=list)
When parsing a YAML file in Python or Java, Flink-Agents materializes it into the Pydantic BaseModel or Java POJO above, and relies on Pydantic / Jackson to perform schema validation.
Consistency guarantees
Because authoring a Pydantic BaseModel is considerably easier than hand-writing JSON Schema, the canonical JSON Schema file is exported from the Pydantic BaseModel and serves as the ground truth.
We will add tests to keep the Python API, Java API, and YAML API consistent:
The JSON Schema exported from the Pydantic BaseModel matches the ground truth.
The JSON Schema exported from the Java POJO matches the ground truth.
The Pydantic BaseModel is consistent with the Python Agent API.
The Java POJO is consistent with the Java Agent API.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
-
Introduction
Flink-Agents currently provides Python and Java APIs. On top of these, we want to offer a declarative YAML API.
Compared with the programmatic APIs, the YAML API has the following advantages:
Agent Declaration
This section describes how to declare agents with the YAML API, including:
How to declare a single agent
An agent typically consists of three kinds of components: Events, Resources, and Actions.
ChatModel,Tool,Prompt, andVectorStore.Declaring an agent usually means declaring its Resources and Actions. The YAML below declares an agent named
Example Agent:Agent properties
name(required): The agent's name.description(optional): A short description of what the agent does or how it works.namespace(optional): Corresponds to a Java package or a Python module.namespaceis set on the agent,functionmay be omitted on Actions and Tools — the framework will derive the fully-qualified function path fromnamespaceandname. This keeps YAML files more readable.Resources
Prompt
name(required): The prompt's name.messages/text(required): Correspond toPrompt.fromMessagesandPrompt.fromTextrespectively. Exactly one of the two must be set.Tool
name(required): The tool's name.function(optional):namespace:functionmay be just the method name; the framework will joinnamespaceandfunctioninto a fully-qualified name.functionmay also be omitted entirely; the tool'snamewill be treated as the method name, and the framework will joinnamespaceandnameinto a fully-qualified name.functionandnamespaceare absent, an error is raised.type(optional): The implementation language of the function,javaorpython.ResourceDescriptor-based Resources (chat model, vector store, embedding model, MCP, skills)
name(required): The resource's name.clazz(required): The fully-qualified class name of the resource.ollama,openai_completion,anthropic.type(optional): The implementation language of the resource,pythonorjava.base_url,endpoint): Passed through verbatim to the resource implementation.Actions
actionsis a list whose elements may each be either a map or a string.name(required): The action's name.function(optional): The fully-qualified name of the method backing this action. Same semantics asfunctionon a tool.type(optional): The implementation language of the action,pythonorjava. Same semantics astypeon a tool.on(required): The event types this action listens to.input,chat_request,chat_response.typestring.How to declare multiple agents
Multiple agents may be declared in a single YAML file:
How to declare and reuse common Resources and Actions
Shared Resources and Actions can be declared at the same level as
agentsin a YAML file. Such shared components can be referenced by agents declared in the same file or in other YAML files.Declaring and using shared Resources
Shared Resources are registered with the
AgentsExecutionEnvironment. Any agent running in the sameAgentsExecutionEnvironmentcan use them directly.Declaring and using shared Actions
Agent Building
AgentsExecutionEnvironmentexposes aload_yaml/loadYamlmethod that loads and parses one or more YAML files. The method:YAML API Specification
To help users and coding agents understand and use the YAML API — and to make YAML files validatable — we provide a formal specification of the API. Because Flink-Agents exposes APIs in multiple languages, we describe the specification with JSON Schema, a language-neutral format.
The specification is codegen'd from the Python and Java data structures, so it never needs to be written by hand. This brings two benefits:
JSON Schema
The JSON Schema below is illustrative — the final specification will continue to evolve alongside the YAML API.
{ "$defs": { "ActionSpec": { "additionalProperties": false, "description": "An action references a user function and the event types it listens to.\n\nWhen ``function:`` is omitted, the loader falls back to\n``<namespace>.<name>``.", "properties": { "config": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Config" }, "function": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Function" }, "name": { "title": "Name", "type": "string" }, "on": { "items": { "type": "string" }, "title": "On", "type": "array" } }, "required": [ "name", "on" ], "title": "ActionSpec", "type": "object" }, "AgentSpec": { "additionalProperties": false, "description": "One agent inside a YAML file's ``agents:`` list.\n\nHolds the agent's own resources and actions. Resources/actions declared\nat the file level (siblings of ``agents:``) are merged in by the loader.", "properties": { "actions": { "items": { "anyOf": [ { "$ref": "#/$defs/ActionSpec" }, { "type": "string" } ] }, "title": "Actions", "type": "array" }, "chat_model_connections": { "items": { "$ref": "#/$defs/DescriptorSpec" }, "title": "Chat Model Connections", "type": "array" }, "chat_model_setups": { "items": { "$ref": "#/$defs/DescriptorSpec" }, "title": "Chat Model Setups", "type": "array" }, "description": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Description" }, "embedding_model_connections": { "items": { "$ref": "#/$defs/DescriptorSpec" }, "title": "Embedding Model Connections", "type": "array" }, "embedding_model_setups": { "items": { "$ref": "#/$defs/DescriptorSpec" }, "title": "Embedding Model Setups", "type": "array" }, "events": { "items": { "$ref": "#/$defs/EventSchema" }, "title": "Events", "type": "array" }, "mcp_servers": { "items": { "$ref": "#/$defs/DescriptorSpec" }, "title": "Mcp Servers", "type": "array" }, "name": { "title": "Name", "type": "string" }, "namespace": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Namespace" }, "prompts": { "items": { "$ref": "#/$defs/PromptSpec" }, "title": "Prompts", "type": "array" }, "skills": { "items": { "$ref": "#/$defs/SkillsSpec" }, "title": "Skills", "type": "array" }, "tools": { "items": { "$ref": "#/$defs/ToolSpec" }, "title": "Tools", "type": "array" }, "vector_stores": { "items": { "$ref": "#/$defs/DescriptorSpec" }, "title": "Vector Stores", "type": "array" } }, "required": [ "name" ], "title": "AgentSpec", "type": "object" }, "DescriptorSpec": { "additionalProperties": true, "description": "Schema for any ResourceDescriptor-backed resource.\n\nRequired: ``name`` and ``clazz``. ``type`` selects the implementation\nlanguage (``\"python\"`` or ``\"java\"``; ``None`` means Python). All\nremaining fields are forwarded verbatim to ``ResourceDescriptor`` as\nkwargs (or as the Java wrapper's kwargs when ``type: java``).", "properties": { "clazz": { "title": "Clazz", "type": "string" }, "name": { "title": "Name", "type": "string" }, "type": { "anyOf": [ { "enum": [ "python", "java" ], "type": "string" }, { "type": "null" } ], "default": null, "title": "Type" } }, "required": [ "name", "clazz" ], "title": "DescriptorSpec", "type": "object" }, "EventAttribute": { "additionalProperties": false, "description": "Attribute spec under an event schema.", "properties": { "required": { "default": false, "title": "Required", "type": "boolean" }, "type": { "title": "Type", "type": "string" } }, "required": [ "type" ], "title": "EventAttribute", "type": "object" }, "EventSchema": { "additionalProperties": false, "description": "Advisory schema for a unified event's ``attributes`` map.", "properties": { "attributes": { "additionalProperties": { "$ref": "#/$defs/EventAttribute" }, "title": "Attributes", "type": "object" }, "name": { "title": "Name", "type": "string" }, "strict": { "default": false, "title": "Strict", "type": "boolean" } }, "required": [ "name" ], "title": "EventSchema", "type": "object" }, "MessageRole": { "description": "Role of a message in a chat conversation.", "enum": [ "system", "user", "assistant", "tool" ], "title": "MessageRole", "type": "string" }, "PromptMessage": { "additionalProperties": false, "description": "One message in a multi-turn prompt template.", "properties": { "content": { "title": "Content", "type": "string" }, "role": { "$ref": "#/$defs/MessageRole", "default": "user" } }, "required": [ "content" ], "title": "PromptMessage", "type": "object" }, "PromptSpec": { "additionalProperties": false, "description": "Declarative prompt: either a single ``text`` template or a list of\nrole-tagged ``messages``. Exactly one of the two fields must be set.", "properties": { "messages": { "anyOf": [ { "items": { "$ref": "#/$defs/PromptMessage" }, "type": "array" }, { "type": "null" } ], "default": null, "title": "Messages" }, "name": { "title": "Name", "type": "string" }, "text": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Text" } }, "required": [ "name" ], "title": "PromptSpec", "type": "object" }, "SkillsSpec": { "additionalProperties": false, "description": "Declarative Skills resource pointing at one or more skill source\ndirectories on the local filesystem.", "properties": { "name": { "title": "Name", "type": "string" }, "paths": { "items": { "type": "string" }, "title": "Paths", "type": "array" } }, "required": [ "name", "paths" ], "title": "SkillsSpec", "type": "object" }, "ToolSpec": { "additionalProperties": false, "description": "Points ``function:`` at a module attribute that is a callable tool.\n\nWhen ``function:`` is omitted, the loader falls back to\n``<namespace>.<name>``.", "properties": { "function": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Function" }, "name": { "title": "Name", "type": "string" } }, "required": [ "name" ], "title": "ToolSpec", "type": "object" } }, "additionalProperties": false, "description": "Top-level YAML document.\n\nAlways wraps one or more agents under ``agents:``. Resources and\nactions declared at the same level as ``agents:`` are shared:\nresources are registered on the environment; actions can be\nreferenced from any agent by name string.", "properties": { "actions": { "items": { "$ref": "#/$defs/ActionSpec" }, "title": "Actions", "type": "array" }, "agents": { "items": { "$ref": "#/$defs/AgentSpec" }, "title": "Agents", "type": "array" }, "chat_model_connections": { "items": { "$ref": "#/$defs/DescriptorSpec" }, "title": "Chat Model Connections", "type": "array" }, "chat_model_setups": { "items": { "$ref": "#/$defs/DescriptorSpec" }, "title": "Chat Model Setups", "type": "array" }, "embedding_model_connections": { "items": { "$ref": "#/$defs/DescriptorSpec" }, "title": "Embedding Model Connections", "type": "array" }, "embedding_model_setups": { "items": { "$ref": "#/$defs/DescriptorSpec" }, "title": "Embedding Model Setups", "type": "array" }, "events": { "items": { "$ref": "#/$defs/EventSchema" }, "title": "Events", "type": "array" }, "mcp_servers": { "items": { "$ref": "#/$defs/DescriptorSpec" }, "title": "Mcp Servers", "type": "array" }, "prompts": { "items": { "$ref": "#/$defs/PromptSpec" }, "title": "Prompts", "type": "array" }, "skills": { "items": { "$ref": "#/$defs/SkillsSpec" }, "title": "Skills", "type": "array" }, "tools": { "items": { "$ref": "#/$defs/ToolSpec" }, "title": "Tools", "type": "array" }, "vector_stores": { "items": { "$ref": "#/$defs/DescriptorSpec" }, "title": "Vector Stores", "type": "array" } }, "required": [ "agents" ], "title": "YamlAgentsDocument", "type": "object" }Mapping and validation in Python and Java
JSON Schema describes the YAML API specification in a language-neutral way. In Python it maps to a Pydantic
BaseModel, and in Java to a POJO.Python — Pydantic
BaseModel:Java — POJO:
When parsing a YAML file in Python or Java, Flink-Agents materializes it into the Pydantic
BaseModelor Java POJO above, and relies on Pydantic / Jackson to perform schema validation.Consistency guarantees
Because authoring a Pydantic
BaseModelis considerably easier than hand-writing JSON Schema, the canonical JSON Schema file is exported from the PydanticBaseModeland serves as the ground truth.We will add tests to keep the Python API, Java API, and YAML API consistent:
BaseModelmatches the ground truth.BaseModelis consistent with the PythonAgentAPI.AgentAPI.Beta Was this translation helpful? Give feedback.
All reactions