skill-self-evolution 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- skill_self_evolution/__init__.py +27 -0
- skill_self_evolution/ai_assisted_executor.py +244 -0
- skill_self_evolution/config.py +87 -0
- skill_self_evolution/config_loader.py +241 -0
- skill_self_evolution/context.py +22 -0
- skill_self_evolution/deepseek.py +168 -0
- skill_self_evolution/evolver.py +386 -0
- skill_self_evolution/executor.py +471 -0
- skill_self_evolution/fallback.py +129 -0
- skill_self_evolution/loader.py +201 -0
- skill_self_evolution/logger.py +84 -0
- skill_self_evolution/models.py +147 -0
- skill_self_evolution-0.2.0.dist-info/METADATA +12 -0
- skill_self_evolution-0.2.0.dist-info/RECORD +16 -0
- skill_self_evolution-0.2.0.dist-info/WHEEL +5 -0
- skill_self_evolution-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Skill 动态加载器 — 加载 run.py / summarize_input / evolve.toml / evolve_prompt.yaml / skill.md。
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import importlib.util
|
|
6
|
+
import logging
|
|
7
|
+
import os
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any, Callable
|
|
10
|
+
|
|
11
|
+
import yaml
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
# Skill 根目录:优先 SKILL_BASE_DIR 环境变量;兼容 housekeeping 的路径
|
|
16
|
+
_skill_base_env = os.getenv("SKILL_BASE_DIR", "")
|
|
17
|
+
if _skill_base_env:
|
|
18
|
+
DEFAULT_SKILL_BASE = Path(_skill_base_env)
|
|
19
|
+
else:
|
|
20
|
+
# 尝试相对于 housekeeping 项目根目录(兼容老部署)
|
|
21
|
+
_hk_root = Path(__file__).resolve().parents[3].parent / "housekeeping_ai_match"
|
|
22
|
+
_hk_skill = _hk_root / "backend" / "config" / "services" / "skill"
|
|
23
|
+
if _hk_skill.is_dir():
|
|
24
|
+
DEFAULT_SKILL_BASE = _hk_skill
|
|
25
|
+
else:
|
|
26
|
+
DEFAULT_SKILL_BASE = Path.cwd() / "backend" / "config" / "services" / "skill"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class SkillModule:
|
|
30
|
+
"""已加载的 Skill 模块,包含 execute / benchmark / summarize_input 函数和元数据。"""
|
|
31
|
+
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
skill_name: str,
|
|
35
|
+
execute: Callable,
|
|
36
|
+
benchmark: Callable | None = None,
|
|
37
|
+
summarize_input: Callable | None = None,
|
|
38
|
+
evolve_toml: dict[str, Any] | None = None,
|
|
39
|
+
evolve_prompt_yaml: dict[str, Any] | None = None,
|
|
40
|
+
skill_md: str | None = None,
|
|
41
|
+
):
|
|
42
|
+
self.skill_name = skill_name
|
|
43
|
+
self.execute = execute
|
|
44
|
+
self.benchmark = benchmark or (lambda executor: (0, 0, []))
|
|
45
|
+
self.summarize_input = summarize_input
|
|
46
|
+
self.evolve_toml = evolve_toml or {}
|
|
47
|
+
self.evolve_prompt_yaml = evolve_prompt_yaml
|
|
48
|
+
self.skill_md = skill_md
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def ai_role(self) -> str:
|
|
52
|
+
"""从 evolve.toml 读取 ai_role,默认 "correction"。"""
|
|
53
|
+
return self.evolve_toml.get("skill", {}).get("ai_role", "correction")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class SkillLoader:
|
|
57
|
+
"""Skill 加载器,负责从磁盘动态导入 run.py 并解析配置文件。"""
|
|
58
|
+
|
|
59
|
+
def __init__(self, skill_base_dir: Path | None = None):
|
|
60
|
+
self._base_dir = skill_base_dir or DEFAULT_SKILL_BASE
|
|
61
|
+
self._cache: dict[str, SkillModule] = {}
|
|
62
|
+
|
|
63
|
+
def load(self, skill_name: str) -> SkillModule:
|
|
64
|
+
"""加载指定 Skill(含缓存)"""
|
|
65
|
+
if skill_name in self._cache:
|
|
66
|
+
return self._cache[skill_name]
|
|
67
|
+
|
|
68
|
+
skill_dir = self._base_dir / skill_name
|
|
69
|
+
if not skill_dir.is_dir():
|
|
70
|
+
raise FileNotFoundError(f"Skill 目录不存在: {skill_dir}")
|
|
71
|
+
|
|
72
|
+
# 1. 加载 run.py(强制导出 execute)
|
|
73
|
+
run_path = skill_dir / "scripts" / "run.py"
|
|
74
|
+
if not run_path.is_file():
|
|
75
|
+
raise FileNotFoundError(f"run.py 不存在: {run_path}")
|
|
76
|
+
|
|
77
|
+
spec = importlib.util.spec_from_file_location(
|
|
78
|
+
f"skill_{skill_name.replace('-', '_')}", str(run_path)
|
|
79
|
+
)
|
|
80
|
+
if spec is None or spec.loader is None:
|
|
81
|
+
raise ImportError(f"无法加载 run.py: {run_path}")
|
|
82
|
+
|
|
83
|
+
module = importlib.util.module_from_spec(spec)
|
|
84
|
+
spec.loader.exec_module(module)
|
|
85
|
+
|
|
86
|
+
if not hasattr(module, "execute"):
|
|
87
|
+
raise AttributeError(f"{run_path} 缺少强制函数 execute()")
|
|
88
|
+
|
|
89
|
+
execute_fn = getattr(module, "execute")
|
|
90
|
+
benchmark_fn = getattr(module, "benchmark", None)
|
|
91
|
+
summarize_input_fn = getattr(module, "summarize_input", None)
|
|
92
|
+
|
|
93
|
+
# 2. 加载 evolve.toml
|
|
94
|
+
evolve_toml = self._load_evolve_toml(skill_dir)
|
|
95
|
+
|
|
96
|
+
# 3. 加载 evolve_prompt.yaml(Skill 自定义优先)
|
|
97
|
+
evolve_prompt_yaml = self._load_evolve_prompt_yaml(skill_dir)
|
|
98
|
+
|
|
99
|
+
# 4. 加载 skill.md
|
|
100
|
+
skill_md = self._load_skill_md(skill_dir)
|
|
101
|
+
|
|
102
|
+
skill_module = SkillModule(
|
|
103
|
+
skill_name=skill_name,
|
|
104
|
+
execute=execute_fn,
|
|
105
|
+
benchmark=benchmark_fn,
|
|
106
|
+
summarize_input=summarize_input_fn,
|
|
107
|
+
evolve_toml=evolve_toml,
|
|
108
|
+
evolve_prompt_yaml=evolve_prompt_yaml,
|
|
109
|
+
skill_md=skill_md,
|
|
110
|
+
)
|
|
111
|
+
self._cache[skill_name] = skill_module
|
|
112
|
+
logger.info("Skill 加载完成: %s (ai_role=%s)", skill_name, skill_module.ai_role)
|
|
113
|
+
return skill_module
|
|
114
|
+
|
|
115
|
+
def invalidate_cache(self, skill_name: str | None = None) -> None:
|
|
116
|
+
"""清除缓存(配置热加载后调用)。"""
|
|
117
|
+
if skill_name:
|
|
118
|
+
self._cache.pop(skill_name, None)
|
|
119
|
+
else:
|
|
120
|
+
self._cache.clear()
|
|
121
|
+
|
|
122
|
+
@staticmethod
|
|
123
|
+
def _load_evolve_toml(skill_dir: Path) -> dict[str, Any]:
|
|
124
|
+
toml_path = skill_dir / "evolve.toml"
|
|
125
|
+
if not toml_path.is_file():
|
|
126
|
+
logger.debug("evolve.toml 未找到,使用默认值: %s", toml_path)
|
|
127
|
+
return {"skill": {"ai_role": "correction"}}
|
|
128
|
+
|
|
129
|
+
# 简单 TOML 解析(仅支持顶层表 [section] 和 key=value,不依赖 toml 库)
|
|
130
|
+
return _parse_simple_toml(toml_path.read_text(encoding="utf-8"))
|
|
131
|
+
|
|
132
|
+
@staticmethod
|
|
133
|
+
def _load_evolve_prompt_yaml(skill_dir: Path) -> dict[str, Any]:
|
|
134
|
+
yaml_path = skill_dir / "evolve_prompt.yaml"
|
|
135
|
+
if not yaml_path.is_file():
|
|
136
|
+
return {}
|
|
137
|
+
return yaml.safe_load(yaml_path.read_text(encoding="utf-8"))
|
|
138
|
+
|
|
139
|
+
@staticmethod
|
|
140
|
+
def _load_skill_md(skill_dir: Path) -> str | None:
|
|
141
|
+
md_path = skill_dir / "skill.md"
|
|
142
|
+
if not md_path.is_file():
|
|
143
|
+
return None
|
|
144
|
+
return md_path.read_text(encoding="utf-8")
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _parse_simple_toml(content: str) -> dict[str, Any]:
|
|
148
|
+
"""极简 TOML 解析器,支持一层嵌套的 [parent.child] 节。"""
|
|
149
|
+
result: dict[str, Any] = {}
|
|
150
|
+
current_path: list[str] = [] # 当前 section 路径栈
|
|
151
|
+
|
|
152
|
+
for line in content.split("\n"):
|
|
153
|
+
line = line.strip()
|
|
154
|
+
if not line or line.startswith("#"):
|
|
155
|
+
continue
|
|
156
|
+
|
|
157
|
+
# [section] 或 [section.sub]
|
|
158
|
+
if line.startswith("[") and "]" in line:
|
|
159
|
+
section_name = line[1:].split("]")[0].strip()
|
|
160
|
+
parts = section_name.split(".")
|
|
161
|
+
current_path = parts
|
|
162
|
+
|
|
163
|
+
# 确保路径存在(若中间节点已被占用为非 dict,则替换为 dict)
|
|
164
|
+
cursor = result
|
|
165
|
+
for part in parts:
|
|
166
|
+
if part not in cursor or not isinstance(cursor.get(part), dict):
|
|
167
|
+
cursor[part] = {}
|
|
168
|
+
cursor = cursor[part]
|
|
169
|
+
continue
|
|
170
|
+
|
|
171
|
+
# key = value
|
|
172
|
+
if "=" in line:
|
|
173
|
+
key, _, value = line.partition("=")
|
|
174
|
+
key = key.strip()
|
|
175
|
+
value = value.strip().strip('"').strip("'")
|
|
176
|
+
|
|
177
|
+
# 类型推断
|
|
178
|
+
if value.lower() in ("true", "false"):
|
|
179
|
+
parsed: Any = value.lower() == "true"
|
|
180
|
+
elif value.isdigit():
|
|
181
|
+
parsed = int(value)
|
|
182
|
+
elif _is_float(value):
|
|
183
|
+
parsed = float(value)
|
|
184
|
+
else:
|
|
185
|
+
parsed = value
|
|
186
|
+
|
|
187
|
+
# 写入当前 section
|
|
188
|
+
cursor = result
|
|
189
|
+
for part in current_path:
|
|
190
|
+
cursor = cursor[part]
|
|
191
|
+
cursor[key] = parsed
|
|
192
|
+
|
|
193
|
+
return result
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _is_float(s: str) -> bool:
|
|
197
|
+
try:
|
|
198
|
+
float(s)
|
|
199
|
+
return "." in s
|
|
200
|
+
except ValueError:
|
|
201
|
+
return False
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""
|
|
2
|
+
JSONL 日志器 — 追加写入 Skill 执行日志,Pydantic 校验。
|
|
3
|
+
|
|
4
|
+
日志路径: /data/skill-logs/{skill_name}/{date}.jsonl
|
|
5
|
+
(可通过 SKILL_LOG_DIR 环境变量覆盖)
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import logging
|
|
10
|
+
import os
|
|
11
|
+
from datetime import datetime, timezone, timedelta
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
from skill_self_evolution.models import LogEntry
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
_BEIJING_TZ = timezone(timedelta(hours=8))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _beijing_now() -> datetime:
|
|
22
|
+
return datetime.now(_BEIJING_TZ)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _beijing_today_str() -> str:
|
|
26
|
+
return _beijing_now().strftime("%Y-%m-%d")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _get_log_dir(skill_name: str) -> Path:
|
|
30
|
+
"""获取日志目录,优先取环境变量 SKILL_LOG_DIR。"""
|
|
31
|
+
base = os.environ.get("SKILL_LOG_DIR", "/data/skill-logs")
|
|
32
|
+
return Path(base) / skill_name
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class SkillLogger:
|
|
36
|
+
"""Skill 执行日志器,每行一个 JSON(Pydantic LogEntry 校验)。"""
|
|
37
|
+
|
|
38
|
+
def __init__(self, skill_name: str):
|
|
39
|
+
self.skill_name = skill_name
|
|
40
|
+
self._log_path: Path | None = None
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def log_path(self) -> Path:
|
|
44
|
+
if self._log_path is None:
|
|
45
|
+
log_dir = _get_log_dir(self.skill_name)
|
|
46
|
+
log_dir.mkdir(parents=True, exist_ok=True)
|
|
47
|
+
self._log_path = log_dir / f"{_beijing_today_str()}.jsonl"
|
|
48
|
+
return self._log_path
|
|
49
|
+
|
|
50
|
+
def write(self, entry: dict) -> None:
|
|
51
|
+
"""追加一行 JSON 到日志文件(接受已校验的 dict)。"""
|
|
52
|
+
try:
|
|
53
|
+
with open(self.log_path, "a", encoding="utf-8") as f:
|
|
54
|
+
f.write(json.dumps(entry, ensure_ascii=False) + "\n")
|
|
55
|
+
except Exception as e:
|
|
56
|
+
logger.warning("Skill 日志写入失败: %s", e)
|
|
57
|
+
|
|
58
|
+
def log_execution(
|
|
59
|
+
self,
|
|
60
|
+
trace_id: str,
|
|
61
|
+
is_failure: bool,
|
|
62
|
+
input_summary: dict,
|
|
63
|
+
rule_output: dict,
|
|
64
|
+
ai_validation: dict | None,
|
|
65
|
+
ai_reselection: dict | None,
|
|
66
|
+
final_output: dict,
|
|
67
|
+
warnings: list[str],
|
|
68
|
+
elapsed_ms: float,
|
|
69
|
+
) -> None:
|
|
70
|
+
"""写入标准执行日志条目(Pydantic 校验后持久化)。"""
|
|
71
|
+
entry = LogEntry(
|
|
72
|
+
trace_id=trace_id,
|
|
73
|
+
skill_name=self.skill_name,
|
|
74
|
+
timestamp=_beijing_now().isoformat(),
|
|
75
|
+
is_failure=is_failure,
|
|
76
|
+
input_summary=input_summary,
|
|
77
|
+
rule_output=rule_output,
|
|
78
|
+
ai_validation=ai_validation,
|
|
79
|
+
ai_reselection=ai_reselection,
|
|
80
|
+
final_output=final_output,
|
|
81
|
+
warnings=warnings,
|
|
82
|
+
elapsed_ms=round(elapsed_ms, 1),
|
|
83
|
+
)
|
|
84
|
+
self.write(entry.model_dump())
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Pydantic input/output models ? all Skills must use these.
|
|
4
|
+
|
|
5
|
+
All AI-related enums/literals use pre-constructed string constants to
|
|
6
|
+
avoid encoding issues across platforms.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from typing import Any, Generic, Literal, TypeVar
|
|
10
|
+
from uuid import uuid4
|
|
11
|
+
|
|
12
|
+
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
13
|
+
|
|
14
|
+
T = TypeVar("T")
|
|
15
|
+
|
|
16
|
+
# ?? AI judgement literals ?????????????????????????????????????
|
|
17
|
+
# Use constants to avoid platform encoding quirks with Chinese Unicode
|
|
18
|
+
_REASONABLE = "\u5408\u7406" # ??
|
|
19
|
+
_UNREASONABLE = "\u4e0d\u5408\u7406" # ???
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# ?? P0: AI intermediate results ??????????????????????????????
|
|
23
|
+
|
|
24
|
+
class AiValidationResult(BaseModel):
|
|
25
|
+
"""Standardised output of AI common-sense validation."""
|
|
26
|
+
|
|
27
|
+
model_config = ConfigDict(frozen=True)
|
|
28
|
+
|
|
29
|
+
result: Literal[
|
|
30
|
+
"\u5408\u7406",
|
|
31
|
+
"\u4e0d\u5408\u7406",
|
|
32
|
+
] = Field(..., description="AI judgement: reasonable / unreasonable")
|
|
33
|
+
|
|
34
|
+
reason: str = Field(default="", description="AI reasoning (max 500 chars)")
|
|
35
|
+
|
|
36
|
+
@field_validator("reason")
|
|
37
|
+
@classmethod
|
|
38
|
+
def _truncate_reason(cls, v: str) -> str:
|
|
39
|
+
return v[:500]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class AiReselectionResult(BaseModel):
|
|
43
|
+
"""Standardised output of AI reselection."""
|
|
44
|
+
|
|
45
|
+
model_config = ConfigDict(frozen=True)
|
|
46
|
+
|
|
47
|
+
result: str = Field(
|
|
48
|
+
..., description="Reselected value, or '\u4e0d\u5408\u7406' if still unreasonable"
|
|
49
|
+
)
|
|
50
|
+
reason: str = Field(default="", description="AI reasoning (max 500 chars)")
|
|
51
|
+
|
|
52
|
+
@field_validator("reason")
|
|
53
|
+
@classmethod
|
|
54
|
+
def _truncate_reason(cls, v: str) -> str:
|
|
55
|
+
return v[:500]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# ?? P1: DeepSeek client models ???????????????????????????????
|
|
59
|
+
|
|
60
|
+
class DeepSeekChatResponse(BaseModel):
|
|
61
|
+
"""Validated OpenAI Chat Completions response wrapper."""
|
|
62
|
+
|
|
63
|
+
content: str = Field(default="")
|
|
64
|
+
finish_reason: str | None = Field(default=None)
|
|
65
|
+
prompt_tokens: int | None = Field(default=None)
|
|
66
|
+
completion_tokens: int | None = Field(default=None)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
# ?? P1: Log entry ????????????????????????????????????????????
|
|
70
|
+
|
|
71
|
+
class LogEntry(BaseModel):
|
|
72
|
+
"""Single JSONL log entry schema.
|
|
73
|
+
|
|
74
|
+
All fields defaulted so legacy / partial reads won't fail.
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
trace_id: str = Field(default="")
|
|
78
|
+
skill_name: str = Field(default="")
|
|
79
|
+
timestamp: str = Field(default="")
|
|
80
|
+
is_failure: bool = Field(default=False)
|
|
81
|
+
input_summary: dict[str, Any] = Field(default_factory=dict)
|
|
82
|
+
rule_output: dict[str, Any] = Field(default_factory=dict)
|
|
83
|
+
ai_validation: dict[str, Any] | None = Field(default=None)
|
|
84
|
+
ai_reselection: dict[str, Any] | None = Field(default=None)
|
|
85
|
+
final_output: dict[str, Any] = Field(default_factory=dict)
|
|
86
|
+
warnings: list[str] = Field(default_factory=list)
|
|
87
|
+
elapsed_ms: float = Field(default=0.0)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
# ?? P2: Fallback config ??????????????????????????????????????
|
|
91
|
+
|
|
92
|
+
class FallbackConfigModel(BaseModel):
|
|
93
|
+
"""Validated fallback configuration."""
|
|
94
|
+
|
|
95
|
+
validate_timeout_seconds: float = Field(default=3.0, gt=0)
|
|
96
|
+
reselect_timeout_seconds: float = Field(default=5.0, gt=0)
|
|
97
|
+
max_retries: int = Field(default=1, ge=0, le=5)
|
|
98
|
+
circuit_breaker_threshold: int = Field(default=3, ge=1)
|
|
99
|
+
circuit_breaker_cooldown_seconds: float = Field(default=60.0, gt=0)
|
|
100
|
+
conservative_mode: bool = Field(default=False)
|
|
101
|
+
enabled: bool = Field(default=True)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# ?? P2: Evolve proposal ??????????????????????????????????????
|
|
105
|
+
|
|
106
|
+
class EvolveProposalModel(BaseModel):
|
|
107
|
+
"""Serializable evolve proposal."""
|
|
108
|
+
|
|
109
|
+
rules_changes: dict[str, Any] = Field(default_factory=dict)
|
|
110
|
+
prompt_changes: dict[str, Any] = Field(default_factory=dict)
|
|
111
|
+
rules_text: str | None = Field(default=None)
|
|
112
|
+
prompt_text: str | None = Field(default=None)
|
|
113
|
+
analysis_raw: str = Field(default="")
|
|
114
|
+
failure_count: int = Field(default=0)
|
|
115
|
+
applied: bool = Field(default=False)
|
|
116
|
+
rolled_back: bool = Field(default=False)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
# ?? P3: Skill-specific result sub-models (example) ?????????
|
|
120
|
+
|
|
121
|
+
class NicknameSkillResult(BaseModel):
|
|
122
|
+
"""nickname-selector Skill result sub-structure."""
|
|
123
|
+
|
|
124
|
+
nickname: str = Field(default="", description="Selected nickname")
|
|
125
|
+
source: str = Field(default="rule", description="rule | ai")
|
|
126
|
+
candidates: list[str] = Field(default_factory=list)
|
|
127
|
+
band_id: str = Field(default="")
|
|
128
|
+
screenshot_id: str = Field(default="")
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
# ?? Core framework models (existing) ????????????????????????
|
|
132
|
+
|
|
133
|
+
class SkillOutput(BaseModel):
|
|
134
|
+
"""All Skills must return this structure."""
|
|
135
|
+
|
|
136
|
+
source: str = Field(..., description="rule | ai")
|
|
137
|
+
result: dict = Field(..., description="Business result (per-Skill schema)")
|
|
138
|
+
ai_validated: bool = False
|
|
139
|
+
ai_reselected: bool = False
|
|
140
|
+
warnings: list[str] = Field(default_factory=list)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class SkillInput(BaseModel, Generic[T]):
|
|
144
|
+
"""All Skills must receive this structure."""
|
|
145
|
+
|
|
146
|
+
trace_id: str = Field(default_factory=lambda: str(uuid4()))
|
|
147
|
+
input_data: T = Field(..., description="Business input data")
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: skill_self_evolution
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Skill 自进化框架:Pydantic 全链路校验 + 规则执行 + AI 常识判断 + 离线进化的可插拔 Skill 执行引擎
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Requires-Dist: pydantic>=2.0
|
|
7
|
+
Requires-Dist: pyyaml>=6.0
|
|
8
|
+
Requires-Dist: httpx>=0.25
|
|
9
|
+
Requires-Dist: pymysql>=1.1
|
|
10
|
+
Provides-Extra: dev
|
|
11
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
12
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
skill_self_evolution/__init__.py,sha256=jV_l30ZPiwizIGEZj4re3xcfo-Whm075T00zAdpi3Rk,931
|
|
2
|
+
skill_self_evolution/ai_assisted_executor.py,sha256=azSjW2RDuo5pJP59zM-BNhVMpAWzSRvhDxGIP4uNCKo,10250
|
|
3
|
+
skill_self_evolution/config.py,sha256=UPVWO9RBM8EA2Rm4K6vcfWyEIBt9hQyGwHomxMzdV6Q,2680
|
|
4
|
+
skill_self_evolution/config_loader.py,sha256=NZOZIUvhUDU-SJqPQRlUdYuTbCrDWsi9rxz1NJhOT9E,9919
|
|
5
|
+
skill_self_evolution/context.py,sha256=u5ZHIBobMQl43I_TQzMuqjWL5c8_3q8DQlck_19GEhs,635
|
|
6
|
+
skill_self_evolution/deepseek.py,sha256=8SGjMKRJUgOsmIxGX6RC8i3Jv6W0KlgOzAvGdSHyCoA,6110
|
|
7
|
+
skill_self_evolution/evolver.py,sha256=hvqgS-V2PCTQy6GiqvAPQfOjHczFBRu2ZpWlqTuYx1A,15617
|
|
8
|
+
skill_self_evolution/executor.py,sha256=sJOUf1kngMVNq_0rR6RQFRA5FgRd-VRnqYMfapWGhEc,20989
|
|
9
|
+
skill_self_evolution/fallback.py,sha256=F9fRVFiKt_EYN9T7mnyR4P_ycAr5ZoORK1jo7CH5xKg,4649
|
|
10
|
+
skill_self_evolution/loader.py,sha256=98c9AExsd50AQcyLRIZLGI7cf6-a_xv5JHYbZjHltuc,7257
|
|
11
|
+
skill_self_evolution/logger.py,sha256=dCAfgx6roBCXdRTJmrba4Nu8CmvIf2VfTKTOrYJJy1k,2671
|
|
12
|
+
skill_self_evolution/models.py,sha256=v6JuKArffGA_y8uFpqGRtl5-7fW1yfG42tcZwsRZCZ4,5033
|
|
13
|
+
skill_self_evolution-0.2.0.dist-info/METADATA,sha256=yKZmqXOAsTh6Ami9Mw-2SVK1gs_XXwh3gYzTxvlHhlM,463
|
|
14
|
+
skill_self_evolution-0.2.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
15
|
+
skill_self_evolution-0.2.0.dist-info/top_level.txt,sha256=dAqlk1foGIgtvJ_3WElSlP3TvIO69qSMVLThHpbFy0U,21
|
|
16
|
+
skill_self_evolution-0.2.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
skill_self_evolution
|