ゆうふの研究室
AI更新日 2026.09.28

PowerShellからDifyへ送った日本語が『??』になった原因と解決方法

PowerShellからDify APIへ日本語を送信した際、食品名が『??』へ文字化けしてfoodsが空になった問題について、原因の切り分けとUTF-8で送信する修正方法をまとめます。

食事・体重記録アプリ「Reppal」では、文章で入力された食事内容から食品名、量、単位を取り出し、食品データベースを使ってカロリーや栄養素を計算する機能を開発しています。

DifyとSupabase Edge Functionsの連携を確認するため、PowerShellからDify APIへ日本語を含むリクエストを送信したところ、食品名が ?? に文字化けする問題が発生しました。

その結果、Difyが食品を正しく抽出できず、Edge Functionから「食品が指定されていません」という400エラーが返されました。

この記事では、問題が発生したときの状況、原因を切り分けた方法、JSONをUTF-8で送信する修正方法、修正後の確認結果をまとめます。

Reppalの処理構成

Reppalの栄養計算では、大まかに次の順番で処理します。

食事内容を入力
    ↓
Difyで食品名・量・単位を抽出
    ↓
Supabase Edge Functionへ送信
    ↓
食品データベースを検索
    ↓
単位をグラムへ換算
    ↓
カロリーと栄養素を計算

たとえば「白米を1杯食べた」という文章から、次のようなデータを作ります。

{
  "foods": [
    {
      "name": "白米",
      "amount": 1,
      "unit": "杯"
    }
  ]
}

このデータをEdge Functionへ送り、foods と food_units の情報から栄養価を計算します。

発生した問題

PowerShellからDify APIへ日本語のメッセージを送信しましたが、Dify側で確認すると、日本語部分が次のように変化していました。

????1?

本来送信したかった「白米1杯」の内容が ? に置き換わっており、Difyは食品名を判別できませんでした。

食品抽出後のデータを確認すると、foods は空になっていました。

{
  "foods": []
}

この状態でEdge Functionを呼び出したため、次の400エラーが返されました。

食品が指定されていません

最初はEdge Functionの問題だと考えた

画面上ではEdge Functionから400エラーが返されていたため、最初は次のような可能性を考えました。

  • Edge Functionのリクエスト形式が間違っている
  • foods の取得処理に問題がある
  • Difyの変数名とEdge Functionの項目名が一致していない
  • Supabaseの食品データを取得できていない
  • 「白米」がデータベースに登録されていない

しかし、エラーメッセージは「食品が見つかりません」ではなく、「食品が指定されていません」でした。

つまり、データベースを検索する前の段階で、リクエスト内の食品配列が空になっている可能性があります。

処理を一段階ずつ確認する

原因を調べるため、処理全体を次のように分けて確認しました。

  1. PowerShellで作成したJSON
  2. Dify APIが受け取った入力
  3. Difyの食品抽出結果
  4. HTTPリクエストノードから送られたデータ
  5. Edge Functionが受け取ったリクエスト
  6. Edge Functionのレスポンス

Edge Functionが受け取った時点では、すでに foods: [] になっていました。

さらに前のDifyの入力を確認すると、日本語が ?? に変わっていました。このことから、問題はSupabaseやEdge Functionではなく、PowerShellからDifyへ送信する段階にあると判断できました。

原因は文字エンコーディング

今回の原因は、PowerShellから送信したリクエスト本文の文字エンコーディングでした。

JSONの見た目が正しくても、HTTPリクエストとして送信される際の文字コードが送信側と受信側で一致していないと、日本語を正しく復元できないことがあります。

英数字だけのリクエストでは問題が表面化しないため、API自体は動いているように見えます。しかし、日本語を含めたときだけ文字化けし、後続の食品抽出に失敗しました。

今回の処理は次のような状態になっていました。

PowerShell上の文字列:白米1杯
        ↓
HTTPリクエスト送信時に文字コードが一致しない
        ↓
