接入OpenAI无法发送请求,响应为空?Bug: C++ 接入 OpenAI 中转 API

一、Bug 起因

项目使用C++ 封装 OpenAI 中转 API(ofox.ai)实现大模型调用,核心功能为ChatGPTProvider::sendMessage接口,用于发送对话请求并解析返回结果。测试阶段接口始终调用失败,返回空结果,测试用例全部不通过,初步怀疑是代理配置、网络、API 密钥问题。

于是,我在全网发狠似的配置代理服务器,让云服务器走本地代理。好家伙,后面发现的买的ofoxai中转不需要代理。可是根据官方给我的文档和协议,我都按照其写的,我也是真找不到自己的代码哪里错了
中转API官网查到的base_url

二、Bug 排查经过

  1. 错误方向排查
    • 反复检查代理配置:确认本地代理地址、端口无误,注释代码中代理设置后问题仍存在,排除代理干扰。后面又关闭代理,在apifox知道不需要代理
    • 核对API 密钥与请求头 :确认Authorization请求头格式正确,环境变量OFOX_APIKEY读取正常,排除密钥与鉴权问题。
    • 检查依赖与编译配置 :确认cpp-httplib开启OpenSSL支持、jsoncpp解析正常,网络超时设置合理,排除编译与依赖问题。

2. 关键问题定位

  1. 对比接口文档与请求代码,发现接口地址配置错误 :初始化时endpoint错误填写为https://api.ofox.ai/v1,而/v1属于接口路径,并非域名一部分。

  2. 请求路径错误:原代码Post("/responses")缺少/v1前缀,正确路径应为/v1/responses。

所以我只能说这张图,不亚于诈骗,其他高级语言怎么接入我不知道。C++的endpoint里是不能有/v1。之所以会对其深信不疑,是因为在apifox测试接口时,就是如此填写的,通过了我也就不以为意了
测试通过

三、Bug 修复结果

  1. 修正接口地址 :初始化endpoint改为https://api.ofox.ai
  2. 修正请求路径Post请求路径改为/v1/responses

