Skip to content

Commit 73e665b

Browse files
committed
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.
1 parent 4b1bda5 commit 73e665b

2 files changed

Lines changed: 43 additions & 8 deletions

File tree

astrbot/core/astr_main_agent.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -840,6 +840,18 @@ def _apply_sandbox_tools(
840840
"Example: use `baidu_homepage.png` instead of `/workspace/baidu_homepage.png`.\n"
841841
)
842842

843+
req.system_prompt += (
844+
"\n[Neo Skill Lifecycle Workflow]\n"
845+
"When user asks to create/update a reusable skill in Neo mode, use lifecycle tools instead of directly writing local skill folders.\n"
846+
"Preferred sequence:\n"
847+
"1) Use `astrbot_create_skill_payload` to store canonical payload content and get `payload_ref`.\n"
848+
"2) Use `astrbot_create_skill_candidate` with `skill_key` + `source_execution_ids` (and optional `payload_ref`) to create a candidate.\n"
849+
"3) Use `astrbot_promote_skill_candidate` to release: `stage=canary` for trial; `stage=stable` for production.\n"
850+
"For stable release, set `sync_to_local=true` to sync `payload.skill_markdown` into local `SKILL.md`.\n"
851+
"Do not treat ad-hoc generated files as reusable Neo skills unless they are captured via payload/candidate/release.\n"
852+
"To update an existing skill, create a new payload/candidate and promote a new release version; avoid patching old local folders directly.\n"
853+
)
854+
843855
# Determine sandbox capabilities from an already-booted session.
844856
# If no session exists yet (first request), capabilities is None
845857
# and we register all tools conservatively.

astrbot/core/computer/tools/neo_skills.py

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -155,15 +155,19 @@ async def call(
155155
@dataclass
156156
class CreateSkillPayloadTool(NeoSkillToolBase):
157157
name: str = "astrbot_create_skill_payload"
158-
description: str = "Create a generic skill payload and return payload_ref."
158+
description: str = (
159+
"Step 1/3 for Neo skill authoring: create immutable payload content and return payload_ref. "
160+
"Use this to store skill_markdown and structured metadata; do NOT write local skill folders directly."
161+
)
159162
parameters: dict = field(
160163
default_factory=lambda: {
161164
"type": "object",
162165
"properties": {
163166
"payload": {
164167
"anyOf": [{"type": "object"}, {"type": "array"}],
165168
"description": (
166-
"Skill payload JSON. Recommended fields: skill_markdown, commands, meta."
169+
"Skill payload JSON. Typical schema: {skill_markdown, inputs, outputs, meta}. "
170+
"This only stores content and returns payload_ref; it does not create a candidate or release."
167171
),
168172
},
169173
"kind": {
@@ -221,18 +225,31 @@ async def call(
221225
@dataclass
222226
class CreateSkillCandidateTool(NeoSkillToolBase):
223227
name: str = "astrbot_create_skill_candidate"
224-
description: str = "Create a skill candidate from source execution IDs."
228+
description: str = (
229+
"Step 2/3 for Neo skill authoring: create a candidate by binding execution evidence "
230+
"(source_execution_ids) with skill identity (skill_key) and optional payload_ref."
231+
)
225232
parameters: dict = field(
226233
default_factory=lambda: {
227234
"type": "object",
228235
"properties": {
229-
"skill_key": {"type": "string"},
236+
"skill_key": {
237+
"type": "string",
238+
"description": "Stable logical identifier, e.g. image-collage-9grid.",
239+
},
230240
"source_execution_ids": {
231241
"type": "array",
232242
"items": {"type": "string"},
243+
"description": "Execution evidence IDs captured from sandbox history.",
244+
},
245+
"scenario_key": {
246+
"type": "string",
247+
"description": "Optional scenario namespace for grouping candidates.",
248+
},
249+
"payload_ref": {
250+
"type": "string",
251+
"description": "Optional payload reference created by astrbot_create_skill_payload.",
233252
},
234-
"scenario_key": {"type": "string"},
235-
"payload_ref": {"type": "string"},
236253
},
237254
"required": ["skill_key", "source_execution_ids"],
238255
}
@@ -338,7 +355,10 @@ async def call(
338355
@dataclass
339356
class PromoteSkillCandidateTool(NeoSkillToolBase):
340357
name: str = "astrbot_promote_skill_candidate"
341-
description: str = "Promote one candidate to release stage (canary/stable)."
358+
description: str = (
359+
"Step 3/3 for Neo skill authoring: promote candidate to canary/stable release. "
360+
"If stage=stable and sync_to_local=true, payload.skill_markdown is synced to local SKILL.md automatically."
361+
)
342362
parameters: dict = field(
343363
default_factory=lambda: {
344364
"type": "object",
@@ -351,7 +371,10 @@ class PromoteSkillCandidateTool(NeoSkillToolBase):
351371
},
352372
"sync_to_local": {
353373
"type": "boolean",
354-
"description": "When stage is stable, sync payload.skill_markdown to local SKILL.md.",
374+
"description": (
375+
"Only used with stage=stable. true means sync payload.skill_markdown to local SKILL.md; "
376+
"false means release remains Neo-side only."
377+
),
355378
"default": True,
356379
},
357380
},

0 commit comments

Comments
 (0)