From 97c3fc83c11186cce270f53f1832fc3b090e0f82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Thu, 1 Oct 2026 16:51:50 +0800 Subject: [PATCH] =?UTF-8?q?=E9=9D=9E=E5=AD=97=E7=AC=A6=E4=B8=B2=E5=85=A5?= =?UTF-8?q?=E5=8F=82=E7=9A=84=E5=9B=9E=E6=98=BE=E6=88=AA=E6=96=AD=E5=88=B0?= =?UTF-8?q?=20120=20=E5=AD=97=E7=AC=A6=EF=BC=8C=E4=B8=8D=E5=86=8D=E6=8A=8A?= =?UTF-8?q?=E6=95=B4=E4=B8=AA=E5=AF=B9=E8=B1=A1=E5=8E=9F=E6=A0=B7=E5=B8=A6?= =?UTF-8?q?=E8=BF=9B=E8=AF=8A=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - arguments::optional_text 里的 not_text 改为传截断后的序列化值:类型错只需要看出「传了什么」 - got 同时出现在 to_user_msg 与序列化 error 里,限长后工具结果与诊断 sidecar 都不会被超大数组/对象灌爆 - 复用 runtime_state::truncate_agent_runtime_text,不新增第二套截断实现 - 测试补一条:超大数组回显必须带截断标记且长度可控 验证:cargo test --bin genarrative-ai-game-creator-shell -- non_text_field_values 通过;cargo fmt --check 通过 --- .../src-tauri/src/agent/direct_tool_bridge.rs | 23 +++++++++++++++++++ .../src-tauri/src/agent/tool/arguments.rs | 12 +++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_tool_bridge.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_tool_bridge.rs index bfff40a20..254ebd93e 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_tool_bridge.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_tool_bridge.rs @@ -4965,6 +4965,29 @@ mod tests { ); assert!(!prompt_not_text.contains("不能为空"), "{prompt_not_text}"); + let oversized_not_text = match resource_generation_input(&json!({ + "kind": "video", + "mode": "create", + "prompt": vec!["需要回显但必须截断的字段内容"; 40], + "assetName": "开场" + })) { + Ok(_) => panic!("prompt 不是字符串时必须被拒绝"), + Err(error) => error.to_user_msg(), + }; + assert!( + oversized_not_text.contains("prompt 必须是字符串"), + "{oversized_not_text}" + ); + assert!( + oversized_not_text.contains('…'), + "超长回显必须带截断标记:{oversized_not_text}" + ); + assert!( + oversized_not_text.chars().count() < 200, + "超长回显必须被截断:{}", + oversized_not_text.chars().count() + ); + let brief_not_text = required_text(&json!({ "brief": 5 }), PrepareGameArtTextField::Brief) .expect_err("brief 不是字符串时必须被拒绝") .to_user_msg(); diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/tool/arguments.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/tool/arguments.rs index 961fee811..8b49b1f30 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/tool/arguments.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/tool/arguments.rs @@ -6,6 +6,13 @@ use serde_json::Value; use super::error::ToolPageRejection; +use crate::agent::runtime_state::truncate_agent_runtime_text; + +/// 字段给了非字符串值时,回显进诊断的那个值最多保留多少字符。 +/// +/// 类型错只需要让模型看出「传进来的是什么」,不必把整个对象或数组原样带进工具结果与 +/// 诊断 sidecar;`got` 会同时出现在 `to_user_msg` 和序列化后的 `error` 里。 +const NOT_TEXT_GOT_MAX_CHARS: usize = 120; /// 一个会被边界校验的文本字段。 pub(crate) trait ToolTextField: Copy { @@ -35,7 +42,10 @@ pub(crate) fn optional_text, E>( return Ok(None); }; let Some(value) = value.as_str() else { - return Err(field.not_text(value.to_string())); + return Err(field.not_text(truncate_agent_runtime_text( + &value.to_string(), + NOT_TEXT_GOT_MAX_CHARS, + ))); }; let value = value.trim(); if value.is_empty() {