fix(skills): track current active skill explicitly and relocate to .agents/skills/

This commit is contained in:
Petar Zivkovic 2026-03-10 19:38:14 +01:00
parent 49b8285c73
commit a6717bed9b
9 changed files with 20 additions and 16 deletions

View File

@ -21,7 +21,7 @@ Workflow:
1. If the task needs computation or a repeatable transformation, activate this skill. 1. If the task needs computation or a repeatable transformation, activate this skill.
2. If you need examples, call `read_skill_file` for `references/examples.md`. 2. If you need examples, call `read_skill_file` for `references/examples.md`.
3. Write a short Python script for the exact task. 3. Write a short Python script for the exact task.
4. Prefer `run_python_script` with the script in its `script` argument. 4. Prefer `execute_code`.
5. Use the script output in the final answer. 5. Use the script output in the final answer.
6. Keep scripts small and task-specific. 6. Keep scripts small and task-specific.

View File

@ -1,7 +1,7 @@
--- ---
name: rest-api-caller name: rest-api-caller
description: Call REST APIs from Python, parse JSON responses, and report the useful fields back to the user. description: Call REST APIs from Python, parse JSON responses, and report the useful fields back to the user.
allowed-tools: run_python_script execute_code allowed-tools: execute_code
--- ---
# REST API Caller # REST API Caller
@ -14,14 +14,14 @@ This skill is intended for:
- endpoints where the user specifies headers, query params, or environment variable names - endpoints where the user specifies headers, query params, or environment variable names
Requirements: Requirements:
- The agent should have access to `run_python_script` or `execute_code`. - The agent should have access to `execute_code`.
Workflow: Workflow:
1. Activate this skill when the task requires calling an API. 1. Activate this skill when the task requires calling an API.
2. If you need examples, call `read_skill_file` for `references/examples.md`. 2. If you need examples, call `read_skill_file` for `references/examples.md`.
3. Write a short Python script that performs the request. 3. Write a short Python script that performs the request.
4. Prefer the `requests` library if available in the environment. 4. Prefer the `requests` library if available in the environment.
5. Prefer `run_python_script`; if it is unavailable, fall back to `execute_code`. 5. Prefer `execute_code`.
6. Parse the response and print only the fields needed for the final answer. 6. Parse the response and print only the fields needed for the final answer.
7. Summarize the API result clearly for the user. 7. Summarize the API result clearly for the user.

View File

@ -33,14 +33,14 @@ The Agent node is the most fundamental node type in the DevAll platform, used to
| Field | Type | Default | Description | | Field | Type | Default | Description |
|-------|------|---------|-------------| |-------|------|---------|-------------|
| `enabled` | bool | `false` | Enable Agent Skills discovery for this node | | `enabled` | bool | `false` | Enable Agent Skills discovery for this node |
| `allow` | list[object] | `[]` | Optional allowlist of skills from the project-level `skills/` directory; each entry uses `name` | | `allow` | list[object] | `[]` | Optional allowlist of skills from the project-level `.agents/skills/` directory; each entry uses `name` |
### Agent Skills Notes ### Agent Skills Notes
- Skills are discovered from the fixed project-level `skills/` directory. - Skills are discovered from the fixed project-level `.agents/skills/` directory.
- The runtime exposes two built-in skill tools: `activate_skill` and `read_skill_file`. - The runtime exposes two built-in skill tools: `activate_skill` and `read_skill_file`.
- `read_skill_file` only works after the relevant skill has been activated. - `read_skill_file` only works after the relevant skill has been activated.
- Skill `SKILL.md` frontmatter may include optional `allowed-tools` using the Agent Skills spec format, for example `allowed-tools: run_python_script execute_code`. - Skill `SKILL.md` frontmatter may include optional `allowed-tools` using the Agent Skills spec format, for example `allowed-tools: execute_code`.
- If a selected skill requires tools that are not bound on the node, that skill is skipped at runtime. - If a selected skill requires tools that are not bound on the node, that skill is skipped at runtime.
- If no compatible skills remain, the agent is explicitly instructed not to claim skill usage. - If no compatible skills remain, the agent is explicitly instructed not to claim skill usage.

View File

