feat: Evolution Engine Phase 3-5 — WorkflowComposer+FeedbackCollector+EvolutionMiddleware+反馈闭环
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled

Phase 3:
- EvolutionMiddleware (priority 78): 管家对话中注入进化确认提示
- GrowthIntegration.check_evolution() API 串入

Phase 4:
- WorkflowComposer: 轨迹工具链模式聚类 + Pipeline YAML prompt 构建 + JSON 解析
- EvolutionEngine.analyze_trajectory_patterns() L3 入口

Phase 5:
- FeedbackCollector: 反馈信号收集 + 信任度管理 + 推荐(Optimize/Archive/Promote)
- EvolutionEngine 反馈闭环方法: submit_feedback/get_artifacts_needing_optimization

新增 12 个测试(111→123),全 workspace 701 测试通过。
This commit is contained in:
iven
2026-04-18 21:27:59 +08:00
parent 7d03e6a90c
commit f97e6fdbb6
6 changed files with 727 additions and 4 deletions

View File

@@ -0,0 +1,298 @@
//! 反馈信号收集与信任度管理Phase 5 反馈闭环)
//! 收集用户对进化产物(技能/Pipeline的显式/隐式反馈
//! 管理信任度衰减和优化循环
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
/// 反馈信号类型
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum FeedbackSignal {
/// 用户直接表达的意见
Explicit,
/// 从使用行为推断
ImplicitUsage,
/// 使用频率
UsageCount,
/// 任务完成率
CompletionRate,
}
/// 情感倾向
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum Sentiment {
Positive,
Negative,
Neutral,
}
/// 进化产物类型
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum EvolutionArtifact {
Skill,
Pipeline,
}
/// 单条反馈记录
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FeedbackEntry {
pub artifact_id: String,
pub artifact_type: EvolutionArtifact,
pub signal: FeedbackSignal,
pub sentiment: Sentiment,
pub details: Option<String>,
pub timestamp: DateTime<Utc>,
}
/// 信任度记录
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TrustRecord {
pub artifact_id: String,
pub artifact_type: EvolutionArtifact,
pub trust_score: f32,
pub total_feedback: u32,
pub positive_count: u32,
pub negative_count: u32,
pub last_updated: DateTime<Utc>,
}
/// 反馈收集器
/// 管理反馈记录和信任度评分
pub struct FeedbackCollector {
/// 信任度记录表(内存,可持久化到 SQLite
trust_records: Vec<TrustRecord>,
}
impl FeedbackCollector {
pub fn new() -> Self {
Self {
trust_records: Vec::new(),
}
}
/// 提交一条反馈
pub fn submit_feedback(&mut self, entry: FeedbackEntry) -> TrustUpdate {
let record = self.get_or_create_record(&entry.artifact_id, &entry.artifact_type);
// 更新计数
record.total_feedback += 1;
match entry.sentiment {
Sentiment::Positive => record.positive_count += 1,
Sentiment::Negative => record.negative_count += 1,
Sentiment::Neutral => {}
}
// 重新计算信任度
let old_score = record.trust_score;
record.trust_score = Self::calculate_trust_internal(
record.positive_count,
record.negative_count,
record.total_feedback,
record.last_updated,
);
record.last_updated = Utc::now();
let new_score = record.trust_score;
let total = record.total_feedback;
let action = Self::recommend_action_internal(new_score, total);
TrustUpdate {
artifact_id: entry.artifact_id.clone(),
old_score,
new_score,
action,
}
}
/// 获取信任度记录
pub fn get_trust(&self, artifact_id: &str) -> Option<&TrustRecord> {
self.trust_records.iter().find(|r| r.artifact_id == artifact_id)
}
/// 获取所有需要优化的产物(信任度 < 0.4
pub fn get_artifacts_needing_optimization(&self) -> Vec<&TrustRecord> {
self.trust_records
.iter()
.filter(|r| r.trust_score < 0.4 && r.total_feedback >= 2)
.collect()
}
/// 获取所有应该归档的产物(信任度 < 0.2 且反馈 >= 5
pub fn get_artifacts_to_archive(&self) -> Vec<&TrustRecord> {
self.trust_records
.iter()
.filter(|r| r.trust_score < 0.2 && r.total_feedback >= 5)
.collect()
}
/// 获取所有高信任产物(信任度 >= 0.8
pub fn get_recommended_artifacts(&self) -> Vec<&TrustRecord> {
self.trust_records
.iter()
.filter(|r| r.trust_score >= 0.8)
.collect()
}
fn get_or_create_record(
&mut self,
artifact_id: &str,
artifact_type: &EvolutionArtifact,
) -> &mut TrustRecord {
let exists = self
.trust_records
.iter()
.any(|r| r.artifact_id == artifact_id);
if !exists {
self.trust_records.push(TrustRecord {
artifact_id: artifact_id.to_string(),
artifact_type: artifact_type.clone(),
trust_score: 0.5, // 初始信任度
total_feedback: 0,
positive_count: 0,
negative_count: 0,
last_updated: Utc::now(),
});
}
self.trust_records
.iter_mut()
.find(|r| r.artifact_id == artifact_id)
.unwrap()
}
fn calculate_trust_internal(
positive: u32,
negative: u32,
total: u32,
last_updated: DateTime<Utc>,
) -> f32 {
if total == 0 {
return 0.5;
}
let positive_ratio = positive as f32 / total as f32;
let negative_penalty = negative as f32 * 0.1;
let days_since = (Utc::now() - last_updated).num_days().max(0) as f32;
let time_decay = 1.0 - (days_since * 0.005).min(0.5);
(positive_ratio * time_decay - negative_penalty).clamp(0.0, 1.0)
}
fn recommend_action_internal(trust_score: f32, total_feedback: u32) -> RecommendedAction {
if trust_score >= 0.8 {
RecommendedAction::Promote
} else if trust_score < 0.2 && total_feedback >= 5 {
RecommendedAction::Archive
} else if trust_score < 0.4 && total_feedback >= 2 {
RecommendedAction::Optimize
} else {
RecommendedAction::Monitor
}
}
}
impl Default for FeedbackCollector {
fn default() -> Self {
Self::new()
}
}
/// 信任度更新结果
#[derive(Debug, Clone)]
pub struct TrustUpdate {
pub artifact_id: String,
pub old_score: f32,
pub new_score: f32,
pub action: RecommendedAction,
}
/// 建议动作
#[derive(Debug, Clone, PartialEq)]
pub enum RecommendedAction {
/// 继续观察
Monitor,
/// 需要优化
Optimize,
/// 建议归档(降级为记忆)
Archive,
/// 建议提升为推荐技能
Promote,
}
#[cfg(test)]
mod tests {
use super::*;
fn make_feedback(artifact_id: &str, sentiment: Sentiment) -> FeedbackEntry {
FeedbackEntry {
artifact_id: artifact_id.to_string(),
artifact_type: EvolutionArtifact::Skill,
signal: FeedbackSignal::Explicit,
sentiment,
details: None,
timestamp: Utc::now(),
}
}
#[test]
fn test_initial_trust() {
let collector = FeedbackCollector::new();
assert!(collector.get_trust("skill-1").is_none());
}
#[test]
fn test_positive_feedback_increases_trust() {
let mut collector = FeedbackCollector::new();
collector.submit_feedback(make_feedback("skill-1", Sentiment::Positive));
let record = collector.get_trust("skill-1").unwrap();
assert!(record.trust_score > 0.5);
assert_eq!(record.positive_count, 1);
}
#[test]
fn test_negative_feedback_decreases_trust() {
let mut collector = FeedbackCollector::new();
collector.submit_feedback(make_feedback("skill-1", Sentiment::Negative));
let record = collector.get_trust("skill-1").unwrap();
assert!(record.trust_score < 0.5);
}
#[test]
fn test_mixed_feedback() {
let mut collector = FeedbackCollector::new();
collector.submit_feedback(make_feedback("skill-1", Sentiment::Positive));
collector.submit_feedback(make_feedback("skill-1", Sentiment::Positive));
collector.submit_feedback(make_feedback("skill-1", Sentiment::Negative));
let record = collector.get_trust("skill-1").unwrap();
assert_eq!(record.total_feedback, 3);
assert!(record.trust_score > 0.3); // 2/3 positive
}
#[test]
fn test_recommend_optimize() {
let mut collector = FeedbackCollector::new();
// 2 negative → trust < 0.4
collector.submit_feedback(make_feedback("skill-1", Sentiment::Negative));
let update = collector.submit_feedback(make_feedback("skill-1", Sentiment::Negative));
assert_eq!(update.action, RecommendedAction::Optimize);
}
#[test]
fn test_needs_optimization_filter() {
let mut collector = FeedbackCollector::new();
collector.submit_feedback(make_feedback("bad-skill", Sentiment::Negative));
collector.submit_feedback(make_feedback("bad-skill", Sentiment::Negative));
collector.submit_feedback(make_feedback("good-skill", Sentiment::Positive));
let needs = collector.get_artifacts_needing_optimization();
assert_eq!(needs.len(), 1);
assert_eq!(needs[0].artifact_id, "bad-skill");
}
#[test]
fn test_promote_recommendation() {
let mut collector = FeedbackCollector::new();
for _ in 0..5 {
collector.submit_feedback(make_feedback("great-skill", Sentiment::Positive));
}
let recommended = collector.get_recommended_artifacts();
assert_eq!(recommended.len(), 1);
}
}