Skip to content

Commit 36b93b0

Browse files
authored
feat: skill with check list (#512)
* feat: skill with check list * fix: check list for instruction
1 parent 930fabe commit 36b93b0

File tree

4 files changed

+101
-1
lines changed

4 files changed

+101
-1
lines changed

veadk/agent.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,8 @@ class Agent(LlmAgent):
156156

157157
enable_dataset_gen: bool = False
158158

159+
_skills_with_checklist: Dict[str, Any] = {}
160+
159161
def model_post_init(self, __context: Any) -> None:
160162
super().model_post_init(None) # for sub_agents init
161163

@@ -300,6 +302,21 @@ def model_post_init(self, __context: Any) -> None:
300302

301303
if self.skills:
302304
self.load_skills()
305+
from veadk.skills.utils import create_init_skill_check_list_callback
306+
307+
init_callback = create_init_skill_check_list_callback(
308+
self._skills_with_checklist
309+
)
310+
if self.before_tool_callback:
311+
if isinstance(self.before_tool_callback, list):
312+
self.before_tool_callback.append(init_callback)
313+
else:
314+
self.before_tool_callback = [
315+
self.before_tool_callback,
316+
init_callback,
317+
]
318+
else:
319+
self.before_tool_callback = init_callback
303320

304321
if self.example_store:
305322
from google.adk.tools.example_tool import ExampleTool
@@ -433,12 +450,23 @@ def load_skills(self):
433450
for skill in load_skills_from_cloud(item):
434451
skills[skill.name] = skill
435452
if skills:
453+
self._skills_with_checklist = skills
454+
436455
self.instruction += "\nYou have the following skills:\n"
437456

457+
has_checklist = False
438458
for skill in skills.values():
439459
self.instruction += (
440460
f"- name: {skill.name}\n- description: {skill.description}\n\n"
441461
)
462+
if skill.checklist:
463+
has_checklist = True
464+
465+
if has_checklist:
466+
self.instruction += (
467+
"Some skills have a checklist that you must complete step by step. "
468+
"Use the `update_check_list` tool to mark each item as completed.\n\n"
469+
)
442470

443471
if self.skills_mode not in [
444472
"skills_sandbox",

veadk/skills/skill.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# limitations under the License.
1414

1515
from pydantic import BaseModel
16-
from typing import Optional
16+
from typing import Optional, List, Dict
1717

1818

1919
class Skill(BaseModel):
@@ -22,3 +22,7 @@ class Skill(BaseModel):
2222
path: str # local path or tos path
2323
skill_space_id: Optional[str] = None
2424
bucket_name: Optional[str] = None
25+
checklist: List[Dict[str, str]] = []
26+
27+
def get_checklist_items(self) -> List[str]:
28+
return [item.get("item", item.get("id", "")) for item in self.checklist]

veadk/skills/utils.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,74 @@
1717
import os
1818
import frontmatter
1919

20+
from google.adk.tools import BaseTool, ToolContext
21+
from typing import Any, Dict, Optional, Callable
22+
2023
from veadk.skills.skill import Skill
2124
from veadk.utils.logger import get_logger
2225
from veadk.utils.volcengine_sign import ve_request
2326

2427
logger = get_logger(__name__)
2528

2629

30+
def update_check_list(
31+
tool_context: ToolContext, skill_name: str, check_item: str, state: bool
32+
):
33+
"""
34+
Update the checklist item state for a specific skill.
35+
Use this tool to mark checklist items as completed during skill execution.
36+
37+
eg:
38+
update_check_list(skill_name="skill-creator", check_item="analyze_content", state=True)
39+
"""
40+
agent_name = tool_context.agent_name
41+
if agent_name not in tool_context.state:
42+
tool_context.state[agent_name] = {}
43+
if skill_name not in tool_context.state[agent_name]:
44+
tool_context.state[agent_name][skill_name] = {}
45+
if "check_list" not in tool_context.state[agent_name][skill_name]:
46+
tool_context.state[agent_name][skill_name]["check_list"] = {}
47+
tool_context.state[agent_name][skill_name]["check_list"][check_item] = state
48+
logger.info(f"Updated agent[{agent_name}] state: {tool_context.state[agent_name]}")
49+
50+
51+
def create_init_skill_check_list_callback(
52+
skills_with_checklist: Dict[str, Skill],
53+
) -> Callable[[BaseTool, Dict[str, Any], ToolContext], Optional[Dict]]:
54+
"""
55+
Create a callback function to initialize checklist when a skill is invoked.
56+
57+
Args:
58+
skills_with_checklist: Dictionary mapping skill names to Skill objects
59+
60+
Returns:
61+
A callback function for before_tool_callback
62+
"""
63+
64+
def init_skill_check_list(
65+
tool: BaseTool, args: Dict[str, Any], tool_context: ToolContext
66+
) -> Optional[Dict]:
67+
"""Callback to initialize checklist when a skill is invoked."""
68+
if tool.name == "skills_tool":
69+
skill_name = args.get("command")
70+
agent_name = tool_context.agent_name
71+
if skill_name in skills_with_checklist:
72+
skill = skills_with_checklist[skill_name]
73+
check_list_items = skill.get_checklist_items()
74+
check_list_state = {item: False for item in check_list_items}
75+
if agent_name not in tool_context.state:
76+
tool_context.state[agent_name] = {}
77+
tool_context.state[agent_name][skill_name] = {
78+
"check_list": check_list_state
79+
}
80+
logger.info(
81+
f"Initialized agent[{agent_name}] skill[{skill_name}] check_list: {check_list_state}"
82+
)
83+
return None
84+
85+
return init_skill_check_list
86+
87+
2788
def load_skill_from_directory(skill_directory: Path) -> Skill:
2889
logger.info(f"Load skill from {skill_directory}")
2990
skill_readme = skill_directory / "SKILL.md"
@@ -35,6 +96,7 @@ def load_skill_from_directory(skill_directory: Path) -> Skill:
3596

3697
skill_name = skill.get("name", "")
3798
skill_description = skill.get("description", "")
99+
checklist = skill.get("checklist", [])
38100

39101
if not skill_name or not skill_description:
40102
logger.error(
@@ -47,10 +109,14 @@ def load_skill_from_directory(skill_directory: Path) -> Skill:
47109
logger.info(
48110
f"Successfully loaded skill {skill_name} locally from {skill_readme}, name={skill_name}, description={skill_description}"
49111
)
112+
if checklist:
113+
logger.info(f"Skill {skill_name} checklist: {checklist}")
114+
50115
return Skill(
51116
name=skill_name, # type: ignore
52117
description=skill_description, # type: ignore
53118
path=str(skill_directory),
119+
checklist=checklist,
54120
)
55121

56122

veadk/tools/skills_tools/skills_toolset.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
bash_tool,
3535
register_skills_tool,
3636
)
37+
from veadk.skills.utils import update_check_list
3738
from veadk.utils.logger import get_logger
3839

3940
logger = get_logger(__name__)
@@ -73,6 +74,7 @@ def __init__(self, skills: Dict[str, Skill], skills_mode: str) -> None:
7374
"edit_file": FunctionTool(edit_file_tool),
7475
"bash": FunctionTool(bash_tool),
7576
"register_skills": FunctionTool(register_skills_tool),
77+
"update_check_list": FunctionTool(update_check_list),
7678
}
7779

7880
@override

0 commit comments

Comments
 (0)