feat(neo): guide skill lifecycle tool workflow

Add explicit Neo lifecycle instructions to the main agent prompt so
skill creation and updates follow payload -> candidate -> promotion
instead of direct local folder writes.

Clarify lifecycle tool descriptions and parameter semantics, including
skill_key/source_execution_ids usage and stable release sync_to_local
behavior, to reduce ambiguity and improve consistent skill publishing.
This commit is contained in:
RC-CHN
2026-02-26 16:14:16 +08:00
parent 4b1bda5f2e
commit 73e665bef7
2 changed files with 43 additions and 8 deletions
+12
View File
@@ -840,6 +840,18 @@ def _apply_sandbox_tools(
"Example: use `baidu_homepage.png` instead of `/workspace/baidu_homepage.png`.\n"
)
req.system_prompt += (
"\n[Neo Skill Lifecycle Workflow]\n"
"When user asks to create/update a reusable skill in Neo mode, use lifecycle tools instead of directly writing local skill folders.\n"
"Preferred sequence:\n"
"1) Use `astrbot_create_skill_payload` to store canonical payload content and get `payload_ref`.\n"
"2) Use `astrbot_create_skill_candidate` with `skill_key` + `source_execution_ids` (and optional `payload_ref`) to create a candidate.\n"
"3) Use `astrbot_promote_skill_candidate` to release: `stage=canary` for trial; `stage=stable` for production.\n"
"For stable release, set `sync_to_local=true` to sync `payload.skill_markdown` into local `SKILL.md`.\n"
"Do not treat ad-hoc generated files as reusable Neo skills unless they are captured via payload/candidate/release.\n"
"To update an existing skill, create a new payload/candidate and promote a new release version; avoid patching old local folders directly.\n"
)
# Determine sandbox capabilities from an already-booted session.
# If no session exists yet (first request), capabilities is None
# and we register all tools conservatively.
+31 -8
View File
@@ -155,7 +155,10 @@ class AnnotateExecutionTool(NeoSkillToolBase):
@dataclass
class CreateSkillPayloadTool(NeoSkillToolBase):
name: str = "astrbot_create_skill_payload"
description: str = "Create a generic skill payload and return payload_ref."
description: str = (
"Step 1/3 for Neo skill authoring: create immutable payload content and return payload_ref. "
"Use this to store skill_markdown and structured metadata; do NOT write local skill folders directly."
)
parameters: dict = field(
default_factory=lambda: {
"type": "object",
@@ -163,7 +166,8 @@ class CreateSkillPayloadTool(NeoSkillToolBase):
"payload": {
"anyOf": [{"type": "object"}, {"type": "array"}],
"description": (
"Skill payload JSON. Recommended fields: skill_markdown, commands, meta."
"Skill payload JSON. Typical schema: {skill_markdown, inputs, outputs, meta}. "
"This only stores content and returns payload_ref; it does not create a candidate or release."
),
},
"kind": {
@@ -221,18 +225,31 @@ class GetSkillPayloadTool(NeoSkillToolBase):
@dataclass
class CreateSkillCandidateTool(NeoSkillToolBase):
name: str = "astrbot_create_skill_candidate"
description: str = "Create a skill candidate from source execution IDs."
description: str = (
"Step 2/3 for Neo skill authoring: create a candidate by binding execution evidence "
"(source_execution_ids) with skill identity (skill_key) and optional payload_ref."
)
parameters: dict = field(
default_factory=lambda: {
"type": "object",
"properties": {
"skill_key": {"type": "string"},
"skill_key": {
"type": "string",
"description": "Stable logical identifier, e.g. image-collage-9grid.",
},
"source_execution_ids": {
"type": "array",
"items": {"type": "string"},
"description": "Execution evidence IDs captured from sandbox history.",
},
"scenario_key": {
"type": "string",
"description": "Optional scenario namespace for grouping candidates.",
},
"payload_ref": {
"type": "string",
"description": "Optional payload reference created by astrbot_create_skill_payload.",
},
"scenario_key": {"type": "string"},
"payload_ref": {"type": "string"},
},
"required": ["skill_key", "source_execution_ids"],
}
@@ -338,7 +355,10 @@ class EvaluateSkillCandidateTool(NeoSkillToolBase):
@dataclass
class PromoteSkillCandidateTool(NeoSkillToolBase):
name: str = "astrbot_promote_skill_candidate"
description: str = "Promote one candidate to release stage (canary/stable)."
description: str = (
"Step 3/3 for Neo skill authoring: promote candidate to canary/stable release. "
"If stage=stable and sync_to_local=true, payload.skill_markdown is synced to local SKILL.md automatically."
)
parameters: dict = field(
default_factory=lambda: {
"type": "object",
@@ -351,7 +371,10 @@ class PromoteSkillCandidateTool(NeoSkillToolBase):
},
"sync_to_local": {
"type": "boolean",
"description": "When stage is stable, sync payload.skill_markdown to local SKILL.md.",
"description": (
"Only used with stage=stable. true means sync payload.skill_markdown to local SKILL.md; "
"false means release remains Neo-side only."
),
"default": True,
},
},