You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity调用OpenAI API持续出现HTTP 400错误的求助

Unity调用OpenAI API出现HTTP 400 Bad Request错误排查与解决

问题描述

在Unity新项目中调用OpenAI API,无论使用直接JSON还是WWWForm方式,均返回HTTP/1.1 400 Bad Request错误。终端使用-k参数调用API可正常工作,但Unity中始终报错。以下是两个版本的测试代码:

版本1(调用/v1/completions接口)

using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
using System.Collections.Generic;

public class OpenAICommunication : MonoBehaviour
{
    private string apiKey = "sk-....."; // 隐藏密钥
    private string apiUrl = "https://api.openai.com/v1/completions";

    private void Update()
    {
        if(Input.GetButtonDown("Jump"))
            AskQuestion("Hola! Este es un juego de unity!");
    }

    IEnumerator SendQuestion(string question)
    {
        MessagesList messages = new MessagesList();
        messages.list = new Message[] { new Message() };
        messages.list[0].role = "user";
        messages.list[0].content = question;
        ApiCall apiCall = new ApiCall();
        apiCall.model = "text-davinci-003";
        apiCall.prompt = question;

        string json = JsonUtility.ToJson(apiCall);
        Debug.Log("JSON: " + json);

        // 配置授权头
        Dictionary<string, string> headers = new Dictionary<string, string>
        {
            { "Content-Type", "application/json" },
            { "Authorization", "Bearer " + apiKey }
        };
        
        // 发送POST请求到OpenAI API
        using (UnityWebRequest www = UnityWebRequest.PostWwwForm(apiUrl, json))
        {
            foreach (var header in headers)
            {
                Debug.Log("HEADER: " + header.Key + header.Value);
                www.SetRequestHeader(header.Key, header.Value);
            }

            yield return www.SendWebRequest();

            if (www.result == UnityWebRequest.Result.ConnectionError || www.result == UnityWebRequest.Result.ProtocolError)
            {
                Debug.LogError("请求错误: " + www.error);
            }
            else
            {
                string responseJson = www.downloadHandler.text;
                // 处理响应JSON(提取并显示助手回复)
                Debug.Log("响应: " + responseJson);
            }
        }
    }

    public void AskQuestion(string question)
    {
        StartCoroutine(SendQuestion(question));
    }
}

[System.Serializable]
public class Message
{
    public string role;
    public string content;
}
public class MessagesList
{
    public Message[] list;
}
public class ApiCall
{
    public string model;
    public string prompt;
}

版本2(调用/v1/chat/completions接口)

using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
using System.Collections.Generic;

public class OpenAICommunication : MonoBehaviour
{
    private string apiKey = "sk-...."; // 隐藏密钥
    private string apiUrl = "https://api.openai.com/v1/chat/completions";

    private void Update()
    {
        if (Input.GetButtonDown("Jump"))
            AskQuestion("Hola! Este es un juego de unity!");
    }

    IEnumerator SendQuestion(string question)
    {
        // 配置授权头
        Dictionary<string, string> headers = new Dictionary<string, string>
        {
            { "Content-Type", "application/json" },
            { "Authorization", "Bearer " + apiKey }
        };

        MessagesList messages = new MessagesList();
        messages.list = new Message[] { new Message() };
        messages.list[0].role = "user";
        messages.list[0].content = question;
        string json = JsonUtility.ToJson(messages);

        // 删除不需要的字符串部分以格式化JSON
        // 删除的是:{ list: .... 和最后的 }
        json = json.Substring(8, json.Length - 8 - 1);
        // 验证格式正确
        Debug.Log("JSON: " + json);

        // 准备POST请求数据,同时尝试chat接口但无变化...
        WWWForm form = new WWWForm();
        form.AddField("model", "gpt-3.5-turbo");
        form.AddField("messages", json);

        // 发送POST请求到OpenAI API
        using (UnityWebRequest www = UnityWebRequest.Post(apiUrl, form))
        {
            foreach (var header in headers)
            {
                Debug.Log("HEADER: " + header.Key + header.Value);
                www.SetRequestHeader(header.Key, header.Value);
            }

            yield return www.SendWebRequest();

            if (www.result == UnityWebRequest.Result.ConnectionError || www.result == UnityWebRequest.Result.ProtocolError)
            {
                Debug.LogError("请求错误: " + www.error);
            }
            else
            {
                string responseJson = www.downloadHandler.text;
                // 处理响应JSON(提取并显示助手回复)
                Debug.Log("响应: " + responseJson);
            }
        }
    }

