mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 03:13:41 +08:00
fix(agent): unbreak DashScope tool calls, sharpen error class, rebalance plan triage (refs #246)
This commit is contained in:
parent
18f3675fd3
commit
9f02a0a221
@ -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",
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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';
|
||||
@ -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';
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user