This is an automated email from the ASF dual-hosted git repository.

xintongsong pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/flink-agents.git


The following commit(s) were added to refs/heads/main by this push:
     new 1628a2b7 [docs][examples] Add Agent Skills documentation and 
quickstart example (#707)
1628a2b7 is described below

commit 1628a2b74f31c8f717b0c9525a44fba161a595c6
Author: Wenjin Xie <[email protected]>
AuthorDate: Thu May 28 00:02:27 2026 +0800

    [docs][examples] Add Agent Skills documentation and quickstart example 
(#707)
    
    Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
---
 docs/content/docs/development/skills.md            | 222 +++++++++++++
 .../docs/get-started/quickstart/skills_agent.md    | 360 +++++++++++++++++++++
 .../flink/agents/examples/SkillsAgentExample.java  |  71 ++++
 .../flink/agents/examples/agents/MathAgent.java    | 101 ++++++
 .../main/resources/skills/math-calculator/SKILL.md |  43 +++
 .../examples/quickstart/agents/math_agent.py       |  99 ++++++
 .../resources/skills/math-calculator/SKILL.md      |  43 +++
 .../examples/quickstart/skills_agent_example.py    |  77 +++++
 python/pyproject.toml                              |   2 +-
 9 files changed, 1017 insertions(+), 1 deletion(-)

diff --git a/docs/content/docs/development/skills.md 
b/docs/content/docs/development/skills.md
new file mode 100644
index 00000000..d07c84ed
--- /dev/null
+++ b/docs/content/docs/development/skills.md
@@ -0,0 +1,222 @@
+---
+title: Skills
+weight: 10
+type: docs
+---
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+## Overview
+
+A **Skill** is a self-contained package of instructions, and optionally 
scripts and reference files, that teaches the agent how to perform a 
specialized task. A skill is just a directory containing a `SKILL.md` file. 
Flink Agents discovers the skills you declare, lets the agent decide which one 
is relevant to the current request, and loads its full instructions only when 
needed.
+
+Skills follow a **progressive disclosure** model, so that providing the agent 
with many capabilities does not bloat every request:
+
+1. **Discovery** — at startup, only each skill's `name` and `description` are 
injected into the system prompt (a few dozen tokens per skill), so the agent 
knows what is available.
+2. **Activation** — when the agent judges a skill relevant, it calls the 
built-in `load_skill` tool to read the full `SKILL.md` instructions into the 
context.
+3. **Execution** — the agent follows the loaded instructions, running any 
bundled scripts or shell commands through the built-in `bash` tool, and reading 
additional reference files only on demand.
+
+Skills are a good fit when a capability is best described as a procedure (a 
runbook the agent follows) rather than a single function call. For a single, 
well-typed operation, prefer a [tool]({{< ref "docs/development/tool_use" >}}) 
instead.
+
+## Skill Format
+
+A skill is a directory whose name matches the skill, containing a `SKILL.md` 
file with YAML frontmatter and a Markdown body:
+
+````markdown
+---
+name: math-calculator
+description: Calculate mathematical expressions using shell commands. Use when 
the user asks to perform arithmetic like addition, subtraction, multiplication, 
division, or powers.
+license: Apache-2.0
+compatibility: Requires bash with bc (basic calculator)
+---
+
+# Math Calculator Skill
+
+## When to Use
+Use this skill whenever the user asks to evaluate a numeric expression.
+
+## Method
+Evaluate expressions with the `bc` calculator:
+
+```bash
+echo "(2 + 3) * 4" | bc
+# Output: 20
+```
+````
+
+**Frontmatter fields:**
+
+| Field | Required | Description |
+|-------|----------|-------------|
+| `name` | Yes | Skill identifier. 1–64 characters, lowercase letters, numbers 
and hyphens only (no leading/trailing hyphen). Must match the value referenced 
in the chat model's `skills` list. |
+| `description` | Yes | 1–1024 characters. Loaded at discovery time — write it 
so the agent can decide *when* to use the skill. State both what it does and 
when to use it. |
+| `license` | No | License of the skill. |
+| `compatibility` | No | Free-text note on runtime requirements (e.g. required 
commands), up to 500 characters. |
+
+The Markdown body is the full instruction set loaded on activation. It may 
reference bundled files using paths relative to the skill directory (for 
example `python scripts/gen_joke.py`); those scripts and reference files are 
loaded only when the agent actually needs them.
+
+A skill source is a directory holding one or more such skill subdirectories 
(or a `.zip` of that layout):
+
+```
+skills/
+├── math-calculator/
+│   └── SKILL.md
+└── joke-generator/
+    ├── SKILL.md
+    └── scripts/
+        └── gen_joke.py
+```
+
+## Declare Skills in an Agent
+
+Declare where to load skills from with the `@skills`/`@Skills` 
decorator/annotation. The method returns a `Skills` resource built with one of 
its factory methods.
+
+{{< tabs "Declare Skills in Agent" >}}
+
+{{< tab "Python" >}}
+```python
+from flink_agents.api.agents.agent import Agent
+from flink_agents.api.decorators import skills
+from flink_agents.api.skills import Skills
+
+
+class MathAgent(Agent):
+
+    @skills
+    @staticmethod
+    def my_skills() -> Skills:
+        # Load all skill subdirectories under a local directory.
+        return Skills.from_local_dir("/path/to/skills")
+```
+{{< /tab >}}
+
+{{< tab "Java" >}}
+```java
+import org.apache.flink.agents.api.agents.Agent;
+// The @Skills annotation and the Skills resource share a simple name but live
+// in different packages, so fully-qualify the annotation when importing the 
class.
+import org.apache.flink.agents.api.skills.Skills;
+
+public class MathAgent extends Agent {
+
+    @org.apache.flink.agents.api.annotation.Skills
+    public static Skills mySkills() {
+        // Load all skill subdirectories from a classpath resource (packaged 
in the jar).
+        return Skills.fromClasspath("skills");
+    }
+}
+```
+{{< /tab >}}
+
+{{< /tabs >}}
+
+**Key points:**
+- Use the decorator/annotation to declare a skill source.
+  - In Python, use `@skills`.
+  - In Java, use `@Skills`.
+- Declare more than one `@skills`/`@Skills` method on the same agent to 
combine sources; the runtime merges them and de-duplicates identical entries.
+- Declaring a skill source only makes the skills *available*. A skill is 
exposed to a chat model only when that model lists it in its `skills` (see 
[Enable Skills on a Chat Model](#enable-skills-on-a-chat-model)).
+
+### Skill Sources
+
+Each factory method creates a source with a different scheme:
+
+| Factory method (Python / Java) | Scheme | Description |
+|--------------------------------|--------|-------------|
+| `Skills.from_local_dir(*paths)` / `Skills.fromLocalDir(String...)` | `local` 
| One or more local directories, or `.zip` files, holding skill subdirectories. 
The path must be resolvable on the Flink TaskManager that runs the agent. |
+| `Skills.from_url(*urls)` / `Skills.fromUrl(String...)` | `url` | One or more 
`http(s)` URLs, each pointing to a `.zip` whose top level holds the skill 
subdirectories. |
+| `Skills.from_package(*pairs)` | `package` | **Python only.** One or more 
`(package, resource)` tuples locating skills inside an installed Python 
package. |
+| `Skills.fromClasspath(String...)` | `classpath` | **Java only.** One or more 
classpath resource paths (e.g. under `src/main/resources/skills`). When 
packaged into a jar, the resource is materialized to a temp directory at 
runtime. |
+
+{{< hint info >}}
+The `package` scheme is Python-only and the `classpath` scheme is Java-only. A 
plan written in one language using the other language's scheme deserializes 
fine, but fails fast at load time. Use `local` or `url` for cross-language 
skill sources.
+{{< /hint >}}
+
+## Enable Skills on a Chat Model
+
+A declared skill becomes usable only when a chat model opts in by listing it 
in `skills`. When `skills` is set, the framework automatically:
+
+- injects the discovery prompt (the names and descriptions of the listed 
skills) into the system messages, and
+- adds the two built-in tools the agent needs — `load_skill` (to read a 
skill's full instructions) and `bash` (to run its commands and scripts).
+
+{{< tabs "Enable Skills on a Chat Model" >}}
+
+{{< tab "Python" >}}
+```python
+@chat_model_setup
+@staticmethod
+def math_model() -> ResourceDescriptor:
+    return ResourceDescriptor(
+        clazz=ResourceName.ChatModel.OLLAMA_SETUP,
+        connection="ollama_server",
+        model="qwen3.5:9b",
+        prompt="system_prompt",
+        # Expose declared skills to this model by name.
+        skills=["math-calculator"],
+        # Whitelist the shell commands the bash tool is allowed to run.
+        allowed_commands=["echo", "bc"],
+    )
+```
+{{< /tab >}}
+
+{{< tab "Java" >}}
+```java
+@ChatModelSetup
+public static ResourceDescriptor mathModel() {
+    return 
ResourceDescriptor.Builder.newBuilder(ResourceName.ChatModel.OLLAMA_SETUP)
+            .addInitialArgument("connection", "ollamaChatModelConnection")
+            .addInitialArgument("model", "qwen3.5:9b")
+            .addInitialArgument("prompt", "systemPrompt")
+            // Expose declared skills to this model by name.
+            .addInitialArgument("skills", List.of("math-calculator"))
+            // Whitelist the shell commands the bash tool is allowed to run.
+            .addInitialArgument("allowed_commands", List.of("echo", "bc"))
+            .build();
+}
+```
+{{< /tab >}}
+
+{{< /tabs >}}
+
+**Key points:**
+- `skills` lists the skill names (matching the `name` field in each 
`SKILL.md`) the agent may use.
+- `allowed_commands` is a whitelist of shell command names the built-in `bash` 
tool may execute. Any command not on the list is rejected, so keep it as narrow 
as the skills require (for example `echo` and `bc` for arithmetic).
+- The `load_skill` and `bash` tools are added automatically — you do not 
declare them in `tools`. They are added alongside any tools you do declare.
+- Make sure the system prompt instructs the agent to load the relevant skill 
before acting, for example: *"You must load the skill first and strictly follow 
its instructions."* Without this nudge, smaller models may answer directly 
instead of consulting the skill.
+
+Skills work with both the [Workflow Agent]({{< ref 
"docs/development/workflow_agent" >}}) (configure the chat model via 
`@chat_model_setup`/`@ChatModelSetup` as above) and the [ReAct Agent]({{< ref 
"docs/development/react_agent" >}}) (set `skills` and `allowed_commands` on the 
`ReActAgent`'s chat model descriptor, and register the `Skills` resource on the 
execution environment with `add_resource(..., ResourceType.SKILLS, ...)`).
+
+## How Skills Work
+
+Once enabled, a request flows through the three progressive-disclosure stages:
+
+1. **Discovery.** The discovery prompt lists each available skill's `name` and 
`description` and explains how to load one. This is the only skill content the 
agent sees by default:
+   ```text
+   ## Available Skills
+   <available_skills>
+   <skill>
+   <name>math-calculator</name>
+   <description>Calculate mathematical expressions using shell commands. Use 
when ...</description>
+   </skill>
+   </available_skills>
+   ```
+2. **Activation.** When the agent decides a skill applies, it calls 
`load_skill(name="math-calculator")`. The framework returns the full `SKILL.md` 
body, including the skill's base directory and the absolute paths of its 
bundled resources.
+3. **Execution.** Following the loaded instructions, the agent invokes `bash` 
to run commands (e.g. `echo "(2 + 3) * 4" | bc`) or bundled scripts (e.g. 
`python scripts/gen_joke.py`), and loads additional reference files only when 
an instruction points to them.
+
+This keeps each request lean — a skill the agent never activates costs only 
its one-line description.
diff --git a/docs/content/docs/get-started/quickstart/skills_agent.md 
b/docs/content/docs/get-started/quickstart/skills_agent.md
new file mode 100644
index 00000000..04eb0461
--- /dev/null
+++ b/docs/content/docs/get-started/quickstart/skills_agent.md
@@ -0,0 +1,360 @@
+---
+title: 'Skills'
+weight: 3
+type: docs
+---
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+## Overview
+
+A [Skill]({{< ref "docs/development/skills" >}}) is a self-contained package 
of instructions, and optionally scripts and reference files, that teaches the 
agent how to perform a specialized task. Skills are loaded with *progressive 
disclosure*: only each skill's name and description are shown to the agent up 
front, and the full instructions are pulled in on demand when the agent decides 
a skill is relevant.
+
+This quickstart builds a small streaming agent that answers arithmetic 
questions. Rather than letting the LLM compute by itself, the agent exposes a 
`math-calculator` skill; for each question the agent loads the skill and 
follows its instructions to compute the result with the `bc` calculator through 
the built-in `bash` tool. It demonstrates the full skill lifecycle — discovery, 
activation, and execution — in a Flink streaming job.
+
+## Code Walkthrough
+
+### Define the Skill
+
+A skill is a directory containing a `SKILL.md` file with YAML frontmatter 
(loaded at discovery time) and a Markdown body (loaded on activation). Here is 
`skills/math-calculator/SKILL.md`:
+
+````markdown
+---
+name: math-calculator
+description: Calculate mathematical expressions using shell commands. Use when 
the user asks to perform arithmetic calculations like addition, subtraction, 
multiplication, division, or powers.
+license: Apache-2.0
+compatibility: Requires bash with bc (basic calculator)
+---
+
+# Math Calculator Skill
+
+## When to Use
+Use this skill when the user asks to evaluate a numeric expression.
+
+## Method
+Pipe the expression into the `bc` (basic calculator) command:
+
+```bash
+echo "(2 + 3) * 4" | bc
+# Output: 20
+```
+````
+
+### Create the Agent
+
+The agent declares where to load skills from with the `@skills`/`@Skills` 
decorator/annotation, and enables the skill on its chat model by listing it in 
`skills` together with the `allowed_commands` whitelist for the `bash` tool. 
For more details, please refer to the [Skills]({{< ref 
"docs/development/skills" >}}) documentation.
+
+{{< tabs "Create the Agent" >}}
+
+{{< tab "Python" >}}
+```python
+class MathAgent(Agent):
+    """An agent that answers arithmetic questions using the math-calculator 
skill."""
+
+    @skills
+    @staticmethod
+    def my_skills() -> Skills:
+        """Declare where to load skills from."""
+        # Skills are bundled under this example package, loaded by package 
name.
+        return Skills.from_package(
+            ("flink_agents.examples.quickstart", "resources/skills")
+        )
+
+    @prompt
+    @staticmethod
+    def system_prompt() -> Prompt:
+        """System prompt instructing the agent to use the skill."""
+        return Prompt.from_messages(
+            messages=[
+                ChatMessage(
+                    role=MessageRole.SYSTEM,
+                    content="You are a helpful math assistant. Use the "
+                    "math-calculator skill when asked to evaluate an 
expression. "
+                    "You must load the skill first and strictly follow its "
+                    "instructions. Reply with only the final numeric result.",
+                )
+            ],
+        )
+
+    @chat_model_setup
+    @staticmethod
+    def math_model() -> ResourceDescriptor:
+        """ChatModel with the math-calculator skill enabled."""
+        return ResourceDescriptor(
+            clazz=ResourceName.ChatModel.OLLAMA_SETUP,
+            connection="ollama_server",
+            model="qwen3:8b",
+            prompt="system_prompt",
+            # Expose the declared skill to this model by name.
+            skills=["math-calculator"],
+            # Whitelist the shell commands the built-in bash tool may run.
+            allowed_commands=["echo", "bc"],
+        )
+
+    @action(InputEvent.EVENT_TYPE)
+    @staticmethod
+    def process_input(event: Event, ctx: RunnerContext) -> None:
+        """Process input event and send a chat request to evaluate the 
question."""
+        question: str = InputEvent.from_event(event).input
+        ctx.send_event(
+            ChatRequestEvent(
+                model="math_model",
+                messages=[ChatMessage(role=MessageRole.USER, 
content=question)],
+            )
+        )
+
+    @action(ChatResponseEvent.EVENT_TYPE)
+    @staticmethod
+    def process_chat_response(event: Event, ctx: RunnerContext) -> None:
+        """Process chat response event and send the answer as output."""
+        chat_response = ChatResponseEvent.from_event(event)
+        ctx.send_event(OutputEvent(output=chat_response.response.content))
+```
+{{< /tab >}}
+
+{{< tab "Java" >}}
+```java
+/** An agent that answers arithmetic questions using the math-calculator 
skill. */
+public class MathAgent extends Agent {
+
+    /** Load skills from the skills/ directory packaged on the classpath. */
+    @org.apache.flink.agents.api.annotation.Skills
+    public static Skills mySkills() {
+        return Skills.fromClasspath("skills");
+    }
+
+    /** System prompt instructing the agent to use the skill. */
+    @Prompt
+    public static org.apache.flink.agents.api.prompt.Prompt systemPrompt() {
+        return org.apache.flink.agents.api.prompt.Prompt.fromMessages(
+                Collections.singletonList(
+                        new ChatMessage(
+                                MessageRole.SYSTEM,
+                                "You are a helpful math assistant. Use the 
math-calculator skill "
+                                        + "when asked to evaluate an 
expression. You must load the "
+                                        + "skill first and strictly follow its 
instructions. Reply "
+                                        + "with only the final numeric 
result.")));
+    }
+
+    /** ChatModel with the math-calculator skill enabled. */
+    @ChatModelSetup
+    public static ResourceDescriptor mathModel() {
+        return 
ResourceDescriptor.Builder.newBuilder(ResourceName.ChatModel.OLLAMA_SETUP)
+                .addInitialArgument("connection", "ollamaChatModelConnection")
+                .addInitialArgument("model", "qwen3:8b")
+                .addInitialArgument("prompt", "systemPrompt")
+                // Expose the declared skill to this model by name.
+                .addInitialArgument("skills", List.of("math-calculator"))
+                // Whitelist the shell commands the built-in bash tool may run.
+                .addInitialArgument("allowed_commands", List.of("echo", "bc"))
+                .build();
+    }
+
+    /** Process input event and send a chat request to evaluate the question. 
*/
+    @Action(listenEventTypes = {InputEvent.EVENT_TYPE})
+    public static void processInput(InputEvent event, RunnerContext ctx) {
+        ctx.sendEvent(
+                new ChatRequestEvent(
+                        "mathModel",
+                        Collections.singletonList(
+                                new ChatMessage(MessageRole.USER, (String) 
event.getInput()))));
+    }
+
+    /** Process chat response event and send the answer as output. */
+    @Action(listenEventTypes = {ChatResponseEvent.EVENT_TYPE})
+    public static void processChatResponse(ChatResponseEvent event, 
RunnerContext ctx) {
+        ctx.sendEvent(new OutputEvent(event.getResponse().getContent()));
+    }
+}
+```
+{{< /tab >}}
+
+{{< /tabs >}}
+
+**Key points:**
+- `@skills`/`@Skills` declares a skill source. `Skills.from_package` (Python) 
loads skills bundled inside an installed package by `(package, resource)`; 
`Skills.fromClasspath` (Java) loads them from a classpath resource packaged in 
the jar.
+- A declared skill is exposed to a model only when the model lists it in 
`skills`. The `load_skill` and `bash` tools are then added automatically.
+- `allowed_commands` whitelists the shell commands the `bash` tool may run — 
keep it as narrow as the skill requires.
+
+### Integrate the Agent with Flink
+
+Register the Ollama chat model connection, create a stream of questions, and 
apply the agent.
+
+{{< tabs "Integrate the Agent with Flink" >}}
+
+{{< tab "Python" >}}
+```python
+# Set up the Flink streaming environment and the Agents execution environment.
+env = StreamExecutionEnvironment.get_execution_environment()
+agents_env = AgentsExecutionEnvironment.get_execution_environment(env)
+
+# Add Ollama chat model connection to be used by the MathAgent.
+agents_env.add_resource(
+    "ollama_server", ResourceType.CHAT_MODEL_CONNECTION, 
ollama_server_descriptor
+)
+
+# A small stream of arithmetic questions to answer.
+question_stream = env.from_collection(
+    ["What is (2 + 3) * 4?", "Compute 2 ^ 10.", "What is 144 divided by 12?"]
+)
+
+# Use the MathAgent to answer each question with the math-calculator skill.
+answer_stream = (
+    agents_env.from_datastream(input=question_stream, key_selector=lambda x: x)
+    .apply(MathAgent())
+    .to_datastream()
+)
+
+# Print the answers to stdout, then execute the Flink pipeline.
+answer_stream.print()
+agents_env.execute()
+```
+{{< /tab >}}
+
+{{< tab "Java" >}}
+```java
+// Set up the Flink streaming environment and the Agents execution environment.
+StreamExecutionEnvironment env = 
StreamExecutionEnvironment.getExecutionEnvironment();
+env.setParallelism(1);
+AgentsExecutionEnvironment agentsEnv =
+        AgentsExecutionEnvironment.getExecutionEnvironment(env);
+
+// Add Ollama chat model connection to be used by the MathAgent.
+agentsEnv.addResource(
+        "ollamaChatModelConnection",
+        ResourceType.CHAT_MODEL_CONNECTION,
+        CustomTypesAndResources.OLLAMA_SERVER_DESCRIPTOR);
+
+// A small stream of arithmetic questions to answer.
+DataStream<String> questionStream =
+        env.fromData("What is (2 + 3) * 4?", "Compute 2 ^ 10.", "What is 144 
divided by 12?");
+
+// Use the MathAgent to answer each question with the math-calculator skill.
+DataStream<Object> answerStream =
+        agentsEnv.fromDataStream(questionStream).apply(new 
MathAgent()).toDataStream();
+
+// Print the answers to stdout, then execute the Flink pipeline.
+answerStream.print();
+agentsEnv.execute();
+```
+{{< /tab >}}
+
+{{< /tabs >}}
+
+## Run the Example
+
+### Prerequisites
+
+* Unix-like environment (we use Linux, Mac OS X, Cygwin, WSL)
+* Git
+* Java 11+
+* Python 3.10, 3.11 or 3.12
+* `bc` (basic calculator), used by the `math-calculator` skill — preinstalled 
on most Unix-like systems
+
+### Preparation
+
+#### Prepare Flink and Flink Agents
+
+Follow the [installation]({{< ref "docs/get-started/installation" >}}) 
instructions to setup Flink and the Flink Agents.
+
+#### Clone the Flink Agents Repository (if not done already)
+
+```bash
+git clone https://github.com/apache/flink-agents.git
+cd flink-agents
+```
+
+{{< hint info >}}
+For python examples, you can skip this step and submit the python file in 
installed flink-agents wheel.
+{{< /hint >}}
+
+#### Deploy a Standalone Flink Cluster
+
+You can deploy a standalone Flink cluster in your local environment with the 
following command.
+
+{{< tabs "Deploy a Standalone Flink Cluster" >}}
+
+{{< tab "Python" >}}
+```bash
+export PYTHONPATH=$(python -c 'import sysconfig; 
print(sysconfig.get_paths()["purelib"])')
+$FLINK_HOME/bin/start-cluster.sh
+```
+{{< /tab >}}
+
+{{< tab "Java" >}}
+1. Build Flink Agents from source to generate example jar. See 
[installation]({{< ref "docs/get-started/installation" >}}) for more details.
+2. Start the Flink cluster
+    ```bash
+    $FLINK_HOME/bin/start-cluster.sh
+    ```
+
+{{< hint info >}}
+To run example on JDK 21+, append jvm option 
`--add-exports=java.base/jdk.internal.vm=ALL-UNNAMED` to 
[env.java.opts.all](https://nightlies.apache.org/flink/flink-docs-stable/docs/deployment/config/#env-java-opts-all)
 in `$FLINK_HOME/conf/config.yaml` before starting the Flink cluster.
+{{< /hint >}}
+{{< /tab >}}
+
+{{< /tabs >}}
+You can refer to the [local 
cluster](https://nightlies.apache.org/flink/flink-docs-release-1.20/docs/try-flink/local_installation/#starting-and-stopping-a-local-cluster)
 instructions for more detailed steps.
+
+{{< hint info >}}
+If you can't navigate to the web UI at [localhost:8081](localhost:8081), you 
can find the reason in `$FLINK_HOME/log`. If the reason is port conflict, you 
can change the port in `$FLINK_HOME/conf/config.yaml`.
+{{< /hint >}}
+
+#### Prepare Ollama
+
+Download and install Ollama from the official 
[website](https://ollama.com/download).
+
+{{< hint info >}}
+Ollama server **0.9.0** or higher is required.
+{{< /hint >}}
+
+Then pull the qwen3:8b model, which is required by the quickstart examples.
+
+```bash
+ollama pull qwen3:8b
+```
+
+### Submit Flink Agents Job to Standalone Flink Cluster
+
+#### Submit to Flink Cluster
+
+{{< tabs "Submit to Flink Cluster" >}}
+
+{{< tab "Python" >}}
+```bash
+export PYTHONPATH=$(python -c 'import sysconfig; 
print(sysconfig.get_paths()["purelib"])')
+
+# Run the agent skills example
+$FLINK_HOME/bin/flink run -py 
./flink-agents/python/flink_agents/examples/quickstart/skills_agent_example.py
+# or submit the example python file in installed flink-agents wheel
+$FLINK_HOME/bin/flink run -py  
$PYTHONPATH/flink_agents/examples/quickstart/skills_agent_example.py
+```
+{{< /tab >}}
+
+{{< tab "Java" >}}
+```bash
+$FLINK_HOME/bin/flink run -c 
org.apache.flink.agents.examples.SkillsAgentExample 
./flink-agents/examples/target/flink-agents-examples-$VERSION.jar
+```
+{{< /tab >}}
+
+{{< /tabs >}}
+
+Now you should see a Flink job submitted to the Flink Cluster in Flink web UI 
[localhost:8081](localhost:8081).
+
+After a few minutes, you can check for the output in the TaskManager output 
log.
diff --git 
a/examples/src/main/java/org/apache/flink/agents/examples/SkillsAgentExample.java
 
b/examples/src/main/java/org/apache/flink/agents/examples/SkillsAgentExample.java
new file mode 100644
index 00000000..8335dec0
--- /dev/null
+++ 
b/examples/src/main/java/org/apache/flink/agents/examples/SkillsAgentExample.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.flink.agents.examples;
+
+import org.apache.flink.agents.api.AgentsExecutionEnvironment;
+import org.apache.flink.agents.api.agents.AgentExecutionOptions;
+import org.apache.flink.agents.api.resource.ResourceType;
+import org.apache.flink.agents.examples.agents.CustomTypesAndResources;
+import org.apache.flink.agents.examples.agents.MathAgent;
+import org.apache.flink.streaming.api.datastream.DataStream;
+import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
+
+/**
+ * Java example demonstrating Agent Skills with Flink Agents.
+ *
+ * <p>A stream of arithmetic questions is processed by {@link MathAgent}, 
whose chat model has a
+ * {@code math-calculator} skill enabled. For each question the model loads 
the skill and follows
+ * its instructions to compute the answer with the {@code bc} calculator, then 
the result is printed
+ * to stdout. This serves as a minimal, end-to-end example of integrating 
skill-powered agents with
+ * Flink streaming jobs.
+ */
+public class SkillsAgentExample {
+
+    /** Runs the example pipeline. */
+    public static void main(String[] args) throws Exception {
+        // Set up the Flink streaming environment and the Agents execution 
environment.
+        StreamExecutionEnvironment env = 
StreamExecutionEnvironment.getExecutionEnvironment();
+        env.setParallelism(1);
+        AgentsExecutionEnvironment agentsEnv =
+                AgentsExecutionEnvironment.getExecutionEnvironment(env);
+
+        // limit async request to avoid overwhelming ollama server
+        agentsEnv.getConfig().set(AgentExecutionOptions.NUM_ASYNC_THREADS, 2);
+
+        // Add Ollama chat model connection to be used by the MathAgent.
+        agentsEnv.addResource(
+                "ollamaChatModelConnection",
+                ResourceType.CHAT_MODEL_CONNECTION,
+                CustomTypesAndResources.OLLAMA_SERVER_DESCRIPTOR);
+
+        // A small stream of arithmetic questions to answer.
+        DataStream<String> questionStream =
+                env.fromData(
+                        "What is (2 + 3) * 4?", "Compute 2 ^ 10.", "What is 
144 divided by 12?");
+
+        // Use the MathAgent to answer each question with the math-calculator 
skill.
+        DataStream<Object> answerStream =
+                agentsEnv.fromDataStream(questionStream).apply(new 
MathAgent()).toDataStream();
+
+        // Print the answers to stdout.
+        answerStream.print();
+
+        // Execute the Flink pipeline.
+        agentsEnv.execute();
+    }
+}
diff --git 
a/examples/src/main/java/org/apache/flink/agents/examples/agents/MathAgent.java 
b/examples/src/main/java/org/apache/flink/agents/examples/agents/MathAgent.java
new file mode 100644
index 00000000..1056f877
--- /dev/null
+++ 
b/examples/src/main/java/org/apache/flink/agents/examples/agents/MathAgent.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.flink.agents.examples.agents;
+
+import org.apache.flink.agents.api.InputEvent;
+import org.apache.flink.agents.api.OutputEvent;
+import org.apache.flink.agents.api.agents.Agent;
+import org.apache.flink.agents.api.annotation.Action;
+import org.apache.flink.agents.api.annotation.ChatModelSetup;
+import org.apache.flink.agents.api.annotation.Prompt;
+import org.apache.flink.agents.api.chat.messages.ChatMessage;
+import org.apache.flink.agents.api.chat.messages.MessageRole;
+import org.apache.flink.agents.api.context.RunnerContext;
+import org.apache.flink.agents.api.event.ChatRequestEvent;
+import org.apache.flink.agents.api.event.ChatResponseEvent;
+import org.apache.flink.agents.api.resource.ResourceDescriptor;
+import org.apache.flink.agents.api.resource.ResourceName;
+import org.apache.flink.agents.api.skills.Skills;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * An agent that answers arithmetic questions using the {@code 
math-calculator} skill.
+ *
+ * <p>Instead of relying on the LLM to compute by itself, this agent exposes a 
{@code
+ * math-calculator} skill. When asked to evaluate an expression, the model 
loads the skill ({@code
+ * load_skill}) and follows its instructions to compute the result with the 
{@code bc} calculator
+ * through the built-in {@code bash} tool.
+ */
+public class MathAgent extends Agent {
+
+    /**
+     * Load skills from the {@code skills/} directory packaged on the 
classpath.
+     *
+     * <p>The {@code @Skills} annotation and the {@link Skills} resource share 
a simple name but
+     * live in different packages, so the annotation is fully-qualified here.
+     */
+    @org.apache.flink.agents.api.annotation.Skills
+    public static Skills mySkills() {
+        return Skills.fromClasspath("skills");
+    }
+
+    /** System prompt instructing the model to use the skill. */
+    @Prompt
+    public static org.apache.flink.agents.api.prompt.Prompt systemPrompt() {
+        return org.apache.flink.agents.api.prompt.Prompt.fromMessages(
+                Collections.singletonList(
+                        new ChatMessage(
+                                MessageRole.SYSTEM,
+                                "You are a helpful math assistant. Use the 
math-calculator skill "
+                                        + "when asked to evaluate an 
expression. You must load the "
+                                        + "skill first and strictly follow its 
instructions. Reply "
+                                        + "with only the final numeric 
result.")));
+    }
+
+    /** ChatModel with the math-calculator skill enabled. */
+    @ChatModelSetup
+    public static ResourceDescriptor mathModel() {
+        return 
ResourceDescriptor.Builder.newBuilder(ResourceName.ChatModel.OLLAMA_SETUP)
+                .addInitialArgument("connection", "ollamaChatModelConnection")
+                .addInitialArgument("model", "qwen3.5:9b")
+                .addInitialArgument("prompt", "systemPrompt")
+                // Expose the declared skill to this model by name.
+                .addInitialArgument("skills", List.of("math-calculator"))
+                // Whitelist the shell commands the built-in bash tool may run.
+                .addInitialArgument("allowed_commands", List.of("echo", "bc"))
+                .build();
+    }
+
+    /** Process input event and send a chat request to evaluate the question. 
*/
+    @Action(listenEventTypes = {InputEvent.EVENT_TYPE})
+    public static void processInput(InputEvent event, RunnerContext ctx) {
+        ctx.sendEvent(
+                new ChatRequestEvent(
+                        "mathModel",
+                        Collections.singletonList(
+                                new ChatMessage(MessageRole.USER, (String) 
event.getInput()))));
+    }
+
+    /** Process chat response event and send the answer as output. */
+    @Action(listenEventTypes = {ChatResponseEvent.EVENT_TYPE})
+    public static void processChatResponse(ChatResponseEvent event, 
RunnerContext ctx) {
+        ctx.sendEvent(new OutputEvent(event.getResponse().getContent()));
+    }
+}
diff --git a/examples/src/main/resources/skills/math-calculator/SKILL.md 
b/examples/src/main/resources/skills/math-calculator/SKILL.md
new file mode 100644
index 00000000..c4f1071b
--- /dev/null
+++ b/examples/src/main/resources/skills/math-calculator/SKILL.md
@@ -0,0 +1,43 @@
+---
+name: math-calculator
+description: Calculate mathematical expressions using shell commands. Use when 
the user asks to perform arithmetic calculations like addition, subtraction, 
multiplication, division, or powers.
+license: Apache-2.0
+compatibility: Requires bash with bc (basic calculator)
+---
+
+# Math Calculator Skill
+
+This skill calculates mathematical expressions using shell commands.
+
+## When to Use
+
+Use this skill when the user asks to evaluate a numeric expression, such as:
+
+- Basic arithmetic (add, subtract, multiply, divide)
+- Expressions with parentheses
+- Powers and square roots
+
+## Method
+
+Pipe the expression into the `bc` (basic calculator) command:
+
+```bash
+echo "(2 + 3) * 4" | bc
+# Output: 20
+
+echo "2 ^ 10" | bc
+# Output: 1024
+```
+
+For decimal results, set the scale first:
+
+```bash
+echo "scale=2; 10 / 3" | bc
+# Output: 3.33
+```
+
+## Instructions
+
+1. Translate the user's question into a single `bc` expression.
+2. Run it with the `bash` tool: `echo "<expression>" | bc`.
+3. Report the number printed by `bc` as the answer.
diff --git a/python/flink_agents/examples/quickstart/agents/math_agent.py 
b/python/flink_agents/examples/quickstart/agents/math_agent.py
new file mode 100644
index 00000000..20e3b8ac
--- /dev/null
+++ b/python/flink_agents/examples/quickstart/agents/math_agent.py
@@ -0,0 +1,99 @@
+################################################################################
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+# limitations under the License.
+#################################################################################
+from flink_agents.api.agents.agent import Agent
+from flink_agents.api.chat_message import ChatMessage, MessageRole
+from flink_agents.api.decorators import action, chat_model_setup, prompt, 
skills
+from flink_agents.api.events.chat_event import ChatRequestEvent, 
ChatResponseEvent
+from flink_agents.api.events.event import Event, InputEvent, OutputEvent
+from flink_agents.api.prompts.prompt import Prompt
+from flink_agents.api.resource import ResourceDescriptor, ResourceName
+from flink_agents.api.runner_context import RunnerContext
+from flink_agents.api.skills import Skills
+
+
+class MathAgent(Agent):
+    """An agent that answers arithmetic questions using the math-calculator 
skill.
+
+    Instead of relying on the LLM to compute by itself, this agent exposes a
+    ``math-calculator`` skill. When asked to evaluate an expression, the model
+    loads the skill (``load_skill``) and follows its instructions to compute 
the
+    result with the ``bc`` calculator through the built-in ``bash`` tool.
+    """
+
+    @skills
+    @staticmethod
+    def my_skills() -> Skills:
+        """Declare where to load skills from.
+
+        The skills are bundled under this example package's 
``resources/skills``
+        directory and loaded by package name, so the agent resolves them the
+        same way whether run from the source tree or the installed wheel.
+        """
+        return Skills.from_package(
+            ("flink_agents.examples.quickstart", "resources/skills")
+        )
+
+    @prompt
+    @staticmethod
+    def system_prompt() -> Prompt:
+        """System prompt instructing the model to use the skill."""
+        return Prompt.from_messages(
+            messages=[
+                ChatMessage(
+                    role=MessageRole.SYSTEM,
+                    content="You are a helpful math assistant. Use the "
+                    "math-calculator skill when asked to evaluate an 
expression. "
+                    "You must load the skill first and strictly follow its "
+                    "instructions. Reply with only the final numeric result.",
+                )
+            ],
+        )
+
+    @chat_model_setup
+    @staticmethod
+    def math_model() -> ResourceDescriptor:
+        """ChatModel with the math-calculator skill enabled."""
+        return ResourceDescriptor(
+            clazz=ResourceName.ChatModel.OLLAMA_SETUP,
+            connection="ollama_server",
+            model="qwen3.5:9b",
+            prompt="system_prompt",
+            # Expose the declared skill to this model by name.
+            skills=["math-calculator"],
+            # Whitelist the shell commands the built-in bash tool may run.
+            allowed_commands=["echo", "bc"],
+        )
+
+    @action(InputEvent.EVENT_TYPE)
+    @staticmethod
+    def process_input(event: Event, ctx: RunnerContext) -> None:
+        """Process input event and send a chat request to evaluate the 
question."""
+        question: str = InputEvent.from_event(event).input
+        ctx.send_event(
+            ChatRequestEvent(
+                model="math_model",
+                messages=[ChatMessage(role=MessageRole.USER, 
content=question)],
+            )
+        )
+
+    @action(ChatResponseEvent.EVENT_TYPE)
+    @staticmethod
+    def process_chat_response(event: Event, ctx: RunnerContext) -> None:
+        """Process chat response event and send the answer as output."""
+        chat_response = ChatResponseEvent.from_event(event)
+        ctx.send_event(OutputEvent(output=chat_response.response.content))
diff --git 
a/python/flink_agents/examples/quickstart/resources/skills/math-calculator/SKILL.md
 
b/python/flink_agents/examples/quickstart/resources/skills/math-calculator/SKILL.md
new file mode 100644
index 00000000..c4f1071b
--- /dev/null
+++ 
b/python/flink_agents/examples/quickstart/resources/skills/math-calculator/SKILL.md
@@ -0,0 +1,43 @@
+---
+name: math-calculator
+description: Calculate mathematical expressions using shell commands. Use when 
the user asks to perform arithmetic calculations like addition, subtraction, 
multiplication, division, or powers.
+license: Apache-2.0
+compatibility: Requires bash with bc (basic calculator)
+---
+
+# Math Calculator Skill
+
+This skill calculates mathematical expressions using shell commands.
+
+## When to Use
+
+Use this skill when the user asks to evaluate a numeric expression, such as:
+
+- Basic arithmetic (add, subtract, multiply, divide)
+- Expressions with parentheses
+- Powers and square roots
+
+## Method
+
+Pipe the expression into the `bc` (basic calculator) command:
+
+```bash
+echo "(2 + 3) * 4" | bc
+# Output: 20
+
+echo "2 ^ 10" | bc
+# Output: 1024
+```
+
+For decimal results, set the scale first:
+
+```bash
+echo "scale=2; 10 / 3" | bc
+# Output: 3.33
+```
+
+## Instructions
+
+1. Translate the user's question into a single `bc` expression.
+2. Run it with the `bash` tool: `echo "<expression>" | bc`.
+3. Report the number printed by `bc` as the answer.
diff --git a/python/flink_agents/examples/quickstart/skills_agent_example.py 
b/python/flink_agents/examples/quickstart/skills_agent_example.py
new file mode 100644
index 00000000..c9dd8f16
--- /dev/null
+++ b/python/flink_agents/examples/quickstart/skills_agent_example.py
@@ -0,0 +1,77 @@
+################################################################################
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+# limitations under the License.
+#################################################################################
+from pyflink.datastream import StreamExecutionEnvironment
+
+from flink_agents.api.core_options import AgentExecutionOptions
+from flink_agents.api.execution_environment import AgentsExecutionEnvironment
+from flink_agents.api.resource import ResourceType
+from flink_agents.examples.quickstart.agents.custom_types_and_resources import 
(
+    ollama_server_descriptor,
+)
+from flink_agents.examples.quickstart.agents.math_agent import MathAgent
+
+
+def main() -> None:
+    """Main function for the agent skills quickstart example.
+
+    This example demonstrates how to use Agent Skills with Flink Agents. A 
stream
+    of arithmetic questions is processed by ``MathAgent``, whose chat model 
has a
+    ``math-calculator`` skill enabled. For each question the model loads the 
skill
+    and follows its instructions to compute the answer with the ``bc`` 
calculator,
+    then the result is printed to stdout. This serves as a minimal, end-to-end
+    example of integrating skill-powered agents with Flink streaming jobs.
+    """
+    # Set up the Flink streaming environment and the Agents execution 
environment.
+    env = StreamExecutionEnvironment.get_execution_environment()
+    agents_env = AgentsExecutionEnvironment.get_execution_environment(env)
+
+    # limit async request to avoid overwhelming ollama server
+    agents_env.get_config().set(AgentExecutionOptions.NUM_ASYNC_THREADS, 2)
+
+    # Add Ollama chat model connection to be used by the MathAgent.
+    agents_env.add_resource(
+        "ollama_server",
+        ResourceType.CHAT_MODEL_CONNECTION,
+        ollama_server_descriptor,
+    )
+
+    # A small stream of arithmetic questions to answer.
+    question_stream = env.from_collection(
+        [
+            "What is (2 + 3) * 4?",
+            "Compute 2 ^ 10.",
+            "What is 144 divided by 12?",
+        ],
+    )
+
+    # Use the MathAgent to answer each question with the math-calculator skill.
+    answer_stream = (
+        agents_env.from_datastream(input=question_stream, key_selector=lambda 
x: x)
+        .apply(MathAgent())
+        .to_datastream()
+    )
+
+    # Print the answers to stdout.
+    answer_stream.print()
+
+    # Execute the Flink pipeline.
+    agents_env.execute()
+
+
+if __name__ == "__main__":
+    main()
diff --git a/python/pyproject.toml b/python/pyproject.toml
index 0c8f74e5..f5d9814c 100644
--- a/python/pyproject.toml
+++ b/python/pyproject.toml
@@ -71,7 +71,7 @@ exclude = ["_build_backend*"]
 [tool.setuptools.package-data]
 "flink_agents.lib" = ["**/*.jar"]
 "flink_agents.examples.quickstart" = ["**/*.yaml"]
-"flink_agents.examples.quickstart.resources" = ["**/*.txt"]
+"flink_agents.examples.quickstart.resources" = ["**/*.txt", "**/*.md"]
 
 # Optional dependencies (dependency groups)
 [project.optional-dependencies]


Reply via email to