Difyが受け取った文字列:????1?
        ↓
食品名を抽出できない
        ↓
foods: []
        ↓
Edge Functionが400を返す

最終的な400エラーだけを見るとEdge Functionに問題があるように見えますが、本当の原因はさらに前の送信処理でした。

修正前のPowerShell

修正前は、JSONへ変換した文字列をそのまま -Body へ渡していました。

$body = @{
    inputs = @{}
    query = "白米を1杯食べました"
    response_mode = "blocking"
    user = "test-user"
} | ConvertTo-Json -Depth 10

$headers = @{
    Authorization = "Bearer $env:DIFY_API_KEY"
}

Invoke-RestMethod `
    -Uri "https://example.com/v1/chat-messages" `
    -Method Post `
    -Headers $headers `
    -ContentType "application/json" `
    -Body $body

環境やPowerShellのバージョンによっては、この方法で日本語が期待した形式で送信されない場合があります。

なお、URLやAPIキーは例です。実際のAPIキーをスクリプトへ直接書いたり、GitHubへ登録したりしないよう注意します。

UTF-8のバイト列として送信する

解決するため、JSON文字列をUTF-8のバイト列へ明示的に変換してから送信しました。

$bodyObject = @{
    inputs = @{}
    query = "白米を1杯食べました"
    response_mode = "blocking"
    user = "test-user"
}

$json = $bodyObject | ConvertTo-Json -Depth 10
$utf8Body = [System.Text.Encoding]::UTF8.GetBytes($json)

$headers = @{
    Authorization = "Bearer $env:DIFY_API_KEY"
}

$response = Invoke-RestMethod `
    -Uri "https://example.com/v1/chat-messages" `
    -Method Post `
    -Headers $headers `
    -ContentType "application/json; charset=utf-8" `
    -Body $utf8Body

$response

重要なのは次の2点です。

$utf8Body = [System.Text.Encoding]::UTF8.GetBytes($json)
-ContentType "application/json; charset=utf-8"

JSONをUTF-8のバイト列へ変換し、Content-Typeにも charset=utf-8 を指定します。

APIキーは環境変数から読み込む

テスト用コマンドであっても、APIキーをコードへ直接書くと、画面共有やGitのコミットによって外部へ漏れる可能性があります。

PowerShellでは、一時的に次のように環境変数へ設定できます。

$env:DIFY_API_KEY = "app-xxxxxxxxxxxxxxxx"

スクリプトからは次のように参照します。

$headers = @{
    Authorization = "Bearer $env:DIFY_API_KEY"
}

ターミナルの履歴やスクリーンショットを公開する場合も、APIキーが写っていないことを確認します。

修正後の結果

UTF-8のバイト列として送信したところ、Difyが日本語を正しく受け取れるようになりました。

食品抽出結果にも白米が入りました。

{
  "foods": [
    {
      "name": "白米",
      "amount": 1,
      "unit": "杯"
    }
  ]
}

その後、Edge Functionで食品と単位情報を取得し、白米1杯を234kcalとして計算できることを確認しました。

{
  "name": "白米",
  "amount": 1,
  "unit": "杯",
  "calculated_kcal": 234
}

これにより、Dify、Edge Function、Supabaseの食品データを通した一連の処理が正常に動作していることを確認できました。

修正後に確認した項目

文字化けの修正後は、白米だけでなく複数の入力方法を試しました。

  • 日本語だけの文章を送信できるか
  • 日本語と数字が混ざった文章を送信できるか
  • 「白米200g」のようなグラム入力を抽出できるか
  • 「白米1杯」のような単位入力を抽出できるか
  • 複数の食品を一度に送信できるか
  • Difyの入力画面で日本語が正しく表示されるか
  • Edge Functionへ送られる foods が空になっていないか
  • 最終回答にも日本語が正しく表示されるか

文字化けは送信時だけでなく、ログ出力やレスポンス表示でも起こる可能性があります。そのため、処理の入口から最終結果まで確認しました。