@ -33,14 +33,14 @@ Agent 节点是 DevAll 平台中最核心的节点类型,用于调用大语言
| 字段 | 类型 | 默认值 | 说明 | | 字段 | 类型 | 默认值 | 说明 |
|------|------|--------|------| |------|------|--------|------|
| `enabled` | bool | `false` | 是否为该节点启用 Agent Skills | | `enabled` | bool | `false` | 是否为该节点启用 Agent Skills |
| `allow` | list[object] | `[]` | 可选的技能白名单,来源于项目级 `skills/` 目录;每个条目使用 `name` | | `allow` | list[object] | `[]` | 可选的技能白名单,来源于项目级 `.agents/skills/` 目录;每个条目使用 `name` |
### Agent Skills 说明 ### Agent Skills 说明
- 技能统一从固定的项目级 `skills/` 目录中发现。 - 技能统一从固定的项目级 `.agents/skills/` 目录中发现。
- 运行时会暴露两个内置技能工具:`activate_skill``read_skill_file` - 运行时会暴露两个内置技能工具:`activate_skill``read_skill_file`
- `read_skill_file` 只有在对应技能已经激活后才可用。 - `read_skill_file` 只有在对应技能已经激活后才可用。
- 技能 `SKILL.md` 的 frontmatter 可以包含可选的 `allowed-tools`,格式遵循 Agent Skills 规范,例如 `allowed-tools: run_python_script execute_code`。 - 技能 `SKILL.md` 的 frontmatter 可以包含可选的 `allowed-tools`,格式遵循 Agent Skills 规范,例如 `allowed-tools: execute_code`。
- 如果某个已选择技能依赖的工具没有绑定到当前节点,该技能会在运行时被跳过。 - 如果某个已选择技能依赖的工具没有绑定到当前节点,该技能会在运行时被跳过。
- 如果最终没有任何兼容技能可用Agent 会被明确告知不要声称自己使用了技能。 - 如果最终没有任何兼容技能可用Agent 会被明确告知不要声称自己使用了技能。

View File

@ -18,7 +18,7 @@ from entity.configs.base import (
REPO_ROOT = Path(__file__).resolve().parents[3] REPO_ROOT = Path(__file__).resolve().parents[3]
DEFAULT_SKILLS_ROOT = (REPO_ROOT / "skills").resolve() DEFAULT_SKILLS_ROOT = (REPO_ROOT / ".agents" / "skills").resolve()
def _discover_default_skills() -> List[tuple[str, str]]: def _discover_default_skills() -> List[tuple[str, str]]:
if not DEFAULT_SKILLS_ROOT.exists() or not DEFAULT_SKILLS_ROOT.is_dir(): if not DEFAULT_SKILLS_ROOT.exists() or not DEFAULT_SKILLS_ROOT.is_dir():
return [] return []

View File

@ -11,7 +11,7 @@ from entity.tool_spec import ToolSpec
REPO_ROOT = Path(__file__).resolve().parents[4] REPO_ROOT = Path(__file__).resolve().parents[4]
DEFAULT_SKILLS_ROOT = (REPO_ROOT / "skills").resolve() DEFAULT_SKILLS_ROOT = (REPO_ROOT / ".agents" / "skills").resolve()
MAX_SKILL_FILE_BYTES = 128 * 1024 MAX_SKILL_FILE_BYTES = 128 * 1024
@ -126,6 +126,7 @@ class AgentSkillManager:
self._skills_by_name: Dict[str, SkillMetadata] | None = None self._skills_by_name: Dict[str, SkillMetadata] | None = None
self._skill_content_cache: Dict[str, str] = {} self._skill_content_cache: Dict[str, str] = {}
self._activation_state: Dict[str, bool] = {} self._activation_state: Dict[str, bool] = {}
self._current_skill_name: str | None = None
self._discovery_warnings: List[str] = [] self._discovery_warnings: List[str] = []
def discover(self) -> List[SkillMetadata]: def discover(self) -> List[SkillMetadata]:
@ -176,6 +177,7 @@ class AgentSkillManager:
cached = skill.skill_file.read_text(encoding="utf-8") cached = skill.skill_file.read_text(encoding="utf-8")
self._skill_content_cache[skill.name] = cached self._skill_content_cache[skill.name] = cached
self._activation_state[skill.name] = True self._activation_state[skill.name] = True
self._current_skill_name = skill.name
return { return {
"skill_name": skill.name, "skill_name": skill.name,
"path": str(skill.skill_file), "path": str(skill.skill_file),
@ -214,10 +216,12 @@ class AgentSkillManager:
return bool(self._activation_state.get(skill_name)) return bool(self._activation_state.get(skill_name))
def active_skill(self) -> SkillMetadata | None: def active_skill(self) -> SkillMetadata | None:
for skill in self.discover(): if self._current_skill_name is None:
if self.is_activated(skill.name): return None
return skill skills = self._skills_by_name
return None if skills is None:
return None
return skills.get(self._current_skill_name)
def discovery_warnings(self) -> List[str]: def discovery_warnings(self) -> List[str]:
self.discover() self.discover()