fix(agent): unbreak DashScope tool calls, sharpen error class, rebalance plan triage (refs #246)

This commit is contained in:
matevip 2026-06-03 08:38:48 +08:00
parent 18f3675fd3
commit 9f02a0a221
8 changed files with 63 additions and 29 deletions

View File

@ -718,7 +718,7 @@ public class AgentBindingService implements AgentBindingResolver {
// refuses ("Tool not found: search"). Observed 2026-05-01 on the
// Code Reviewer agent the model called search got
// not-found gave up before ever reaching renderDocx.
"search",
"web_search",
"browser_use",
"read_file",
"send_file",

View File

@ -412,18 +412,22 @@ public class NodeStreamingChatHelper {
return ErrorType.BILLING;
}
// RFC-009 P3.2: MODEL_NOT_FOUND provider rejects the requested model id.
// Includes DashScope's "[InvalidParameter] url error, please check url"
// (https://help.aliyun.com/zh/model-studio/error-code#error-url) which despite
// the wording is the provider rejecting an unknown/unsupported model id on
// the native protocol. Splitting this out from CLIENT_ERROR lets us hand off
// to the fallback chain instead of terminating a different provider may
// recognize the model name (or have an equivalent default).
// DashScope signals an unknown/unsupported model id specifically as
// "[InvalidParameter] url error, please check url"
// (https://help.aliyun.com/zh/model-studio/error-code#error-url). Splitting this
// out from CLIENT_ERROR lets us hand off to the fallback chain instead of
// terminating a different provider may recognize the model name (or have an
// equivalent default).
//
// Note: we match on the specific "url error" wording rather than a bare
// "InvalidParameter", because DashScope reuses the InvalidParameter code for
// request-shape problems that have nothing to do with the model id (an illegal
// tool name, or an unsupported parameter) those are handled as CLIENT_ERROR
// below so a healthy model is not evicted from the failover pool.
if (msg.contains("Model not exist")
|| msg.contains("model_not_found")
|| msg.contains("Model not found")
|| msg.contains("does not exist")
|| msg.contains("[InvalidParameter]")
|| msg.contains("InvalidParameter")
|| msg.contains("url error")
// Volcano Ark: model exists but the user's account hasn't opened it,
// or the id isn't valid for this region. Both are hard failures
@ -432,9 +436,16 @@ public class NodeStreamingChatHelper {
|| msg.contains("InvalidEndpointOrModel")) {
return ErrorType.MODEL_NOT_FOUND;
}
// Client errors (400 Bad Request unsupported format, invalid params, etc.) NOT retryable
// Client errors (400 Bad Request unsupported format, invalid params, etc.) NOT retryable.
// DashScope's remaining "InvalidParameter" responses are request-shape bugs, e.g. a reserved
// or illegal tool name ("Tool names are not allowed to be [search]") or an unsupported
// parameter. These fail identically on every provider, so classifying them as CLIENT_ERROR
// (rather than MODEL_NOT_FOUND) keeps the model in the failover pool and surfaces the real
// cause instead of a misleading "model not available" message.
if (msg.contains("400") || msg.contains("Bad Request")
|| msg.contains("invalid_request_error") || msg.contains("unsupported")) {
|| msg.contains("invalid_request_error") || msg.contains("unsupported")
|| msg.contains("Tool names are not allowed")
|| msg.contains("InvalidParameter")) {
return ErrorType.CLIENT_ERROR;
}
// Server errors and transient TLS / socket-level network hiccups.

View File

@ -70,26 +70,30 @@ public class PlanGenerationNode implements NodeAction {
硬性规则
1. 只返回一个 JSON 对象不允许 markdown 代码块不允许任何 JSON 以外的文字
2. 不要解释不要寒暄不要说"我来...""我先..."
3. 不确定时优先选择"单步"而不是拆成多步
3. 判断依据是"目标是否由多个明显独立的子任务/交付物组成"而不是难度高低
单个连贯动作不要拆但目标确实分成多个部分时也不要硬压成一步
三类分流
(A) 直接回答 纯知识问答模型凭自身知识即可回答不需要任何工具不需要读文件不需要查询当前状态
(A) 直接回答 简单的纯知识问答凭自身知识用一两段话即可答完不需要任何工具不需要读文件
不需要查询当前状态且目标本身不包含多个需要分别完成的子任务
注意成段的分析对比方案规划教程等通常不属于此类应走 B C
输出{"needs_planning": false, "direct_answer": "<你的回答>"}
(B) 单步任务 需要工具本质是一个连贯动作一次文件读取 / 一次搜索 / 一次命令 / 一次记忆读写 / 一次计算
执行器会在这一步内部迭代调用多次工具**不要**提前拆分
(B) 单步任务 本质是一个连贯动作一次文件读取 / 一次搜索 / 一次命令 / 一次记忆读写 / 一次计算 /
一段集中产出执行器会在这一步内部迭代调用多次工具**不要**提前拆分
输出{"needs_planning": true, "steps": ["<将用户目标复述为一句清晰可执行的指令>"]}
(C) 多步任务 用户目标包含 2 个及以上明显独立必须先后完成的子任务例如"先调研 A 再调研 B 然后对比"
"读配置、迁移数据、验证结果"子任务之间如果可以合并应当合并
(C) 多步任务 用户目标包含 2 个及以上明显独立需要先后完成的子任务或交付物例如"先调研 A 再调研 B
然后对比""读配置迁移数据验证结果""分阶段制定计划""产出由若干独立部分组成的方案")。
这是规划型智能体的主路径当目标确实由多个部分组成时就走这里
输出{"needs_planning": true, "steps": ["步骤1", "步骤2", ...]}2 6 个步骤
关键原则
- 单工具调用绝对不拆成多步"读 A 文件并总结" 是单步B不是两步
- 默认不要把 MEMORY.md / PROFILE.md / 技能文件读取当成独立步骤仅当用户明确询问偏好历史决策或长期约束时才加入
- 每个步骤必须是可执行动作不写"思考一下""确认一下"之类的空话
- 解析不出来时视作(B) 单步宁愿单步也不要无脑拆分
- 多部分多阶段需要逐步推进的目标走(C)真正单一原子动作走(B)只有简单一问一答才用(A)
""";
public PlanGenerationNode(ChatModel chatModel, PlanningService planningService,

View File

@ -21,7 +21,12 @@ public class WebSearchTool {
private final WebSearchService webSearchService;
@Tool(description = "Search the internet for latest information. Use when querying real-time news, latest data, or uncertain facts. "
// Tool name is pinned to "web_search" rather than the method-derived "search":
// DashScope's native protocol reserves the function name "search" and rejects the
// whole request with "InvalidParameter: Tool names are not allowed to be [search]",
// which breaks tool use for every qwen/DashScope-native model that has this tool bound.
@Tool(name = "web_search",
description = "Search the internet for latest information. Use when querying real-time news, latest data, or uncertain facts. "
+ "Supports optional freshness, language, count parameters.")
public String search(
@ToolParam(description = "Search keywords") String query,

View File

@ -0,0 +1,7 @@
-- Rename the built-in web-search tool from "search" to "web_search".
-- DashScope's native protocol reserves the function name "search" and rejects any
-- request that declares a tool with that name ("InvalidParameter: Tool names are not
-- allowed to be [search]"), which broke tool use for every qwen/DashScope-native model
-- that had this tool bound. Migrate existing agent bindings to the new name so they
-- keep resolving after the tool was renamed in code. Idempotent.
UPDATE mate_agent_tool SET tool_name = 'web_search' WHERE tool_name = 'search';

View File

@ -0,0 +1,7 @@
-- Rename the built-in web-search tool from "search" to "web_search".
-- DashScope's native protocol reserves the function name "search" and rejects any
-- request that declares a tool with that name ("InvalidParameter: Tool names are not
-- allowed to be [search]"), which broke tool use for every qwen/DashScope-native model
-- that had this tool bound. Migrate existing agent bindings to the new name so they
-- keep resolving after the tool was renamed in code. Idempotent.
UPDATE mate_agent_tool SET tool_name = 'web_search' WHERE tool_name = 'search';

View File

@ -37,11 +37,11 @@ tool.execute_shell_command.desc=\u5728\u672c\u5730\u670d\u52a1\u5668\u4e0a\u6267
tool.execute_shell_command.param.command=\u8981\u6267\u884c\u7684 Shell \u547d\u4ee4
tool.execute_shell_command.param.timeoutSeconds=\u8d85\u65f6\u79d2\u6570\uff0c\u9ed8\u8ba4 60 \u79d2
tool.search.desc=\u5728\u4e92\u8054\u7f51\u4e0a\u641c\u7d22\u6700\u65b0\u4fe1\u606f\u3002\u5f53\u9700\u8981\u67e5\u8be2\u5b9e\u65f6\u65b0\u95fb\u3001\u6700\u65b0\u6570\u636e\u6216\u4e0d\u786e\u5b9a\u7684\u4e8b\u5b9e\u65f6\u4f7f\u7528\u6b64\u5de5\u5177\u3002\u652f\u6301 freshness\u3001language\u3001count \u53ef\u9009\u53c2\u6570\u3002
tool.search.param.query=\u641c\u7d22\u5173\u952e\u8bcd
tool.search.param.freshness=\u65f6\u95f4\u8303\u56f4\u8fc7\u6ee4: day (\u4eca\u5929), week (\u672c\u5468), month (\u672c\u6708), year (\u4eca\u5e74)
tool.search.param.language=\u8bed\u8a00\u504f\u597d: zh-CN (\u4e2d\u6587), en (\u82f1\u6587)
tool.search.param.count=\u6700\u5927\u7ed3\u679c\u6570\u91cf: 1-10, \u9ed8\u8ba4 5
tool.web_search.desc=\u5728\u4e92\u8054\u7f51\u4e0a\u641c\u7d22\u6700\u65b0\u4fe1\u606f\u3002\u5f53\u9700\u8981\u67e5\u8be2\u5b9e\u65f6\u65b0\u95fb\u3001\u6700\u65b0\u6570\u636e\u6216\u4e0d\u786e\u5b9a\u7684\u4e8b\u5b9e\u65f6\u4f7f\u7528\u6b64\u5de5\u5177\u3002\u652f\u6301 freshness\u3001language\u3001count \u53ef\u9009\u53c2\u6570\u3002
tool.web_search.param.query=\u641c\u7d22\u5173\u952e\u8bcd
tool.web_search.param.freshness=\u65f6\u95f4\u8303\u56f4\u8fc7\u6ee4: day (\u4eca\u5929), week (\u672c\u5468), month (\u672c\u6708), year (\u4eca\u5e74)
tool.web_search.param.language=\u8bed\u8a00\u504f\u597d: zh-CN (\u4e2d\u6587), en (\u82f1\u6587)
tool.web_search.param.count=\u6700\u5927\u7ed3\u679c\u6570\u91cf: 1-10, \u9ed8\u8ba4 5
tool.create_cron_job.desc=\u521b\u5efa\u5b9a\u65f6\u4efb\u52a1\u3002\u4efb\u52a1\u5c06\u5728\u6307\u5b9a\u65f6\u95f4\u81ea\u52a8\u8fd0\u884c\u5e76\u5411\u5f53\u524d Agent \u53d1\u9001\u89e6\u53d1\u6d88\u606f\u3002\u4f7f\u7528 5 \u5b57\u6bb5 cron \u8868\u8fbe\u5f0f\uff1a\u5206 \u65f6 \u65e5 \u6708 \u5468\u3002
tool.list_cron_jobs.desc=\u5217\u51fa\u6240\u6709\u5b9a\u65f6\u4efb\u52a1\uff0c\u5305\u542b\u540d\u79f0\u3001cron \u8868\u8fbe\u5f0f\u3001\u4e0b\u6b21\u8fd0\u884c\u65f6\u95f4\u3001\u542f\u7528\u72b6\u6001\u3002

View File

@ -37,11 +37,11 @@ tool.execute_shell_command.desc=Execute a shell command on the local server. For
tool.execute_shell_command.param.command=Shell command to execute
tool.execute_shell_command.param.timeoutSeconds=Timeout in seconds, default 60
tool.search.desc=Search the internet for latest information. Use when querying real-time news, latest data, or uncertain facts. Supports optional freshness, language, count parameters.
tool.search.param.query=Search keywords
tool.search.param.freshness=Time range filter: day (today), week (this week), month (this month), year (this year)
tool.search.param.language=Language preference: zh-CN (Chinese), en (English)
tool.search.param.count=Max results: 1-10, default 5
tool.web_search.desc=Search the internet for latest information. Use when querying real-time news, latest data, or uncertain facts. Supports optional freshness, language, count parameters.
tool.web_search.param.query=Search keywords
tool.web_search.param.freshness=Time range filter: day (today), week (this week), month (this month), year (this year)
tool.web_search.param.language=Language preference: zh-CN (Chinese), en (English)
tool.web_search.param.count=Max results: 1-10, default 5
tool.create_cron_job.desc=Create a scheduled task (cron job). Runs automatically at specified time and sends trigger message to current agent. Use 5-field cron: minute hour day month weekday.
tool.list_cron_jobs.desc=List all scheduled tasks with name, cron expression, next run time, and enabled status.