1use super::clarifier::{ClarificationStrategy, RequirementClarifier};
6use super::coordinator::{DispatchStrategy, TaskCoordinator};
7use super::monitor::TaskMonitor;
8use super::reporter::{ReportConfig, Reporter};
9use super::todo::TodoManager;
10use super::types::*;
11
12use crate::secretary::agent_router::{AgentInfo, AgentProvider, AgentRouter};
13use crate::secretary::llm::{ChatMessage, ConversationHistory, LLMProvider};
14
15use mofa_kernel::agent::secretary::{SecretaryBehavior, SecretaryContext};
17
18use async_trait::async_trait;
19use std::collections::HashMap;
20use std::sync::Arc;
21
22pub struct DefaultSecretaryState {
28 pub todo_manager: TodoManager,
30 pub clarifier: RequirementClarifier,
32 pub coordinator: TaskCoordinator,
34 pub monitor: TaskMonitor,
36 pub reporter: Reporter,
38 pub conversation_history: Vec<ChatMessage>,
40 pub current_phase: WorkPhase,
42}
43
44impl DefaultSecretaryState {
45 pub fn new(
47 clarification_strategy: ClarificationStrategy,
48 dispatch_strategy: DispatchStrategy,
49 report_config: ReportConfig,
50 ) -> Self {
51 Self {
52 todo_manager: TodoManager::new(),
53 clarifier: RequirementClarifier::new(clarification_strategy),
54 coordinator: TaskCoordinator::new(dispatch_strategy),
55 monitor: TaskMonitor::new(),
56 reporter: Reporter::new(report_config),
57 conversation_history: Vec::new(),
58 current_phase: WorkPhase::ReceivingIdea,
59 }
60 }
61}
62
63#[derive(Debug, Clone)]
69pub struct DefaultSecretaryConfig {
70 pub name: String,
72 pub clarification_strategy: ClarificationStrategy,
74 pub dispatch_strategy: DispatchStrategy,
76 pub report_config: ReportConfig,
78 pub auto_clarify: bool,
80 pub auto_dispatch: bool,
82 pub use_llm: bool,
84 pub system_prompt: Option<String>,
86}
87
88impl Default for DefaultSecretaryConfig {
89 fn default() -> Self {
90 Self {
91 name: "智能秘书".to_string(),
92 clarification_strategy: ClarificationStrategy::Automatic,
93 dispatch_strategy: DispatchStrategy::CapabilityFirst,
94 report_config: ReportConfig::default(),
95 auto_clarify: true,
96 auto_dispatch: true,
97 use_llm: true,
98 system_prompt: None,
99 }
100 }
101}
102
103pub struct DefaultSecretaryBehavior {
116 config: DefaultSecretaryConfig,
118 llm: Option<Arc<dyn LLMProvider>>,
120 agent_provider: Option<Arc<dyn AgentProvider>>,
122 agent_router: Option<Arc<dyn AgentRouter>>,
124 executors: Vec<AgentInfo>,
126}
127
128impl DefaultSecretaryBehavior {
129 pub fn new(config: DefaultSecretaryConfig) -> Self {
131 Self {
132 config,
133 llm: None,
134 agent_provider: None,
135 agent_router: None,
136 executors: Vec::new(),
137 }
138 }
139
140 pub fn with_llm(mut self, llm: Arc<dyn LLMProvider>) -> Self {
142 self.llm = Some(llm);
143 self
144 }
145
146 pub fn with_agent_provider(mut self, provider: Arc<dyn AgentProvider>) -> Self {
148 self.agent_provider = Some(provider);
149 self
150 }
151
152 pub fn with_agent_router(mut self, router: Arc<dyn AgentRouter>) -> Self {
154 self.agent_router = Some(router);
155 self
156 }
157
158 pub fn with_executor(mut self, executor: AgentInfo) -> Self {
160 self.executors.push(executor);
161 self
162 }
163
164 fn default_system_prompt(&self) -> &'static str {
166 r#"你是一个专业的项目秘书Agent,负责帮助用户管理任务和协调工作。
167
168你的主要职责包括:
1691. 接收用户的想法和需求,记录为TODO任务
1702. 与用户交互澄清需求,生成结构化的项目需求文档
1713. 根据需求分析,将任务分配给合适的执行Agent
1724. 监控任务执行进度,在需要时请求用户决策
1735. 汇总执行结果,生成汇报并更新TODO状态
174
175你应该:
176- 始终保持专业、礼貌的态度
177- 主动询问澄清模糊的需求
178- 合理评估任务优先级
179- 及时汇报重要进展
180- 在遇到需要人类决策的问题时,清晰地呈现选项"#
181 }
182
183 async fn handle_idea(
189 &self,
190 content: &str,
191 priority: Option<TodoPriority>,
192 metadata: Option<HashMap<String, String>>,
193 ctx: &mut SecretaryContext<DefaultSecretaryState>,
194 ) -> anyhow::Result<Vec<DefaultOutput>> {
195 let mut outputs = Vec::new();
196
197 let todo = ctx
199 .state_mut()
200 .todo_manager
201 .receive_idea(content, priority, metadata)
202 .await;
203
204 outputs.push(DefaultOutput::Acknowledgment {
205 message: format!(
206 "已记录您的需求,任务ID: {}。优先级: {:?}",
207 todo.id, todo.priority
208 ),
209 });
210
211 tracing::info!("Received idea: {} -> {}", todo.id, content);
212
213 if self.config.auto_clarify {
215 let clarify_outputs = self.clarify_requirement_internal(&todo.id, ctx).await?;
216 outputs.extend(clarify_outputs);
217 }
218
219 Ok(outputs)
220 }
221
222 async fn clarify_requirement_internal(
224 &self,
225 todo_id: &str,
226 ctx: &mut SecretaryContext<DefaultSecretaryState>,
227 ) -> anyhow::Result<Vec<DefaultOutput>> {
228 let mut outputs = Vec::new();
229
230 let todo = ctx
231 .state()
232 .todo_manager
233 .get_todo(todo_id)
234 .await
235 .ok_or_else(|| anyhow::anyhow!("Todo not found: {}", todo_id))?;
236
237 ctx.state_mut()
238 .todo_manager
239 .update_status(todo_id, TodoStatus::Clarifying)
240 .await;
241
242 let requirement = if let Some(ref llm) = self.llm {
244 tracing::info!("Using LLM for requirement clarification");
245
246 let mut conversation = ConversationHistory::new();
248
249 conversation.add_system(
251 "你是一个专业的需求分析师,请将用户的需求想法转换为结构化的项目需求文档。",
252 );
253
254 conversation.add_user(format!("用户需求: {}", todo.raw_idea));
256
257 let response = llm.chat(conversation.to_vec()).await?;
259 tracing::info!("LLM response: {}", response);
260
261 serde_json::from_str::<ProjectRequirement>(response.as_str())?
263 } else {
264 ctx.state()
266 .clarifier
267 .quick_clarify(todo_id, &todo.raw_idea)
268 .await?
269 };
270
271 ctx.state_mut()
272 .todo_manager
273 .set_requirement(todo_id, requirement.clone())
274 .await;
275
276 outputs.push(DefaultOutput::Acknowledgment {
277 message: format!(
278 "需求已分析完成:\n标题: {}\n子任务数: {}\n验收标准: {} 条",
279 requirement.title,
280 requirement.subtasks.len(),
281 requirement.acceptance_criteria.len()
282 ),
283 });
284
285 if self.config.auto_dispatch {
287 let dispatch_outputs = self.dispatch_task_internal(todo_id, ctx).await?;
288 outputs.extend(dispatch_outputs);
289 }
290
291 Ok(outputs)
292 }
293
294 async fn dispatch_task_internal(
296 &self,
297 todo_id: &str,
298 ctx: &mut SecretaryContext<DefaultSecretaryState>,
299 ) -> anyhow::Result<Vec<DefaultOutput>> {
300 let mut outputs = Vec::new();
301
302 let todo = ctx
303 .state()
304 .todo_manager
305 .get_todo(todo_id)
306 .await
307 .ok_or_else(|| anyhow::anyhow!("Todo not found: {}", todo_id))?;
308
309 let requirement = todo
310 .clarified_requirement
311 .ok_or_else(|| anyhow::anyhow!("Requirement not clarified for: {}", todo_id))?;
312
313 let executors = ctx.state().coordinator.list_available_executors().await;
315 if executors.is_empty() {
316 outputs.push(DefaultOutput::Message {
317 content: "当前没有可用的执行Agent,任务将等待分配。".to_string(),
318 });
319 return Ok(outputs);
320 }
321
322 let mut context = HashMap::new();
324 context.insert("todo_id".to_string(), todo_id.to_string());
325 context.insert("raw_idea".to_string(), todo.raw_idea.clone());
326
327 let results = ctx
329 .state()
330 .coordinator
331 .dispatch_requirement(&requirement, context)
332 .await?;
333
334 let agent_ids: Vec<String> = results.iter().map(|r| r.agent_id.clone()).collect();
336 ctx.state_mut()
337 .todo_manager
338 .assign_agents(todo_id, agent_ids.clone())
339 .await;
340
341 for result in &results {
343 ctx.state()
344 .monitor
345 .start_monitoring(&result.subtask_id, &result.agent_id)
346 .await;
347 }
348
349 ctx.state_mut()
350 .todo_manager
351 .update_status(todo_id, TodoStatus::InProgress)
352 .await;
353
354 outputs.push(DefaultOutput::Acknowledgment {
355 message: format!(
356 "任务已分配给 {} 个执行Agent: {}",
357 results.len(),
358 agent_ids.join(", ")
359 ),
360 });
361
362 Ok(outputs)
363 }
364
365 async fn handle_decision(
367 &self,
368 decision_id: &str,
369 selected_option: usize,
370 comment: Option<String>,
371 ctx: &mut SecretaryContext<DefaultSecretaryState>,
372 ) -> anyhow::Result<Vec<DefaultOutput>> {
373 ctx.state()
374 .monitor
375 .submit_human_response(decision_id, selected_option, comment)
376 .await?;
377
378 Ok(vec![DefaultOutput::Acknowledgment {
379 message: format!(
380 "决策 {} 已记录,选择了选项 {}",
381 decision_id, selected_option
382 ),
383 }])
384 }
385
386 async fn handle_query(
388 &self,
389 query: QueryType,
390 ctx: &mut SecretaryContext<DefaultSecretaryState>,
391 ) -> anyhow::Result<Vec<DefaultOutput>> {
392 match query {
393 QueryType::ListTodos { filter } => {
394 let todos = if let Some(status) = filter {
395 ctx.state().todo_manager.list_by_status(status).await
396 } else {
397 ctx.state().todo_manager.list_todos().await
398 };
399
400 let summary = if todos.is_empty() {
401 "当前没有任务。".to_string()
402 } else {
403 let mut s = format!("共 {} 个任务:\n", todos.len());
404 for todo in &todos {
405 s.push_str(&format!(
406 "- {} [{:?}] {:?}: {}\n",
407 todo.id,
408 todo.priority,
409 todo.status,
410 todo.raw_idea.chars().take(30).collect::<String>()
411 ));
412 }
413 s
414 };
415
416 Ok(vec![DefaultOutput::Message { content: summary }])
417 }
418 QueryType::GetTodo { todo_id } => {
419 if let Some(todo) = ctx.state().todo_manager.get_todo(&todo_id).await {
420 let detail = format!(
421 "任务详情:\nID: {}\n需求: {}\n状态: {:?}\n优先级: {:?}\n分配Agent: {:?}",
422 todo.id, todo.raw_idea, todo.status, todo.priority, todo.assigned_agents
423 );
424 Ok(vec![DefaultOutput::Message { content: detail }])
425 } else {
426 Ok(vec![DefaultOutput::Error {
427 message: format!("未找到任务: {}", todo_id),
428 }])
429 }
430 }
431 QueryType::Statistics => {
432 let stats = ctx.state().todo_manager.get_statistics().await;
433 let summary = format!(
434 "统计信息:\n{}",
435 stats
436 .iter()
437 .map(|(k, v)| format!("- {}: {}", k, v))
438 .collect::<Vec<_>>()
439 .join("\n")
440 );
441 Ok(vec![DefaultOutput::Message { content: summary }])
442 }
443 QueryType::PendingDecisions => {
444 let decisions = ctx.state().monitor.get_pending_decisions().await;
445 if decisions.is_empty() {
446 Ok(vec![DefaultOutput::Message {
447 content: "当前没有待处理的决策。".to_string(),
448 }])
449 } else {
450 Ok(decisions
451 .into_iter()
452 .map(|d| DefaultOutput::DecisionRequired { decision: d })
453 .collect())
454 }
455 }
456 QueryType::Reports { report_type } => {
457 let reports = if let Some(rt) = report_type {
458 ctx.state().reporter.get_by_type(rt).await
459 } else {
460 ctx.state().reporter.get_history().await
461 };
462
463 if reports.is_empty() {
464 Ok(vec![DefaultOutput::Message {
465 content: "暂无汇报历史。".to_string(),
466 }])
467 } else {
468 Ok(reports
469 .into_iter()
470 .map(|r| DefaultOutput::Report { report: r })
471 .collect())
472 }
473 }
474 }
475 }
476
477 async fn handle_command(
479 &self,
480 cmd: SecretaryCommand,
481 ctx: &mut SecretaryContext<DefaultSecretaryState>,
482 ) -> anyhow::Result<Vec<DefaultOutput>> {
483 match cmd {
484 SecretaryCommand::Clarify { todo_id } => {
485 self.clarify_requirement_internal(&todo_id, ctx).await
486 }
487 SecretaryCommand::Dispatch { todo_id } => {
488 self.dispatch_task_internal(&todo_id, ctx).await
489 }
490 SecretaryCommand::Cancel { todo_id, reason } => {
491 ctx.state_mut()
492 .todo_manager
493 .update_status(&todo_id, TodoStatus::Cancelled)
494 .await;
495 Ok(vec![DefaultOutput::Acknowledgment {
496 message: format!("任务 {} 已取消,原因: {}", todo_id, reason),
497 }])
498 }
499 SecretaryCommand::GenerateReport { report_type } => {
500 let todos = ctx.state().todo_manager.list_todos().await;
501 let report = match report_type {
502 ReportType::Progress => {
503 let stats = ctx.state().todo_manager.get_statistics().await;
504 let stats_json: HashMap<String, serde_json::Value> = stats
505 .into_iter()
506 .map(|(k, v)| (k, serde_json::json!(v)))
507 .collect();
508 ctx.state()
509 .reporter
510 .generate_progress_report(&todos, stats_json)
511 .await
512 }
513 ReportType::DailySummary => {
514 ctx.state().reporter.generate_daily_summary(&todos).await
515 }
516 _ => {
517 let stats = ctx.state().todo_manager.get_statistics().await;
518 let stats_json: HashMap<String, serde_json::Value> = stats
519 .into_iter()
520 .map(|(k, v)| (k, serde_json::json!(v)))
521 .collect();
522 ctx.state()
523 .reporter
524 .generate_progress_report(&todos, stats_json)
525 .await
526 }
527 };
528 Ok(vec![DefaultOutput::Report { report }])
529 }
530 SecretaryCommand::Pause | SecretaryCommand::Resume | SecretaryCommand::Shutdown => {
531 Ok(vec![DefaultOutput::Acknowledgment {
533 message: "命令已收到".to_string(),
534 }])
535 }
536 }
537 }
538}
539
540#[async_trait]
541impl SecretaryBehavior for DefaultSecretaryBehavior {
542 type Input = DefaultInput;
543 type Output = DefaultOutput;
544 type State = DefaultSecretaryState;
545
546 fn initial_state(&self) -> Self::State {
547 let mut state = DefaultSecretaryState::new(
548 self.config.clarification_strategy.clone(),
549 self.config.dispatch_strategy.clone(),
550 self.config.report_config.clone(),
551 );
552
553 if let Some(ref provider) = self.agent_provider {
555 state.coordinator.set_agent_provider(provider.clone());
556 }
557 if let Some(ref router) = self.agent_router {
558 state.coordinator.set_agent_router(router.clone());
559 }
560
561 let system_prompt = self
563 .config
564 .system_prompt
565 .as_deref()
566 .unwrap_or_else(|| self.default_system_prompt());
567 state
568 .conversation_history
569 .push(ChatMessage::system(system_prompt));
570
571 state
572 }
573
574 fn welcome_message(&self) -> Option<Self::Output> {
575 Some(DefaultOutput::Message {
576 content: format!(
577 "您好!我是{},您的智能秘书。我可以帮您管理任务、协调工作。请告诉我您的需求。",
578 self.config.name
579 ),
580 })
581 }
582
583 async fn handle_input(
584 &self,
585 input: Self::Input,
586 ctx: &mut SecretaryContext<Self::State>,
587 ) -> anyhow::Result<Vec<Self::Output>> {
588 match input {
589 DefaultInput::Idea {
590 content,
591 priority,
592 metadata,
593 } => self.handle_idea(&content, priority, metadata, ctx).await,
594 DefaultInput::Decision {
595 decision_id,
596 selected_option,
597 comment,
598 } => {
599 self.handle_decision(&decision_id, selected_option, comment, ctx)
600 .await
601 }
602 DefaultInput::Query(query) => self.handle_query(query, ctx).await,
603 DefaultInput::Command(cmd) => self.handle_command(cmd, ctx).await,
604 }
605 }
606
607 async fn periodic_check(
608 &self,
609 ctx: &mut SecretaryContext<Self::State>,
610 ) -> anyhow::Result<Vec<Self::Output>> {
611 let pending_decisions = ctx.state().monitor.get_pending_decisions().await;
613 Ok(pending_decisions
614 .into_iter()
615 .map(|d| DefaultOutput::DecisionRequired { decision: d })
616 .collect())
617 }
618
619 fn handle_error(&self, error: &anyhow::Error) -> Option<Self::Output> {
620 Some(DefaultOutput::Error {
621 message: format!("处理请求时出错: {}", error),
622 })
623 }
624}
625
626pub struct DefaultSecretaryBuilder {
632 config: DefaultSecretaryConfig,
633 llm: Option<Arc<dyn LLMProvider>>,
634 agent_provider: Option<Arc<dyn AgentProvider>>,
635 agent_router: Option<Arc<dyn AgentRouter>>,
636 executors: Vec<AgentInfo>,
637}
638
639impl DefaultSecretaryBuilder {
640 pub fn new() -> Self {
642 Self {
643 config: DefaultSecretaryConfig::default(),
644 llm: None,
645 agent_provider: None,
646 agent_router: None,
647 executors: Vec::new(),
648 }
649 }
650
651 pub fn with_name(mut self, name: impl Into<String>) -> Self {
653 self.config.name = name.into();
654 self
655 }
656
657 pub fn with_llm(mut self, llm: Arc<dyn LLMProvider>) -> Self {
659 self.llm = Some(llm);
660 self
661 }
662
663 pub fn with_clarification_strategy(mut self, strategy: ClarificationStrategy) -> Self {
665 self.config.clarification_strategy = strategy;
666 self
667 }
668
669 pub fn with_dispatch_strategy(mut self, strategy: DispatchStrategy) -> Self {
671 self.config.dispatch_strategy = strategy;
672 self
673 }
674
675 pub fn with_auto_clarify(mut self, auto: bool) -> Self {
677 self.config.auto_clarify = auto;
678 self
679 }
680
681 pub fn with_auto_dispatch(mut self, auto: bool) -> Self {
683 self.config.auto_dispatch = auto;
684 self
685 }
686
687 pub fn with_system_prompt(mut self, prompt: impl Into<String>) -> Self {
689 self.config.system_prompt = Some(prompt.into());
690 self
691 }
692
693 pub fn with_agent_provider(mut self, provider: Arc<dyn AgentProvider>) -> Self {
695 self.agent_provider = Some(provider);
696 self
697 }
698
699 pub fn with_agent_router(mut self, router: Arc<dyn AgentRouter>) -> Self {
701 self.agent_router = Some(router);
702 self
703 }
704
705 pub fn with_executor(mut self, executor: AgentInfo) -> Self {
707 self.executors.push(executor);
708 self
709 }
710
711 pub fn build(self) -> DefaultSecretaryBehavior {
713 let mut behavior = DefaultSecretaryBehavior::new(self.config);
714
715 if let Some(llm) = self.llm {
716 behavior = behavior.with_llm(llm);
717 }
718 if let Some(provider) = self.agent_provider {
719 behavior = behavior.with_agent_provider(provider);
720 }
721 if let Some(router) = self.agent_router {
722 behavior = behavior.with_agent_router(router);
723 }
724 for executor in self.executors {
725 behavior = behavior.with_executor(executor);
726 }
727
728 behavior
729 }
730}
731
732impl Default for DefaultSecretaryBuilder {
733 fn default() -> Self {
734 Self::new()
735 }
736}
737
738#[cfg(test)]
739mod tests {
740 use super::*;
741
742 #[test]
743 fn test_builder() {
744 let behavior = DefaultSecretaryBuilder::new()
745 .with_name("测试秘书")
746 .with_auto_clarify(false)
747 .build();
748
749 assert!(!behavior.config.auto_clarify);
750 assert_eq!(behavior.config.name, "测试秘书");
751 }
752
753 #[test]
754 fn test_welcome_message() {
755 let behavior = DefaultSecretaryBuilder::new().with_name("小助手").build();
756
757 let welcome = behavior.welcome_message().unwrap();
758 match welcome {
759 DefaultOutput::Message { content } => {
760 assert!(content.contains("小助手"));
761 }
762 _ => panic!("Expected Message output"),
763 }
764 }
765}