修复: DeepSeek reasoning_content 全链路透传(跨6 crate补字段+映射+agentic loop累积回传)

This commit is contained in:
2026-06-17 18:08:04 +08:00
parent 1cd7652a34
commit 74003bc04f
11 changed files with 251 additions and 20 deletions

View File

@@ -131,6 +131,8 @@ enum StreamOutcome {
usage: df_ai::provider::TokenUsage,
incomplete: bool,
resolved_model: String,
/// DeepSeek thinking 模式推理内容(需回传到下一轮请求)
reasoning_content: Option<String>,
},
InitFailedExhausted,
Fatal,
@@ -164,6 +166,7 @@ async fn stream_one_provider(
retry_deadline: tokio::time::Instant,
model_override: &Option<String>,
agentic_req: &TaskRequirements,
last_reasoning_content: &Option<String>,
) -> StreamOutcome {
// resolve→ensure→build 三步(复用 secret::build_provider_for,DRY)。
// key 缺失/损坏 → Err → 归 Fatal(stream_llm Fatal 也是 key 类错,语义一致)。
@@ -209,17 +212,19 @@ async fn stream_one_provider(
stream: true,
tools: if tool_defs.is_empty() { None } else { Some(tool_defs.to_vec()) },
tool_choice: None,
reasoning_content: last_reasoning_content.clone(),
};
match stream_llm(&*provider, retry_request, app_handle, stop_flag, notify, conv_id).await {
StreamResult::Complete { text, tool_calls, usage } => {
StreamResult::Complete { text, tool_calls, usage, reasoning_content } => {
return StreamOutcome::Success {
text, tool_calls, usage,
incomplete: false,
resolved_model,
reasoning_content,
};
}
StreamResult::Partial { text, tool_calls, usage } => {
StreamResult::Partial { text, tool_calls, usage, reasoning_content } => {
// MidStream 保文不重试(决策 a1)。携带 incomplete=true 交调用方走保文路径。
tracing::warn!(
conv_id = %conv_id,
@@ -231,6 +236,7 @@ async fn stream_one_provider(
text, tool_calls, usage,
incomplete: true,
resolved_model,
reasoning_content,
};
}
StreamResult::InitFailed { retryable } => {
@@ -435,6 +441,9 @@ pub(crate) async fn run_agentic_loop(
// 区分"正常收敛退出"与"达 MAX 被截断退出"——后者末轮 tool_calls 仍非空(tool_result 不再回传 LLM),属异常
let mut converged = false;
// BUG-260617-12: DeepSeek thinking 模式推理内容跨轮透传
let mut last_reasoning_content: Option<String> = None;
// F-260616-13: system_prompt 是 run_agentic_loop 的不变参数(整个 loop 期间文本不变),
// 其 token 估算在 loop 外算一次缓存复用,避免每轮/每次重试重复 estimate_text(低收益优化,行为不变)。
let sys_tokens = TokenEstimator::default().estimate_text(&system_prompt);
@@ -656,9 +665,9 @@ pub(crate) async fn run_agentic_loop(
let mut candidate_chain: Vec<AiProviderRecord> = Vec::with_capacity(1 + candidates.len());
candidate_chain.push(provider_config.clone());
candidate_chain.extend(candidates.iter().cloned());
let (full_text, tool_calls_acc, round_usage, incomplete) = {
let (full_text, tool_calls_acc, round_usage, incomplete, round_reasoning_content) = {
// outcome 累积成功结果(Complete/Partial),InitFailed 不写入。
let mut outcome: Option<(String, std::collections::HashMap<u32, super::ToolCallDraft>, df_ai::provider::TokenUsage, bool)> = None;
let mut outcome: Option<(String, std::collections::HashMap<u32, super::ToolCallDraft>, df_ai::provider::TokenUsage, bool, Option<String>)> = None;
'candidate: for candidate in &candidate_chain {
// per-provider permit(可选):set_provider_caps 未配置时返回 None(单 provider 零变化);
@@ -678,14 +687,16 @@ pub(crate) async fn run_agentic_loop(
retry_deadline,
&model_override,
&agentic_req,
&last_reasoning_content,
).await {
StreamOutcome::Success { text, tool_calls, usage, incomplete, resolved_model: m } => {
StreamOutcome::Success { text, tool_calls, usage, incomplete, resolved_model: m, reasoning_content: rc } => {
// 成功:更新迭代级 resolved_model + provider_config(供后续 push/save/标题)。
// mut 解构(已在顶部声明 mut):本轮后续 save/push 用「实际成功所用 provider」
// 而非主 candidate。compress 已在本轮 stream 之前用过 primary,不受影响。
resolved_model = m;
provider_config = candidate.clone();
outcome = Some((text, tool_calls, usage, incomplete));
last_reasoning_content = rc;
outcome = Some((text, tool_calls, usage, incomplete, last_reasoning_content.clone()));
break 'candidate;
}
StreamOutcome::InitFailedExhausted => {
@@ -750,6 +761,8 @@ pub(crate) async fn run_agentic_loop(
if !full_text.is_empty() {
let mut msg = ChatMessage::assistant(&full_text);
msg.model = Some(resolved_model.clone());
// BUG-260617-12: MidStream 保文也回填 reasoning_content
msg.reasoning_content = round_reasoning_content.clone();
session.messages.push(msg);
// 追加系统提示消息:响应因网络中断不完整(对齐决策 a1 系统提示机制)
let mut notice = ChatMessage::system("⚠ 响应因网络中断不完整,以上为已接收的部分内容。可重新发送以获取完整回复。");
@@ -807,10 +820,13 @@ pub(crate) async fn run_agentic_loop(
.collect();
let mut msg = ChatMessage::assistant_with_tools(&full_text, ai_tool_calls);
msg.model = Some(resolved_model.clone());
// BUG-260617-12: 回填 reasoning_content 供下一轮请求透传
msg.reasoning_content = last_reasoning_content.clone();
session.messages.push(msg);
} else if !full_text.is_empty() {
let mut msg = ChatMessage::assistant(&full_text);
msg.model = Some(resolved_model.clone());
msg.reasoning_content = last_reasoning_content.clone();
session.messages.push(msg);
}
}