mirror of
https://github.com/agent0ai/agent-zero.git
synced 2026-07-09 17:08:29 +00:00
Persist loaded skills through compaction
Store explicitly loaded skill IDs as chat-wide context data while keeping full skill bodies in normal tool-result history. Reattach any loaded skill body that is no longer visible after compaction by reimporting the current skill from its source, without revision hashes or protocol reinjection. Update compaction and summary prompts to preserve loaded skill names only, refresh DOX contracts, and add focused coverage for context-data persistence, legacy agent-data migration, duplicate suppression, and post-compaction reattachment.
This commit is contained in:
parent
bd584da2f4
commit
53ad7ba2dc
14 changed files with 559 additions and 81 deletions
|
|
@ -8,7 +8,7 @@ from helpers import skills as skills_helper
|
|||
from helpers.print_style import PrintStyle
|
||||
|
||||
|
||||
DATA_NAME_LOADED_SKILLS = skills_helper.AGENT_DATA_NAME_LOADED_SKILLS
|
||||
DATA_NAME_LOADED_SKILLS = skills_helper.CONTEXT_DATA_NAME_LOADED_SKILLS
|
||||
|
||||
|
||||
class SkillsTool(Tool):
|
||||
|
|
@ -111,7 +111,7 @@ class SkillsTool(Tool):
|
|||
skill_name = self._normalize_skill_name(
|
||||
str(kwargs.get("skill_name") or self.args.get("skill_name") or "")
|
||||
)
|
||||
return Response(message=self._load(skill_name), break_loop=False)
|
||||
return self._load(skill_name)
|
||||
if action == "read_file":
|
||||
skill_name = self._normalize_skill_name(
|
||||
str(kwargs.get("skill_name") or self.args.get("skill_name") or "")
|
||||
|
|
@ -185,11 +185,14 @@ class SkillsTool(Tool):
|
|||
)
|
||||
return "\n".join(lines)
|
||||
|
||||
def _load(self, skill_name: str) -> str:
|
||||
def _load(self, skill_name: str) -> Response:
|
||||
skill_name = self._normalize_skill_name(skill_name)
|
||||
|
||||
if not skill_name:
|
||||
return "Error: 'skill_name' is required for action=load."
|
||||
return Response(
|
||||
message="Error: 'skill_name' is required for action=load.",
|
||||
break_loop=False,
|
||||
)
|
||||
|
||||
# Verify skill exists
|
||||
skill = skills_helper.find_skill(
|
||||
|
|
@ -198,18 +201,63 @@ class SkillsTool(Tool):
|
|||
agent=self.agent,
|
||||
)
|
||||
if not skill:
|
||||
return f"Error: skill not found: {skill_name!r}. Try skills_tool action=list or action=search."
|
||||
return Response(
|
||||
message=(
|
||||
f"Error: skill not found: {skill_name!r}. "
|
||||
"Try skills_tool action=list or action=search."
|
||||
),
|
||||
break_loop=False,
|
||||
)
|
||||
|
||||
# Store skill name for fresh loading each turn
|
||||
if not self.agent.data.get(DATA_NAME_LOADED_SKILLS):
|
||||
self.agent.data[DATA_NAME_LOADED_SKILLS] = []
|
||||
loaded = self.agent.data[DATA_NAME_LOADED_SKILLS]
|
||||
if skill.name in loaded:
|
||||
loaded.remove(skill.name)
|
||||
loaded.append(skill.name)
|
||||
self.agent.data[DATA_NAME_LOADED_SKILLS] = loaded[-max_loaded_skills():]
|
||||
skill_data = skills_helper.load_skill_for_agent(
|
||||
skill_name=skill.name,
|
||||
agent=self.agent,
|
||||
)
|
||||
metadata = {
|
||||
"name": skill.name,
|
||||
"path": str(skill.path),
|
||||
"source": "skills_tool:load",
|
||||
"content_included": True,
|
||||
}
|
||||
|
||||
return f"Loaded skill '{skill.name}' into Protocol."
|
||||
skills_helper.add_loaded_skill_name(
|
||||
self.agent,
|
||||
skill.name,
|
||||
limit=max_loaded_skills(),
|
||||
)
|
||||
|
||||
if self._visible_skill_loaded(skill.name):
|
||||
return Response(
|
||||
message=(
|
||||
f"Skill '{skill.name}' is already loaded in visible "
|
||||
"chat history."
|
||||
),
|
||||
break_loop=False,
|
||||
additional={
|
||||
"skill_instructions": {
|
||||
**metadata,
|
||||
"content_included": False,
|
||||
"already_loaded": True,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return Response(
|
||||
message=skill_data,
|
||||
break_loop=False,
|
||||
additional={"skill_instructions": metadata},
|
||||
)
|
||||
|
||||
def _visible_skill_loaded(self, skill_name: str) -> bool:
|
||||
history_obj = getattr(self.agent, "history", None)
|
||||
output = getattr(history_obj, "output", None)
|
||||
if not callable(output):
|
||||
return False
|
||||
|
||||
return any(
|
||||
skills_helper.skill_instruction_name(message) == skill_name
|
||||
for message in output()
|
||||
)
|
||||
|
||||
def _read_file(self, skill_name: str, file_path: str) -> str:
|
||||
if not skill_name:
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
- `get_log_object(self)`
|
||||
- `async before_execution(self, **kwargs)`
|
||||
- `async execute(self, **kwargs) -> Response`
|
||||
- `_visible_skill_loaded(self, skill_name: str) -> bool`
|
||||
- Top-level functions:
|
||||
- `max_loaded_skills() -> int`
|
||||
- Notable constants/configuration names: `DATA_NAME_LOADED_SKILLS`.
|
||||
|
|
@ -25,13 +26,15 @@
|
|||
- Update this file whenever tool arguments, output shape, `break_loop` behavior, intervention handling, prompt instructions, or side effects change.
|
||||
- `SkillsTool` is a `Tool`.
|
||||
- `SkillsTool` defines `execute(...)`.
|
||||
- Loading a skill stores it for prompt Protocol injection on subsequent turns.
|
||||
- Observed side-effect areas: filesystem reads, filesystem deletion, settings/state persistence.
|
||||
- Loading a skill appends the full skill body as a normal tool-result history message with `skill_instructions` metadata containing name, path, source, and content visibility.
|
||||
- Loaded skill IDs are stored in chat-wide context data.
|
||||
- Duplicate loads omit the full body when the same skill name remains visible in model history.
|
||||
- Observed side-effect areas: filesystem reads, filesystem deletion, settings/state persistence, chat history persistence.
|
||||
- Imported dependency areas include: `__future__`, `helpers`, `helpers.print_style`, `helpers.tool`, `pathlib`, `typing`.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
- Important called helpers/classes observed in the source: `str.strip.lower.replace`, `skill_name.strip`, `super.get_log_object`, `self._normalize_skill_name`, `self.get_log_object`, `skills_helper.list_skills`, `join`, `skills_helper.search_skills`, `skills_helper.find_skill`, `skill.path.resolve`, `Path`, `resolved.read_text`, `skill_name.startswith`, `skill_name.endswith`, `self._current_action`, `self.agent.context.log.log`, `Response`, `strip`, `loaded.remove`, `target.is_absolute`.
|
||||
- Important called helpers/classes observed in the source: `str.strip.lower.replace`, `skill_name.strip`, `super.get_log_object`, `self._normalize_skill_name`, `self.get_log_object`, `skills_helper.list_skills`, `join`, `skills_helper.search_skills`, `skills_helper.find_skill`, `skills_helper.load_skill_for_agent`, `skills_helper.add_loaded_skill_name`, `skills_helper.skill_instruction_name`, `skill.path.resolve`, `Path`, `resolved.read_text`, `skill_name.startswith`, `skill_name.endswith`, `self._current_action`, `self.agent.context.log.log`, `Response`, `strip`, `target.is_absolute`.
|
||||
- Keep request/response, tool, or helper semantics documented here at the same time as source changes.
|
||||
|
||||
## Work Guidance
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue