[Docs] quick-start 示例照抄即复现 400:工具 inputType 使用 String.class

Author: z1258Created Sep 12, 2026Updated Sep 13, 2026
Labelskind/bugneeds-triage

Is there an existing issue for this?

  • I have searched the existing issues

Current Behavior

照抄官方 quick-start 的「构建一个基础 Agent」小节(FunctionToolCallback 使用 .inputType(String.class)),第二轮请求返回 400,工具调用链路中断。

第一轮请求正常;第二轮(回传模型返回的 tool_call 时)失败:

HTTP 400 - {"request_id":"330f8d4d-77f5-9671-86ba-a1b223fbb175","code":"InvalidParameter",
"message":"<400> InternalError.Algo.InvalidParameter: The \"function.arguments\" parameter of the code model must be in JSON format."}

同一页「构建一个真实的 Agent」步骤 2 的两个工具(WeatherForLocationTool / UserLocationTool)写法相同,同样会失败。

Expected Behavior

正常返回天气结果,且 get_weather 被真实调用(ReAct 循环能正常走完两轮)。

Steps To Reproduce

  1. 打开 https://java2ai.com/docs/quick-start 的「构建一个基础 Agent」小节,原样复制该小节代码;
  2. 执行 agent.call("what is the weather in San Francisco")
  3. 第二轮请求返回上述 400。

补充:以下最小验证不需要 API Key、不联网,可直接确认根因(约 30 秒):

java
ToolCallback tool = FunctionToolCallback.builder("get_weather", new WeatherTool())
        .description("Get weather for a given city")
        .inputType(String.class)
        .build();

System.out.println(tool.getToolDefinition().inputSchema());
// 输出: { "type" : "string", "additionalProperties" : false }

inputType 换成 record 类型后,输出为 "type" : "object",且 400 消失。

Environment

markdown
Spring AI Alibaba version(s): 1.1.2.0

| 项                                  | 值                                                          |
| ----------------------------------- | ----------------------------------------------------------- |
| JDK                                 | 21.0.10                                                     |
| Spring Boot                         | 3.5.5                                                       |
| spring-ai-alibaba-agent-framework   | 1.1.2.0                                                     |
| spring-ai-alibaba-starter-dashscope | 1.1.2.0                                                     |
| 模型                                | 默认(未显式指定 model)                                    |
| 文档页 / 源码                       | https://java2ai.com/docs/quick-start ・ docs/quick-start.md |

Debug logs

失败时的日志(第二轮):

WARN  o.s.a.r.a.SpringAiRetryAutoConfiguration : Retry error. Retry count: 1, Exception: HTTP 400 -
{"request_id":"330f8d4d-77f5-9671-86ba-a1b223fbb175","code":"InvalidParameter",
"message":"<400> InternalError.Algo.InvalidParameter: The \"function.arguments\" parameter of the code model must be in JSON format."}

org.springframework.ai.retry.NonTransientAiException: HTTP 400 - {"request_id":"330f8d4d-77f5-9671-86ba-a1b223fbb175", ...}

改为 inputType(WeatherRequest.class) 后,工具被真实调用:

DEBUG o.s.a.t.function.FunctionToolCallback : Starting execution of tool: get_weather
DEBUG o.s.a.t.function.FunctionToolCallback : Successful execution of tool: get_weather
DEBUG o.s.a.t.e.DefaultToolCallResultConverter : Converting tool result to JSON.

Anything else?

根因

FunctionToolCallback.Builder.build() 只依据 inputType 生成 schema,不读取 apply() 的方法签名

java
.inputSchema(StringUtils.hasText(this.inputSchema) ? this.inputSchema
        : JsonSchemaGenerator.generateForType(this.inputType))   // ← 只看 inputType

因此两种写法的产出完全不同(以下均在本机通过 toolCallback.getToolDefinition().inputSchema() 实测打印):

inputType(String.class)          → { "type" : "string",  "additionalProperties" : false }

inputType(WeatherRequest.class)  → { "type" : "object",
                                     "properties" : { "city" : { "type" : "string", ... } },
                                     "required" : [ "city" ], "additionalProperties" : false }

DashScope 要求 function 的 parameters 为 object。schema 为 string 时模型无法按对象结构产参, 回传时被服务端判定为非法 JSON —— 即上面这个 400。

