This commit is contained in:
yumoqing 2026-01-16 18:20:11 +08:00
parent 218166c41c
commit 4099c30156
7 changed files with 115 additions and 0 deletions

18
pyproject.toml Normal file
View File

@ -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"
]

0
skillagent/__init__.py Normal file
View File

43
skillagent/agent.py Normal file
View File

@ -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": "<skill_name>", "script": "<script_name>" }}
"""
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)

View File

@ -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"

View File

@ -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

View File

@ -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)

10
skillagent/utils.py Normal file
View File

@ -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()