skill-self-evolution 0.2.0__tar.gz
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-0.2.0/PKG-INFO +12 -0
- skill_self_evolution-0.2.0/README.md +99 -0
- skill_self_evolution-0.2.0/pyproject.toml +25 -0
- skill_self_evolution-0.2.0/setup.cfg +4 -0
- skill_self_evolution-0.2.0/src/skill_self_evolution/__init__.py +27 -0
- skill_self_evolution-0.2.0/src/skill_self_evolution/ai_assisted_executor.py +244 -0
- skill_self_evolution-0.2.0/src/skill_self_evolution/config.py +87 -0
- skill_self_evolution-0.2.0/src/skill_self_evolution/config_loader.py +241 -0
- skill_self_evolution-0.2.0/src/skill_self_evolution/context.py +22 -0
- skill_self_evolution-0.2.0/src/skill_self_evolution/deepseek.py +168 -0
- skill_self_evolution-0.2.0/src/skill_self_evolution/evolver.py +386 -0
- skill_self_evolution-0.2.0/src/skill_self_evolution/executor.py +471 -0
- skill_self_evolution-0.2.0/src/skill_self_evolution/fallback.py +129 -0
- skill_self_evolution-0.2.0/src/skill_self_evolution/loader.py +201 -0
- skill_self_evolution-0.2.0/src/skill_self_evolution/logger.py +84 -0
- skill_self_evolution-0.2.0/src/skill_self_evolution/models.py +147 -0
- skill_self_evolution-0.2.0/src/skill_self_evolution.egg-info/PKG-INFO +12 -0
- skill_self_evolution-0.2.0/src/skill_self_evolution.egg-info/SOURCES.txt +27 -0
- skill_self_evolution-0.2.0/src/skill_self_evolution.egg-info/dependency_links.txt +1 -0
- skill_self_evolution-0.2.0/src/skill_self_evolution.egg-info/requires.txt +8 -0
- skill_self_evolution-0.2.0/src/skill_self_evolution.egg-info/top_level.txt +1 -0
- skill_self_evolution-0.2.0/tests/test_ai_assisted_executor.py +121 -0
- skill_self_evolution-0.2.0/tests/test_context.py +37 -0
- skill_self_evolution-0.2.0/tests/test_deepseek.py +48 -0
- skill_self_evolution-0.2.0/tests/test_executor.py +160 -0
- skill_self_evolution-0.2.0/tests/test_fallback.py +71 -0
- skill_self_evolution-0.2.0/tests/test_loader.py +128 -0
- skill_self_evolution-0.2.0/tests/test_logger.py +64 -0
- skill_self_evolution-0.2.0/tests/test_models.py +76 -0
|
@@ -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,99 @@
|
|
|
1
|
+
# skill_self_evolution v0.2.0
|
|
2
|
+
|
|
3
|
+
Skill 自进化框架:**Pydantic 全链路校验** + 规则执行 + AI 常识判断 + 离线进化的可插拔 Skill 执行引擎。
|
|
4
|
+
|
|
5
|
+
## v0.2.0 更新
|
|
6
|
+
|
|
7
|
+
- **Pydantic 全链路覆盖**:12 个源文件,8 个完整 Pydantic 校验(4 个有理据豁免)
|
|
8
|
+
- 新增 11 个 Pydantic 模型,覆盖 AI 中间结果、DeepSeek 响应、JSONL 日志、降级配置、进化提案、Skill 子结构
|
|
9
|
+
- `AiValidationResult` / `AiReselectionResult` 用 `Literal["合理","不合理"]` 替代裸 `dict.get("result")`
|
|
10
|
+
- `DeepSeekChatResponse` 替代 dataclass,字段缺失自动默认值
|
|
11
|
+
- `ConfigVersionManager` 兼容 `dict` 和 `DbConfig` 双路径
|
|
12
|
+
|
|
13
|
+
## 架构
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
输入 (SkillInput) 规则执行 AI 校验 AI 重选 日志 & 进化
|
|
17
|
+
input_data → execute() → _ai_validate() → _ai_reselect() → JSONL + Evolver
|
|
18
|
+
│ │ │ │ │
|
|
19
|
+
└─ Pydantic ✅ └─ SkillOutput ✅ └─ AiValidation └─ AiReselection └─ LogEntry/Proposal
|
|
20
|
+
Result ✅ Result ✅ ✅
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Pydantic 模型清单
|
|
24
|
+
|
|
25
|
+
### 核心框架
|
|
26
|
+
|
|
27
|
+
| 模型 | 作用 | 校验内容 |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| `SkillInput[T]` | 入口 | `trace_id` + `input_data`(泛型) |
|
|
30
|
+
| `SkillOutput` | 出口 | `source` + `result` + `ai_validated` + `ai_reselected` |
|
|
31
|
+
|
|
32
|
+
### AI 中间结果(v0.2.0 新增)
|
|
33
|
+
|
|
34
|
+
| 模型 | 作用 | 校验约束 |
|
|
35
|
+
|---|---|---|
|
|
36
|
+
| `AiValidationResult` | AI 验证输出 | `Literal["合理","不合理"]` + reason ≤500字 |
|
|
37
|
+
| `AiReselectionResult` | AI 重选输出 | `result: str` + reason ≤500字 |
|
|
38
|
+
|
|
39
|
+
### 数据层(v0.2.0 新增)
|
|
40
|
+
|
|
41
|
+
| 模型 | 作用 |
|
|
42
|
+
|---|---|
|
|
43
|
+
| `DeepSeekChatResponse` | HTTP 响应结构校验 |
|
|
44
|
+
| `LogEntry` | JSONL 日志条目(11 字段全默认值容错) |
|
|
45
|
+
| `FallbackConfigModel` | 降级参数(gt/ge/le 约束) |
|
|
46
|
+
| `EvolveProposalModel` | 进化提案序列化 |
|
|
47
|
+
| `NicknameSkillResult` | nickname-selector 子结构示例 |
|
|
48
|
+
| `DeepSeekEnvConfig` | API 连接配置 |
|
|
49
|
+
| `DbConfig` | 数据库连接配置(port ge=1 le=65535) |
|
|
50
|
+
|
|
51
|
+
## 快速开始
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
from skill_self_evolution import SkillExecutor
|
|
55
|
+
|
|
56
|
+
executor = SkillExecutor(
|
|
57
|
+
deepseek_api_key="sk-xxx", # 或设置 DEEPSEEK_API_KEY
|
|
58
|
+
skill_base_dir="/path/to/skills",
|
|
59
|
+
)
|
|
60
|
+
result = await executor.run("nickname-selector", {
|
|
61
|
+
"screenshot_id": "scr_004",
|
|
62
|
+
"metadata_path": "/path/to/metadata.json",
|
|
63
|
+
"debug_session_derived_path": "/path/to/debug_session_derived.json",
|
|
64
|
+
})
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## 安装
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
pip install skill_self_evolution
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## 开发
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# 本地安装(开发模式)
|
|
77
|
+
pip install -e .
|
|
78
|
+
|
|
79
|
+
# 运行测试
|
|
80
|
+
pytest
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## 环境变量
|
|
84
|
+
|
|
85
|
+
| 变量 | local 默认 | test/prod 默认 |
|
|
86
|
+
|---|---|---|
|
|
87
|
+
| `DEEPSEEK_API_KEY` | DeepSeek 官网 Key | — |
|
|
88
|
+
| `DEEPSEEK_API_BASE` | `https://api.deepseek.com/v1` | — |
|
|
89
|
+
| `WX_MATCH_DEEPSEEK_API_KEY` | — | 华为云 MaaS Key |
|
|
90
|
+
| `WX_MATCH_DEEPSEEK_API_BASE` | — | `https://api.modelarts-maas.com/v2` |
|
|
91
|
+
| `APP_ENV` | `local` | `test` / `prod` |
|
|
92
|
+
| `DB_HOST` | `127.0.0.1` | 环境注入 |
|
|
93
|
+
| `DB_USER` / `DB_PASSWORD` / `DB_NAME` | `root` / (空) / `housekeeping_ai_match_dev` | 同上 |
|
|
94
|
+
| `SKILL_BASE_DIR` | `backend/config/services/skill/` | 同上 |
|
|
95
|
+
| `SKILL_LOG_DIR` | `/data/skill-logs` | 同上 |
|
|
96
|
+
|
|
97
|
+
## License
|
|
98
|
+
|
|
99
|
+
MIT
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "skill_self_evolution"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Skill 自进化框架:Pydantic 全链路校验 + 规则执行 + AI 常识判断 + 离线进化的可插拔 Skill 执行引擎"
|
|
9
|
+
requires-python = ">=3.11"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"pydantic>=2.0",
|
|
12
|
+
"pyyaml>=6.0",
|
|
13
|
+
"httpx>=0.25",
|
|
14
|
+
"pymysql>=1.1",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[project.optional-dependencies]
|
|
18
|
+
dev = [
|
|
19
|
+
"pytest>=8.0",
|
|
20
|
+
"pytest-asyncio>=0.23",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[tool.setuptools]
|
|
24
|
+
package-dir = { "" = "src" }
|
|
25
|
+
packages = ["skill_self_evolution"]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Skill Engine — 自进化框架核心。
|
|
3
|
+
|
|
4
|
+
规则执行 → AI 常识判断 → 合理就过 / 不合理 AI 从原始数据重选 → 日志记录 → 离线进化 → 优化配置
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from skill_self_evolution.executor import SkillExecutor
|
|
8
|
+
from skill_self_evolution.models import SkillInput, SkillOutput
|
|
9
|
+
from skill_self_evolution.context import get_trace_id, set_trace_id
|
|
10
|
+
from skill_self_evolution.logger import SkillLogger
|
|
11
|
+
from skill_self_evolution.fallback import FallbackStrategy
|
|
12
|
+
from skill_self_evolution.config_loader import ConfigVersionManager
|
|
13
|
+
from skill_self_evolution.evolver import Evolver
|
|
14
|
+
from skill_self_evolution.ai_assisted_executor import AiAssistedExecutor
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"SkillExecutor",
|
|
18
|
+
"SkillInput",
|
|
19
|
+
"SkillOutput",
|
|
20
|
+
"get_trace_id",
|
|
21
|
+
"set_trace_id",
|
|
22
|
+
"SkillLogger",
|
|
23
|
+
"FallbackStrategy",
|
|
24
|
+
"ConfigVersionManager",
|
|
25
|
+
"Evolver",
|
|
26
|
+
"AiAssistedExecutor",
|
|
27
|
+
]
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AiAssistedExecutor — 通用 AI 辅助执行基类。
|
|
3
|
+
|
|
4
|
+
提供「验证 + 重选」的通用流程模板,Skill 只需定义:
|
|
5
|
+
- ai_validate():AI 常识判断(correction 角色) → AiValidationResult
|
|
6
|
+
- ai_reselect():AI 重新选择/提取(correction 角色) → AiReselectionResult
|
|
7
|
+
- ai_enhance():AI 增强(enhancement 角色) → dict | None
|
|
8
|
+
|
|
9
|
+
框架自动处理:降级、熔断、is_failure 计算、JSONL 日志。
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import logging
|
|
13
|
+
import time
|
|
14
|
+
from abc import ABC, abstractmethod
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
from skill_self_evolution.context import set_trace_id
|
|
18
|
+
from skill_self_evolution.fallback import FallbackConfig, FallbackResult, FallbackStrategy
|
|
19
|
+
from skill_self_evolution.loader import SkillModule
|
|
20
|
+
from skill_self_evolution.logger import SkillLogger
|
|
21
|
+
from skill_self_evolution.models import (
|
|
22
|
+
AiReselectionResult,
|
|
23
|
+
AiValidationResult,
|
|
24
|
+
SkillInput,
|
|
25
|
+
SkillOutput,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
logger = logging.getLogger(__name__)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class AiAssistedExecutor(ABC):
|
|
32
|
+
"""AI 辅助执行基类。
|
|
33
|
+
|
|
34
|
+
子类只需实现 2-3 个抽象方法,框架自动处理流程编排和降级。
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
@abstractmethod
|
|
38
|
+
async def ai_validate(
|
|
39
|
+
self,
|
|
40
|
+
skill_input: SkillInput,
|
|
41
|
+
rule_output: SkillOutput,
|
|
42
|
+
prompt_config: dict | None,
|
|
43
|
+
) -> AiValidationResult:
|
|
44
|
+
"""AI 常识判断(correction 角色)。
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
AiValidationResult: result=合理/不合理 + reason
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
@abstractmethod
|
|
51
|
+
async def ai_reselect(
|
|
52
|
+
self,
|
|
53
|
+
skill_input: SkillInput,
|
|
54
|
+
rule_output: SkillOutput,
|
|
55
|
+
prompt_config: dict | None,
|
|
56
|
+
) -> AiReselectionResult:
|
|
57
|
+
"""AI 重新选择/提取(correction 角色)。
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
AiReselectionResult: result=重选值(或"不合理") + reason
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
async def ai_enhance(
|
|
64
|
+
self,
|
|
65
|
+
skill_input: SkillInput,
|
|
66
|
+
rule_output: SkillOutput,
|
|
67
|
+
prompt_config: dict | None,
|
|
68
|
+
) -> dict | None:
|
|
69
|
+
"""AI 增强(enhancement 角色)。默认不增强,子类按需覆盖。"""
|
|
70
|
+
return None
|
|
71
|
+
|
|
72
|
+
async def run_correction_flow(
|
|
73
|
+
self,
|
|
74
|
+
skill: SkillModule,
|
|
75
|
+
skill_input: SkillInput,
|
|
76
|
+
rule_output: SkillOutput,
|
|
77
|
+
prompt_config: dict | None,
|
|
78
|
+
fallback: FallbackStrategy,
|
|
79
|
+
) -> SkillOutput:
|
|
80
|
+
"""correction 角色完整流程:验证 → 重选。"""
|
|
81
|
+
t0 = time.monotonic()
|
|
82
|
+
warnings: list[str] = list(rule_output.warnings) if rule_output.warnings else []
|
|
83
|
+
|
|
84
|
+
# 1. 熔断检查
|
|
85
|
+
fb_check = fallback.check_before_ai()
|
|
86
|
+
if fb_check.skip_ai:
|
|
87
|
+
warnings.extend(fb_check.warnings)
|
|
88
|
+
elapsed = (time.monotonic() - t0) * 1000
|
|
89
|
+
rule_output.warnings = warnings
|
|
90
|
+
self._log(skill, skill_input, rule_output, None, None, rule_output, warnings, elapsed)
|
|
91
|
+
return rule_output
|
|
92
|
+
|
|
93
|
+
# 2. AI 验证
|
|
94
|
+
ai_validation: AiValidationResult | None = None
|
|
95
|
+
try:
|
|
96
|
+
ai_validation = await self.ai_validate(skill_input, rule_output, prompt_config)
|
|
97
|
+
rule_output.ai_validated = True
|
|
98
|
+
except Exception as e:
|
|
99
|
+
logger.warning("Skill [%s] AI 验证异常: %s", skill.skill_name, e)
|
|
100
|
+
fb_result = fallback.on_validate_failure(e)
|
|
101
|
+
warnings.extend(fb_result.warnings)
|
|
102
|
+
if fb_result.skip_ai:
|
|
103
|
+
elapsed = (time.monotonic() - t0) * 1000
|
|
104
|
+
rule_output.ai_validated = False
|
|
105
|
+
rule_output.warnings = warnings
|
|
106
|
+
is_failure = self._is_failure(skill.ai_role, rule_output, None, None)
|
|
107
|
+
self._log(skill, skill_input, rule_output, None, None, rule_output, warnings, elapsed)
|
|
108
|
+
return rule_output
|
|
109
|
+
|
|
110
|
+
# 3. 验证不合理 → 重选
|
|
111
|
+
if ai_validation and ai_validation.result == "不合理":
|
|
112
|
+
fb_reselect = fallback.check_before_ai()
|
|
113
|
+
if fb_reselect.skip_ai:
|
|
114
|
+
warnings.extend(fb_reselect.warnings)
|
|
115
|
+
elapsed = (time.monotonic() - t0) * 1000
|
|
116
|
+
is_failure = self._is_failure(skill.ai_role, rule_output, ai_validation, None)
|
|
117
|
+
rule_output.warnings = warnings
|
|
118
|
+
self._log(skill, skill_input, rule_output, ai_validation, None, rule_output, warnings, elapsed)
|
|
119
|
+
return rule_output
|
|
120
|
+
|
|
121
|
+
try:
|
|
122
|
+
ai_reselection = await self.ai_reselect(skill_input, rule_output, prompt_config)
|
|
123
|
+
if ai_reselection and ai_reselection.result != "不合理":
|
|
124
|
+
final_result = ai_reselection.result
|
|
125
|
+
final_output = SkillOutput(
|
|
126
|
+
source="ai",
|
|
127
|
+
result=final_result if isinstance(final_result, dict) else {"value": final_result},
|
|
128
|
+
ai_validated=True,
|
|
129
|
+
ai_reselected=True,
|
|
130
|
+
warnings=warnings,
|
|
131
|
+
)
|
|
132
|
+
elapsed = (time.monotonic() - t0) * 1000
|
|
133
|
+
is_failure = self._is_failure(skill.ai_role, rule_output, ai_validation, ai_reselection)
|
|
134
|
+
self._log(skill, skill_input, rule_output, ai_validation, ai_reselection, final_output, warnings, elapsed)
|
|
135
|
+
return final_output
|
|
136
|
+
else:
|
|
137
|
+
warnings.append("AI 重选后仍不合理")
|
|
138
|
+
rule_output.ai_reselected = True
|
|
139
|
+
except Exception as e:
|
|
140
|
+
logger.warning("Skill [%s] AI 重选异常: %s", skill.skill_name, e)
|
|
141
|
+
fb_result2 = fallback.on_reselect_failure(e)
|
|
142
|
+
warnings.extend(fb_result2.warnings)
|
|
143
|
+
|
|
144
|
+
# 4. 验证合理或重选失败 → 返回规则结果
|
|
145
|
+
rule_output.warnings = warnings
|
|
146
|
+
elapsed = (time.monotonic() - t0) * 1000
|
|
147
|
+
is_failure = self._is_failure(skill.ai_role, rule_output, ai_validation, None)
|
|
148
|
+
self._log(skill, skill_input, rule_output, ai_validation, None, rule_output, warnings, elapsed)
|
|
149
|
+
return rule_output
|
|
150
|
+
|
|
151
|
+
async def run_enhancement_flow(
|
|
152
|
+
self,
|
|
153
|
+
skill: SkillModule,
|
|
154
|
+
skill_input: SkillInput,
|
|
155
|
+
rule_output: SkillOutput,
|
|
156
|
+
prompt_config: dict | None,
|
|
157
|
+
fallback: FallbackStrategy,
|
|
158
|
+
) -> SkillOutput:
|
|
159
|
+
"""enhancement 角色流程:规则 + AI 增强。"""
|
|
160
|
+
t0 = time.monotonic()
|
|
161
|
+
warnings: list[str] = list(rule_output.warnings) if rule_output.warnings else []
|
|
162
|
+
|
|
163
|
+
fb_check = fallback.check_before_ai()
|
|
164
|
+
if fb_check.skip_ai:
|
|
165
|
+
warnings.extend(fb_check.warnings)
|
|
166
|
+
elapsed = (time.monotonic() - t0) * 1000
|
|
167
|
+
rule_output.warnings = warnings
|
|
168
|
+
self._log(skill, skill_input, rule_output, None, None, rule_output, warnings, elapsed)
|
|
169
|
+
return rule_output
|
|
170
|
+
|
|
171
|
+
try:
|
|
172
|
+
ai_result = await self.ai_enhance(skill_input, rule_output, prompt_config)
|
|
173
|
+
merged = {**rule_output.result} if isinstance(rule_output.result, dict) else {}
|
|
174
|
+
if ai_result:
|
|
175
|
+
merged.update(ai_result)
|
|
176
|
+
final_output = SkillOutput(
|
|
177
|
+
source=rule_output.source,
|
|
178
|
+
result=merged,
|
|
179
|
+
ai_validated=True,
|
|
180
|
+
ai_reselected=False,
|
|
181
|
+
warnings=warnings,
|
|
182
|
+
)
|
|
183
|
+
elapsed = (time.monotonic() - t0) * 1000
|
|
184
|
+
self._log(skill, skill_input, rule_output, None, None, final_output, warnings, elapsed)
|
|
185
|
+
return final_output
|
|
186
|
+
except Exception as e:
|
|
187
|
+
logger.warning("Skill [%s] AI 增强异常: %s", skill.skill_name, e)
|
|
188
|
+
elapsed = (time.monotonic() - t0) * 1000
|
|
189
|
+
rule_output.warnings = warnings
|
|
190
|
+
self._log(skill, skill_input, rule_output, None, None, rule_output, warnings, elapsed)
|
|
191
|
+
return rule_output
|
|
192
|
+
|
|
193
|
+
@staticmethod
|
|
194
|
+
def _is_failure(
|
|
195
|
+
ai_role: str,
|
|
196
|
+
rule_output: SkillOutput,
|
|
197
|
+
ai_validation: AiValidationResult | None,
|
|
198
|
+
ai_reselection: AiReselectionResult | None,
|
|
199
|
+
) -> bool:
|
|
200
|
+
"""根据 ai_role 计算 is_failure(Pydantic 属性访问)。"""
|
|
201
|
+
if ai_role == "enhancement":
|
|
202
|
+
return False
|
|
203
|
+
if ai_role == "correction":
|
|
204
|
+
if ai_validation and ai_validation.result == "不合理":
|
|
205
|
+
return True
|
|
206
|
+
if ai_reselection and ai_reselection.result == "不合理":
|
|
207
|
+
return True
|
|
208
|
+
return False
|
|
209
|
+
|
|
210
|
+
def _log(
|
|
211
|
+
self,
|
|
212
|
+
skill: SkillModule,
|
|
213
|
+
skill_input: SkillInput,
|
|
214
|
+
rule_output: SkillOutput,
|
|
215
|
+
ai_validation: AiValidationResult | None,
|
|
216
|
+
ai_reselection: AiReselectionResult | None,
|
|
217
|
+
final_output: SkillOutput,
|
|
218
|
+
warnings: list[str],
|
|
219
|
+
elapsed_ms: float,
|
|
220
|
+
) -> None:
|
|
221
|
+
"""写入 JSONL 日志(Pydantic LogEntry 校验)。"""
|
|
222
|
+
try:
|
|
223
|
+
log_writer = SkillLogger(skill.skill_name)
|
|
224
|
+
if skill.summarize_input:
|
|
225
|
+
input_summary = skill.summarize_input(skill_input.input_data)
|
|
226
|
+
elif isinstance(skill_input.input_data, dict):
|
|
227
|
+
keys = list(skill_input.input_data.keys())[:5]
|
|
228
|
+
input_summary = {k: str(skill_input.input_data[k])[:100] for k in keys}
|
|
229
|
+
else:
|
|
230
|
+
input_summary = {"type": type(skill_input.input_data).__name__}
|
|
231
|
+
|
|
232
|
+
log_writer.log_execution(
|
|
233
|
+
trace_id=skill_input.trace_id or "",
|
|
234
|
+
is_failure=(ai_validation is not None and ai_validation.result == "不合理"),
|
|
235
|
+
input_summary=input_summary,
|
|
236
|
+
rule_output=rule_output.result,
|
|
237
|
+
ai_validation=ai_validation.model_dump() if ai_validation else None,
|
|
238
|
+
ai_reselection=ai_reselection.model_dump() if ai_reselection else None,
|
|
239
|
+
final_output=final_output.result,
|
|
240
|
+
warnings=warnings,
|
|
241
|
+
elapsed_ms=elapsed_ms,
|
|
242
|
+
)
|
|
243
|
+
except Exception as e:
|
|
244
|
+
logger.warning("Skill 日志记录失败: %s", e)
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""
|
|
2
|
+
?????? ? ? APP_ENV ?? DeepSeek API ???????
|
|
3
|
+
|
|
4
|
+
- local: DeepSeek ?? API (api.deepseek.com)
|
|
5
|
+
- test/prod: ??? MaaS (api.modelarts-maas.com/v2)
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
|
|
10
|
+
from pydantic import BaseModel, Field
|
|
11
|
+
|
|
12
|
+
__all__ = ["DeepSeekEnvConfig", "DbConfig", "get_deepseek_config", "get_db_config"]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class DeepSeekEnvConfig(BaseModel):
|
|
16
|
+
"""DeepSeek API ?????Pydantic ????"""
|
|
17
|
+
|
|
18
|
+
api_key: str = Field(default="", description="API Key")
|
|
19
|
+
api_base: str = Field(
|
|
20
|
+
default="https://api.deepseek.com/v1",
|
|
21
|
+
description="API ????",
|
|
22
|
+
)
|
|
23
|
+
model: str = Field(default="deepseek-chat", description="????")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class DbConfig(BaseModel):
|
|
27
|
+
"""????????Pydantic ????"""
|
|
28
|
+
|
|
29
|
+
host: str = Field(default="127.0.0.1")
|
|
30
|
+
port: int = Field(default=3306, ge=1, le=65535)
|
|
31
|
+
user: str = Field(default="root")
|
|
32
|
+
password: str = Field(default="")
|
|
33
|
+
database: str = Field(default="housekeeping_ai_match_dev")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _resolve_app_env() -> str:
|
|
37
|
+
return os.getenv("APP_ENV", "local").strip().lower()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def get_deepseek_config(
|
|
41
|
+
api_key: str = "",
|
|
42
|
+
api_base: str = "",
|
|
43
|
+
model: str = "",
|
|
44
|
+
) -> DeepSeekEnvConfig:
|
|
45
|
+
env = _resolve_app_env()
|
|
46
|
+
if api_key and api_base:
|
|
47
|
+
return DeepSeekEnvConfig(
|
|
48
|
+
api_key=api_key,
|
|
49
|
+
api_base=api_base,
|
|
50
|
+
model=model or "deepseek-chat",
|
|
51
|
+
)
|
|
52
|
+
if env == "local":
|
|
53
|
+
key = api_key or os.getenv("DEEPSEEK_API_KEY", "")
|
|
54
|
+
base = api_base or os.getenv("DEEPSEEK_API_BASE", "https://api.deepseek.com/v1")
|
|
55
|
+
m = model or os.getenv("DEEPSEEK_MODEL", "deepseek-chat")
|
|
56
|
+
return DeepSeekEnvConfig(api_key=key, api_base=base, model=m)
|
|
57
|
+
else:
|
|
58
|
+
key = api_key or os.getenv("WX_MATCH_DEEPSEEK_API_KEY", "")
|
|
59
|
+
base = api_base or os.getenv(
|
|
60
|
+
"WX_MATCH_DEEPSEEK_API_BASE", "https://api.modelarts-maas.com/v2"
|
|
61
|
+
)
|
|
62
|
+
m = model or os.getenv("WX_MATCH_AI_MODEL_NAME", "DeepSeek-V3.2")
|
|
63
|
+
return DeepSeekEnvConfig(api_key=key, api_base=base, model=m)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def get_db_config(
|
|
67
|
+
host: str = "",
|
|
68
|
+
port: int = 0,
|
|
69
|
+
user: str = "",
|
|
70
|
+
password: str = "",
|
|
71
|
+
database: str = "",
|
|
72
|
+
) -> DbConfig:
|
|
73
|
+
if host and user and password:
|
|
74
|
+
return DbConfig(
|
|
75
|
+
host=host,
|
|
76
|
+
port=port or 3306,
|
|
77
|
+
user=user,
|
|
78
|
+
password=password,
|
|
79
|
+
database=database or "housekeeping_ai_match_dev",
|
|
80
|
+
)
|
|
81
|
+
return DbConfig(
|
|
82
|
+
host=host or os.getenv("DB_HOST", "127.0.0.1"),
|
|
83
|
+
port=port or int(os.getenv("DB_PORT", "3306")),
|
|
84
|
+
user=user or os.getenv("DB_USER", "root"),
|
|
85
|
+
password=password or os.getenv("DB_PASSWORD", ""),
|
|
86
|
+
database=database or os.getenv("DB_NAME", "housekeeping_ai_match_dev"),
|
|
87
|
+
)
|