一点背景(供参考):方法式工具(@Tool 注解的方法 / MethodToolCallback)走的是 JsonSchemaGenerator.generateForMethodInput(),它始终先写 "type": "object" 再填 properties, 所以「只有一个 String 参数就直接传 String.class」在方法式工具上不会出问题。

建议修复

java
// 工具的入参必须是一个 JSON 对象(record / POJO),不能是 String
public record WeatherRequest(
    @ToolParam(description = "The city name") String city) {
}

// 定义天气查询工具
public class WeatherTool implements BiFunction<WeatherRequest, ToolContext, String> {
    @Override
    public String apply(WeatherRequest request, ToolContext toolContext) {
        return "It's always sunny in " + request.city() + "!";
    }
}

ToolCallback weatherTool = FunctionToolCallback.builder("get_weather", new WeatherTool())
    .description("Get weather for a given city")
    .inputType(WeatherRequest.class)
    .build();

注意:不能只把 inputSchema 手写成 object 而保留 inputType(String.class)。框架会用 inputType 反序列化模型回传的 arguments:

java
I request = JsonParser.fromJson(toolInput, this.toolInputType);

两者必须一致,否则只是把问题挪到另一个地方。

同一页面的其它问题(次要,可随 PR 一并处理)

  1. 「构建一个真实的 Agent」步骤 2 的两个工具是同一个 bugWeatherForLocationToolUserLocationTool 都是 BiFunction<String, ToolContext, String> + .inputType(String.class)
  2. @ToolParam 目前放在不生效的位置:它写在 apply() 的方法参数上,而 schema 完全由 inputType 决定,这个 description 不会进入发给模型的 schema(实测:inputType(String.class) 生成的 schema 里没有任何 description 字段)。若改用 record,应写在 record 组件上。
  3. SYSTEM_PROMPT 中的工具名与实际注册名不一致:提示词里是 get_weather_for_location / get_user_location,实际注册的是 getWeatherForLocation / getUserLocation
  4. 依赖版本在同一页面内不一致:「添加依赖」小节是 1.1.2.0,步骤 3 是 1.1.2.1。 (经核对,1.1.2.1 是 Maven 中央仓库中的有效版本,统一即可。)
  5. HumanInTheLoopHook 示例中的工具名 .approvalOn("getWeatherTool", ...) 在本文档中不存在 (文档注册的是 getWeatherForLocation / getUserLocation)。若该参数接受的是 Bean 名而非工具名, 请忽略此条。
  6. 文案:步骤 1 列表中的 System Prom 应为 System Prompt

相关 issue / PR(同一根因,供参考)

  • #4942(PR,open):fix: use object input schema for ls and glob filesystem tools —— 框架自身也在把 ListFilesTool / GlobToolinputType(String.class) 改为对象类型, PR 描述里明确写到「inputType(String.class) produces a top-level JSON schema with type: "string", providers ... returning HTTP 400」,并说明这与 ReadFileTool / WriteFileTool / EditFileTool / GrepTool 的既有写法保持一致。也就是说,框架内部已经认为这种写法不正确,但文档示例仍在教它
  • #4084(closed):同样照抄 quick-start 的示例代码,报错为 Conversion from JSON to java.lang.String failedFunctionToolCallback.callJsonParser.fromJson), 评论区已有同学给出与本文一致的修复方向(改用 Request DTO)。
  • #4565(open):同一个 400 报错,定位到 ListFilesTool.inputType(String.class), 同样通过包一层 record 解决。
  • #4533(closed):同一个 400 报错,示例写法同为 .inputType(String.class)ChatModel + withToolCallbacks 路径)。

这也正是我在文档层面单独提这个 issue 的原因:代码侧已经在修,而文档示例会持续产生同样的报告。

补充

如需要,我可以按上述内容向 spring-ai-alibaba/website 提交一个 PR(改动集中在 docs/quick-start.md)。

如果维护者希望保持「基础 Agent」示例的极简风格,也可以只加一句说明、不改结构:

注意:FunctionToolCallbackinputType 必须是 JSON 对象类型(record / POJO), 不能传 String.class,否则 DashScope 会返回 400。

Source: alibaba/spring-ai-alibaba