use rucora::agent::{SimpleAgent, ToolAgent};
use rucora::prelude::Agent;
use rucora::provider::OpenAiProvider;
use rucora::tools::{EchoTool, ShellTool};
use serde::{Deserialize, Serialize};
use tracing::{Level, info};
use tracing_subscriber::FmtSubscriber;
#[derive(Debug, Clone, Serialize, Deserialize)]
struct SubQuestion {
id: usize,
question: String,
description: Option<String>,
}
#[derive(Debug, Clone)]
struct SubAnswer {
question_id: usize,
question: String,
answer: String,
confidence: u8,
}
#[derive(Debug, Clone)]
struct SynthesizedResult {
sub_questions: Vec<SubQuestion>,
sub_answers: Vec<SubAnswer>,
summary: String,
quality_score: u8,
}
struct TaskDecomposer<P> {
agent: SimpleAgent<P>,
}
impl<P> TaskDecomposer<P>
where
P: rucora_core::provider::LlmProvider + Send + Sync + 'static,
{
fn new(provider: P, model: &str) -> Self {
let agent = SimpleAgent::builder()
.provider(provider)
.model(model.to_string())
.system_prompt(
"你是一个专业的任务拆解专家。你的职责是:\n\
1. 分析复杂问题,识别核心要点\n\
2. 将问题拆解为 3-5 个独立的子问题\n\
3. 确保子问题之间逻辑清晰、相互独立\n\
4. 子问题应该覆盖原问题的所有重要方面\n\n\
请以 JSON 格式返回子问题列表,格式如下:\n\
[\n\
{\"id\": 1, \"question\": \"子问题 1\", \"description\": \"简要说明\"},\n\
{\"id\": 2, \"question\": \"子问题 2\", \"description\": \"简要说明\"}\n\
]\n\n\
注意:只返回 JSON 数组,不要有其他内容。",
)
.build();
Self { agent }
}
async fn decompose(&self, question: &str) -> anyhow::Result<Vec<SubQuestion>> {
info!("🔍 正在拆解问题...");
let prompt = format!("请拆解以下问题:\n\n{question}");
let output = self.agent.run(prompt.into()).await?;
let response = output.text().unwrap_or("[]");
let sub_questions: Vec<SubQuestion> = serde_json::from_str(response).unwrap_or_else(|_| {
info!("⚠ JSON 解析失败,使用默认拆解");
vec![SubQuestion {
id: 1,
question: question.to_string(),
description: Some("默认子问题".to_string()),
}]
});
info!("✓ 拆解为 {} 个子问题", sub_questions.len());
Ok(sub_questions)
}
}
struct AnswerSynthesizer<P> {
agent: SimpleAgent<P>,
}
impl<P> AnswerSynthesizer<P>
where
P: rucora_core::provider::LlmProvider + Send + Sync + 'static,
{
fn new(provider: P, model: &str) -> Self {
let agent = SimpleAgent::builder()
.provider(provider)
.model(model.to_string())
.system_prompt(
"你是一个专业的答案综合专家。你的职责是:\n\
1. 阅读所有子问题及其答案\n\
2. 整合信息,形成连贯、完整的综合回答\n\
3. 消除重复和矛盾的信息\n\
4. 结构化组织内容,使其易于理解\n\
5. 评估答案的整体质量(1-5 分)\n\n\
请以以下格式返回:\n\
【综合总结】\n\
(你的综合回答)\n\n\
【质量评分】X/5\n\n\
【改进建议】\n\
(可选的改进建议)",
)
.build();
Self { agent }
}
async fn synthesize(
&self,
original_question: &str,
sub_answers: &[SubAnswer],
) -> anyhow::Result<(String, u8)> {
info!("🔗 正在综合答案...");
let mut prompt = format!("原始问题:{original_question}\n\n");
prompt.push_str("子问题答案:\n\n");
for answer in sub_answers {
prompt.push_str(&format!(
"【子问题 {}】{}\n【答案】{}\n【置信度】{}/5\n\n",
answer.question_id, answer.question, answer.answer, answer.confidence
));
}
prompt.push_str("\n请综合以上所有答案,回答原始问题。");
let output = self.agent.run(prompt.into()).await?;
let response = output.text().unwrap_or("无法综合答案").to_string();
let quality_score = response
.lines()
.find(|line| line.contains("质量评分"))
.and_then(|line| {
line.chars()
.find(|c| c.is_ascii_digit())
.and_then(|c| c.to_digit(10).map(|d| d as u8))
})
.unwrap_or(3);
info!("✓ 答案综合完成,质量评分:{}/5", quality_score);
Ok((response, quality_score))
}
}
struct SubQuestionAnswerer<P> {
agent: ToolAgent<P>,
}
impl<P> SubQuestionAnswerer<P>
where
P: rucora_core::provider::LlmProvider + Send + Sync + 'static,
{
fn new(provider: P, model: &str) -> Self {
let agent = ToolAgent::builder()
.provider(provider)
.model(model.to_string())
.system_prompt(
"你是一个专业的子问题回答专家。你的职责是:\n\
1. 准确理解子问题\n\
2. 提供详细、准确的答案\n\
3. 如有必要,使用可用的工具获取额外信息\n\
4. 在答案末尾评估你的置信度(1-5 分)\n\n\
请以以下格式返回:\n\
【答案】\n\
(你的详细回答)\n\n\
【置信度】X/5",
)
.tool(EchoTool)
.tool(ShellTool::new())
.max_steps(5)
.build();
Self { agent }
}
async fn answer(&self, sub_question: &SubQuestion) -> anyhow::Result<SubAnswer> {
info!(
" 📝 回答子问题 #{}: {}",
sub_question.id, sub_question.question
);
let prompt = if let Some(desc) = &sub_question.description {
format!(
"问题:{}\n说明:{}\n\n请详细回答。",
sub_question.question, desc
)
} else {
format!("问题:{}\n\n请详细回答。", sub_question.question)
};
let output = self.agent.run(prompt.into()).await?;
let response = output.text().unwrap_or("无法回答");
let confidence = response
.lines()
.find(|line| line.contains("置信度"))
.and_then(|line| {
line.chars()
.find(|c| c.is_ascii_digit())
.and_then(|c| c.to_digit(10).map(|d| d as u8))
})
.unwrap_or(3);
let answer = response
.lines()
.filter(|line| !line.contains("置信度") && !line.contains("【置信度】"))
.collect::<Vec<_>>()
.join("\n");
info!(" ✓ 完成,置信度:{}/5", confidence);
Ok(SubAnswer {
question_id: sub_question.id,
question: sub_question.question.clone(),
answer,
confidence,
})
}
}
struct TaskDecompositionSystem {
make_provider: Box<dyn Fn() -> anyhow::Result<OpenAiProvider> + Send + Sync>,
model: String,
}
impl TaskDecompositionSystem {
fn new(
make_provider: impl Fn() -> anyhow::Result<OpenAiProvider> + Send + Sync + 'static,
model: &str,
) -> Self {
Self {
make_provider: Box::new(make_provider),
model: model.to_string(),
}
}
async fn process(&self, question: &str) -> anyhow::Result<SynthesizedResult> {
info!("╔════════════════════════════════════════╗");
info!("║ 开始处理复杂问题 ║");
info!("╚════════════════════════════════════════╝\n");
info!("📋 原始问题:{}\n", question);
let provider = (self.make_provider)()?;
let decomposer = TaskDecomposer::new(provider, &self.model);
let sub_questions = decomposer.decompose(question).await?;
info!("✓ 问题拆解完成\n");
info!("═══════════════════════════════════════");
info!("步骤 2: 回答子问题");
info!("═══════════════════════════════════════\n");
let mut sub_answers = Vec::new();
for sub_question in &sub_questions {
let provider = (self.make_provider)()?;
let answerer = SubQuestionAnswerer::new(provider, &self.model);
let answer = answerer.answer(sub_question).await?;
sub_answers.push(answer);
}
info!("✓ 所有子问题回答完成\n");
info!("═══════════════════════════════════════");
info!("步骤 3: 综合答案");
info!("═══════════════════════════════════════\n");
let provider = (self.make_provider)()?;
let synthesizer = AnswerSynthesizer::new(provider, &self.model);
let (summary, quality_score) = synthesizer.synthesize(question, &sub_answers).await?;
info!("✓ 答案综合完成\n");
Ok(SynthesizedResult {
sub_questions,
sub_answers,
summary,
quality_score,
})
}
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
dotenv::dotenv().ok();
let subscriber = FmtSubscriber::builder()
.with_max_level(Level::DEBUG)
.with_target(false)
.finish();
tracing::subscriber::set_global_default(subscriber)?;
info!("╔════════════════════════════════════════╗");
info!("║ rucora 任务拆解示例 ║");
info!("╚════════════════════════════════════════╝\n");
if std::env::var("OPENAI_API_KEY").is_err() && std::env::var("OPENAI_BASE_URL").is_err() {
info!("⚠ 未设置 API 配置");
info!(" 使用 OpenAI: export OPENAI_API_KEY=sk-your-key");
info!(" 使用 Ollama: export OPENAI_BASE_URL=http://localhost:11434");
return Ok(());
}
let model = "qwen3.5:9b";
info!("使用模型: {}\n", model);
let make_provider = || Ok(OpenAiProvider::from_env()?);
let system = TaskDecompositionSystem::new(make_provider, model);
info!("═══════════════════════════════════════");
info!("演示任务 1: 技术调研");
info!("═══════════════════════════════════════\n");
let question1 =
"如何设计一个高可用的分布式系统?请从架构设计、容错机制、数据一致性等方面分析。";
match system.process(question1).await {
Ok(result) => {
info!("╔════════════════════════════════════════╗");
info!("║ 任务 1 结果 ║");
info!("╚════════════════════════════════════════╝\n");
info!("📋 拆解的子问题:");
for sq in &result.sub_questions {
info!(
" #{}: {} - {}",
sq.id,
sq.question,
sq.description.as_deref().unwrap_or("")
);
}
info!("");
info!("📝 子问题答案摘要:");
for ans in &result.sub_answers {
let preview: String = ans.answer.chars().take(50).collect();
info!(
" #{}: {}... (置信度:{}/5)",
ans.question_id, preview, ans.confidence
);
}
info!("");
info!("🔗 综合总结:");
info!("{}\n", result.summary);
info!("⭐ 质量评分:{}/5\n", result.quality_score);
}
Err(e) => {
info!("❌ 处理失败:{}\n", e);
}
}
info!("═══════════════════════════════════════");
info!("演示任务 2: 学习计划制定");
info!("═══════════════════════════════════════\n");
let question2 = "我想在 3 个月内学会 Rust 编程并能够开发实际项目,请帮我制定详细的学习计划,包括学习资源、实践项目和时间安排。";
match system.process(question2).await {
Ok(result) => {
info!("╔════════════════════════════════════════╗");
info!("║ 任务 2 结果 ║");
info!("╚════════════════════════════════════════╝\n");
info!("📋 拆解的子问题:");
for sq in &result.sub_questions {
info!(
" #{}: {} - {}",
sq.id,
sq.question,
sq.description.as_deref().unwrap_or("")
);
}
info!("");
info!("📝 子问题答案摘要:");
for ans in &result.sub_answers {
let preview: String = ans.answer.chars().take(50).collect();
info!(
" #{}: {}... (置信度:{}/5)",
ans.question_id, preview, ans.confidence
);
}
info!("");
info!("🔗 综合总结:");
info!("{}\n", result.summary);
info!("⭐ 质量评分:{}/5\n", result.quality_score);
}
Err(e) => {
info!("❌ 处理失败:{}\n", e);
}
}
info!("═══════════════════════════════════════");
info!("演示任务 3: 产品分析");
info!("═══════════════════════════════════════\n");
let question3 = "分析开发一个 AI 驱动的个人知识管理应用需要考虑的关键因素,包括技术选型、用户体验、数据安全等。";
match system.process(question3).await {
Ok(result) => {
info!("╔════════════════════════════════════════╗");
info!("║ 任务 3 结果 ║");
info!("╚════════════════════════════════════════╝\n");
info!("📋 拆解的子问题:");
for sq in &result.sub_questions {
info!(
" #{}: {} - {}",
sq.id,
sq.question,
sq.description.as_deref().unwrap_or("")
);
}
info!("");
info!("📝 子问题答案摘要:");
for ans in &result.sub_answers {
let preview: String = ans.answer.chars().take(50).collect();
info!(
" #{}: {}... (置信度:{}/5)",
ans.question_id, preview, ans.confidence
);
}
info!("");
info!("🔗 综合总结:");
info!("{}\n", result.summary);
info!("⭐ 质量评分:{}/5\n", result.quality_score);
}
Err(e) => {
info!("❌ 处理失败:{}\n", e);
}
}
info!("═══════════════════════════════════════");
info!("示例完成!");
info!("═══════════════════════════════════════\n");
info!("系统架构:\n");
info!(
" 复杂问题 → TaskDecomposer(拆解) → SubQuestionAnswerer(逐个回答) → AnswerSynthesizer(综合)"
);
info!("");
info!("适用场景:复杂技术咨询、研究报告、决策支持、学习规划");
Ok(())
}