400エラーの内容も改善する

今回、Edge Functionは foods が空だったため、400エラーを返していました。この動作自体は正しいものです。

ただし、「食品が指定されていません」だけでは、利用者の未入力なのか、Difyの抽出失敗なのか、文字化けなのかを区別できません。

開発時のログには、原因を調べられる情報を残すようにしました。

if (!Array.isArray(body.foods) || body.foods.length === 0) {
  console.error("calculate-foods: foods is empty", {
    hasFoodsProperty: Object.prototype.hasOwnProperty.call(body, "foods"),
    foodsType: typeof body.foods,
  });

  return Response.json(
    {
      error: "食品が指定されていません。",
      code: "FOODS_REQUIRED",
    },
    { status: 400 },
  );
}

食品名などの利用者データを必要以上にログへ残さず、配列の有無や型など、原因の特定に必要な情報だけを記録します。

また、エラーコードを追加すると、Dify側でもエラーの種類を判定しやすくなります。

日本語APIテストで確認したいこと

日本語を含むAPIをPowerShellで確認するときは、次の項目を確認すると原因を探しやすくなります。

リクエスト本文

  • ConvertTo-Json 後の文字列に日本語が残っているか
  • JSONの階層が欠けていないか
  • 必要なプロパティが含まれているか

HTTPヘッダー

  • Content-Type が application/json になっているか
  • charset=utf-8 を指定しているか
  • 認証ヘッダーが正しいか

送信データ

  • JSONをUTF-8のバイト列として渡しているか
  • 日本語と数字の両方を含む入力で確認したか

受信側

  • Difyが受け取った文字列は正しいか
  • 食品抽出ノードの結果は正しいか
  • HTTPリクエストノードへ正しいデータが渡っているか
  • Edge Functionのログとステータスコードは何か

foods: [] は結果であり、原因ではなかった

今回の調査で特に重要だったのは、foods: [] を問題の原因だと決めつけなかったことです。

食品配列が空になったのは、さらに前の段階で日本語が文字化けした結果でした。

文字エンコーディングの不一致
    ↓
日本語が「??」になる
    ↓
Difyが食品名を抽出できない
    ↓
foodsが空になる
    ↓
Edge Functionが400を返す

最後に表示されたエラーから調べ始めるだけでなく、データがどの段階で最初に変化したかを追うことで、本当の原因を見つけられました。

今回学んだこと

英数字だけのAPIテストが成功していても、日本語を含むリクエストが同じように動くとは限りません。

また、複数のサービスを連携している場合、最後にエラーを返したサービスが原因とは限りません。今回、Edge Functionは受け取ったデータに対して正しく400エラーを返していました。

問題が発生したときは、次のようにデータの流れを一つずつ確認することが重要です。

  1. 送信前のJSONを確認する
  2. 受信側の入力を確認する
  3. 各ノードの出力を確認する
  4. データが最初に変化した場所を探す
  5. 修正後は入口から最終結果まで再確認する

まとめ

PowerShellからDifyへ送った日本語が ?? になった原因は、リクエスト本文の文字エンコーディングでした。

今回は、JSONをUTF-8のバイト列へ変換し、Content-Typeへ charset=utf-8 を指定することで解決しました。

$json = $bodyObject | ConvertTo-Json -Depth 10
$utf8Body = [System.Text.Encoding]::UTF8.GetBytes($json)

Invoke-RestMethod `
    -Uri "https://example.com/v1/chat-messages" `
    -Method Post `
    -Headers $headers `
    -ContentType "application/json; charset=utf-8" `
    -Body $utf8Body

修正後はDifyが「白米1杯」を正しく抽出し、Edge Functionを通じて234kcalとして計算できました。

API連携の問題では、レスポンスのエラーだけでなく、送信したデータが各段階でどのように変化したかを確認することが大切です。今後もReppalの開発で発生した問題と、その解決方法を記録していきます。