wenjin272 commented on code in PR #1223:
URL: https://github.com/apache/flink-agents/pull/1223#discussion_r4244289295


##########
python/flink_agents/runtime/java_resource_adapter.py:
##########
@@ -56,6 +56,10 @@ def getSkillDirs(self, skill_names: list[str]) -> list[str]:
         """Resolve the directories of Java-hosted skills."""
         return list(self._bridge.getSkillDirs(skill_names) or [])
 
+    def resolveSkillNamesForSources(self, sources: list[dict]) -> list[str]:
+        """Resolve the names of Java-hosted skills declared by source specs."""
+        return list(self._bridge.resolveSkillNamesForSources(sources) or [])

Review Comment:
   The actual bridge here is Java `PythonResourceAdapterImpl`, but 
`resolveSkillNamesForSources()` was added to `JavaResourceAdapter`. A Java 
agent using a Python model with constructor-provided `Skills` therefore raises 
`AttributeError`, confirmed through real Pemja. Could we add the missing 
delegation and test the actual bridge?



##########
docs/content/docs/development/react_agent.md:
##########
@@ -332,3 +332,36 @@ RowTypeInfo myRowTypeInfo =
 
 {{< /tabs >}}
 
+### Skills
+User can pass the names of [skills]({{< ref "docs/development/skills" >}}) the 
agent may use directly to the constructor, instead of adding a `skills` 
argument to the chat model descriptor.
+
+{{< tabs "Skills" >}}
+
+{{< tab "Python" >}}
+```python
+my_react_agent = ReActAgent(
+    chat_model=chat_model_descriptor,
+    prompt=my_prompt,
+    skills=["math-calculator"],
+)
+```
+{{< /tab >}}
+
+{{< tab "Java" >}}
+```java
+ReActAgent myReActAgent =
+        new ReActAgent(chatModelDescriptor, myPrompt, null, 
List.of("math-calculator"));
+```
+{{< /tab >}}
+
+{{< /tabs >}}
+
+The constructor skills are combined with any `skills` already set on the chat 
model descriptor:
+
+- 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 skill sources must still be registered on the execution environment with 
`add_resource(..., ResourceType.SKILLS, ...)`.

Review Comment:
   Could we also document the `Skills`-object constructor with Java/Python 
examples? This section only describes skill-name lists and says sources must be 
registered on the environment, but passing a `Skills` object registers its 
sources on the agent automatically. Please distinguish the two forms and 
document how source-derived names combine with explicit descriptor skills, 
including the annotation → environment → agent precedence.



##########
api/src/main/java/org/apache/flink/agents/api/agents/Agent.java:
##########
@@ -101,10 +100,22 @@ public Agent addAction(
         return this;
     }
 
+    /**
+     * Merge environment-level resources into this agent. Entries already 
present at the agent level
+     * are kept (agent wins on duplicates). Environment entries are placed 
before agent entries in
+     * iteration order, so downstream merging sees env-then-agent precedence.
+     */
     public void addResourcesIfAbsent(Map<ResourceType, Map<String, Object>> 
resources) {
         for (ResourceType type : resources.keySet()) {
-            Map<String, Object> typedResources = resources.get(type);
-            typedResources.forEach(this.resources.get(type)::putIfAbsent);
+            Map<String, Object> envTyped = resources.get(type);
+            if (envTyped == null || envTyped.isEmpty()) {
+                continue;
+            }
+            Map<String, Object> existing = this.resources.get(type);
+            // Rebuild to establish env-before-agent iteration order.
+            Map<String, Object> merged = new LinkedHashMap<>(envTyped);
+            merged.putAll(existing);

Review Comment:
   `LinkedHashMap.putAll()` replaces values without moving existing keys. With 
environment keys `[z, a]` and an agent override for `z`, the order becomes 
`[agent-z, env-a]`, allowing environment skills to override agent skills. Could 
we remove overlapping keys before appending agent entries? The 
annotation-to-resource merge needs the same treatment.



##########
api/src/main/java/org/apache/flink/agents/api/chat/model/BaseChatModelSetup.java:
##########
@@ -99,6 +106,22 @@ public void open() throws Exception {
             this.prompt =
                     this.resourceContext.getResource((String) this.prompt, 
ResourceType.PROMPT);
         }
+
+        // Resolve skills_sources marker (plan-time source declarations) into 
skill names,
+        // then merge with the descriptor's explicit "skills" list. Explicit 
names come first;
+        // resolved names are appended, de-duplicated while preserving order.
+        List<SkillSourceSpec> skillsSources =
+                toSkillSourceSpecs(descriptor.getArgument("skills_sources"));
+        if (!skillsSources.isEmpty()) {
+            List<String> resolved = 
this.resourceContext.resolveSkillNamesForSources(skillsSources);
+            Set<String> merged = new LinkedHashSet<>();
+            if (this.skills != null) {
+                merged.addAll(this.skills);
+            }
+            merged.addAll(resolved);
+            this.skills = new ArrayList<>(merged);

Review Comment:
   Could we synchronize the resolved skill names into both cross-language 
wrappers after the underlying model opens? `PythonChatModelSetup.open()` only 
delegates to Python, leaving the Java wrapper’s `skills` at its original 
descriptor value. With source-only configuration, `getSkills()` remains `null`, 
so Java `ChatModelAction` omits the skill directories from the bash call’s 
`allowed_script_dirs`, even after the bridge issue is fixed. The reverse 
wrapper has the same gap: Python `JavaChatModelSetup.skills` is not refreshed 
after Java `open()`. Copying the final list after each successful `open()` 
should address this. Please verify the resulting bash arguments in both 
directions.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to