news 2026/10/8 22:59:19

OpenAI协议02、AgentForge 适配OpenAI接入核心实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OpenAI协议02、AgentForge 适配OpenAI接入核心实践

前言

在进入正文之前,先交代一下这些文章的来龙去脉。

AgentForge是一个面向 Java 开发者、从LLM 最底层能力开始构建的开源 Agent 框架。它不从高度封装的 Agent API 起步,而是先建立稳定、统一、可扩展的模型抽象,再逐层向上锻造 Tool、Memory、Middleware、Reasoning 与 Agent Runtime 等能力。

AgentForge = Agent + Forge:Agent代表能理解目标、进行推理、调用工具并完成任务的智能体,Forge则强调把原始智能持续加工、塑形、强化,最终锻造成真正可用的产品。

本系列《AgentForge 核心模块设计原理》沿着这条自底向上的路径,逐个模块拆解它的设计原理与实现细节。本文聚焦AgentForge 适配 OpenAI 的接入实践(统一类型与 Chat Completions 的双向映射),对应模块agentforge-model-openai。

  • 开源仓库(GitHub):https://github.com/changluya/AgentForge
  • 项目文档站:https://changluya.github.io/AgentForge/
  • Gitee 镜像:https://gitee.com/changluJava/agent-forge
  • 开源协议:MIT
gitclone https://github.com/changluya/AgentForge.gitcdAgentForge mvn cleaninstall-DskipTests

如果这套「自底向上」的设计对你有帮助,欢迎到 GitHub 给 AgentForge 点一个 Star。


一、背景与问题引入

1.1、场景驱动:一套 Core,适配多家模型

AgentForge 的 Agent Runtime 只依赖ChatModel/StreamingChatModel接口。我们希望"换模型"只换一个 Provider 依赖,而不是改 Agent 代码。

于是 OpenAI Provider 的职责被限定为协议翻译层:

Agent Runtime │ ChatRequest / ChatResponse(Provider 无关) ▼ OpenAiChatModel / OpenAiStreamingChatModel │ Chat Completions wire ▼ OpenAI / OpenAI-compatible 服务

1.2、问题引导:统一类型如何落到 OpenAI wire?

问题:AgentForge 的ChatMessage、ChatRequestParameters、ChatResponse分别怎么落到 OpenAI 的messages/ 请求字段 / 响应字段?工具调用如何双向映射?流式如何聚合?

本文逐项给出映射表与实现位置。


