devsquad 3.6.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.
- devsquad-3.6.0.dist-info/METADATA +944 -0
- devsquad-3.6.0.dist-info/RECORD +95 -0
- devsquad-3.6.0.dist-info/WHEEL +5 -0
- devsquad-3.6.0.dist-info/entry_points.txt +2 -0
- devsquad-3.6.0.dist-info/licenses/LICENSE +21 -0
- devsquad-3.6.0.dist-info/top_level.txt +2 -0
- scripts/__init__.py +0 -0
- scripts/ai_semantic_matcher.py +512 -0
- scripts/alert_manager.py +505 -0
- scripts/api/__init__.py +43 -0
- scripts/api/models.py +386 -0
- scripts/api/routes/__init__.py +20 -0
- scripts/api/routes/dispatch.py +348 -0
- scripts/api/routes/lifecycle.py +330 -0
- scripts/api/routes/metrics_gates.py +347 -0
- scripts/api_server.py +318 -0
- scripts/auth.py +451 -0
- scripts/cli/__init__.py +1 -0
- scripts/cli/cli_visual.py +642 -0
- scripts/cli.py +1094 -0
- scripts/collaboration/__init__.py +212 -0
- scripts/collaboration/_version.py +1 -0
- scripts/collaboration/agent_briefing.py +656 -0
- scripts/collaboration/ai_semantic_matcher.py +260 -0
- scripts/collaboration/anchor_checker.py +281 -0
- scripts/collaboration/anti_rationalization.py +470 -0
- scripts/collaboration/async_integration_example.py +255 -0
- scripts/collaboration/batch_scheduler.py +149 -0
- scripts/collaboration/checkpoint_manager.py +561 -0
- scripts/collaboration/ci_feedback_adapter.py +351 -0
- scripts/collaboration/code_map_generator.py +247 -0
- scripts/collaboration/concern_pack_loader.py +352 -0
- scripts/collaboration/confidence_score.py +496 -0
- scripts/collaboration/config_loader.py +188 -0
- scripts/collaboration/consensus.py +244 -0
- scripts/collaboration/context_compressor.py +533 -0
- scripts/collaboration/coordinator.py +668 -0
- scripts/collaboration/dispatcher.py +1636 -0
- scripts/collaboration/dual_layer_context.py +128 -0
- scripts/collaboration/enhanced_worker.py +539 -0
- scripts/collaboration/feature_usage_tracker.py +206 -0
- scripts/collaboration/five_axis_consensus.py +334 -0
- scripts/collaboration/input_validator.py +401 -0
- scripts/collaboration/integration_example.py +287 -0
- scripts/collaboration/intent_workflow_mapper.py +350 -0
- scripts/collaboration/language_parsers.py +269 -0
- scripts/collaboration/lifecycle_protocol.py +1446 -0
- scripts/collaboration/llm_backend.py +453 -0
- scripts/collaboration/llm_cache.py +448 -0
- scripts/collaboration/llm_cache_async.py +347 -0
- scripts/collaboration/llm_retry.py +387 -0
- scripts/collaboration/llm_retry_async.py +389 -0
- scripts/collaboration/mce_adapter.py +597 -0
- scripts/collaboration/memory_bridge.py +1607 -0
- scripts/collaboration/models.py +537 -0
- scripts/collaboration/null_providers.py +297 -0
- scripts/collaboration/operation_classifier.py +289 -0
- scripts/collaboration/output_slicer.py +225 -0
- scripts/collaboration/performance_monitor.py +462 -0
- scripts/collaboration/permission_guard.py +865 -0
- scripts/collaboration/prompt_assembler.py +756 -0
- scripts/collaboration/prompt_variant_generator.py +483 -0
- scripts/collaboration/protocols.py +267 -0
- scripts/collaboration/report_formatter.py +352 -0
- scripts/collaboration/retrospective.py +279 -0
- scripts/collaboration/role_matcher.py +92 -0
- scripts/collaboration/role_template_market.py +352 -0
- scripts/collaboration/rule_collector.py +678 -0
- scripts/collaboration/scratchpad.py +346 -0
- scripts/collaboration/skill_registry.py +151 -0
- scripts/collaboration/skillifier.py +878 -0
- scripts/collaboration/standardized_role_template.py +317 -0
- scripts/collaboration/task_completion_checker.py +237 -0
- scripts/collaboration/test_quality_guard.py +695 -0
- scripts/collaboration/unified_gate_engine.py +598 -0
- scripts/collaboration/usage_tracker.py +309 -0
- scripts/collaboration/user_friendly_error.py +176 -0
- scripts/collaboration/verification_gate.py +312 -0
- scripts/collaboration/warmup_manager.py +635 -0
- scripts/collaboration/worker.py +513 -0
- scripts/collaboration/workflow_engine.py +684 -0
- scripts/dashboard.py +1088 -0
- scripts/generate_benchmark_report.py +786 -0
- scripts/history_manager.py +604 -0
- scripts/mcp_server.py +289 -0
- skills/__init__.py +32 -0
- skills/dispatch/handler.py +52 -0
- skills/intent/handler.py +59 -0
- skills/registry.py +67 -0
- skills/retrospective/__init__.py +0 -0
- skills/retrospective/handler.py +125 -0
- skills/review/handler.py +356 -0
- skills/security/handler.py +454 -0
- skills/test/__init__.py +0 -0
- skills/test/handler.py +78 -0
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
PromptVariantGenerator - 提示词变体生成器(Skillify 闭环反哺)
|
|
5
|
+
|
|
6
|
+
填补 Skillifier → 提示词系统 的数据流断裂:
|
|
7
|
+
执行成功 → Skillifier 提取模式 → [本模块] → 候选提示词变体
|
|
8
|
+
↓
|
|
9
|
+
WarmupManager 缓存 (A/B 测试)
|
|
10
|
+
↓
|
|
11
|
+
验证通过 → 晋升为默认模板变体
|
|
12
|
+
|
|
13
|
+
核心能力:
|
|
14
|
+
1. 从 SuccessPattern 生成候选 prompt 变体
|
|
15
|
+
2. 变体质量评分(基于模式置信度/频率/角色匹配度)
|
|
16
|
+
3. 缓存到 WarmupManager,支持 A/B 对比
|
|
17
|
+
4. 变体晋升机制(candidate → promoted)
|
|
18
|
+
|
|
19
|
+
与现有组件的关系:
|
|
20
|
+
- 输入: Skillifier.SuccessPattern(成功执行模式)
|
|
21
|
+
- 输出: 候选提示词文本 + 元数据
|
|
22
|
+
- 存储: WarmupManager.set_cache()(进程级缓存)
|
|
23
|
+
- 消费: PromptAssembler.assemble() 可读取并试用候选变体
|
|
24
|
+
|
|
25
|
+
设计原则:
|
|
26
|
+
- 不修改原始 ROLE_TEMPLATES,只生成候选变体
|
|
27
|
+
- 变体必须经过验证才可晋升
|
|
28
|
+
- 自动降级:连续 N 次负面反馈则自动淘汰
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
import hashlib
|
|
32
|
+
import threading
|
|
33
|
+
from dataclasses import dataclass, field
|
|
34
|
+
from datetime import datetime, timedelta
|
|
35
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
36
|
+
|
|
37
|
+
try:
|
|
38
|
+
from .skillifier import SuccessPattern
|
|
39
|
+
except ImportError:
|
|
40
|
+
class SuccessPattern:
|
|
41
|
+
pattern_id = ""
|
|
42
|
+
name = ""
|
|
43
|
+
description = ""
|
|
44
|
+
steps_template = []
|
|
45
|
+
trigger_keywords = []
|
|
46
|
+
applicable_roles = []
|
|
47
|
+
frequency = 1
|
|
48
|
+
confidence = 0.5
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass
|
|
52
|
+
class PromptVariant:
|
|
53
|
+
"""
|
|
54
|
+
提示词变体
|
|
55
|
+
|
|
56
|
+
Attributes:
|
|
57
|
+
variant_id: 变体唯一标识
|
|
58
|
+
role_id: 适用角色
|
|
59
|
+
source_pattern_id: 来源成功模式的 ID
|
|
60
|
+
prompt_text: 生成的提示词文本
|
|
61
|
+
status: 状态 (candidate/promoted/deprecated)
|
|
62
|
+
quality_score: 质量评分 [0, 100]
|
|
63
|
+
usage_count: 使用次数
|
|
64
|
+
positive_feedback: 正面反馈次数
|
|
65
|
+
negative_feedback: 负面反馈次数
|
|
66
|
+
created_at: 创建时间
|
|
67
|
+
promoted_at: 晋升时间(如有)
|
|
68
|
+
"""
|
|
69
|
+
variant_id: str = field(default_factory=lambda: f"pv-{hashlib.md5(str(datetime.now()).encode()).hexdigest()[:12]}")
|
|
70
|
+
role_id: str = ""
|
|
71
|
+
source_pattern_id: str = ""
|
|
72
|
+
prompt_text: str = ""
|
|
73
|
+
status: str = "candidate"
|
|
74
|
+
quality_score: float = 0.0
|
|
75
|
+
usage_count: int = 0
|
|
76
|
+
positive_feedback: int = 0
|
|
77
|
+
negative_feedback: int = 0
|
|
78
|
+
created_at: datetime = field(default_factory=datetime.now)
|
|
79
|
+
promoted_at: Optional[datetime] = None
|
|
80
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
81
|
+
|
|
82
|
+
def to_dict(self) -> Dict:
|
|
83
|
+
"""
|
|
84
|
+
序列化为字典(用于持久化或API返回)
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
Dict: 包含 variant_id/role_id/status/quality_score/
|
|
88
|
+
usage_count/feedback_ratio/created_at 的字典
|
|
89
|
+
"""
|
|
90
|
+
return {
|
|
91
|
+
"variant_id": self.variant_id,
|
|
92
|
+
"role_id": self.role_id,
|
|
93
|
+
"source_pattern_id": self.source_pattern_id,
|
|
94
|
+
"status": self.status,
|
|
95
|
+
"quality_score": round(self.quality_score, 1),
|
|
96
|
+
"usage_count": self.usage_count,
|
|
97
|
+
"positive_feedback": self.positive_feedback,
|
|
98
|
+
"negative_feedback": self.negative_feedback,
|
|
99
|
+
"feedback_ratio": round(self._feedback_ratio(), 2),
|
|
100
|
+
"created_at": self.created_at.isoformat(),
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
def _feedback_ratio(self) -> float:
|
|
104
|
+
total = self.positive_feedback + self.negative_feedback
|
|
105
|
+
if total == 0:
|
|
106
|
+
return 0.0
|
|
107
|
+
return self.positive_feedback / total
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@dataclass
|
|
111
|
+
class VariantGenerationResult:
|
|
112
|
+
"""
|
|
113
|
+
变体生成结果
|
|
114
|
+
|
|
115
|
+
Attributes:
|
|
116
|
+
success: 是否成功生成变体
|
|
117
|
+
variant: 生成的 PromptVariant 对象(失败时为 None)
|
|
118
|
+
reason: 失败原因说明(成功时为空字符串)
|
|
119
|
+
"""
|
|
120
|
+
success: bool
|
|
121
|
+
variant: Optional[PromptVariant] = None
|
|
122
|
+
reason: str = ""
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class PromptVariantGenerator:
|
|
126
|
+
"""
|
|
127
|
+
提示词变体生成器
|
|
128
|
+
|
|
129
|
+
将 Skillifier 的成功模式转化为可用的提示词变体,
|
|
130
|
+
通过 WarmupManager 缓存实现 A/B 测试和自动晋升。
|
|
131
|
+
|
|
132
|
+
使用示例:
|
|
133
|
+
gen = PromptVariantGenerator()
|
|
134
|
+
result = gen.generate_from_pattern(success_pattern)
|
|
135
|
+
if result.success:
|
|
136
|
+
gen.cache_variant(result.variant) # 存入缓存供 A/B 测试
|
|
137
|
+
# ... 后续使用中收集反馈 ...
|
|
138
|
+
gen.record_feedback(result.variant.variant_id, positive=True)
|
|
139
|
+
gen.try_promote(result.variant.variant_id) # 尝试晋升
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
MIN_CONFIDENCE = 0.7
|
|
143
|
+
MIN_FREQUENCY = 2
|
|
144
|
+
PROMOTION_THRESHOLD = 0.75
|
|
145
|
+
DEPRECATION_THRESHOLD = 0.35
|
|
146
|
+
MIN_USAGE_FOR_PROMOTION = 3
|
|
147
|
+
|
|
148
|
+
def __init__(self):
|
|
149
|
+
"""初始化变体生成器"""
|
|
150
|
+
self._variants: Dict[str, PromptVariant] = {}
|
|
151
|
+
self._lock = threading.RLock()
|
|
152
|
+
|
|
153
|
+
def generate_from_pattern(self,
|
|
154
|
+
pattern: SuccessPattern,
|
|
155
|
+
base_template: str = "") -> VariantGenerationResult:
|
|
156
|
+
"""
|
|
157
|
+
从成功模式生成候选提示词变体
|
|
158
|
+
|
|
159
|
+
生成逻辑:
|
|
160
|
+
1. 质量门槛检查(置信度 >= 0.7 且频率 >= 2)
|
|
161
|
+
2. 从模式步骤提取关键指令片段
|
|
162
|
+
3. 组装为该角色的增强型提示词
|
|
163
|
+
4. 计算质量评分
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
pattern: 成功执行模式(来自 Skillifier.analyze_history())
|
|
167
|
+
base_template: 该角色的基础提示词模板(可选,用于对比增强)
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
VariantGenerationResult: 包含生成的变体或失败原因
|
|
171
|
+
"""
|
|
172
|
+
if pattern.confidence < self.MIN_CONFIDENCE:
|
|
173
|
+
return VariantGenerationResult(
|
|
174
|
+
success=False,
|
|
175
|
+
reason=f"置信度不足: {pattern.confidence:.2f} < {self.MIN_CONFIDENCE}"
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
if pattern.frequency < self.MIN_FREQUENCY:
|
|
179
|
+
return VariantGenerationResult(
|
|
180
|
+
success=False,
|
|
181
|
+
reason=f"频率不足: {pattern.frequency} < {self.MIN_FREQUENCY}"
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
if not pattern.applicable_roles:
|
|
185
|
+
return VariantGenerationResult(
|
|
186
|
+
success=False,
|
|
187
|
+
reason="模式未关联任何角色"
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
role_id = pattern.applicable_roles[0]
|
|
191
|
+
prompt_text = self._assemble_variant_prompt(pattern, base_template)
|
|
192
|
+
quality = self._calculate_quality(pattern, prompt_text)
|
|
193
|
+
|
|
194
|
+
variant = PromptVariant(
|
|
195
|
+
role_id=role_id,
|
|
196
|
+
source_pattern_id=pattern.pattern_id,
|
|
197
|
+
prompt_text=prompt_text,
|
|
198
|
+
status="candidate",
|
|
199
|
+
quality_score=quality,
|
|
200
|
+
metadata={
|
|
201
|
+
"pattern_name": pattern.name,
|
|
202
|
+
"pattern_confidence": pattern.confidence,
|
|
203
|
+
"pattern_frequency": pattern.frequency,
|
|
204
|
+
"trigger_keywords": list(pattern.trigger_keywords)[:10],
|
|
205
|
+
"step_count": len(pattern.steps_template),
|
|
206
|
+
"generated_at": datetime.now().isoformat(),
|
|
207
|
+
},
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
with self._lock:
|
|
211
|
+
self._variants[variant.variant_id] = variant
|
|
212
|
+
|
|
213
|
+
return VariantGenerationResult(success=True, variant=variant)
|
|
214
|
+
|
|
215
|
+
def _assemble_variant_prompt(self,
|
|
216
|
+
pattern: SuccessPattern,
|
|
217
|
+
base_template: str) -> str:
|
|
218
|
+
"""
|
|
219
|
+
从模式组装变体提示词
|
|
220
|
+
|
|
221
|
+
结构:
|
|
222
|
+
[角色特定头部]
|
|
223
|
+
[从模式提取的关键步骤指引]
|
|
224
|
+
[触发关键词作为上下文锚点]
|
|
225
|
+
[输出要求]
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
pattern: 成功模式
|
|
229
|
+
base_template: 基础模板(用于参考头部格式)
|
|
230
|
+
|
|
231
|
+
Returns:
|
|
232
|
+
str: 组装后的变体提示词
|
|
233
|
+
"""
|
|
234
|
+
parts = []
|
|
235
|
+
|
|
236
|
+
header = f"=== {pattern.name or '优化提示词'} (来自 {pattern.frequency} 次成功经验) ===\n"
|
|
237
|
+
parts.append(header)
|
|
238
|
+
|
|
239
|
+
if pattern.steps_template:
|
|
240
|
+
parts.append("【推荐执行步骤】")
|
|
241
|
+
for i, step in enumerate(pattern.steps_template[:6], 1):
|
|
242
|
+
desc = step.description_template or step.action_type.value.replace("_", " ")
|
|
243
|
+
risk_note = f" ⚠风险{step.estimated_risk:.0%}" if step.estimated_risk > 0.5 else ""
|
|
244
|
+
req_mark = " *" if step.is_required else ""
|
|
245
|
+
parts.append(f" {i}. {desc}{risk_note}{req_mark}")
|
|
246
|
+
parts.append("")
|
|
247
|
+
|
|
248
|
+
if pattern.trigger_keywords:
|
|
249
|
+
kw_str = ", ".join(pattern.trigger_keywords[:8])
|
|
250
|
+
parts.append(f"【适用场景】包含以下关键词的任务: {kw_str}")
|
|
251
|
+
parts.append("")
|
|
252
|
+
|
|
253
|
+
output_guide = (
|
|
254
|
+
"【输出要求】\n"
|
|
255
|
+
"- 基于以上经验步骤执行\n"
|
|
256
|
+
"- 标注每步的决策依据\n"
|
|
257
|
+
"- 如遇偏差请说明原因"
|
|
258
|
+
)
|
|
259
|
+
parts.append(output_guide)
|
|
260
|
+
|
|
261
|
+
return "\n".join(parts)
|
|
262
|
+
|
|
263
|
+
def _calculate_quality(self, pattern: SuccessPattern,
|
|
264
|
+
prompt_text: str) -> float:
|
|
265
|
+
"""
|
|
266
|
+
计算变体质量评分 [0, 100]
|
|
267
|
+
|
|
268
|
+
维度:
|
|
269
|
+
- 置信度贡献 (40%): 来自模式本身的置信度
|
|
270
|
+
- 频率贡献 (20%): 出现频率越高越可信
|
|
271
|
+
- 内容丰富度 (20%): 提示词长度和结构完整性
|
|
272
|
+
- 特异性 (15%): 触发关键词数量
|
|
273
|
+
- 步骤质量 (5%): 低风险步骤占比
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
pattern: 来源模式
|
|
277
|
+
prompt_text: 生成的提示词
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
float: 质量评分 [0, 100]
|
|
281
|
+
"""
|
|
282
|
+
confidence_score = pattern.confidence * 40
|
|
283
|
+
|
|
284
|
+
freq_score = min(20.0, pattern.frequency * 5)
|
|
285
|
+
|
|
286
|
+
length_score = min(20.0, len(prompt_text) / 20 * 5)
|
|
287
|
+
has_structure = "===" in prompt_text or "【" in prompt_text
|
|
288
|
+
structure_bonus = 5.0 if has_structure else 0.0
|
|
289
|
+
content_score = min(20.0, length_score + structure_bonus)
|
|
290
|
+
|
|
291
|
+
specificity_score = min(15.0, len(pattern.trigger_keywords) * 1.5)
|
|
292
|
+
|
|
293
|
+
safe_steps = sum(1 for ps in pattern.steps_template
|
|
294
|
+
if ps.estimated_risk < 0.5)
|
|
295
|
+
total_steps = max(len(pattern.steps_template), 1)
|
|
296
|
+
step_score = 5.0 * (safe_steps / total_steps)
|
|
297
|
+
|
|
298
|
+
return round(confidence_score + freq_score + content_score +
|
|
299
|
+
specificity_score + step_score, 1)
|
|
300
|
+
|
|
301
|
+
def get_variant(self, variant_id: str) -> Optional[PromptVariant]:
|
|
302
|
+
"""
|
|
303
|
+
按 ID 获取变体
|
|
304
|
+
|
|
305
|
+
Args:
|
|
306
|
+
variant_id: 变体 ID
|
|
307
|
+
|
|
308
|
+
Returns:
|
|
309
|
+
Optional[PromptVariant]: 变体对象,不存在返回 None
|
|
310
|
+
"""
|
|
311
|
+
with self._lock:
|
|
312
|
+
return self._variants.get(variant_id)
|
|
313
|
+
|
|
314
|
+
def get_candidates_for_role(self, role_id: str) -> List[PromptVariant]:
|
|
315
|
+
"""
|
|
316
|
+
获取指定角色的所有候选变体(按质量排序)
|
|
317
|
+
|
|
318
|
+
Args:
|
|
319
|
+
role_id: 角色 ID
|
|
320
|
+
|
|
321
|
+
Returns:
|
|
322
|
+
List[PromptVariant]: 候选变体列表(质量分降序)
|
|
323
|
+
"""
|
|
324
|
+
with self._lock:
|
|
325
|
+
candidates = [v for v in self._variants.values()
|
|
326
|
+
if v.role_id == role_id and v.status == "candidate"]
|
|
327
|
+
candidates.sort(key=lambda v: v.quality_score, reverse=True)
|
|
328
|
+
return candidates
|
|
329
|
+
|
|
330
|
+
def get_promoted_variants(self, role_id: str = "") -> List[PromptVariant]:
|
|
331
|
+
"""
|
|
332
|
+
获取已晋升的变体
|
|
333
|
+
|
|
334
|
+
Args:
|
|
335
|
+
role_id: 角色 ID(为空则返回全部已晋升变体)
|
|
336
|
+
|
|
337
|
+
Returns:
|
|
338
|
+
List[PromptVariant]: 已晋升变体列表
|
|
339
|
+
"""
|
|
340
|
+
with self._lock:
|
|
341
|
+
results = [v for v in self._variants.values() if v.status == "promoted"]
|
|
342
|
+
if role_id:
|
|
343
|
+
results = [v for v in results if v.role_id == role_id]
|
|
344
|
+
return results
|
|
345
|
+
|
|
346
|
+
def record_usage(self, variant_id: str) -> bool:
|
|
347
|
+
"""
|
|
348
|
+
记录一次变体使用
|
|
349
|
+
|
|
350
|
+
Args:
|
|
351
|
+
variant_id: 变体 ID
|
|
352
|
+
|
|
353
|
+
Returns:
|
|
354
|
+
bool: 是否成功记录
|
|
355
|
+
"""
|
|
356
|
+
with self._lock:
|
|
357
|
+
variant = self._variants.get(variant_id)
|
|
358
|
+
if variant:
|
|
359
|
+
variant.usage_count += 1
|
|
360
|
+
return True
|
|
361
|
+
return False
|
|
362
|
+
|
|
363
|
+
def record_feedback(self, variant_id: str,
|
|
364
|
+
positive: bool = True) -> bool:
|
|
365
|
+
"""
|
|
366
|
+
记录用户/AI 对变体的反馈
|
|
367
|
+
|
|
368
|
+
Args:
|
|
369
|
+
variant_id: 变体 ID
|
|
370
|
+
positive: True=正面反馈, False=负面反馈
|
|
371
|
+
|
|
372
|
+
Returns:
|
|
373
|
+
bool: 是否成功记录
|
|
374
|
+
"""
|
|
375
|
+
with self._lock:
|
|
376
|
+
variant = self._variants.get(variant_id)
|
|
377
|
+
if not variant:
|
|
378
|
+
return False
|
|
379
|
+
if positive:
|
|
380
|
+
variant.positive_feedback += 1
|
|
381
|
+
else:
|
|
382
|
+
variant.negative_feedback += 1
|
|
383
|
+
return True
|
|
384
|
+
|
|
385
|
+
def try_promote(self, variant_id: str) -> Tuple[bool, str]:
|
|
386
|
+
"""
|
|
387
|
+
尝试将候选变体晋升为正式变体
|
|
388
|
+
|
|
389
|
+
晋升条件(需同时满足):
|
|
390
|
+
1. 使用次数 >= MIN_USAGE_FOR_PROMOTION (3次)
|
|
391
|
+
2. 正面反馈率 >= PROMOTION_THRESHOLD (75%)
|
|
392
|
+
3. 当前状态为 candidate
|
|
393
|
+
|
|
394
|
+
Args:
|
|
395
|
+
variant_id: 变体 ID
|
|
396
|
+
|
|
397
|
+
Returns:
|
|
398
|
+
Tuple[bool, str]: (是否晋升成功, 原因说明)
|
|
399
|
+
"""
|
|
400
|
+
with self._lock:
|
|
401
|
+
variant = self._variants.get(variant_id)
|
|
402
|
+
if not variant:
|
|
403
|
+
return False, "变体不存在"
|
|
404
|
+
|
|
405
|
+
if variant.status != "candidate":
|
|
406
|
+
return False, f"当前状态非candidate: {variant.status}"
|
|
407
|
+
|
|
408
|
+
if variant.usage_count < self.MIN_USAGE_FOR_PROMOTION:
|
|
409
|
+
return False, f"使用次数不足: {variant.usage_count} < {self.MIN_USAGE_FOR_PROMOTION}"
|
|
410
|
+
|
|
411
|
+
ratio = variant._feedback_ratio()
|
|
412
|
+
if ratio < self.PROMOTION_THRESHOLD:
|
|
413
|
+
return False, f"正面反馈率不足: {ratio:.0%} < {self.PROMOTION_THRESHOLD:.0%}"
|
|
414
|
+
|
|
415
|
+
variant.status = "promoted"
|
|
416
|
+
variant.promoted_at = datetime.now()
|
|
417
|
+
return True, f"晋升成功 (使用{variant.usage_count}次, 反馈率{ratio:.0%})"
|
|
418
|
+
|
|
419
|
+
def auto_deprecate(self) -> List[str]:
|
|
420
|
+
"""
|
|
421
|
+
自动淘汰低质量候选变体
|
|
422
|
+
|
|
423
|
+
淘汰条件:
|
|
424
|
+
1. 负面反馈率 >= DEPRECATION_THRESHOLD (35%) 且使用 >= 3 次
|
|
425
|
+
2. 或创建超过 30 天且无任何使用
|
|
426
|
+
|
|
427
|
+
Returns:
|
|
428
|
+
List[str]: 被淘汰的变体 ID 列表
|
|
429
|
+
"""
|
|
430
|
+
deprecated = []
|
|
431
|
+
cutoff = datetime.now() - timedelta(days=30)
|
|
432
|
+
|
|
433
|
+
with self._lock:
|
|
434
|
+
for vid, variant in list(self._variants.items()):
|
|
435
|
+
if variant.status != "candidate":
|
|
436
|
+
continue
|
|
437
|
+
|
|
438
|
+
ratio = variant._feedback_ratio()
|
|
439
|
+
should_deprecate = False
|
|
440
|
+
|
|
441
|
+
if variant.usage_count >= 3 and ratio <= self.DEPRECATION_THRESHOLD:
|
|
442
|
+
should_deprecate = True
|
|
443
|
+
elif variant.created_at < cutoff and variant.usage_count == 0:
|
|
444
|
+
should_deprecate = True
|
|
445
|
+
|
|
446
|
+
if should_deprecate:
|
|
447
|
+
variant.status = "deprecated"
|
|
448
|
+
deprecated.append(vid)
|
|
449
|
+
|
|
450
|
+
return deprecated
|
|
451
|
+
|
|
452
|
+
def get_statistics(self) -> Dict[str, Any]:
|
|
453
|
+
"""
|
|
454
|
+
获取变体统计信息
|
|
455
|
+
|
|
456
|
+
Returns:
|
|
457
|
+
Dict[str, Any]: 统计字典
|
|
458
|
+
"""
|
|
459
|
+
with self._lock:
|
|
460
|
+
all_vars = list(self._variants.values())
|
|
461
|
+
candidates = [v for v in all_vars if v.status == "candidate"]
|
|
462
|
+
promoted = [v for v in all_vars if v.status == "promoted"]
|
|
463
|
+
deprecated = [v for v in all_vars if v.status == "deprecated"]
|
|
464
|
+
|
|
465
|
+
total_usage = sum(v.usage_count for v in all_vars)
|
|
466
|
+
total_positive = sum(v.positive_feedback for v in all_vars)
|
|
467
|
+
total_negative = sum(v.negative_feedback for v in all_vars)
|
|
468
|
+
|
|
469
|
+
return {
|
|
470
|
+
"total_variants": len(all_vars),
|
|
471
|
+
"candidates": len(candidates),
|
|
472
|
+
"promoted": len(promoted),
|
|
473
|
+
"deprecated": len(deprecated),
|
|
474
|
+
"total_usage": total_usage,
|
|
475
|
+
"total_positive_feedback": total_positive,
|
|
476
|
+
"total_negative_feedback": total_negative,
|
|
477
|
+
"overall_feedback_ratio": round(
|
|
478
|
+
total_positive / max(total_positive + total_negative, 1), 2
|
|
479
|
+
),
|
|
480
|
+
"avg_quality_score": round(
|
|
481
|
+
sum(v.quality_score for v in all_vars) / max(len(all_vars), 1), 1
|
|
482
|
+
) if all_vars else 0.0,
|
|
483
|
+
}
|