资讯动态

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

发布时间:2026/10/8 22:59:31 来源:尧图企业网站定制
前言在进入正文之前先交代一下这些文章的来龙去脉。AgentForge是一个面向 Java 开发者、从LLM 最底层能力开始构建的开源 Agent 框架。它不从高度封装的 Agent API 起步而是先建立稳定、统一、可扩展的模型抽象再逐层向上锻造 Tool、Memory、Middleware、Reasoning 与 Agent Runtime 等能力。AgentForge Agent ForgeAgent代表能理解目标、进行推理、调用工具并完成任务的智能体Forge则强调把原始智能持续加工、塑形、强化最终锻造成真正可用的产品。本系列《AgentForge 核心模块设计原理》沿着这条自底向上的路径逐个模块拆解它的设计原理与实现细节。本文聚焦AgentForge 适配 OpenAI 的接入实践统一类型与 Chat Completions 的双向映射对应模块agentforge-model-openai。开源仓库GitHubhttps://github.com/changluya/AgentForge项目文档站https://changluya.github.io/AgentForge/Gitee 镜像https://gitee.com/changluJava/agent-forge开源协议MITgitclone 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 / ChatResponseProvider 无关 ▼ OpenAiChatModel / OpenAiStreamingChatModel │ Chat Completions wire ▼ OpenAI / OpenAI-compatible 服务1.2、问题引导统一类型如何落到 OpenAI wire问题AgentForge 的ChatMessage、ChatRequestParameters、ChatResponse分别怎么落到 OpenAI 的messages/ 请求字段 / 响应字段工具调用如何双向映射流式如何聚合本文逐项给出映射表与实现位置。1.3、实现边界Wire APIPOST {baseUrl}/chat/completions默认https://api.openai.com/v1release_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_tokenstopPnull映射top_pstopSequencesnull映射stopcustomParameter空透传 Provider 扩展顶层字段customHeader空追加自定义 HTTP HeaderhttpTransportJdkHttpTransportHTTP SPIconnectTimeoutMillis10000连接超时readTimeoutMillis60000读取超时重点OpenAiStreamingChatModel复用同一套配置并强制写入streamtrue与stream_options.include_usagetrue。二、核心概念2.1、统一入参ChatRequest{ListChatMessagemessages;ChatRequestParametersparameters;}ChatRequestParameters{StringmodelName();Doubletemperature();IntegermaxTokens();DoubletopP();ListStringstopSequences();MapString,ObjectcustomParameters();}2.2、统一出参ChatResponse{AiMessageaiMessage;// text toolExecutionRequestsTokenUsagetokenUsage;FinishReasonfinishReason;MapString,Objectmetadata;}重点Provider 的职责就是把 wire 字段无损地落到这两个统一对象上。三、实现思路与映射3.1、HTTP HeadersContent-Type: application/json Accept: application/json Authorization: Bearer ${apiKey} # apiKey 非空时之后追加customHeaders企业内部网关可用customHeader(...)增加租户、路由、trace 等。3.2、消息映射AgentForgeOpenAI rolecontent / 关键字段SystemMessagesystemmessage.text()UserMessage单文本usermessage.text()UserMessage多Contentusercontent[]逐条TextContent→{type:text,text:...}AiMessage纯文本assistantmessage.text()AiMessage含工具调用assistantcontent可为nulltool_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 映射OpenAIAgentForgeFinishReasonstopSTOPlengthLENGTHtool_callsTOOL_EXECUTIONfunction_callTOOL_EXECUTIONcontent_filterCONTENT_FILTER其他非空OTHERnullnull3.6、响应结构异常以下情况直接抛ModelException不静默返回空choices 不存在 / 为空 / choices[0].message 不存在3.7、流式实现请求同 Endpoint强制streamtruestream_options.include_usagetrue。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 chunkchoices[]由先读 usage、再判断 choices 是否为空处理流中断时最终 usage 可能缺失ChatResponse.tokenUsage()不保证一定存在。四、实战代码4.1、非流式OpenAiChatModelmodelOpenAiChatModel.builder().baseUrl(https://api.openai.com/v1).apiKey(System.getenv(OPENAI_API_KEY)).modelName(your-model).temperature(0.2).maxTokens(1024).build();ChatRequestrequestChatRequest.builder().message(SystemMessage.from(You are a concise Java assistant.)).message(UserMessage.from(What is CAS?)).build();ChatResponseresponsemodel.chat(request);System.out.println(response.aiMessage().text());运行输出模拟终端$curl-shttps://api.openai.com/v1/chat/completions-HAuthorization: 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、流式OpenAiStreamingChatModelstreamingOpenAiStreamingChatModel.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 服务OpenAiChatModelmodelOpenAiChatModel.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 reasoningAiMessage.thinking()字段位已留未接线、多choices。customParameters可临时透传请求字段但若返回结构需要框架理解仍须正式扩展 Core / Provider 类型。六、总结与演进已落地toolrole 的tool_call_id请求映射、tools/tool_choice含SPECIFIC、tool_calls非流式解析与流式按index聚合、UserMessage多Content→content[]。OpenAI 官方建议新应用优先 Responses API因此后续采用并存 AdapterOpenAiChatModel → /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

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