    public void AskQuestion(string question)
    {
        StartCoroutine(SendQuestion(question));
    }
}

[System.Serializable]
public class Message
{
    public string role;
    public string content;
}
public class MessagesList
{
    public Message[] list;
}

错误信息

请求错误: HTTP/1.1 400 Bad Request
0x00007ff62ae8229d (Unity) StackWalker::GetCurrentCallstack
0x00007ff62ae87249 (Unity) StackWalker::ShowCallstack
0x00007ff62be533a1 (Unity) GetStacktrace
0x00007ff62c514c02 (Unity) DebugStringToFile
0x00007ff629d98736 (Unity) DebugLogHandler_CUSTOM_Internal_Log
0x0000025ca0dfc363 (Mono JIT Code) (wrapper managed-to-native) UnityEngine.DebugLogHandler:Internal_Log (UnityEngine.LogType,UnityEngine.LogOption,string,UnityEngine.Object)
0x0000025ca0dfc27b (Mono JIT Code) UnityEngine.DebugLogHandler:LogFormat (UnityEngine.LogType,UnityEngine.Object,string,object[])
0x0000025ca0dfbfc0 (Mono JIT Code) UnityEngine.Logger:Log (UnityEngine.LogType,object)
0x0000025c7c685ab5 (Mono JIT Code) UnityEngine.Debug:LogError (object)
0x0000025c7c67b363 (Mono JIT Code) OpenAICommunication/<SendQuestion>d__3:MoveNext () (at D:/Projects/Unity/AI_Project/Assets/Scripts/OpenAICommunication.cs:57)
0x0000025c7c67ab00 (Mono JIT Code) UnityEngine.SetupCoroutine:InvokeMoveNext (System.Collections.IEnumerator,intptr)
0x0000025c7c67ac2f (Mono JIT Code) (wrapper runtime-invoke) <Module>:runtime_invoke_void_object_intptr (object,intptr,intptr,intptr)
0x00007ffcf45ee0d4 (mono-2.0-bdwgc) mono_jit_runtime_invoke (at C:/build/output/Unity-Technologies/mono/mono/mini/mini-runtime.c:3445)
0x00007ffcf452eb74 (mono-2.0-bdwgc) do_runtime_invoke (at C:/build/output/Unity-Technologies/mono/mono/metadata/object.c:3066)
0x00007ffcf452ed0c (mono-2.0-bdwgc) mono_runtime_invoke (at C:/build/output/Unity-Technologies/mono/mono/metadata/object.c:3113)
0x00007ff62ad99724 (Unity) scripting_method_invoke
0x00007ff62ad77944 (Unity) ScriptingInvocation::Invoke
0x00007ff62ad4041a (Unity) Coroutine::Run
0x00007ff62ad3de0f (Unity) Coroutine::ContinueCoroutine
0x00007ff62a9fb653 (Unity) AsyncOperation::InvokeCoroutine
0x00007ff62b33d7bc (Unity) UnityWebRequestAsyncOperation::InvokeCoroutine
0x00007ff62b33d9a1 (Unity) UnityWebRequestProto<UnityWebRequestTransport,AtomicRefCounter,RedirectHelper,ResponseHelper,DownloadHandler,UploadHandler,CertificateHandler,HeaderHelper,AsyncOperation>::Job_InvokeCoroutine
0x00007ff62a9aed9a (Unity) BackgroundJobQueue::ExecuteMainThreadJobs
0x00007ff62aa3127c (Unity) `InitPlayerLoopCallbacks'::`2'::EarlyUpdateExecuteMainThreadJobsRegistrator::Forward
0x00007ff62aa1133a (Unity) ExecutePlayerLoop
0x00007ff62aa114c6 (Unity) ExecutePlayerLoop
0x00007ff62aa17d85 (Unity) PlayerLoop
0x00007ff62b9dac6f (Unity) PlayerLoopController::InternalUpdateScene
0x00007ff62b9e784d (Unity) PlayerLoopController::UpdateSceneIfNeededFromMainLoop
0x00007ff62b9e5b51 (Unity) Application::TickTimer
0x00007ff62be59cda (Unity) MainMessageLoop
0x00007ff62be5f540 (Unity) WinMain
0x00007ff62d248bae (Unity) __scrt_common_main_seh
0x00007ffd46ea7614 (KERNEL32) BaseThreadInitThunk
0x00007ffd47c026b1 (ntdll) RtlUserThreadStart

