From e9f50248b97af11b7ad50406444f2df2ae2d9bb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Wed, 5 Aug 2026 14:30:30 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=9F=E4=B8=80=20VectorEngine=20LLM=20?= =?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=AB=AF=E5=91=BD=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将 editor_agent_llm_client 重命名为 vector_engine_llm_client 更新图标生成、角色动画、美术 Agent 与应用状态调用 同步排障文档中的客户端名称 --- docs/project-memory/shared-memory/pitfalls.md | 2 +- .../src/character_animation_assets.rs | 8 +++---- .../crates/api-server/src/editor_agent/api.rs | 2 +- .../crates/api-server/src/editor_project.rs | 24 +++++++++---------- server-rs/crates/api-server/src/state.rs | 16 ++++++------- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/docs/project-memory/shared-memory/pitfalls.md b/docs/project-memory/shared-memory/pitfalls.md index 203f91101..1f94b38d0 100644 --- a/docs/project-memory/shared-memory/pitfalls.md +++ b/docs/project-memory/shared-memory/pitfalls.md @@ -3833,7 +3833,7 @@ ## 画布 Agent 的规划请求不能关闭瞬时失败重试 - 现象:美术 Agent 对话返回红色错误气泡 `completion error: LLM 请求超时,累计尝试 1 次`;HTTP 本身仍返回 200,前端 20 分钟 transport timeout 没有触发。 -- 原因:规划请求虽然有 Agent 专用单次 timeout,但 `editor_agent_llm_client` 把 `max_retries` 硬编码为 0;VectorEngine `gpt-5.4-mini` 的偶发长尾、连接超时或可重试上游状态会在第一次失败后直接持久化成 system error。framework 的英文 `completion error` 前缀也被原样暴露给用户。 +- 原因:规划请求虽然有 Agent 专用单次 timeout,但 `vector_engine_llm_client` 把 `max_retries` 硬编码为 0;VectorEngine `gpt-5.4-mini` 的偶发长尾、连接超时或可重试上游状态会在第一次失败后直接持久化成 system error。framework 的英文 `completion error` 前缀也被原样暴露给用户。 - 处理:120 秒改为前端软提示阈值:POST 仍 pending 时显示不入库的“仍在处理中,请耐心等待”;provider 明确断开/失败才写正式错误。专用 provider 单 attempt 使用 8 分钟 hard timeout,请求发起阶段读取 `GENARRATIVE_LLM_MAX_RETRIES`,但画布 Agent 最多重试 1 次且重试退避最多 60 秒。不要只计算单次 complete 的最坏时间:runner 还可因非法 JSON/工具校验失败进入后续轮次,必须从 handler 入口开始计算 18 分钟总 deadline,进入 `agent.prompt(...)` 时扣除会话锁/上下文准备已用时间,为持久化和前端 20 分钟 timeout 留出余量。响应头后的体读取/解析错误按明确失败收口,必须使用真实 attempt 计数;规划、配置和定价错误对用户统一为中文,原始诊断只记后端日志。重试发生在任何生成工具执行前,不会重复提交生成任务或扣费,不要通过提高前端 timeout 或 runner `max_turns` 掩盖 provider 重试缺失。 - 验证:`platform-editor-agent` 测试锁定 8 分钟 hard timeout 与中文错误;前端 fake timer 用例锁定 120 秒前只显示思考动画、到点后显示耐心等待、成功/失败后移除;`platform-llm` 回归用例锁定第二次 attempt 成功响应头后的 body timeout 仍报累计 2 次;`api-server` 测试锁定专用 client retry、18 分钟整体 deadline 与中文直达错误。运行态排障按同一 request id 对齐 `platform_llm` failure stage 与 `/messages` 总耗时,并确认仍 pending 的请求不再在 120 秒形成错误气泡。 - 关联:`server-rs/crates/platform-editor-agent/src/agent/agent.rs`、`server-rs/crates/platform-agent-harness/src/error.rs`、`server-rs/crates/api-server/src/state.rs`、`src/components/image-editor/EditorAgentConversation/useEditorAgentConversation.ts`、`src/components/image-editor/EditorAgentConversation/MessageBubble.tsx`、`src/services/image-editor/editorAgentClient.ts`。 diff --git a/server-rs/crates/api-server/src/character_animation_assets.rs b/server-rs/crates/api-server/src/character_animation_assets.rs index 5a47c01a9..8b06fe5ee 100644 --- a/server-rs/crates/api-server/src/character_animation_assets.rs +++ b/server-rs/crates/api-server/src/character_animation_assets.rs @@ -744,9 +744,9 @@ pub(crate) async fn generate_editor_character_animation_for_owner( // 背景色决策:预扣泥点之后才发起(显式传入 → 视觉 LLM 按源图主体配色自动决策 → 默认色)。 // 决策失败会让本 future 返回 Err,走 execute_billable 的失败退款,用户不损失泥点。 let screen_background_decision = resolve_editor_screen_background_color( - state.llm_client(), - state.editor_agent_llm_client(), - EditorScreenBackgroundDecisionInput { + state.llm_client(), + state.vector_engine_llm_client(), + EditorScreenBackgroundDecisionInput { kind: EditorScreenBackgroundDecisionKind::CharacterAnimation, screen_color: payload.screen_color.clone(), prompt: payload.prompt_text.clone(), @@ -754,7 +754,7 @@ pub(crate) async fn generate_editor_character_animation_for_owner( reference_count: 1, source_image_data_url: Some(source_data_url.clone()), }, - Some(&crate::editor_screen_background_decision::EditorScreenBackgroundDecisionAuditContext { + Some(&crate::editor_screen_background_decision::EditorScreenBackgroundDecisionAuditContext { state: &state, context: crate::external_api_audit::ExternalApiAuditContext { user_id: Some(owner_user_id.clone()), diff --git a/server-rs/crates/api-server/src/editor_agent/api.rs b/server-rs/crates/api-server/src/editor_agent/api.rs index aea2045e9..83c3df142 100644 --- a/server-rs/crates/api-server/src/editor_agent/api.rs +++ b/server-rs/crates/api-server/src/editor_agent/api.rs @@ -201,7 +201,7 @@ pub async fn editor_agent_message( let tool_context = context::build_tool_context(&document); // Build and run agent - let Some(llm_client) = state.editor_agent_llm_client() else { + let Some(llm_client) = state.vector_engine_llm_client() else { tracing::warn!( conversation_id = %conversation.conversation_id, "美术 Agent LLM 客户端未配置" diff --git a/server-rs/crates/api-server/src/editor_project.rs b/server-rs/crates/api-server/src/editor_project.rs index 8a1d7637e..037d1829d 100644 --- a/server-rs/crates/api-server/src/editor_project.rs +++ b/server-rs/crates/api-server/src/editor_project.rs @@ -1892,9 +1892,9 @@ pub(crate) async fn generate_editor_image_for_owner( let screen_background_decision = if is_character_generation { Some( resolve_editor_screen_background_color( - state.llm_client(), - state.editor_agent_llm_client(), - EditorScreenBackgroundDecisionInput { + state.llm_client(), + state.vector_engine_llm_client(), + EditorScreenBackgroundDecisionInput { kind: EditorScreenBackgroundDecisionKind::Character, screen_color: requested_screen_color.clone(), prompt: role_setting.clone(), @@ -1902,7 +1902,7 @@ pub(crate) async fn generate_editor_image_for_owner( reference_count, source_image_data_url: None, }, - Some(&crate::editor_screen_background_decision::EditorScreenBackgroundDecisionAuditContext { + Some(&crate::editor_screen_background_decision::EditorScreenBackgroundDecisionAuditContext { state, context: crate::external_api_audit::ExternalApiAuditContext { user_id: caller.audit_subject_user_id.clone(), @@ -5072,9 +5072,9 @@ pub(crate) async fn generate_editor_icon_spritesheet_for_owner( async { // 背景色决策:预扣泥点之后才发起(决策失败→future 返回 Err→失败退款)。 let screen_background_decision = resolve_editor_screen_background_color( - state.llm_client(), - state.editor_agent_llm_client(), - EditorScreenBackgroundDecisionInput { + state.llm_client(), + state.vector_engine_llm_client(), + EditorScreenBackgroundDecisionInput { kind: EditorScreenBackgroundDecisionKind::IconSpritesheet, screen_color: requested_screen_color.clone(), prompt: user_prompt.clone(), @@ -5082,7 +5082,7 @@ pub(crate) async fn generate_editor_icon_spritesheet_for_owner( reference_count, source_image_data_url: None, }, - Some(&crate::editor_screen_background_decision::EditorScreenBackgroundDecisionAuditContext { + Some(&crate::editor_screen_background_decision::EditorScreenBackgroundDecisionAuditContext { state, context: crate::external_api_audit::ExternalApiAuditContext { user_id: caller.audit_subject_user_id.clone(), @@ -6156,9 +6156,9 @@ pub(crate) async fn extract_editor_ui_design_assets_for_owner( async { // 背景色决策:预扣泥点之后才发起(决策失败→future 返回 Err→失败退款)。 let screen_background_decision = resolve_editor_screen_background_color( - state.llm_client(), - state.editor_agent_llm_client(), - EditorScreenBackgroundDecisionInput { + state.llm_client(), + state.vector_engine_llm_client(), + EditorScreenBackgroundDecisionInput { kind: EditorScreenBackgroundDecisionKind::UiDesignAssetExtraction, screen_color: requested_screen_color.clone(), prompt: decision_prompt.clone(), @@ -6166,7 +6166,7 @@ pub(crate) async fn extract_editor_ui_design_assets_for_owner( reference_count, source_image_data_url: None, }, - Some(&crate::editor_screen_background_decision::EditorScreenBackgroundDecisionAuditContext { + Some(&crate::editor_screen_background_decision::EditorScreenBackgroundDecisionAuditContext { state, context: crate::external_api_audit::ExternalApiAuditContext { user_id: caller.audit_subject_user_id.clone(), diff --git a/server-rs/crates/api-server/src/state.rs b/server-rs/crates/api-server/src/state.rs index 745819c6e..5ac3e917c 100644 --- a/server-rs/crates/api-server/src/state.rs +++ b/server-rs/crates/api-server/src/state.rs @@ -265,7 +265,7 @@ pub struct AppStateInner { wallet_refund_outbox: Option>, editor_generation_pricing_store: EditorGenerationPricingStore, llm_client: Option, - editor_agent_llm_client: Option, + vector_engine_llm_client: Option, matting_client: Option, bgfilter_provider_http_client: reqwest::Client, bgfilter_worker_http_client: reqwest::Client, @@ -513,7 +513,7 @@ impl AppState { ) .map_err(|error| AppStateInitError::DependencyUnavailable(error.to_string()))?; let llm_client = build_llm_client(&config)?; - let editor_agent_llm_client = build_editor_agent_llm_client(&config)?; + let vector_engine_llm_client = build_vector_engine_llm_client(&config)?; let matting_client = build_matting_client(&config)?; let bgfilter_provider_http_client = build_bgfilter_provider_http_client(&config)?; let bgfilter_worker_http_client = build_bgfilter_worker_http_client(&config)?; @@ -570,7 +570,7 @@ impl AppState { wallet_refund_outbox, editor_generation_pricing_store, llm_client, - editor_agent_llm_client, + vector_engine_llm_client, matting_client, bgfilter_provider_http_client, bgfilter_worker_http_client, @@ -1281,8 +1281,8 @@ impl AppState { self.llm_client.as_ref() } - pub fn editor_agent_llm_client(&self) -> Option<&LlmClient> { - self.editor_agent_llm_client.as_ref() + pub fn vector_engine_llm_client(&self) -> Option<&LlmClient> { + self.vector_engine_llm_client.as_ref() } pub fn matting_client(&self) -> Option<&MattingClient> { @@ -2126,7 +2126,7 @@ fn build_llm_client(config: &AppConfig) -> Result, AppStateIni Ok(Some(LlmClient::new(llm_config)?)) } -fn build_editor_agent_llm_client( +fn build_vector_engine_llm_client( config: &AppConfig, ) -> Result, AppStateInitError> { // 中文注释:Apimart 已于 2026-06 弃用,LLM 文本调用统一迁移到 VectorEngine。 @@ -2429,7 +2429,7 @@ mod tests { let state = AppState::new(AppConfig::default()).expect("state should build"); assert!(state.llm_client().is_none()); - assert!(state.editor_agent_llm_client().is_none()); + assert!(state.vector_engine_llm_client().is_none()); } #[test] @@ -2454,7 +2454,7 @@ mod tests { let state = AppState::new(config).expect("state should build"); let client = state - .editor_agent_llm_client() + .vector_engine_llm_client() .expect("editor agent LLM client should exist"); assert_eq!(