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

批量调用OpenAI异步API返回空响应问题排查与重构

问题分析与解决方案

核心问题排查

你的异步等待逻辑是正确的,Task.WhenAll会等待所有异步任务完成,所以不是未正确等待API响应的问题。响应为空的主要可能原因:

  1. OpenAI API返回成功但内容为空(比如prompt质量问题、模型无法生成有效内容)
  2. 批量并行调用触发API速率限制,导致部分请求返回空或异常
  3. 原代码未对API返回的Choices和Content做空值校验,无法定位具体空值来源

代码重构方案

1. 修复API调用方法的空值校验与日志

修改CallApiAsync方法,增加对返回结果的完整校验,同时补充日志便于排查问题:

public async Task<string> CallApiAsync(string requestPrompt)
{
    try
    {
        var completionResult = await _openAiService.ChatCompletion.CreateCompletion(new ChatCompletionCreateRequest
        {
            Messages = new List<ChatMessage>
            {
                ChatMessage.FromSystem("You are a helpful assistant."),
                ChatMessage.FromUser(requestPrompt)
            },
            Model = Models.Gpt_3_5_Turbo,
        });

        if (!completionResult.Successful)
        {
            _logger.LogError($"API call failed for prompt: {requestPrompt}. Error: {completionResult.Error?.Message}");
            return string.Empty;
        }

        if (!completionResult.Choices.Any())
        {
            _logger.LogWarning($"API returned successful but no choices for prompt: {requestPrompt}");
            return string.Empty;
        }

        var responseContent = completionResult.Choices.First().Message.Content?.Trim();
        if (string.IsNullOrEmpty(responseContent))
        {
            _logger.LogWarning($"API returned empty content for prompt: {requestPrompt}");
        }

        return responseContent ?? string.Empty;
    }
    catch (Exception ex)
    {
        _logger.LogError($"Exception calling ChatGPT API for prompt: {requestPrompt}. Message: {ex.Message}", ex);
        throw;
    }
}

2. 控制并发调用数量,避免速率限制

OpenAI API有严格的调用频率限制,批量并行发起所有请求会触发限制,导致部分请求返回空。使用SemaphoreSlim控制并发数:

// 根据你的OpenAI配额设置并发数,比如免费版建议设为3
var semaphore = new SemaphoreSlim(3);

var fetchTasks = videos.Select(async item =>
{
    await semaphore.WaitAsync();
    try
    {
        var formattedItem = new VideoItem
        {
            Title = item.Title,
            Subtitle = _extractService.GetCleanSubtitle(item.Subtitle ?? ""),
            Description = _extractService.GetCleanSubtitle(item.Description ?? ""),
        };

        if (string.IsNullOrEmpty(formattedItem.Subtitle) && string.IsNullOrEmpty(formattedItem.Description))
        {
            _logger.LogWarning($"Video {item.Id} has empty subtitle and description. Marking as not processed.");
            item.NotProcess = true;
            return new { item, processStatus = false, request = string.Empty, response = (string)null };
        }

        var builder = new RequestBuilder(formattedItem).WithYamlFormatPath(yamlPath);
        var yamlRequest = builder.Build();

        _logger.LogInformation($"Built YAML request for video {item.Id}: {yamlRequest}");

        var response = await _apiService.CallApiAsync(yamlRequest);

        _logger.LogInformation($"Response for video {item.Id}: {response ?? "[Empty]"}");

        return new { item, processStatus = true, request = yamlRequest, response };
    }
    catch (Exception ex)
    {
        _logger.LogError($"Error fetching details for video {item.Id}: {ex.Message}", ex);
        return null;
    }
    finally
    {
        semaphore.Release();
    }
});

var fetchedDetails = (await Task.WhenAll(fetchTasks)).Where(result => result != null);

3. 修复数据库保存代码的错误

原代码中访问item.Id是错误的,匿名类型中的视频对象是item属性,需修正:

foreach (var result in fetchedDetails)
{
    var dbResponse = new Response
    {
        Request = result.request,
        YtVideoId = result.item.Id,
        Response = result.response
    };
    // 执行数据库保存操作,例如:
    // await _dbContext.Responses.AddAsync(dbResponse);
}

额外建议

  • 检查你的OpenAI API密钥配额,确保有足够的调用次数和速率
  • 优化prompt内容,明确要求模型返回非空的YAML格式数据,比如在prompt末尾添加:"请务必返回非空的YAML格式数据,不得返回空内容"
  • 对空响应的视频项进行重试逻辑,避免直接丢弃

内容的提问来源于stack exchange,提问作者Rasik

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 00:32:06