This is an automated email from the ASF dual-hosted git repository.
wenjin272 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 bde1069a [docs] Improve Built-in ReAct Agent usage documentation
(#1259)
bde1069a is described below
commit bde1069ae10fcadab497ddd633a89ae090ce9e8f
Author: Yi Zhang <[email protected]>
AuthorDate: Fri Oct 16 16:42:57 2026 +0800
[docs] Improve Built-in ReAct Agent usage documentation (#1259)
Generated-by: Qoder
---
docs/content/docs/development/react_agent.md | 233 ++++++++++++++-------------
1 file changed, 117 insertions(+), 116 deletions(-)
diff --git a/docs/content/docs/development/react_agent.md
b/docs/content/docs/development/react_agent.md
index 19dc49bc..6c374015 100644
--- a/docs/content/docs/development/react_agent.md
+++ b/docs/content/docs/development/react_agent.md
@@ -26,148 +26,119 @@ under the License.
Flink Agents provides `ReActAgent` as a built-in subclass of `Agent` that
implements the ReAct pattern.
-ReAct Agent is a general paradigm that combines reasoning and action
capabilities to solve complex tasks. Leveraging this paradigm, the user only
needs to specify the goal with prompt and provide available tools, and the LLM
will decide how to achieve the goal and take actions autonomously.
+ReAct Agent is a general paradigm that combines reasoning and action
capabilities to solve complex tasks. A `ReActAgent` requires a chat model. You
can optionally provide a prompt, an output schema, tools, and skills; the model
then decides how to achieve the goal and when to use the capabilities you
expose.
{{< hint info >}}
The snippets on this page are fragments that each focus on a single concept.
For an end-to-end runnable walkthrough, see the [Use the Built-in ReAct
Agent]({{< ref "docs/get-started/quickstart/react_agent" >}}) and the full
source:
[`react_agent_example.py`](https://github.com/apache/flink-agents/blob/main/python/flink_agents/examples/quickstart/react_agent_example.py)
(Python) and
[`ReActAgentExample.java`](https://github.com/apache/flink-agents/blob/main/examples/src/main/java/org/apac
[...]
{{< /hint >}}
-The snippets below assume the following imports:
+## Create a ReAct Agent
-{{< tabs "ReAct Agent Imports" >}}
+The only required argument of a ReAct Agent is a chat model descriptor, which
references a registered chat model connection by name. See [Chat Models]({{<
ref "docs/development/calling-llms/chat-models" >}}) for the provider-specific
connection and model configuration.
-{{< tab "Python" >}}
-```python
-from pydantic import BaseModel
-from pyflink.common.typeinfo import BasicTypeInfo, RowTypeInfo
-
-from flink_agents.api.agents.react_agent import ReActAgent
-from flink_agents.api.chat_message import ChatMessage, MessageRole
-from flink_agents.api.execution_environment import AgentsExecutionEnvironment
-from flink_agents.api.prompts.prompt import Prompt
-from flink_agents.api.resource import ResourceDescriptor, ResourceName,
ResourceType
-from flink_agents.api.tools.tool import Tool
-```
-{{< /tab >}}
-
-{{< tab "Java" >}}
-```java
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-import org.apache.flink.agents.api.AgentsExecutionEnvironment;
-import org.apache.flink.agents.api.agents.ReActAgent;
-import org.apache.flink.agents.api.chat.messages.ChatMessage;
-import org.apache.flink.agents.api.chat.messages.MessageRole;
-import org.apache.flink.agents.api.prompt.Prompt;
-import org.apache.flink.agents.api.resource.ResourceDescriptor;
-import org.apache.flink.agents.api.resource.ResourceName;
-import org.apache.flink.agents.api.resource.ResourceType;
-import org.apache.flink.agents.api.tools.Tool;
-import org.apache.flink.api.common.typeinfo.BasicTypeInfo;
-import org.apache.flink.api.common.typeinfo.TypeInformation;
-import org.apache.flink.api.java.typeutils.RowTypeInfo;
-
-import java.util.Arrays;
-import java.util.List;
-```
-{{< /tab >}}
-
-{{< /tabs >}}
-
-## ReAct Agent Example
+The following example registers a chat model connection on the
`AgentsExecutionEnvironment`, defines the chat model descriptor, and creates
the agent:
-{{< tabs "ReAct Agent Example" >}}
+{{< tabs "Create a ReAct Agent" >}}
{{< tab "Python" >}}
```python
-my_react_agent = ReActAgent(
- chat_model=chat_model_descriptor,
- prompt=my_prompt,
- output_schema=MyBaseModelDataType, # or output_schema=my_row_type_info
+# Register a chat model connection under a resource name.
+agents_env.add_resource(
+ "my_ollama_connection",
+ ResourceType.CHAT_MODEL_CONNECTION,
+ ResourceDescriptor(
+ clazz=ResourceName.ChatModel.OLLAMA_CONNECTION, request_timeout=120
+ ),
)
-```
-{{< /tab >}}
-
-{{< tab "Java" >}}
-```java
-ReActAgent myReActAgent =
- new ReActAgent(
- chatModelDescriptor,
- myPrompt,
- MyBaseModelDataType.class
- // or myRowTypeInfo
- );
-```
-{{< /tab >}}
-
-{{< /tabs >}}
-
-## Initialize Arguments
-### Chat Model
-User should specify the chat model used in the ReAct Agent.
-
-We use `ResourceDescriptor` to describe the chat model, includes chat model
type and chat model arguments. See [Chat Model]({{< ref
"docs/development/calling-llms/chat-models" >}}) for more details.
-{{< tabs "ChatModel ResourceDescriptor" >}}
-
-{{< tab "Python" >}}
-```python
+# Define the chat model descriptor.
chat_model_descriptor = ResourceDescriptor(
clazz=ResourceName.ChatModel.OLLAMA_SETUP,
connection="my_ollama_connection",
model="qwen3:8b",
- tools=["my_tool1", "my_tool2"],
)
+
+# Create the ReAct Agent.
+my_react_agent = ReActAgent(chat_model=chat_model_descriptor)
```
{{< /tab >}}
{{< tab "Java" >}}
```java
+// Register a chat model connection under a resource name.
+agentsEnv.addResource(
+ "myOllamaConnection",
+ ResourceType.CHAT_MODEL_CONNECTION,
+
ResourceDescriptor.Builder.newBuilder(ResourceName.ChatModel.OLLAMA_CONNECTION)
+ .addInitialArgument("endpoint", "http://localhost:11434")
+ .addInitialArgument("requestTimeout", 120)
+ .build());
+
+// Define the chat model descriptor.
ResourceDescriptor chatModelDescriptor =
-
ResourceDescriptor.Builder.newBuilder(ResourceName.ChatModel.OLLAMA_SETUP)
- .addInitialArgument("connection", "myOllamaConnection")
- .addInitialArgument("model", "qwen3:8b")
- .addInitialArgument(
- "tools", List.of("myTool1", "myTool2"))
- .build();
+
ResourceDescriptor.Builder.newBuilder(ResourceName.ChatModel.OLLAMA_SETUP)
+ .addInitialArgument("connection", "myOllamaConnection")
+ .addInitialArgument("model", "qwen3:8b")
+ .build();
+
+// Create the ReAct Agent; pass null for the optional prompt and output schema.
+ReActAgent myReActAgent = new ReActAgent(chatModelDescriptor, null, null);
```
{{< /tab >}}
{{< /tabs >}}
-{{< hint warning >}}
-The tools listed in `tools=[...]` must be registered on the
`AgentsExecutionEnvironment` **before** the agent runs, using a name that
matches the entry in the list — otherwise the agent fails at runtime with a
"resource not found" error.
-{{< /hint >}}
+You can optionally expose tools to the ReAct Agent. Register each tool on the
`AgentsExecutionEnvironment` under a resource name (this can also be done with
a YAML file — see [Tool Use]({{< ref
"docs/development/calling-llms/tool_use#register-tool-to-execution-environment"
>}}) for the full guide), then list the registered names in the chat model
descriptor's `tools` argument. Each name must match a registered tool;
otherwise the agent fails at runtime with a "resource not found" error.
-Register the referenced tools (this can also be done with a YAML file — see
[Tool Use]({{< ref
"docs/development/calling-llms/tool_use#register-tool-to-execution-environment"
>}}) for the full guide):
-
-{{< tabs "Register Tools" >}}
+{{< tabs "Expose Tools" >}}
{{< tab "Python" >}}
```python
+# Register each tool under a resource name.
agents_env.add_resource(
"my_tool1", ResourceType.TOOL, Tool.from_callable(my_tool1)
)
+# Register my_tool2 the same way.
+
+# List the registered tool names in tools.
+chat_model_descriptor = ResourceDescriptor(
+ clazz=ResourceName.ChatModel.OLLAMA_SETUP,
+ connection="my_ollama_connection",
+ model="qwen3:8b",
+ tools=["my_tool1", "my_tool2"],
+)
```
{{< /tab >}}
{{< tab "Java" >}}
```java
+// Register each tool under a resource name.
agentsEnv.addResource(
"myTool1",
ResourceType.TOOL,
Tool.fromMethod(MyAgentExample.class.getMethod("myTool1",
String.class)));
+// Register myTool2 the same way.
+
+// List the registered tool names in tools.
+ResourceDescriptor chatModelDescriptor =
+
ResourceDescriptor.Builder.newBuilder(ResourceName.ChatModel.OLLAMA_SETUP)
+ .addInitialArgument("connection", "myOllamaConnection")
+ .addInitialArgument("model", "qwen3:8b")
+ .addInitialArgument("tools", List.of("myTool1", "myTool2"))
+ .build();
```
{{< /tab >}}
{{< /tabs >}}
-### Prompt
-User can provide prompt to instruct agent.
+Registering a Vector Store resource does not make a ReAct Agent retrieve from
it automatically. Implement retrieval explicitly, for example by exposing it as
a tool.
+
+### Optional Arguments
+
+#### Prompt
+The prompt is optional. Without one, the agent directly converts a Java
`String` or primitive wrapper, a Python `str`, `bool`, `int`, `float`, or
`None`, or a Java `Map`/Python `dict` whose only key is `input` into a user
message. Other structured inputs require a prompt to map their fields into one
or more user messages; otherwise, the agent rejects the input at runtime.
-A typical prompt contains two messages: a SYSTEM message that tells the agent
what to do (and gives input and output examples), and a USER message that
describes how to convert the input element into a text string. This is a
recommended pattern, not a strict requirement — the agent uses all messages in
the prompt, and the SYSTEM message is optional (if it is omitted, the framework
still prepends the output-schema instruction automatically).
+A typical prompt contains two messages: a SYSTEM message that tells the agent
what to do (and gives input and output examples), and a USER message that
describes how to convert the input element into a text string. This is a
recommended pattern, not a strict requirement — the agent uses all messages in
the prompt, and the SYSTEM message is optional. When an output schema is
configured, the ReAct Agent adds its own schema instruction as a SYSTEM
message, right after the prompt's first SYS [...]
{{< tabs "Prompt" >}}
@@ -188,9 +159,8 @@ system_prompt_str = """
my_prompt = Prompt.from_messages(
messages=[
ChatMessage.system(system_prompt_str),
- # For react agent, if the input element is not primitive types,
- # framework will deserialize input element to dict and fill the prompt.
- # Note, the input element should be primitive types, BaseModel or Row.
+ # Structured input fields (dict, BaseModel, or Row) fill the matching
+ # placeholders. A primitive input only fills {input}.
ChatMessage.user(
"""
"id": {id},
@@ -199,6 +169,8 @@ my_prompt = Prompt.from_messages(
),
],
)
+
+my_react_agent = ReActAgent(chat_model=chat_model_descriptor, prompt=my_prompt)
```
{{< /tab >}}
@@ -218,6 +190,8 @@ Prompt myPrompt = Prompt.fromMessages(
new ChatMessage(
MessageRole.USER,
"{\"id\": \"{id}\",\n" + "\"review\":
\"{review}\"}")));
+
+ReActAgent myReActAgent = new ReActAgent(chatModelDescriptor, myPrompt, null);
```
{{< /tab >}}
@@ -228,13 +202,13 @@ The USER-message template uses `{placeholder}` syntax.
The placeholder names are
- **Primitive** (`int`, `str`, `float`, `bool`, ...): a single `{input}`
placeholder.
- **`Row`**: one placeholder per field name (`row.as_dict()` keys in Python,
`row.getFieldNames()` in Java).
- **`dict` / `Map`**: the keys are used directly.
-- **`BaseModel` (Python) / Pojo (Java)**: the object's field names.
+- **`BaseModel` (Python) / POJO (Java)**: the object's field names.
For example, the prompt snippet above uses `{id}` and `{review}` because the
input element's fields are named `id` and `review`. A placeholder whose name
does not match a field is left unchanged in the text.
-If the input element is primitive types, like `int`, `str` and so on, the
second message should be
+When a prompt formats a primitive input, use the `{input}` placeholder in its
USER message:
-{{< tabs "Prepare Agents Execution Environment" >}}
+{{< tabs "Primitive Input Placeholder" >}}
{{< tab "Python" >}}
```python
@@ -252,12 +226,12 @@ new ChatMessage(MessageRole.USER, "{input}")
See [Prompt]({{< ref "docs/development/calling-llms/prompts" >}}) for more
details.
-### Output Schema
-User can set output schema to configure the ReAct Agent output type. If output
schema is set, the ReAct Agent will deserialize the llm response to expected
type.
+#### Output Schema
+The output schema is optional. If omitted, the agent returns the text of the
final assistant response as a `String` (Java) or `str` (Python).
-The output schema should be a `BaseModel` subclass (Python) / a Pojo class
(Java), or a `RowTypeInfo` (both).
+When you set an output schema, the agent emits the parsed result instead. The
schema should be a `BaseModel` subclass (Python), a POJO class (Java), or a
`RowTypeInfo` (both). If the final response cannot be parsed after the
configured retries, the chat request fails and the ReAct Agent propagates that
failure.
-When the chat model's provider supports it, the schema is enforced natively by
the provider rather than only described in the prompt. See [Structured
Output]({{< ref "docs/development/calling-llms/structured-output" >}}) for the
strategies, supported providers and schema rules.
+When the chat model's provider supports it, the schema is enforced natively by
the provider rather than only described in the prompt. See [Structured
Output]({{< ref "docs/development/calling-llms/structured-output" >}}) for the
strategies, result types, supported providers, schema rules, and failure
behavior.
{{< tabs "Output Schema" >}}
@@ -273,6 +247,11 @@ my_row_type_info = RowTypeInfo(
[BasicTypeInfo.STRING_TYPE_INFO(), BasicTypeInfo.INT_TYPE_INFO()],
["id", "score"],
)
+
+my_react_agent = ReActAgent(
+ chat_model=chat_model_descriptor,
+ output_schema=MyBaseModelDataType, # or my_row_type_info
+)
```
{{< /tab >}}
@@ -327,15 +306,43 @@ RowTypeInfo myRowTypeInfo =
BasicTypeInfo.STRING_TYPE_INFO,
BasicTypeInfo.INT_TYPE_INFO
},
new String[] {"id", "score"});
+
+ReActAgent myReActAgent =
+ new ReActAgent(
+ chatModelDescriptor,
+ null,
+ MyBaseModelDataType.class // or myRowTypeInfo
+ );
```
{{< /tab >}}
{{< /tabs >}}
-### Skills
-User can pass [skills]({{< ref "docs/development/calling-llms/skills" >}}) to
the constructor instead of adding a `skills` argument to the chat model
descriptor. Two forms are accepted: a list of skill names, or a `Skills` object
declaring where the skills are loaded from.
+#### Skills
+Skills are optional. You can pass [skills]({{< ref
"docs/development/calling-llms/skills" >}}) to the constructor in addition to,
or instead of, a `skills` argument on the chat model descriptor; the agent uses
both. Two forms are accepted: a list of skill names, or a `Skills` object
declaring where the skills are loaded from.
+
+**Skill names.** First register the skill sources on the
`AgentsExecutionEnvironment`. The resource name identifies the collection of
sources:
+
+{{< tabs "Register Skills" >}}
+
+{{< tab "Python" >}}
+```python
+agents_env.add_resource(
+ "my_skills", ResourceType.SKILLS, Skills.from_local_dir("./skills")
+)
+```
+{{< /tab >}}
+
+{{< tab "Java" >}}
+```java
+agentsEnv.addResource(
+ "mySkills", ResourceType.SKILLS, Skills.fromLocalDir("./skills"));
+```
+{{< /tab >}}
+
+{{< /tabs >}}
-**Skill names.** Pass the names of skills that are already declared to the
agent:
+Then pass the names declared in the sources' `SKILL.md` files to the
constructor:
{{< tabs "Skills" >}}
@@ -343,7 +350,6 @@ User can pass [skills]({{< ref
"docs/development/calling-llms/skills" >}}) to th
```python
my_react_agent = ReActAgent(
chat_model=chat_model_descriptor,
- prompt=my_prompt,
skills=["math-calculator"],
)
```
@@ -352,21 +358,17 @@ my_react_agent = ReActAgent(
{{< tab "Java" >}}
```java
ReActAgent myReActAgent =
- new ReActAgent(chatModelDescriptor, myPrompt, null,
List.of("math-calculator"));
+ new ReActAgent(chatModelDescriptor, null, null,
List.of("math-calculator"));
```
{{< /tab >}}
{{< /tabs >}}
-The constructor skills are combined with any `skills` already set on the chat
model descriptor:
+Skills passed to the constructor are combined with those configured on the
chat model descriptor, with duplicate names included only once. Referenced
skills must be declared on the agent or its execution environment.
-- The descriptor's skills come first, followed by the constructor skills; a
name listed more than once is kept only at its first occurrence.
-- Passing no skills, or an empty list, adds nothing: the descriptor is used
exactly as given.
-- The descriptor you pass in is not modified; the agent uses a copy with the
combined skills.
-- Only skill names are combined. The command policy of the built-in `bash`
tool, such as `allowed_commands`, is always taken from the chat model
descriptor, so set it there.
-- The sources behind those names have to be declared somewhere the agent can
see them: a `@Skills`/`@skills` method, the execution environment, or the agent
itself.
+Configure `allowed_commands` on the chat model descriptor to control which
commands the built-in `bash` tool may execute.
-**Skill sources.** Pass a `Skills` object to declare the sources and let the
framework resolve the skill names at runtime:
+**Skill sources.** Alternatively, pass a `Skills` object directly to the
constructor. The constructor registers the sources on the agent, so no separate
registration on the execution environment is required:
{{< tabs "Skill Sources" >}}
@@ -374,7 +376,6 @@ The constructor skills are combined with any `skills`
already set on the chat mo
```python
my_react_agent = ReActAgent(
chat_model=chat_model_descriptor,
- prompt=my_prompt,
skills=Skills.from_local_dir("./skills"),
)
```
@@ -385,7 +386,7 @@ my_react_agent = ReActAgent(
ReActAgent myReActAgent =
new ReActAgent(
chatModelDescriptor,
- myPrompt,
+ null,
null,
Skills.fromLocalDir("./skills"));
```
@@ -393,7 +394,7 @@ ReActAgent myReActAgent =
{{< /tabs >}}
-The constructor registers those sources on the agent, so they do not also need
`add_resource(..., ResourceType.SKILLS, ...)` on the execution environment.
Every skill found in the sources becomes available, and the resolved names are
appended after any explicitly named ones. A single `Skills` object may declare
several sources; to combine sources held in separate objects, gather them into
one `Skills`, declare further `@Skills`/`@skills` methods, or register them on
the agent with `addRe [...]
+Every skill found in the sources becomes available, and the resolved names are
appended after any explicitly named ones. A single `Skills` object may declare
several sources; to combine sources held in separate objects, gather them into
one `Skills`, declare further `@Skills`/`@skills` methods, or register them on
the agent with `addResource(..., ResourceType.SKILLS, ...)`.
**Which declaration wins.** Sources reach the agent from three levels.
`@Skills`/`@skills` methods are merged first, then the execution environment's
`SKILLS` resources, then the agent's own. A declaration from a later level wins
when two of them resolve to the same skill name.