• システム開発に関わる内容をざっくりと書いていく

ChatGPTにエクセルファイルを読み込ませる【C#】

ChatGPTにエクセルファイルをプロンプトに送り、感想などの返答をもらう:簡単なAPIでの実装コード

[HttpPost("FromExcel")]
public async Task<string?> GetResult(IFormFile postedFile)
{
    StringBuilder sb = new();
    using var stream = postedFile.OpenReadStream();
    var reader = ExcelReaderFactory.CreateOpenXmlReader(stream);

    while (reader.Read())
    {
        for (int col = 0; col < reader.FieldCount; col++)
        {
            Debug.WriteLine(reader.GetValue(col));
            sb.Append(reader.GetValue(col));
            sb.Append(',');
        }
        sb.Append(System.Environment.NewLine);
    }
    return await GetChatGptResponse("以下のドキュメントについての感想をください" + sb.ToString());
}

private async Task<string?> GetChatGptResponse(string prompt)
{
    var openAiService = new OpenAIService(new OpenAiOptions()
    {
        ApiKey = "yourApiKey"
    });
    Console.WriteLine(prompt);
    var result = await openAiService.Completions.CreateCompletion(new CompletionCreateRequest()
    {
        Prompt = prompt,
        Echo = false,
        MaxTokens = 300 // about
    }, Models.TextDavinciV3);
    if (result.Successful)
    {
        return result.Choices.Select(x => x.Text).FirstOrDefault();
    }
    else
        return null;
}

実践的には下記のようなものも必要

1. Excelデータのフォーマットに対応した処理

Excelファイルには様々なデータ形式が存在します。数値、文字列、日付、さらには数式など、異なる形式のデータが混在しているため、GetValueメソッドで取得した値を適切に扱う必要があります。現在のコードではデータ型の処理がシンプルに扱われていますが、フォーマットごとに適切に処理することが求められます。

改善案:

  • 取得した値がnullの場合や、特定のフォーマットが必要な場合は、そのデータ型を明示的にチェックし、適切に処理するロジックを追加する。
object value = reader.GetValue(col);
if (value != null)
{
if (value is DateTime)
sb.Append(((DateTime)value).ToString("yyyy-MM-dd"));
else if (value is double)
sb.Append(((double)value).ToString("F2"));
else
sb.Append(value.ToString());
}

2. APIキーの管理とセキュリティ

APIキーをコードに直接書き込むのはセキュリティ上のリスクがあります。環境変数や設定ファイルを使用して、APIキーを安全に管理することが推奨されます。特にプロダクション環境では、APIキーが漏洩すると悪用される可能性があるため、適切なセキュリティ対策が必要です。

推奨方法:

  • APIキーを環境変数や設定ファイル(例: appsettings.json)に保存し、コードから直接参照しないようにする。
var apiKey = Environment.GetEnvironmentVariable("OpenAiApiKey");
var openAiService = new OpenAIService(new OpenAiOptions()
{
ApiKey = apiKey
});

3. 非同期処理の最適化

非同期処理を活用することで、特に大規模なExcelファイルを処理する際のパフォーマンスが向上します。しかし、データが多い場合にはメモリの消費が増加する可能性があるため、ストリーム処理やメモリ管理を適切に行うことが重要です。

改善案:

  • asyncawaitを活用した非同期処理を正しく実装し、レスポンスの高速化を図る。
  • 必要に応じて、IAsyncEnumerableを使用し、大量のデータを逐次処理する設計に変更することも検討。

4. ファイルアップロードの検証とエラーハンドリング

ファイルが正しくアップロードされたかどうかの検証が重要です。例えば、ファイルが空だったり、対応していない形式のファイルがアップロードされた場合に、適切なエラーメッセージを返す仕組みを追加することで、ユーザーの混乱を避けられます。

実装例:

if (postedFile == null || postedFile.Length == 0)
{
return BadRequest("ファイルがアップロードされていません。");
}

if (!postedFile.FileName.EndsWith(".xlsx"))
{
return BadRequest("サポートされていないファイル形式です。");
}

5. エラーハンドリングとログの強化

現在の実装では、エラーが発生した際にnullを返していますが、より詳細なエラー情報をログに記録し、ユーザーに分かりやすいエラーメッセージを返すことで、デバッグや問題解決が容易になります。

改善案:

  • try-catchブロックを使って、例外が発生した場合に適切なエラーメッセージを記録し、ユーザーに返す。
try
{
// Excelファイルの読み込みと処理
}
catch (Exception ex)
{
// ログにエラーを記録
_logger.LogError(ex, "Excelファイルの処理中にエラーが発生しました。");
return StatusCode(500, "内部エラーが発生しました。");
}

6. 複数シートへの対応

Excelファイルには複数のシートが含まれる場合があります。現在の実装では1つのシートしか処理していませんが、複数シートが存在する場合は、それぞれのシートを処理する機能を追加するとより柔軟です。

実装例:

  • ExcelReaderNextResult()メソッドを使って、次のシートに移動しながら全シートを処理する。
do
{
while (reader.Read())
{
// 各行の処理
}
} while (reader.NextResult()); // 次のシートに移動

7. ChatGPTのモデル選択とトークン制限

ChatGPTに送信するテキストの量が多くなると、トークン制限に引っかかる可能性があります。特に、Excelファイルの内容が大きい場合には、適切にテキストを分割して送信したり、トークン数を制御する必要があります。

改善案:

  • テキストを事前に分割して、複数回に分けてChatGPT APIを呼び出す設計を検討。
  • トークン数の上限をMaxTokensで適切に設定し、応答が切れないようにする。