From 4099c30156611f6cb7111695572a63b848006682 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Fri, 16 Jan 2026 18:20:11 +0800 Subject: [PATCH] bugfix --- pyproject.toml | 18 +++++++++++ skillagent/__init__.py | 0 skillagent/agent.py | 43 +++++++++++++++++++++++++++ skillagent/schemas/generic_schemas.py | 12 ++++++++ skillagent/skill_loader.py | 25 ++++++++++++++++ skillagent/skillkit_wrapper.py | 7 +++++ skillagent/utils.py | 10 +++++++ 7 files changed, 115 insertions(+) create mode 100644 pyproject.toml create mode 100644 skillagent/__init__.py create mode 100644 skillagent/agent.py create mode 100644 skillagent/schemas/generic_schemas.py create mode 100644 skillagent/skill_loader.py create mode 100644 skillagent/skillkit_wrapper.py create mode 100644 skillagent/utils.py diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..07ae73a --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,18 @@ +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" + +[project] +name = "skillagent" +version = "0.1.0" +description = "Agent + Skillkit integration module with multi-skill, multi-script support" +authors = [ + { name="Your Name", email="you@example.com" } +] +readme = "README.md" +requires-python = ">=3.10" +dependencies = [ + "pydantic>=1.10", + "skillkit", + "aiohttp" +] diff --git a/skillagent/__init__.py b/skillagent/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/skillagent/agent.py b/skillagent/agent.py new file mode 100644 index 0000000..e57e5e3 --- /dev/null +++ b/skillagent/agent.py @@ -0,0 +1,43 @@ +import os, json +from typing import Dict +from pydantic import ValidationError + +class DAGNode: + def __init__(self, skill, script=None, params=None): + self.skill = skill + self.script = script + self.params = params or {} + self.state = "pending" + self.missing = [] + +class SkillAgent: + def __init__(self, skill_loader, skillkit_wrapper, llm): + self.skill_loader = skill_loader + self.skillkit = skillkit_wrapper + self.llm = llm + self.dag = [] + + async def plan(self, user_text: str): + skills = self.skill_loader.list_skills() + skill_docs = "\n".join([open(os.path.join(self.skill_loader.skillspath, s, "skill.md"), encoding="utf-8").read() for s in skills]) + prompt = f""" +User request: {user_text} +Available Skills: +{skill_docs} + +Task: choose the most suitable skill and script. Output JSON: {{ "skill": "", "script": "" }} +""" + raw = await self.llm.complete(prompt) + data = json.loads(raw) + node = DAGNode(skill=data["skill"], script=data["script"]) + self.dag.append(node) + return node + + async def resume(self, node: DAGNode, user_params: dict = None, schema=None): + try: + validated = schema(**(user_params or node.params)) + node.params = validated.dict() + except ValidationError as e: + node.missing = [err['loc'][0] for err in e.errors()] + return node.missing + return self.skillkit.invoke_skill(node.skill, node.script, node.params) diff --git a/skillagent/schemas/generic_schemas.py b/skillagent/schemas/generic_schemas.py new file mode 100644 index 0000000..90e3def --- /dev/null +++ b/skillagent/schemas/generic_schemas.py @@ -0,0 +1,12 @@ +from pydantic import BaseModel +from typing import Optional + +class SliderParams(BaseModel): + min: int + max: int + step: Optional[int] = 1 + default: Optional[int] = 0 + +class ButtonParams(BaseModel): + label: str + color: Optional[str] = "blue" diff --git a/skillagent/skill_loader.py b/skillagent/skill_loader.py new file mode 100644 index 0000000..9d296cd --- /dev/null +++ b/skillagent/skill_loader.py @@ -0,0 +1,25 @@ +import os, re +from typing import List, Dict + +class SkillLoader: + def __init__(self, skillspath: str): + self.skillspath = skillspath + + def list_skills(self) -> List[str]: + return [name for name in os.listdir(self.skillspath) + if os.path.isdir(os.path.join(self.skillspath, name))] + + def parse_skill_md(self, skill_name: str) -> List[Dict]: + md_path = os.path.join(self.skillspath, skill_name, "skill.md") + if not os.path.exists(md_path): + return [] + scripts = [] + md_text = open(md_path, encoding="utf-8").read() + script_blocks = re.findall(r"### (.+?)\n- 功能: (.+?)\n- 参数: (.+)", md_text) + for name, desc, params in script_blocks: + scripts.append({ + "name": name.strip(), + "description": desc.strip(), + "params": [p.strip() for p in params.split(",")] + }) + return scripts diff --git a/skillagent/skillkit_wrapper.py b/skillagent/skillkit_wrapper.py new file mode 100644 index 0000000..bdcd19b --- /dev/null +++ b/skillagent/skillkit_wrapper.py @@ -0,0 +1,7 @@ +class SkillkitWrapper: + def __init__(self, skillkit_client): + self.client = skillkit_client + + def invoke_skill(self, skill: str, script: str, params: dict): + print(f"Invoking skill={skill}, script={script}, params={params}") + return self.client.invoke_skill(skill, params) diff --git a/skillagent/utils.py b/skillagent/utils.py new file mode 100644 index 0000000..224be17 --- /dev/null +++ b/skillagent/utils.py @@ -0,0 +1,10 @@ +import os + +def feed_doc_layer(skillspath: str, skill_name: str, layer: str, script_name: str = None): + base_path = os.path.join(skillspath, skill_name) + if layer == "skill": + return open(os.path.join(base_path, "skill.md"), encoding="utf-8").read() + elif layer == "script" and script_name: + return open(os.path.join(base_path, "scripts", f"{script_name}.md"), encoding="utf-8").read() + elif layer == "example" and script_name: + return open(os.path.join(base_path, "examples", f"{script_name}_example.md"), encoding="utf-8").read()