use crate::generator::compose::memory::MemoryScope;
use crate::generator::context::GeneratorContext;
use crate::generator::outlet::DocTree;
use crate::generator::research::memory::MemoryRetriever;
use crate::generator::research::types::{AgentType as ResearchAgentType, KeyModuleReport};
use crate::generator::step_forward_agent::{
AgentDataConfig, DataSource, FormatterConfig, LLMCallMode, PromptTemplate, StepForwardAgent,
};
use crate::utils::threads::do_parallel_with_limit;
use anyhow::Result;
#[derive(Default)]
pub struct KeyModulesInsightEditor {}
impl KeyModulesInsightEditor {
pub async fn execute(&self, context: &GeneratorContext, doc_tree: &mut DocTree) -> Result<()> {
if let Some(value) = context
.get_research(&ResearchAgentType::KeyModulesInsight.to_string())
.await
{
let insight_reports: Vec<KeyModuleReport> = serde_json::from_value(value)?;
let max_parallels = context.config.llm.max_parallels;
println!(
"🚀 Starting concurrent analysis of insight reports, max concurrency: {}",
max_parallels
);
let analysis_futures: Vec<_> = insight_reports
.into_iter()
.map(|insight_report| {
let insight_key = format!(
"{}_{}",
ResearchAgentType::KeyModulesInsight,
&insight_report.domain_name
);
let domain_name = insight_report.domain_name.clone();
let kmie = KeyModuleInsightEditor::new(insight_key.clone(), insight_report);
let context_clone = context.clone();
Box::pin(async move {
let result = kmie.execute(&context_clone).await;
(insight_key, domain_name, result)
})
})
.collect();
let analysis_results = do_parallel_with_limit(analysis_futures, max_parallels).await;
for (insight_key, domain_name, result) in analysis_results {
result?;
doc_tree.insert(
&insight_key,
format!("{}/{}.md", context.config.target_language.get_directory_name("deep_exploration"), &domain_name).as_str(),
);
}
}
Ok(())
}
}
struct KeyModuleInsightEditor {
insight_key: String,
report: KeyModuleReport,
}
impl KeyModuleInsightEditor {
fn new(insight_key: String, report: KeyModuleReport) -> Self {
KeyModuleInsightEditor {
insight_key,
report,
}
}
}
impl StepForwardAgent for KeyModuleInsightEditor {
type Output = String;
fn agent_type(&self) -> String {
self.insight_key.to_string()
}
fn memory_scope_key(&self) -> String {
MemoryScope::DOCUMENTATION.to_string()
}
fn should_include_timestamp(&self) -> bool {
true
}
fn data_config(&self) -> AgentDataConfig {
AgentDataConfig {
required_sources: vec![
DataSource::ResearchResult(ResearchAgentType::SystemContextResearcher.to_string()),
DataSource::ResearchResult(ResearchAgentType::DomainModulesDetector.to_string()),
DataSource::ResearchResult(ResearchAgentType::ArchitectureResearcher.to_string()),
DataSource::ResearchResult(ResearchAgentType::WorkflowResearcher.to_string()),
DataSource::ResearchResult(self.insight_key.to_string()),
],
optional_sources: vec![DataSource::knowledge_categories(vec!["architecture", "database"])],
}
}
fn prompt_template(&self) -> PromptTemplate {
let report = &self.report;
let opening_instruction = format!(
r#"The topic you need to analyze is: {}
## Documentation Quality Requirements:
1. **Completeness**: Based on research materials, cover all important aspects of the topic `{}`, without omitting key information
2. **Accuracy**: Based on research data, ensure accuracy of technical details
3. **Professionalism**: Use standard architecture terminology and expressions
4. **Readability**: Clear structure, rich language narrative, and easy to understand
5. **Practicality**: Provide valuable module knowledge and technical implementation details.
"#,
&report.domain_name, &report.domain_name
);
PromptTemplate {
system_prompt: r#"You are a software expert skilled at writing technical documentation. Based on the research materials and requirements provided by users, write technical documentation for the technical implementation of corresponding modules in existing projects.
IMPORTANT: Do not use transition phrases like "Now I have gathered comprehensive information" or "I have collected enough details" - start writing the documentation directly."#.to_string(),
opening_instruction,
closing_instruction: String::new(),
llm_call_mode: LLMCallMode::PromptWithTools,
formatter_config: FormatterConfig::default(),
}
}
}