1.3、实现边界

  • Wire API:POST {baseUrl}/chat/completions(默认https://api.openai.com/v1);
  • release_1.x 以 Chat Completions 为第一版统一协议,Responses API 采用"并存 Adapter"策略(见第六章)。

1.4、Builder 参数

Builder 参数默认值说明
baseUrlhttps://api.openai.com/v1也支持 OpenAI-compatible 服务
apiKeynull非空时发送Authorization: Bearer ...
modelNamenull请求前必须有值
temperaturenull非空才发送
maxTokensnull映射max_tokens
topPnull映射top_p
stopSequencesnull映射stop
customParameter空透传 Provider 扩展顶层字段
customHeader空追加自定义 HTTP Header
httpTransportJdkHttpTransportHTTP SPI
connectTimeoutMillis10000连接超时
readTimeoutMillis60000读取超时

重点:OpenAiStreamingChatModel复用同一套配置,并强制写入stream=true与stream_options.include_usage=true。


二、核心概念

2.1、统一入参

ChatRequest{List<ChatMessage>messages;ChatRequestParametersparameters;}ChatRequestParameters{StringmodelName();Doubletemperature();IntegermaxTokens();DoubletopP();List<String>stopSequences();Map<String,Object>customParameters();}

2.2、统一出参

ChatResponse{AiMessageaiMessage;// text + toolExecutionRequestsTokenUsagetokenUsage;FinishReasonfinishReason;Map<String,Object>metadata;}

重点:Provider 的职责就是把 wire 字段无损地落到这两个统一对象上。


三、实现思路与映射

3.1、HTTP Headers

Content-Type: application/json Accept: application/json Authorization: Bearer ${apiKey} # apiKey 非空时

之后追加customHeaders;企业内部网关可用customHeader(...)增加租户、路由、trace 等。


3.2、消息映射

AgentForgeOpenAI rolecontent / 关键字段
SystemMessagesystemmessage.text()
UserMessage(单文本)usermessage.text()
UserMessage(多Content)usercontent[],逐条TextContent→{"type":"text","text":...}
AiMessage(纯文本)assistantmessage.text()
AiMessage(含工具调用)assistantcontent(可为null)+tool_calls[]
ToolExecutionResultMessagetooltool_call_id = message.id(),content = message.text()

工具调用 + 结果回填的 wire 形态:

{"messages":[{"role":"assistant","content":null,"tool_calls":[{"id":"call_1","type":"function","function":{"name":"getWeather","arguments":"{\"city\":\"hangzhou\"}"}}]},{"role":"tool","tool_call_id":"call_1","content":"{\"temperature\":22}"}]}

注意:CustomMessage当前未实现 wire mapping,遇到会抛IllegalArgumentException。


3.3、参数映射

AgentForge 参数Chat Completions 字段当前行为
modelNamemodel必填;为空本地失败
temperaturetemperature非空才发送
maxTokensmax_tokens非空才发送
topPtop_p非空才发送
stopSequencesstop非空才发送
toolstools[]{"type":"function","function":{name,description,parameters,strict}}
toolChoicetool_choiceAUTO→"auto"、NONE→"none"、REQUIRED→"required"、SPECIFIC→{"type":"function","function":{"name":X}}
customParameters顶层原样写入通用字段写入后覆盖同名 custom 字段

构建顺序(标准字段拥有最终优先级):

1. payload.putAll(customParameters) 2. 写入 model / messages 3. 写入 temperature / max_tokens / top_p / stop 4. 写入 tools / tool_choice

重点:即使customParameters写了另一个model,最终仍会被modelName覆盖。


3.4、响应映射

OpenAI 字段AgentForge 字段
choices[0].message.contentChatResponse.aiMessage().text()
choices[0].message.tool_calls[]ChatResponse.aiMessage().toolExecutionRequests()
usage.prompt_tokensTokenUsage.inputTokens()
usage.completion_tokensTokenUsage.outputTokens()
usage.total_tokensTokenUsage.totalTokens()
choices[0].finish_reasonChatResponse.finishReason()
id/model/createdmetadata["id"] / ["model"] / ["created"]

tool_calls[]映射:id → ToolExecutionRequest.id、function.name → name、function.arguments → arguments(保留原始 JSON 字符串,不重新格式化)。

注意:当tool_calls非空且content为空时,AiMessage.text()返回null;两者同时存在时,两者都会保留。


3.5、finish_reason 映射

OpenAIAgentForgeFinishReason
stopSTOP
lengthLENGTH
tool_callsTOOL_EXECUTION
function_callTOOL_EXECUTION
content_filterCONTENT_FILTER
其他非空OTHER
nullnull

3.6、响应结构异常

以下情况直接抛ModelException(不静默返回空):

choices 不存在 / 为空 / choices[0].message 不存在

3.7、流式实现

请求:同 Endpoint,强制stream=true+stream_options.include_usage=true。

SSE 解析:忽略空行、:注释、非data:行;每个data:chunk 读取choices[0].delta.content、delta.tool_calls[]、finish_reason。

  • delta.content→ 立即handler.onPartialResponse(...),同时本地StringBuilder聚合;
  • delta.tool_calls[]→按index分桶累加(规则见协议篇 4.1),不回调半成品;
  • 结束 →handler.onCompleteResponse(response)。

最终响应:

ChatResponse.builder().aiMessage(AiMessage.from(fullText,toolExecutionRequests)).finishReason(finishReason).tokenUsage(tokenUsage).metadata(metadata).build();

重点:最终 usage chunk(choices=[])由"先读 usage、再判断 choices 是否为空"处理;流中断时最终 usage 可能缺失,ChatResponse.tokenUsage()不保证一定存在。


四、实战代码

4.1、非流式

OpenAiChatModelmodel=OpenAiChatModel.builder().baseUrl("https://api.openai.com/v1").apiKey(System.getenv("OPENAI_API_KEY")).modelName("your-model").temperature(0.2).maxTokens(1024).build();ChatRequestrequest=ChatRequest.builder().message(SystemMessage.from("You are a concise Java assistant.")).message(UserMessage.from("What is CAS?")).build();ChatResponseresponse=model.chat(request);System.out.println(response.aiMessage().text());

运行输出(模拟终端)

$curl-shttps://api.openai.com/v1/chat/completions-H"Authorization: Bearer$OPENAI_API_KEY"\-d'{"model":"your-model","messages":[{"role":"system","content":"..."},{"role":"user","content":"What is CAS?"}]}'{"choices":[{"index":0,"message":{"role":"assistant","content":"CAS means Compare-And-Swap."},"finish_reason":"stop"}],"usage":{"prompt_tokens":20,"completion_tokens":10,"total_tokens":30}}

4.2、流式

OpenAiStreamingChatModelstreaming=OpenAiStreamingChatModel.builder().baseUrl("https://api.openai.com/v1").apiKey(System.getenv("OPENAI_API_KEY")).modelName("your-model").build();streaming.chat(request,newStreamingChatResponseHandler(){@OverridepublicvoidonPartialResponse(Stringpartial){System.out.print(partial);}@OverridepublicvoidonCompleteResponse(ChatResponseresponse){System.out.println();}@OverridepublicvoidonError(Throwableerror){error.printStackTrace();}});

运行输出(模拟终端)

CAS means Compare-And-Swap.

4.3、OpenAI-compatible 服务

OpenAiChatModelmodel=OpenAiChatModel.builder().baseUrl("https://example.com/v1").apiKey("...").modelName("provider-model").build();

五、兼容性、错误与边界

5.1、HTTP 错误

非 2xx →new ModelException("OpenAI request failed with HTTP " + statusCode, statusCode, responseBody);网络层 IOException →ModelException("OpenAI request failed", cause)。流式非 2xx 会累积 body 并通过handler.onError(...)返回。


5.2、协议差距(release_1.x 未映射)

developerrole、图片 / 音频多模态content、流式onPartialToolCall、refusal、logprobs、structured outputs、audio、provider reasoning(AiMessage.thinking()字段位已留未接线)、多choices。

customParameters可临时透传请求字段;但若返回结构需要框架理解,仍须正式扩展 Core / Provider 类型。


六、总结与演进

已落地:toolrole 的tool_call_id请求映射、tools/tool_choice(含SPECIFIC)、tool_calls非流式解析与流式按index聚合、UserMessage多Content→content[]。

OpenAI 官方建议新应用优先 Responses API,因此后续采用并存 Adapter:OpenAiChatModel → /chat/completions、OpenAiStreamingChatModel → /chat/completions + SSE,未来新增OpenAiResponsesModel → /responses。上层仍只依赖 Core 接口,协议迁移不侵入 Agent Runtime。


参考资料

[1]. OpenAI Chat Completions API(官方参考)

[2]. OpenAI 文本生成指南

[3]. 相关内部文档:OpenAI 底层协议快速理解、ChatModel 核心协议层设计


整理者:长路 创建时间:2026.10.5 更新时间:2026.10.5

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/8 22:56:28

前端面试题:让 AI 生成组件,怎么保证不重复造轮子?

一、核心回答 核心就是让 AI 生成前先查&#xff0c;能复用就别新建&#xff1b;如果确实要新建&#xff0c;生成后把它纳入组件库&#xff0c;再人工确认一次。 这句话就够作为第一层答案。二、为什么“让 AI 先查组件”还不够&#xff1f; 因为真正的问题不是&#xff1a; 有…

作者头像 李华
网站建设 2026/10/8 22:54:14

计算机毕业设计选题推荐:基于大数据的全球空气污染数据可视化分析|毕业设计选题|计算机毕设|选题推荐|毕设指导|项目定制|源码|高质量项目

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、PHP、.NET、Node.js、GO、微信小程序、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇…

作者头像 李华
网站建设 2026/10/8 22:54:13

EG2131D 220V 单路半桥栅极驱动芯片|屹晶 EGmicro

一、产品整体概述EG2131D 为单通道 N‑MOS 半桥栅极驱动&#xff0c;SOP‑8 封装&#xff0c;无内置功率管&#xff0c;外接 N 沟 MOS/IGBT&#xff1b;高端 VB 悬浮耐压220V&#xff1b;VCC 供电11‑20V&#xff0c;典型 15V&#xff1b;图腾柱输出拉 1A、灌 1.5A&#xff1b;…

作者头像 李华