排查与解决方案

1. 版本1的核心问题:PostWwwForm误用

UnityWebRequest.PostWwwForm会自动将请求内容格式化为application/x-www-form-urlencoded,但你手动设置了Content-Type: application/json,导致请求格式混乱。OpenAI API无法解析这种混合格式的请求,返回400错误。

修复方式:
替换PostWwwForm为UnityWebRequest.Post并手动设置上传处理器为JSON数据:

// 替换原有的UnityWebRequest创建代码
byte[] jsonBytes = System.Text.Encoding.UTF8.GetBytes(json);
using (UnityWebRequest www = UnityWebRequest.Post(apiUrl, ""))
{
    www.uploadHandler = new UploadHandlerRaw(jsonBytes);
    www.SetRequestHeader("Content-Type", "application/json");
    www.SetRequestHeader("Authorization", "Bearer " + apiKey);

    yield return www.SendWebRequest();

    // 后续错误处理和响应解析逻辑不变
}

2. 版本2的核心问题:WWWForm与JSON格式冲突

WWWForm会自动编码表单数据,而你将JSON字符串作为表单字段值传入,导致最终发送的请求体是URL编码后的内容,并非纯JSON。同时手动截取JSON字符串的操作容易出错,增加格式风险。

修复方式:
放弃WWWForm,直接构造符合Chat Completions接口要求的JSON请求体,并使用上传处理器发送纯JSON:

首先修正数据模型,直接创建符合OpenAI要求的结构:

[System.Serializable]
public class ChatRequest
{
    public string model;
    public Message[] messages;
}

[System.Serializable]
public class Message
{
    public string role;
    public string content;
}

然后修改SendQuestion方法:

IEnumerator SendQuestion(string question)
{
    ChatRequest request = new ChatRequest();
    request.model = "gpt-3.5-turbo";
    request.messages = new Message[]
    {
        new Message { role = "user", content = question }
    };

    string json = JsonUtility.ToJson(request);
    Debug.Log("JSON: " + json);

    byte[] jsonBytes = System.Text.Encoding.UTF8.GetBytes(json);

    using (UnityWebRequest www = UnityWebRequest.Post(apiUrl, ""))
    {
        www.uploadHandler = new UploadHandlerRaw(jsonBytes);
        www.SetRequestHeader("Content-Type", "application/json");
        www.SetRequestHeader("Authorization", "Bearer " + apiKey);

        yield return www.SendWebRequest();

        if (www.result == UnityWebRequest.Result.ConnectionError || www.result == UnityWebRequest.Result.ProtocolError)
        {
            // 关键:打印完整响应内容,OpenAI会返回具体的错误原因
            Debug.LogError("请求错误: " + www.error + "\n响应内容: " + www.downloadHandler.text);
        }
        else
        {
            string responseJson = www.downloadHandler.text;
            Debug.Log("响应: " + responseJson);
        }
    }
}

3. 通用排查步骤

  • 打印完整响应内容:400错误时,OpenAI的响应体中会包含具体的错误描述(比如缺少参数、格式错误),务必打印www.downloadHandler.text,这是定位问题的关键。
  • 验证JSON格式:将Debug.Log输出的JSON复制到Postman或终端中测试,确认格式符合OpenAI接口文档要求。
  • 检查API密钥权限:确保密钥有对应接口的调用权限,且额度充足。
  • 避免手动修改JSON字符串:使用JsonUtility或第三方JSON库自动序列化,减少手动操作导致的格式错误。

内容的提问来源于stack exchange,提问作者Esteban Gabriel Fernandez

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 18:49:50