mirror of
https://github.com/agent0ai/agent-zero.git
synced 2026-05-17 04:01:13 +00:00
Standardize multi-action tools around tool_args.action while keeping parser compatibility for older tool/args, tool_name:action, and method-shaped requests. This keeps new prompts clean without breaking agents that learned the previous dialect. Move A0 connector remote execution/file tools into stable standard prompts, make remote targeting independent of the active chat context, and skill-gate beta computer-use remote so it no longer weighs down the always-on tool list. Align text editor, scheduler, skills, office artifact, memory, notify, and browser prompts/tools around the canonical action contract. Add scheduler update/timezone handling, skills_tool read_file, text editor patch coverage, and fixes for memory_forget, behaviour_adjustment, and code execution progress warnings. Reduce default prompt pressure by compacting browser and scheduler prompts into skill-backed manifests, shortening skill catalog descriptions, and pruning noisy framework knowledge. Remove obsolete connector prompt stubs and root tool-call knowledge examples. Tests: conda run -n a0 pytest tests/test_a0_connector_prompt_gating.py tests/test_tool_action_contracts.py tests/test_task_scheduler_timezone.py tests/test_text_editor_context_patch.py tests/test_tool_request_normalization.py tests/test_office_document_store.py::test_odf_is_advertised_and_docx_remains_explicit_compatibility tests/test_office_document_store.py::test_document_artifact_accepts_method_alias_for_ods_create tests/test_skills_runtime.py tests/test_default_prompt_budget.py::test_a0_small_profile_removed_and_prompt_text_generic -q
100 lines
3 KiB
Python
100 lines
3 KiB
Python
from helpers import files
|
|
from helpers.tool import Tool, Response
|
|
from agent import Agent
|
|
from helpers.log import LogItem
|
|
from plugins._memory.helpers import memory
|
|
|
|
|
|
class UpdateBehaviour(Tool):
|
|
|
|
async def execute(self, adjustments="", **kwargs):
|
|
|
|
# stringify adjustments if needed
|
|
if not isinstance(adjustments, str):
|
|
adjustments = str(adjustments)
|
|
|
|
await update_behaviour(self.agent, self.log, adjustments)
|
|
return Response(
|
|
message=self.agent.read_prompt("behaviour.updated.md"), break_loop=False
|
|
)
|
|
|
|
|
|
async def update_behaviour(agent: Agent, log_item: LogItem, adjustments: str):
|
|
|
|
# get system message and current ruleset
|
|
system = agent.read_prompt("behaviour.merge.sys.md")
|
|
current_rules = read_rules(agent)
|
|
|
|
# log query streamed by LLM
|
|
async def log_callback(content):
|
|
log_item.stream(ruleset=content)
|
|
|
|
msg = agent.read_prompt(
|
|
"behaviour.merge.msg.md", current_rules=current_rules, adjustments=adjustments
|
|
)
|
|
|
|
# call util llm to find solutions in history
|
|
adjustments_merge = await agent.call_utility_model(
|
|
system=system,
|
|
message=msg,
|
|
callback=log_callback,
|
|
)
|
|
adjustments_merge = normalize_ruleset(adjustments_merge)
|
|
|
|
# update rules file
|
|
rules_file = get_custom_rules_file(agent)
|
|
files.write_file(rules_file, adjustments_merge)
|
|
log_item.update(ruleset=adjustments_merge, result="Behaviour updated")
|
|
|
|
|
|
def get_custom_rules_file(agent: Agent):
|
|
return files.get_abs_path(memory.get_memory_subdir_abs(agent), "behaviour.md")
|
|
|
|
|
|
def read_rules(agent: Agent):
|
|
rules_file = get_custom_rules_file(agent)
|
|
if files.exists(rules_file):
|
|
return agent.read_prompt(rules_file)
|
|
else:
|
|
return agent.read_prompt("agent.system.behaviour_default.md")
|
|
|
|
|
|
def normalize_ruleset(ruleset: str):
|
|
text = str(ruleset or "").strip()
|
|
|
|
if text.startswith("```") and text.endswith("```"):
|
|
lines = text.splitlines()
|
|
text = "\n".join(lines[1:-1]).strip()
|
|
|
|
text = text.replace("\r\n", "\n").replace("\r", "\n")
|
|
text = text.replace("!!!", "")
|
|
text = text.replace(".## ", ".\n## ")
|
|
|
|
normalized_lines = []
|
|
seen_structural_lines = set()
|
|
previous_blank = False
|
|
|
|
for raw_line in text.splitlines():
|
|
line = raw_line.rstrip()
|
|
stripped = line.strip()
|
|
|
|
if not stripped:
|
|
if normalized_lines and not previous_blank:
|
|
normalized_lines.append("")
|
|
previous_blank = True
|
|
continue
|
|
|
|
if stripped.startswith("# ") and not stripped.startswith("## "):
|
|
stripped = "#" + stripped
|
|
line = stripped
|
|
|
|
dedupe_key = stripped.casefold()
|
|
if stripped.startswith(("## ", "* ")) and dedupe_key in seen_structural_lines:
|
|
continue
|
|
if stripped.startswith(("## ", "* ")):
|
|
seen_structural_lines.add(dedupe_key)
|
|
|
|
normalized_lines.append(line)
|
|
previous_blank = False
|
|
|
|
return "\n".join(normalized_lines).strip() + "\n"
|