优化: 项目文件树/依赖图组件 + 灵感对抗校验 + 小程序脚本 + 模块管理

This commit is contained in:
lxy
2026-08-10 00:00:31 +08:00
parent 6bafdcd5a5
commit cf00345c35
19 changed files with 521 additions and 185 deletions
+87 -25
View File
@@ -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);
}
}
+67 -3
View File
@@ -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 => "紧急",