四、经验总结

  1. 接口配置严谨性 :第三方 API 接入时,严格区分域名(endpoint)接口路径 ,不随意拼接后缀,避免低级格式错误。(就算官方文档如此写的,我也应该保持怀疑测试一下
  2. 排查逻辑优化 :网络请求类 Bug 优先核对地址、路径、参数、请求头 四大核心要素,再排查代理、网络、依赖等问题,提升排查效率。
  3. 文档可信度:第三方中转 API 文档可能存在疏漏,需结合实际请求返回结果验证,不盲目照搬文档示例。
  4. 日志辅助排查:完善请求地址、参数、响应状态与返回体的日志打印,快速定位请求全流程问题。

本次的代码:(展示代码只是为了让bug实在化,而非空中楼阁

//////////////////////////////////////////////////////testLLM.cpp://////////////////////////////////////////

cpp 复制代码
//op1 :包含头文件
#include <gtest/gtest.h>
#include "../sdk/include/DeepseekProvider.h"
#include <memory>
#include <vector>
#include "../sdk/include/util/myLog.h"
#include "../sdk/include/ChatGPTProvider.h"
TEST(testChatGPT,sendMessage)
{
    //op1:构建 
    auto gptProvider = std::make_shared<ai_model_access_tech::ChatGPTProvider>();
    ASSERT_TRUE(gptProvider.get() != nullptr);
    //op2:初始化
    std::map<std::string,std::string> initModelParams;
    initModelParams.emplace("apiKey",std::getenv("OFOX_APIKEY"));
    initModelParams.emplace("endpoint","https://api.ofox.ai");//////////////太坑了/////////
    auto ret = gptProvider->initModel(initModelParams);
    ASSERT_TRUE(gptProvider->isAvailable());
    //op3:发送消息
    std::vector<ai_model_access_tech::Message> messages;
    messages.push_back({"user","你是谁?"});

    std::map<std::string, std::string> messageParams;
    messageParams.emplace("max_output_tokens","50");
    messageParams.emplace("temperature","0.7");

    std::string ret_s = gptProvider->sendMessage(messages,messageParams);
    ASSERT_FALSE(ret_s.empty());
}
int main(int argc,char* argv[])
{
    bear::myLog::initLogger("testLLM","stdout",spdlog::level::info);
    testing::InitGoogleTest(&argc,argv);
    return RUN_ALL_TESTS();
}

//////////////////////////////////////////////////////ChatGPTProvider.cpp://////////////////////////////////////////

cpp 复制代码
#include "../include/ChatGPTProvider.h"
#include "../include/util/myLog.h"
#include <httplib.h>
#include <jsoncpp/json/json.h>

namespace ai_model_access_tech
{
    bool ChatGPTProvider::initModel(const std::map<std::string, std::string> &modelConfigs)
    {
        // 初始化ChatGPT模型:apiKey endpoint
        //  op1:检查是否有apiKey
        auto apiKey_it = modelConfigs.find("apiKey");
        if (apiKey_it != modelConfigs.end())
        {
            _apiKey = apiKey_it->second;
        }
        else
        {
            bear::ERROR("ChatGPTProvider initModel:apiKey not found");
            return false;
        }
        // op2:检查是否有endpoint
        auto endpoint_it = modelConfigs.find("endpoint");
        if (endpoint_it != modelConfigs.end())
        {
            _endpoint = endpoint_it->second;
        }
        else
        {
            bear::ERROR("ChatGPTProvider initModel:endpoint not found ");
            return false;
        }
        // op3:初始化完成
        bear::INFO("ChatGPTProvider initModel success!apiKey: -- ,endpoint :{} ", _endpoint);
        _isAvailable = true;
        return true;
    }
    bool ChatGPTProvider::isAvailable()
    {
        // 判断ChatGPT模型是否可用
        return _isAvailable;
    }
    std::string ChatGPTProvider::getModelName() const
    {
        // return "gpt-4o-mini";
        return "openai/gpt-4o-mini";
    }
    std::string ChatGPTProvider::getModelDesc() const
    {
        return "OpenAI 推出的轻量级、高性价比模型,核心能力接近 GPT-4 Turbo 但更经济~";
    }
    std::string ChatGPTProvider::sendMessage(const std::vector<Message> &messages, const std::map<std::string, std::string> &params)
    {
        // op1 : 判断模型是否有效
        if(!_isAvailable)
        {
            bear::ERROR("ChatGPT Model wrong , doesn't support sendMessage");
            return "";
        }
        // op2 : 创建客户端
        httplib::Client client(_endpoint.c_str());
        std::cout <<_endpoint << std::endl;
        client.set_address_family(AF_INET);
        client.set_connection_timeout(30,0);
        client.set_read_timeout(60,0);
        //  client.set_proxy("127.0.0.1",7897);
        // op3 : 构建请求
            // 1)头
        httplib::Headers headers =  {
            {"Authorization","Bearer "+ _apiKey},
            {"Content-Type", "application/json"}
        };
            // 2)体
        int max_out_tokens = 2048;
        double temperature = 0.7;
        if(params.find("max_output_tokens") != params.end())
        {
            max_out_tokens = std::stoi(params.at("max_output_tokens"));
        }
        if(params.find("temperature") != params.end())
        {
            temperature = std::stod(params.at("temperature"));
        }

        Json::Value messagesArray(Json::arrayValue);
        for(auto &message:messages)
        {
            Json::Value msg;
            msg["role"] = message._role;
            msg["content"] = message._content;
            messagesArray.append(msg);
        }

        Json::Value requestBody;
        requestBody["model"] = getModelName();//此处已经修改为中转API模型
        requestBody["input"] = messagesArray;
        requestBody["temperature"] = temperature;
        requestBody["max_output_tokens"] = max_out_tokens;
        // 此处stream 默认为false ,不必多个流量去写false

        Json::StreamWriterBuilder swb;
        swb["indentation"] = "";
        auto body = Json::writeString(swb,requestBody);
        // op4 : 发送请求,等待响应
        auto response = client.Post("/v1/responses",headers,body,"application/json");////////////////////
        // op5 : 收到响应后,判断响应状态和解析响应
        if(!response)
        {
            bear::ERROR("ChatGPTProvider sent req FAILED or rev response FAILED");
            perror("error :");
            return "";
        }
        bear::INFO("ChatGPTProvider rev response->status:{}",response->status);
        bear::INFO("ChatGPTProvider rev response->body:{}",response->body);
        if(response->status != 200)
        {
            bear::ERROR("ChatGPTProvider sent req success BUT recv response FAILED,{}",to_string(response.error()));
            return "";
        }

        Json::CharReaderBuilder crb;
        std::istringstream responseStr(response->body);
        Json::Value responseJson;
        std::string errors;
        bool ret = Json::parseFromStream(crb,responseStr,&responseJson,&errors);
        if(ret == false)
        {
            bear::ERROR("ChatGPTProvider recv response success BUT parse response FAILED,{}",errors);
            return "";
        }
        bear::INFO("ChatGPTProvider parse response success");
        if(responseJson.isMember("output") && responseJson["output"].isArray() && !responseJson["output"].empty())
        {
            auto output = responseJson["output"][0];
            if(output.isMember("content") && output["content"].isArray() && !output["content"].empty() &&
                output["content"][0].isMember("text"))
                {
                    std::string replyStr = output["content"][0]["text"].asString();
                    return replyStr;
                }
        }
        bear::ERROR("ChatGPTProvider parse response success BUT got empty Msg!");
        return "";
    }
    std::string ChatGPTProvider::sendMessageStream(const std::vector<Message> &messages,
                                                   const std::map<std::string, std::string> &params,
                                                   std::function<void(const std::string &, bool)>
                                                       callback)
    {
        return "";
    } // 对模型返回的增量数据如何处理

} // end ai_model_access_tech

最终:

相关推荐
ServBay1 天前
打通 AI 编程本地运维边界,利用 MCP 协议简化环境与服务管理
后端·ai编程·mcp
程序员cxuan1 天前
DeepSeek 杀入多模态,识图功能正式上线!
人工智能·后端·程序员
IT_陈寒1 天前
SpringBoot这个自动配置坑我跳了三次
前端·人工智能·后端
用户395240998801 天前
排坑日记:ASP.NET Core 中 "Required field is not provided" 验证错误全记录
后端
JaguarJack1 天前
Openai Codex 重大更新 已支持接入任意开源大模型
ai·openai·codex
用户8356290780511 天前
使用 Python 自动化 PowerPoint 形状布局与格式设置
后端·python
Oneslide1 天前
sudo免密权限配置不生效
后端
站大爷IP1 天前
为什么Python不用var或let声明变量?
后端
赴星半途1 天前
NestJS实战-创建AuthService
后端