优化: 项目文件树/依赖图组件 + 灵感对抗校验 + 小程序脚本 + 模块管理
This commit is contained in:
@@ -13,7 +13,7 @@
|
||||
//!
|
||||
//! 重构(strategy·自底向上):类型定义 / 常量 / prompt 构造 / JSON 解析等无副作用逻辑抽至
|
||||
//! [`adversarial_helpers`],本文件只保留 [`AdversarialEngine`](有状态引擎 + evaluate 主入口,
|
||||
//! 含 ARC-260618-01-e evaluate_with_llm 一致性待决策逻辑,原样保留)。外部已用路径(
|
||||
//! 含 ARC-260618-01-e 自洽性校验,已实施——解析后按 final_score 修正矛盾字段)。外部已用路径(
|
||||
//! `df_ideas::adversarial::{AdversarialEval, Recommendation, …}`)经 `pub use` 不变。
|
||||
|
||||
use std::sync::Arc;
|
||||
@@ -33,8 +33,8 @@ pub use crate::adversarial_helpers::{
|
||||
AdversarialEval, AnalystAnalysis, Argument, AssessmentLevel, EvaluatedBy, Recommendation,
|
||||
};
|
||||
use crate::adversarial_helpers::{
|
||||
SYSTEM_PROMPT, action_hint, assessment_desc, build_adversarial_prompt, parse_llm_eval,
|
||||
priority_label,
|
||||
SYSTEM_PROMPT, action_hint, assessment_desc, assessment_for_score, build_adversarial_prompt,
|
||||
enforce_score_consistency, parse_llm_eval, priority_label, recommendation_for_level,
|
||||
};
|
||||
|
||||
/// 对抗评估引擎
|
||||
@@ -97,8 +97,9 @@ impl AdversarialEngine {
|
||||
/// temperature 取 0.4:低于 0.3 偏机械重复启发式信号,高于 0.5 易发散到无关风险,
|
||||
/// 0.4 在「稳定可复现」与「论点多样性」间取得平衡。
|
||||
///
|
||||
/// ARC-260618-01-e: evaluate_with_llm 返回值一致性未校验(final_score 与
|
||||
/// analyst.final_assessment 自洽性等),待产品决策,当前逻辑原样保留不调整。
|
||||
/// ARC-260618-01-e(已实施): evaluate_with_llm 解析后经 [`enforce_score_consistency`]
|
||||
/// 做自洽性校验——final_score 与 recommendation / analyst.final_assessment 不一致时,
|
||||
/// 按 final_score 统一重新判定两字段(LLM 论点等自由文本保留,不改 LLM 调用/输出结构)。
|
||||
async fn evaluate_with_llm(&self, idea: &Idea, provider: &Arc<dyn LlmProvider>) -> Result<AdversarialEval> {
|
||||
let prompt = build_adversarial_prompt(idea);
|
||||
// 智能路由 — 对抗评估 TaskRequirements(Standard,无工具)。
|
||||
@@ -134,7 +135,9 @@ impl AdversarialEngine {
|
||||
|
||||
// 数值 clamp 由 parse_llm_eval 单点收口(final_score∈[0,10]、confidence∈[0,1],
|
||||
// 均在 parse 内对所有 Ok 路径完成),此处不再重复 clamp(CR-40-1 去冗余)。
|
||||
parse_llm_eval(&resp.text, &idea.id)
|
||||
// ARC-260618-01-e: 解析后做自洽性校验(final_score 与 recommendation /
|
||||
// analyst.final_assessment 矛盾时按 final_score 修正,见 enforce_score_consistency)。
|
||||
Ok(enforce_score_consistency(parse_llm_eval(&resp.text, &idea.id)?))
|
||||
}
|
||||
|
||||
/// 启发式评估(基于评分与内容信号,稳定有区分度)
|
||||
@@ -145,7 +148,7 @@ impl AdversarialEngine {
|
||||
let positive = self.generate_positive_argument(idea, &scores)?;
|
||||
let negative = self.generate_negative_argument(idea, &scores)?;
|
||||
let analyst = self.analyst_analysis(idea, &scores)?;
|
||||
let recommendation = self.recommendation_for(&analyst.final_assessment);
|
||||
let recommendation = recommendation_for_level(&analyst.final_assessment);
|
||||
|
||||
Ok(AdversarialEval {
|
||||
idea_id: idea.id.clone(),
|
||||
@@ -240,13 +243,8 @@ impl AdversarialEngine {
|
||||
|
||||
/// AI 分析师综合分析 — 评估等级由综合评分决定,优势/劣势按维度动态生成
|
||||
fn analyst_analysis(&self, idea: &Idea, scores: &IdeaScores) -> Result<AnalystAnalysis> {
|
||||
let final_assessment = match scores.overall {
|
||||
x if x >= 7.5 => AssessmentLevel::StrongGo,
|
||||
x if x >= 6.0 => AssessmentLevel::Recommended,
|
||||
x if x >= 4.5 => AssessmentLevel::Conditional,
|
||||
x if x >= 3.0 => AssessmentLevel::Revised,
|
||||
_ => AssessmentLevel::Defer,
|
||||
};
|
||||
// 分档与 LLM 自洽性校验共用 assessment_for_score,单源防两处阈值漂移
|
||||
let final_assessment = assessment_for_score(scores.overall);
|
||||
|
||||
let mut strengths = Vec::new();
|
||||
if scores.impact >= 6.0 {
|
||||
@@ -305,22 +303,12 @@ impl AdversarialEngine {
|
||||
final_assessment,
|
||||
})
|
||||
}
|
||||
|
||||
/// 评估等级 → 最终建议
|
||||
fn recommendation_for(&self, level: &AssessmentLevel) -> Recommendation {
|
||||
match level {
|
||||
AssessmentLevel::StrongGo => Recommendation::ImmediateAction,
|
||||
AssessmentLevel::Recommended => Recommendation::Soon,
|
||||
AssessmentLevel::Conditional => Recommendation::WithResources,
|
||||
AssessmentLevel::Revised => Recommendation::ResearchMore,
|
||||
AssessmentLevel::Defer => Recommendation::Monitor,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::adversarial_helpers::recommendation_for_score;
|
||||
use crate::capture::Idea;
|
||||
use crate::scoring::ScoringEngine;
|
||||
use df_types::types::{IdeaStatus, Priority};
|
||||
@@ -594,4 +582,78 @@ mod tests {
|
||||
assert!((eval.final_score - 7.0).abs() < 1e-9);
|
||||
assert_eq!(eval.recommendation, Recommendation::Soon);
|
||||
}
|
||||
|
||||
/// 自洽性校验(ARC-260618-01-e 已实施): LLM 高分配 Monitor/Defer → 按 final_score 修正
|
||||
/// 为 ImmediateAction/StrongGo,仅字段修正不整体降级启发式。
|
||||
#[tokio::test]
|
||||
async fn a13_llm_inconsistent_high_score_corrected() {
|
||||
let idea = make_idea("矛盾测试", "高分却给 Monitor", Priority::High, vec![]);
|
||||
let llm_text = r#"{
|
||||
"positive": {"thesis":"p","evidence":[],"reasoning":[],"confidence":0.8},
|
||||
"negative": {"thesis":"n","evidence":[],"reasoning":[],"confidence":0.3},
|
||||
"analyst": {"summary":"s","strengths":[],"weaknesses":[],"risks":[],"opportunities":[],"final_assessment":"Defer"},
|
||||
"final_score": 8.5,
|
||||
"recommendation": "Monitor"
|
||||
}"#;
|
||||
let provider = Arc::new(MockProvider { text: llm_text.to_string() });
|
||||
let engine = AdversarialEngine::new(provider);
|
||||
let eval = engine.evaluate(&idea).await.unwrap();
|
||||
|
||||
println!("\n[a13] 高分配 Monitor/Defer → 修正");
|
||||
println!(" final_score={:.2} (高分)", eval.final_score);
|
||||
println!(" recommendation={:?} (期望 ImmediateAction)", eval.recommendation);
|
||||
println!(" final_assessment={:?} (期望 StrongGo)", eval.analyst.final_assessment);
|
||||
|
||||
assert_eq!(eval.evaluated_by, EvaluatedBy::Llm, "仅字段矛盾不整体降级");
|
||||
assert!((eval.final_score - 8.5).abs() < 1e-9, "final_score 不被修正");
|
||||
assert_eq!(eval.recommendation, Recommendation::ImmediateAction);
|
||||
assert_eq!(eval.analyst.final_assessment, AssessmentLevel::StrongGo);
|
||||
}
|
||||
|
||||
/// 自洽性校验: LLM 低分配 ImmediateAction/StrongGo → 按 final_score 修正为 Monitor/Defer。
|
||||
#[tokio::test]
|
||||
async fn a14_llm_inconsistent_low_score_corrected() {
|
||||
let idea = make_idea("低分矛盾", "低分却给 ImmediateAction", Priority::Medium, vec![]);
|
||||
let llm_text = r#"{
|
||||
"positive": {"thesis":"p","evidence":[],"reasoning":[],"confidence":0.9},
|
||||
"negative": {"thesis":"n","evidence":[],"reasoning":[],"confidence":0.1},
|
||||
"analyst": {"summary":"s","strengths":[],"weaknesses":[],"risks":[],"opportunities":[],"final_assessment":"StrongGo"},
|
||||
"final_score": 2.0,
|
||||
"recommendation": "ImmediateAction"
|
||||
}"#;
|
||||
let provider = Arc::new(MockProvider { text: llm_text.to_string() });
|
||||
let engine = AdversarialEngine::new(provider);
|
||||
let eval = engine.evaluate(&idea).await.unwrap();
|
||||
|
||||
println!("\n[a14] 低分配 ImmediateAction/StrongGo → 修正");
|
||||
println!(" final_score={:.2} (低分)", eval.final_score);
|
||||
println!(" recommendation={:?} (期望 Monitor)", eval.recommendation);
|
||||
println!(" final_assessment={:?} (期望 Defer)", eval.analyst.final_assessment);
|
||||
|
||||
assert_eq!(eval.evaluated_by, EvaluatedBy::Llm);
|
||||
assert!((eval.final_score - 2.0).abs() < 1e-9, "final_score 不被修正");
|
||||
assert_eq!(eval.recommendation, Recommendation::Monitor);
|
||||
assert_eq!(eval.analyst.final_assessment, AssessmentLevel::Defer);
|
||||
}
|
||||
|
||||
/// 自洽性校验分档单测: 与启发式 analyst_analysis 同一分档(阈值边界逐一覆盖)。
|
||||
#[test]
|
||||
fn consistency_banding_matches_heuristic() {
|
||||
assert_eq!(assessment_for_score(9.0), AssessmentLevel::StrongGo);
|
||||
assert_eq!(assessment_for_score(7.5), AssessmentLevel::StrongGo); // 上界含
|
||||
assert_eq!(assessment_for_score(7.0), AssessmentLevel::Recommended);
|
||||
assert_eq!(assessment_for_score(6.0), AssessmentLevel::Recommended);
|
||||
assert_eq!(assessment_for_score(5.0), AssessmentLevel::Conditional);
|
||||
assert_eq!(assessment_for_score(4.5), AssessmentLevel::Conditional);
|
||||
assert_eq!(assessment_for_score(4.0), AssessmentLevel::Revised);
|
||||
assert_eq!(assessment_for_score(3.0), AssessmentLevel::Revised);
|
||||
assert_eq!(assessment_for_score(2.9), AssessmentLevel::Defer);
|
||||
assert_eq!(assessment_for_score(0.0), AssessmentLevel::Defer);
|
||||
|
||||
assert_eq!(recommendation_for_score(9.0), Recommendation::ImmediateAction);
|
||||
assert_eq!(recommendation_for_score(6.5), Recommendation::Soon);
|
||||
assert_eq!(recommendation_for_score(4.8), Recommendation::WithResources);
|
||||
assert_eq!(recommendation_for_score(3.5), Recommendation::ResearchMore);
|
||||
assert_eq!(recommendation_for_score(1.0), Recommendation::Monitor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
//! 对抗评估 — 纯函数 / 类型 / 常量(自 adversarial.rs 抽离,纯搬迁,零行为变更)。
|
||||
//!
|
||||
//! 本文件承载 [`AdversarialEval`] 及其字段类型、LLM prompt 构造与 JSON 解析等无副作用
|
||||
//! 逻辑;有状态的引擎 [`AdversarialEngine`](含 evaluate / evaluate_with_llm 主入口与
|
||||
//! ARC-260618-01-e 待决策逻辑)仍留在 `adversarial.rs`,二者经 [`use`] 互相引用。
|
||||
//! 本文件承载 [`AdversarialEval`] 及其字段类型、LLM prompt 构造、JSON 解析与自洽性校验
|
||||
//! 等无副作用逻辑;有状态的引擎 [`AdversarialEngine`](含 evaluate / evaluate_with_llm
|
||||
//! 主入口,经 [`enforce_score_consistency`] 落地 ARC-260618-01-e 自洽性校验)仍留在
|
||||
//! `adversarial.rs`,二者经 [`use`] 互相引用。
|
||||
//!
|
||||
//! 抽离边界:类型定义(非 impl)+ 常量 + 顶层 free function。impl 主体不动,外部
|
||||
//! 已用路径(`df_ideas::adversarial::{AdversarialEngine, AdversarialEval, Recommendation}` 等)
|
||||
@@ -336,6 +337,69 @@ pub(crate) fn parse_recommendation(s: &str) -> Result<Recommendation> {
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 自洽性校验(ARC-260618-01-e 已实施)— 综合评分分档 / 修正矛盾字段
|
||||
// ============================================================
|
||||
|
||||
/// 评估等级 → 最终建议(与启发式 recommendation 映射同一套,收敛于此单点实现)。
|
||||
pub(crate) fn recommendation_for_level(level: &AssessmentLevel) -> Recommendation {
|
||||
match level {
|
||||
AssessmentLevel::StrongGo => Recommendation::ImmediateAction,
|
||||
AssessmentLevel::Recommended => Recommendation::Soon,
|
||||
AssessmentLevel::Conditional => Recommendation::WithResources,
|
||||
AssessmentLevel::Revised => Recommendation::ResearchMore,
|
||||
AssessmentLevel::Defer => Recommendation::Monitor,
|
||||
}
|
||||
}
|
||||
|
||||
/// 综合评分 → 评估等级(分档与启发式 analyst_analysis 一致;自洽性校验复用,单源防两处漂移)。
|
||||
pub(crate) fn assessment_for_score(score: f64) -> AssessmentLevel {
|
||||
if score >= 7.5 {
|
||||
AssessmentLevel::StrongGo
|
||||
} else if score >= 6.0 {
|
||||
AssessmentLevel::Recommended
|
||||
} else if score >= 4.5 {
|
||||
AssessmentLevel::Conditional
|
||||
} else if score >= 3.0 {
|
||||
AssessmentLevel::Revised
|
||||
} else {
|
||||
AssessmentLevel::Defer
|
||||
}
|
||||
}
|
||||
|
||||
/// 综合评分 → 最终建议(先分档再映射)。
|
||||
pub(crate) fn recommendation_for_score(score: f64) -> Recommendation {
|
||||
recommendation_for_level(&assessment_for_score(score))
|
||||
}
|
||||
|
||||
/// 自洽性校验: LLM 解析结果可能 final_score 与 recommendation / analyst.final_assessment
|
||||
/// 互相矛盾(如高分配 Monitor、低分配 ImmediateAction),parse 只校验枚举合法性不校验语义。
|
||||
/// 以 final_score 为准重新判定这两个结构化字段消除矛盾;LLM 生成的论点/证据/summary 等
|
||||
/// 自由文本原样保留。不改 LLM 调用/输出结构,只在解析后修正字段值(ARC-260618-01-e)。
|
||||
pub(crate) fn enforce_score_consistency(mut eval: AdversarialEval) -> AdversarialEval {
|
||||
let expected_rec = recommendation_for_score(eval.final_score);
|
||||
let expected_level = assessment_for_score(eval.final_score);
|
||||
if eval.recommendation != expected_rec {
|
||||
tracing::warn!(
|
||||
score = eval.final_score,
|
||||
from = ?eval.recommendation,
|
||||
to = ?expected_rec,
|
||||
"LLM 评估自洽性修正: recommendation 与 final_score 矛盾,按 final_score 重新判定"
|
||||
);
|
||||
eval.recommendation = expected_rec;
|
||||
}
|
||||
if eval.analyst.final_assessment != expected_level {
|
||||
tracing::warn!(
|
||||
score = eval.final_score,
|
||||
from = ?eval.analyst.final_assessment,
|
||||
to = ?expected_level,
|
||||
"LLM 评估自洽性修正: final_assessment 与 final_score 矛盾,按 final_score 重新判定"
|
||||
);
|
||||
eval.analyst.final_assessment = expected_level;
|
||||
}
|
||||
eval
|
||||
}
|
||||
|
||||
pub(crate) fn priority_label(p: &Priority) -> &'static str {
|
||||
match p {
|
||||
Priority::Critical => "紧急",
|
||||
|
||||
Reference in New Issue
Block a user