Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package io.agentscope.core.skill;

import java.nio.file.Path;

/**
* Generates skill system prompts for agents to understand available skills.
*
Expand All @@ -31,6 +33,9 @@ public class AgentSkillPromptProvider {
private final SkillRegistry skillRegistry;
private final String instruction;
private final String template;
private boolean codeExecutionEnabled;
private String uploadDir;
private String codeExecutionInstruction;

public static final String DEFAULT_AGENT_SKILL_INSTRUCTION =
"""
Expand All @@ -44,11 +49,10 @@ public class AgentSkillPromptProvider {
- The skill will be activated and its documentation loaded with detailed instructions
- Additional resources (scripts, assets, references) can be loaded using the same tool with different paths

Path Information:
When you load a skill, the response will include:
- Exact paths to all skill resources
- Code examples for accessing skill files
- Usage instructions specific to that skill
Example:
1. User asks to analyze data → find a matching skill below (e.g. <skill-id>data-analysis_builtin</skill-id>)
2. Load it: load_skill_through_path(skillId="data-analysis_builtin", path="SKILL.md")
3. Follow the instructions returned by the skill

Template fields explanation:
- <name>: The skill's display name
Expand All @@ -60,6 +64,41 @@ public class AgentSkillPromptProvider {

""";

// Every %s placeholder in the template will be replaced with the uploadDir absolute path
public static final String DEFAULT_CODE_EXECUTION_INSTRUCTION =
"""

## Code Execution

<code_execution>
You have access to the execute_shell_command tool. When a task can be accomplished by running\s
a pre-deployed skill script, you MUST execute it yourself using execute_shell_command rather\s
than describing or suggesting commands to the user.

Skills root directory: %s
Each skill's files are located under a subdirectory named by its <skill-id>:
%s/<skill-id>/scripts/
%s/<skill-id>/assets/

Workflow:
1. After loading a skill, use ls to explore its directory structure and discover available scripts/assets
2. Once you find the right script, execute it immediately with its absolute path
3. If execution fails, diagnose and retry — do not fall back to describing the command

Rules:
- Always use absolute paths when executing scripts
- If a script exists for the task, run it directly — do not rewrite its logic inline
- If asset/data files exist for the task, read them directly — do not recreate them

Example:
# Explore what scripts are available for a skill
execute_shell_command(command="ls %s/data-analysis_builtin/scripts/")

# Run an existing script with absolute path
execute_shell_command(command="python3 %s/data-analysis_builtin/scripts/analyze.py")
</code_execution>
""";

// skillName, skillDescription, skillId
public static final String DEFAULT_AGENT_SKILL_TEMPLATE =
"""
Expand Down Expand Up @@ -126,6 +165,50 @@ public String getSkillSystemPrompt() {
// Close available_skills tag
sb.append("</available_skills>");

// Conditionally append code execution instructions
if (codeExecutionEnabled && uploadDir != null) {
String template =
codeExecutionInstruction != null
? codeExecutionInstruction
: DEFAULT_CODE_EXECUTION_INSTRUCTION;
sb.append(template.replace("%s", uploadDir));
}

return sb.toString();
}

/**
* Sets whether code execution instructions are included in the skill system prompt.
*
* @param codeExecutionEnabled {@code true} to append code execution instructions
*/
public void setCodeExecutionEnable(boolean codeExecutionEnabled) {
this.codeExecutionEnabled = codeExecutionEnabled;
}

/**
* Sets the upload directory whose absolute path replaces every {@code %s}
* placeholder in the code execution instruction template.
*
* @param uploadDir the upload directory path, or {@code null} to disable path substitution
*/
public void setUploadDir(Path uploadDir) {
this.uploadDir = uploadDir != null ? uploadDir.toAbsolutePath().toString() : null;
}

/**
* Sets a custom code execution instruction template.
*
* <p>Every {@code %s} placeholder in the template will be replaced with
* the {@code uploadDir} absolute path. Pass {@code null} or blank to
* fall back to {@link #DEFAULT_CODE_EXECUTION_INSTRUCTION}.
*
* @param codeExecutionInstruction the custom template, or {@code null}/blank for default
*/
public void setCodeExecutionInstruction(String codeExecutionInstruction) {
this.codeExecutionInstruction =
codeExecutionInstruction == null || codeExecutionInstruction.isBlank()
? null
: codeExecutionInstruction;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,6 @@ public class SkillBox implements StateModule {
private SkillFileFilter fileFilter;
private boolean autoUploadSkill = true;

/**
* Creates a SkillBox without a toolkit.
*
* <p>This constructor will be removed in the next release. A SkillBox must hold a
* {@link Toolkit} to operate correctly. Relying on automatic toolkit assignment makes
* behavior less explicit and harder to reason about.
*/
@Deprecated
public SkillBox() {
this(null, null, null);
}

public SkillBox(Toolkit toolkit) {
this(toolkit, null, null);
}
Expand Down Expand Up @@ -717,6 +705,7 @@ private Path ensureUploadDirExists() {
}
}

skillPromptProvider.setUploadDir(uploadDir);
return uploadDir;
}

Expand Down Expand Up @@ -855,6 +844,7 @@ public static class CodeExecutionBuilder {
private boolean withShellCalled = false;
private boolean enableRead = false;
private boolean enableWrite = false;
private String codeExecutionInstruction;

CodeExecutionBuilder(SkillBox skillBox) {
this.skillBox = skillBox;
Expand Down Expand Up @@ -993,6 +983,23 @@ public CodeExecutionBuilder withWrite() {
return this;
}

/**
* Set a custom code execution instruction for the system prompt.
*
* <p>The instruction is appended to the skill system prompt when code execution is enabled.
* Use {@code %s} as a placeholder for the upload directory absolute path — every
* occurrence will be replaced with the actual path.
*
* <p>Pass {@code null} or blank to use the default instruction.
*
* @param instruction Custom code execution instruction template
* @return This builder for chaining
*/
public CodeExecutionBuilder codeExecutionInstruction(String instruction) {
this.codeExecutionInstruction = instruction;
return this;
}

/**
* Apply the configuration and enable code execution.
*
Expand All @@ -1017,8 +1024,7 @@ public void enable() {
}

// Handle replacement: remove existing tool group if present
if (skillBox.toolkit != null
&& skillBox.toolkit.getToolGroup("skill_code_execution_tool_group") != null) {
if (skillBox.toolkit.getToolGroup("skill_code_execution_tool_group") != null) {
skillBox.toolkit.removeToolGroups(List.of("skill_code_execution_tool_group"));
logger.info("Replacing existing code execution configuration");
}
Expand Down Expand Up @@ -1107,6 +1113,10 @@ public void enable() {
shellEnabled,
enableRead,
enableWrite);

boolean injectCodeExecutionPrompt = shellEnabled || codeExecutionInstruction != null;
skillBox.skillPromptProvider.setCodeExecutionEnable(injectCodeExecutionPrompt);
skillBox.skillPromptProvider.setCodeExecutionInstruction(codeExecutionInstruction);
}

/**
Expand Down

This file was deleted.

Loading
Loading