Skip to content

feat: feat-agentscope 分支兼容 OpenAI Responses API(含 tool calling)#545

Open
WinSalty wants to merge 2 commits into
spring-ai-alibaba:feat-agentscopefrom
WinSalty:feat/agentscope-responses-api
Open

feat: feat-agentscope 分支兼容 OpenAI Responses API(含 tool calling)#545
WinSalty wants to merge 2 commits into
spring-ai-alibaba:feat-agentscopefrom
WinSalty:feat/agentscope-responses-api

Conversation

@WinSalty

@WinSalty WinSalty commented Jun 11, 2026

Copy link
Copy Markdown

概述

#544 的 Responses API 协议兼容能力移植到 feat-agentscope 分支,并针对 AgentScope 链路的需求扩展了 tool calling 支持,使仅支持 POST /v1/responses 协议的模型也能驱动 AgentScope agent 的完整工具调用循环。

关联:#531(原始需求)、#544(main 分支的 Responses API 兼容实现)

改动内容

移植自 #544(cherry-pick)

  • ChatApiProtocol 枚举、ResponsesApi HTTP 客户端、ResponsesApiChatModel 适配层
  • DynamicModelFactorychatApiProtocol 配置分支创建 ChatModel
  • 数据层全链路(实体/DTO/Converter/Mapper/Service)与 MySQL/H2 schema 增加 chat_api_protocol 字段
  • 前端模型配置增加"接口协议"选项

本 PR 新增:tool calling 支持

AgentScope 链路(SpringAiAgentScopeModeldelegate.stream(prompt))依赖 Spring AI 的 tool calling 协议,本 PR 在适配层补齐双向映射:

请求侧

  • 识别 ToolCallingChatOptions,将 ToolCallbackToolDefinition(name/description/inputSchema)映射为 Responses API 的 tools 字段(扁平 function 结构,inputSchema JSON 字符串解析为对象)
  • 助手历史消息中的 AssistantMessage.toolCallsfunction_call 输入项
  • ToolResponseMessagefunction_call_output 输入项(靠 call_id 与调用配对,多轮 agent 循环回放历史依赖此映射)

响应侧

  • 非流式:从 output 中提取 type=function_call 项转为 AssistantMessage.ToolCall,存在工具调用时 finishReason 为 TOOL_CALLS(与 OpenAiChatModel 对齐)
  • 流式:function_call 项只在 completed 事件的完整 output 中出现一次,由终包携带 toolCalls(文本仍为空串,不破坏上层 FluxUtil 文本聚合);参数增量事件被忽略,AgentScope 桥接层对每个 chunk 读取 toolCalls,单次完整携带即可驱动工具执行

设计边界

  • 本实现不在框架内部执行工具(等价于 internalToolExecutionEnabled=false),工具调用返回给调用方执行——与 AgentScope runtime 自管工具执行的设计一致
  • 非工具场景请求中不出现 tools 字段,纯文本链路行为与 feat: 兼容 OpenAI Responses API #544 完全一致
  • 未知 SSE 事件(含 response.function_call_arguments.delta)静默忽略,保证向前兼容

存量部署升级说明

已有 MySQL 部署需手动执行:

ALTER TABLE model_config
    ADD COLUMN chat_api_protocol varchar(32) DEFAULT 'CHAT_COMPLETIONS'
    COMMENT 'Chat模型接口协议:CHAT_COMPLETIONS(默认)、RESPONSES(OpenAI Responses API格式)。仅model_type=CHAT时生效';

测试方案

  • 单元测试 24 个(ResponsesApiTest 10 个 + ResponsesApiChatModelTest 14 个),新增覆盖:tools 字段映射、工具历史 function_call/function_call_output 映射、响应 toolCalls 提取、流式终包携带 toolCalls、function_call SSE 事件反序列化
  • 模块全部 50 个测试通过(0 failures, 0 errors)
  • spring-javaformat:validate + checkstyle:check 通过
  • 端到端验证(Qwen DashScope /v1/responses,模型 qwen-plus,见下)

端到端测试结果

场景 结果
非流式纯文本 call() ✅ 文本/finishReason/usage 正确
流式纯文本 stream() ✅ delta 逐块、终包空文本+usage、聚合无重复
非流式工具调用(声明 tools → 模型发起 function_call ✅ call_id/name/arguments 正确提取,finishReason=TOOL_CALLS
流式工具调用 ✅ 终包携带完整 toolCalls + 空文本 + usage + TOOL_CALLS
工具结果回传(function_call_output 历史回放) ⚠️ 被 DashScope 服务端 400 拒绝(见下)

关于回传步骤的说明:用标准 OpenAI Responses API 格式的最小 curl 请求可复现同样错误——DashScope 兼容层内部将 function_call_output 转为 role='tool' 消息后,又被其自身校验器拒绝(只接受 user/assistant/system/function)。这是 DashScope /v1/responses 兼容实现的服务端限制,并非本适配层问题;本 PR 的请求格式符合 OpenAI 规范,在规范实现的服务端(如 OpenAI 官方)上完整工具循环可用。错误示例:

<400> InternalError.Algo.InvalidParameter: ... Value error, tool must be one of user,assistant,system,function [input_value='tool']

🤖 Generated with Claude Code

WinSalty and others added 2 commits June 11, 2026 14:14
新增 Responses API 适配层(ResponsesApi + ResponsesApiChatModel),
实现 ChatModel 接口使上层服务无感切换;前端模型配置增加接口协议选项。

代码审核修复:
- 流式终包改为空文本,避免上层 FluxUtil 聚合时全文重复
- SSE 解析捕获 IllegalArgumentException,畸形事件不再中断流
- 运行时 options 改用 ChatOptions 接口取值,兼容任意实现
- 同步调用接入统一重试模板,与 OpenAiChatModel 容错对齐
- 补充 18 个单元测试覆盖协议映射与事件解析

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
请求侧将 ToolCallingChatOptions 的工具声明映射为 tools 字段,
助手历史工具调用与执行结果映射为 function_call / function_call_output;
响应侧从 output 中提取 function_call 转为 AssistantMessage.toolCalls,
流式场景由终包携带(文本仍为空串,不影响上层聚合)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@WinSalty WinSalty force-pushed the feat/agentscope-responses-api branch from 40db5a7 to 4a65e50 Compare June 11, 2026 11:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant