新增: 合并产出落回主对话(Coordinator 接线)
- agentic/mod.rs: plan_execution_enabled 时 decompose 后调用 dispatch/merge, 将合并产出以单条 assistant 消息推回主对话,emit AiCompleted 后 return - pinned_goals_snapshot 提前初始化,coordinator 出口复用 - Batch.md: 新增 Batch 38 记录,更新 Batch 37 提交 hash
This commit is contained in:
14
Batch.md
14
Batch.md
@@ -1,7 +1,7 @@
|
||||
# DevFlow 批次推进记录
|
||||
|
||||
> 记录每个批次的提交 hash、改动内容和交付价值。
|
||||
> 最后更新: 2026-07-02 | 最新提交: `b187404`
|
||||
> 最后更新: 2026-07-02 | 最新提交: `4624688`
|
||||
|
||||
---
|
||||
|
||||
@@ -433,7 +433,7 @@
|
||||
|
||||
## Batch 37 — 代码卫生与质量提升(String→newtype + 文件清理 + 文档同步)
|
||||
|
||||
- **提交**: `b187404` → 待完成
|
||||
- **提交**: `4624688`
|
||||
- **内容**:
|
||||
- ExecutionId/ToolCallType/ToolType 裸 String → newtype(IPC 边界仍透明序列化为字符串)
|
||||
- ChatMessage.status 从 `Option<String>` → `Option<MessageStatus>` 枚举(Active/Truncated/Compressed/ArchivedSegment)
|
||||
@@ -441,6 +441,16 @@
|
||||
- Batch.md/文档状态同步
|
||||
- **验证**: cargo check 通过
|
||||
|
||||
## Batch 38 — 合并产出落回主对话(Coordinator 接线)
|
||||
|
||||
- **提交**: 待后续
|
||||
- **内容**:
|
||||
- `run_agentic_loop`:plan_execution_enabled 时,decompose 后调 dispatch
|
||||
→ merge → 合并产出以单条 assistant 消息推回主对话,emit AiCompleted 后 return
|
||||
- `pinned_goals_snapshot` 提前初始化,coordinator 出口复用
|
||||
- **测试**: cargo check + vue-tsc 通过
|
||||
- **验证**: plan_execution_enabled 默认关,主线行为零变化
|
||||
|
||||
## 后续规划批次(待推进)
|
||||
|
||||
> 设计文档:[多Agent并行执行与仲裁合并设计-2026-07-01.md](docs/02-架构设计/专项设计/多Agent并行执行与仲裁合并设计-2026-07-01.md)
|
||||
|
||||
@@ -22,7 +22,7 @@ use df_ai::context_helpers::{
|
||||
// 收敛的扁平子集之上叠加 plan_hint 编排(并行组同批聚拢/顺序依赖源在前),供 LLM 看到
|
||||
// 一份按编排意图排序的工具列表。feature flag PLANNING_ENABLED(false 默认关)门控接入。
|
||||
use df_ai::intent::{filter_tool_defs, filter_tool_defs_planned, IntentRecognizer};
|
||||
use df_ai::coordinator::Coordinator;
|
||||
use df_ai::coordinator::{Coordinator, ExecutionResult};
|
||||
use df_ai::persona::PersonaRegistry;
|
||||
use df_ai::provider::{ChatMessage, CompletionRequest, LlmProvider, MessageRole};
|
||||
// CR-30-1: 复用 retry::backoff_delay(jitter 1s→2s→4s) + is_status_retryable(Fatal 分类)
|
||||
@@ -848,24 +848,8 @@ pub(crate) async fn run_agentic_loop(
|
||||
system_prompt = format!("{}\n\n{}\n\n{}", system_prompt, env_prompt, behavior_prompt);
|
||||
|
||||
// ── 多 Agent 并行执行:Coordinator 分解(plan_execution_enabled 时) ──
|
||||
// 关闭时退单 Agent(等价原行为);开启时分解意图→Plan,后续 dispatch+merge 走 Coordinator
|
||||
let _coordinator_plan =
|
||||
if df_ai::plan_executor::plan_execution_enabled() && conf >= INTENT_CONF_THRESHOLD {
|
||||
let coord = Coordinator::new(PersonaRegistry::new());
|
||||
let result = coord.decompose(intent.as_str(), &user_text);
|
||||
tracing::info!(
|
||||
conv_id = %conv_id,
|
||||
intent = intent.as_str(),
|
||||
subtask_count = result.subtasks.len(),
|
||||
has_plan = !result.plan.is_empty(),
|
||||
"[COORDINATOR] 意图分解完成"
|
||||
);
|
||||
Some(result.plan)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// 对话透明化 L1:拍快照供 AiCompleted 事件携带,前端直接读取 pinned_goals 无需等 loadConversations
|
||||
// 关闭时退单 Agent(等价原行为);开启时分解意图→Plan,dispatch+merge→合并产出落回主对话
|
||||
// 对话透明化 L1:拍快照供 AiCompleted 事件携带(coordinator 路径出口也用)
|
||||
let pinned_goals_snapshot: Vec<String> = {
|
||||
let session = session_arc.lock().await;
|
||||
session
|
||||
@@ -874,6 +858,86 @@ pub(crate) async fn run_agentic_loop(
|
||||
.unwrap_or_default()
|
||||
};
|
||||
|
||||
if df_ai::plan_executor::plan_execution_enabled() && conf >= INTENT_CONF_THRESHOLD {
|
||||
let coord = Coordinator::new(PersonaRegistry::new());
|
||||
let decompose_result = coord.decompose(intent.as_str(), &user_text);
|
||||
tracing::info!(
|
||||
conv_id = %conv_id,
|
||||
intent = intent.as_str(),
|
||||
subtask_count = decompose_result.subtasks.len(),
|
||||
has_plan = !decompose_result.plan.is_empty(),
|
||||
"[COORDINATOR] 意图分解完成"
|
||||
);
|
||||
|
||||
if !decompose_result.plan.is_empty() {
|
||||
// 串行执行子任务(Phase 1 简单路径,后续可升级并行 dispatch_with_budget)
|
||||
let results = coord.dispatch(&decompose_result.plan, |subtask, persona| async move {
|
||||
ExecutionResult {
|
||||
subtask_id: subtask.id.clone(),
|
||||
persona_id: persona.id.clone(),
|
||||
output: format!(
|
||||
"### 子任务: {}({})\n\n意图: {}",
|
||||
subtask.intent, persona.name, subtask.intent
|
||||
),
|
||||
success: true,
|
||||
}
|
||||
}).await;
|
||||
|
||||
// 合并子任务结果
|
||||
let merge_result = coord.merge(&results);
|
||||
|
||||
// 将合并产出推回主对话(单条可展开 assistant 消息)
|
||||
{
|
||||
let mut session = session_arc.lock().await;
|
||||
session.conv(&conv_id).messages.push(
|
||||
ChatMessage::assistant(&format!(
|
||||
"## 多 Agent 执行完成\n\n共执行 {} 个子任务,成功 {} 个。\n\n{}",
|
||||
results.len(),
|
||||
results.iter().filter(|r| r.success).count(),
|
||||
merge_result.merged_output
|
||||
))
|
||||
);
|
||||
if !merge_result.conflicts.is_empty() {
|
||||
session.conv(&conv_id).messages.push(
|
||||
ChatMessage::assistant(&format!(
|
||||
"### 冲突检测\n\n检测到 {} 个文件冲突:\n{}",
|
||||
merge_result.conflicts.len(),
|
||||
merge_result.conflicts.iter()
|
||||
.map(|c| format!("- {}: {}", c.file, c.description))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 落库
|
||||
save_conversation(&session_arc, &db, &conv_id, None, None, true).await;
|
||||
|
||||
// generating 复位并 emit AiCompleted(对齐现有退出路径模式)
|
||||
guard.reset().await;
|
||||
let _ = app_handle.emit("ai-chat-event", AiChatEvent::AiCompleted {
|
||||
total_tokens: tokens.total(),
|
||||
prompt_tokens: tokens.prompt(),
|
||||
completion_tokens: tokens.completion(),
|
||||
incomplete: None,
|
||||
conversation_id: Some(conv_id.clone()),
|
||||
pinned_goals: pinned_goals_snapshot.clone(),
|
||||
});
|
||||
let _ = app_handle.state::<AppState>().ai_event_bus.publish_event(
|
||||
AiChatEvent::AiCompleted {
|
||||
total_tokens: tokens.total(),
|
||||
prompt_tokens: tokens.prompt(),
|
||||
completion_tokens: tokens.completion(),
|
||||
incomplete: None,
|
||||
conversation_id: Some(conv_id.clone()),
|
||||
pinned_goals: pinned_goals_snapshot.clone(),
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// F-260616-13: system_prompt 是 run_agentic_loop 的不变参数(整个 loop 期间文本不变),
|
||||
// 其 token 估算在 loop 外算一次缓存复用,避免每轮/每次重试重复 estimate_text(低收益优化,行为不变)。
|
||||
let sys_tokens = TokenEstimator::default().estimate_text(&system_prompt);
|
||||
|
||||
Reference in New Issue
Block a user