0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

チャットbotお試し用アプリ開発_14日目_API通信処理の動作確認+修正

Posted at

目次

・本日の成果・考え
・最後に

本日の成果

では、前回お伝えしたAPI通信クラス(DifyApiClient.java)の動作確認からやっていきたいと思います。

DifyApiClientTest.java
/**
 * 
 */
package app.test;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import org.junit.Test;

import app.windowView.api.DifyApiClient;
import app.windowView.api.DifyRequestDto;

/**
 * 
 */
public class DifyApiClientTest {
	
	//API URL
	private String testAPI_URL;
	//API key
	private String testapiKey;
	
	//リクエストDto
	DifyRequestDto testDto;

	@Test
	public void test() {
		 // 待機用ラッチ(1回の完了を待つ)
        CountDownLatch latch = new CountDownLatch(1);
		
		//動作検証用API
		testAPI_URL = "https://api.dify~~~~~~";
		//API key
		testapiKey = "app-~~~~~~~~~~~~~~~~~";
		
		//リクエストDtoの生成
		testDto = new DifyRequestDto("君の役割を答えてくれ");
		
		//DifyAPIClientクラスのインスタンス準備
		DifyApiClient testClient = new DifyApiClient(testAPI_URL, testapiKey);
		try {
			testClient.streamingMsg(
					testDto, 
					chunk ->{
						System.out.println("チャンク受信:"+chunk);
					}, 
					()->{
						System.out.println("完了通知:チャット終了");
						 latch.countDown();  // 完了したら待機解除
					});
			// 非同期完了まで最大10秒待機(タイムアウトも検出可能)
	        boolean completed = latch.await(10, TimeUnit.SECONDS);
	        assert completed : "ストリーミングがタイムアウトしました";
			
		} catch (Exception e) {
			throw new IllegalStateException("何かしらのエラー:"+e);
		}
		
	}

}


このテスト実行時点でのビルドパス

スクリーンショット 2025-07-05 10.10.48.png

新たに追加したのは以下です。
・kotlin-stdlib-1.9.0.jar
・okio-jvm-3.9.0.jar
「okio-3.9.0.jar」だとBufferedSourceが型解決できませんでした。

では、実行。

はい、エラーと。
以下、エラー内容

7月 05, 2025 10:44:27 午前 okhttp3.internal.platform.Platform log
情報: Callback failure for call to https://api.dify.ai/...
java.io.IOException: API通信ステータスエラー;400
	at app.windowView.api.DifyApiClient$1.onResponse(DifyApiClient.java:60)
	at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:519)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
	at java.base/java.lang.Thread.run(Thread.java:1583)

HTTP 400エラー(Bad Request)は、リクエストの構造やパラメータに誤りがある場合に返されるということで、
JSONの構造がDifyの仕様に合っていない可能性が高いみたい。

この時点で以下が原因と想定
・リクエストヘッダー(APIのURLまたはAPIキー)が正しくない。
・リクエストボディのJsonの形式が正しくない。

では、それぞれを出力。

リクエストヘッダ
 Request{method=POST, url=https://api.dify.ai/v1/chat-messages, headers=[Authorization:Bearer app-自分で作成したAPIキー]}
 
リクエストボディ
request.json
{"query":"君の役割を答えてくれ","response_mode":"streaming"}

以下を確認してみました。

ヘッダはおそらく正しそうです。
で、ボディですが
{
"inputs": {},
"query": "君の役割を答えてくれ",
"response_mode": "streaming",
"conversation_id": null,
"user": "test-user"
}
最低でも、"inputs","user"は追加する必要がありそう?

早速追加したリクエストDto

DifyRequestDto.java
package app.windowView.api;

import java.util.HashMap;
import java.util.Map;

public class DifyRequestDto {

	//ユーザーが入力したメッセージ
	private final String query;

	//レスポンスの通信設定。streaming:ストリーミング blocking:一括
	private final String response_mode = "streaming";

	//共通関数のPCMACアドレス取得メソッドから取得。
	private final String usrMacAddress;

	//ユーザーチャット履歴追跡用ID 追加機能予定のため未使用
	private final String conversation_id;

	//ユーザーリクエストをフォーム項目として格納する用。 7/7時点で未使用。
	private final Map<String, Object> inputs;

	//使用ユーザーID。後々は使用端末のMacアドレスを格納予定。
	private final String user;

	/**
	 *ユーザーメッセージを引数にするコンストラクタ 未使用項目(MACアドレス、チャット追跡ID、フォーム項目)あり。
	 * @param query ユーザーメッセージ、Jsonの項目名と同一
	 */
	public DifyRequestDto(String usrInputs) {
		this.query = usrInputs;
		//以下は空で用意。
		this.usrMacAddress = null;
		this.conversation_id = null;
		this.inputs = new HashMap<String, Object>();
		this.user = "test-user";
	}

	public String getUser() {
		return user;
	}

	public String getQuery() {
		return query;
	}

	public String getResponse_mode() {
		return response_mode;
	}

	public String getUsrMacAddress() {
		return usrMacAddress;
	}

	public String getConversation_id() {
		return conversation_id;
	}

	public Map<String, Object> getInputs() {
		return inputs;
	}

}

では、再度実行

スクリーンショット 2025-07-06 7.29.11.png

テスト自体は落ちてますが、API通信でチャットbotとやり取り自体はできてそうです。
落ちた理由としては、チャンク終了をキャッチできずにnullで落ちたみたいです。

調べてみた限りですが、
公式HPでストリーミング通信の終了時のJsonの状態について触れているページを見つけることができませんでした。

ちょっと、公式外になりますが以下のやり取りでJsonの内容について触れていました。

解説.json
event: proxy bTOYc1
data: sage", ... "answer": "..."} [cite: 2]
data: 
data: data: {"event": "message_end", ... } // Double 'data:' prefix [cite: 9]
data: 
data:

どうしてdata→dataの2重構造になっているのかは不明ですが、
eventのmessage_endが終了を表しているみたいです。

では、以下の2つを修正(それぞれ修正箇所だけ載せます)

DifyResponseDto(追加).java
//チャンクのイベント(終了キャッチ用)
	private String event;

    
	public String getEvent() {
		return event;
	}

DifyApiClient(修正).java
				//チャンク毎にレスポンスの受け取り
				try (BufferedSource source = responseBdoy.source()) {

					while (!source.exhausted()) {
						//1行ずつUTF-8形式で格納
						String resline = source.readUtf8LineStrict();

						if (resline != null && resline.startsWith("data:")) {

							String data = resline.substring(6);
							//受信チャンクが終了文かどうかチェック
							//							if ("[DONE]".equals(data)) {
							//								onComplete.run();
							//								break;
							//							}

							DifyResponseDto chunk = gson.fromJson(data, DifyResponseDto.class);
							
							//受信チャンクのイベントをチェックし、終了イベントなら処理終了。
							String event = chunk.getEvent();
							//nullでないなら、event.trim()、nullなら空文字を返す。
							if("message_end".equals(event!= null ? event.trim():"")) {
								onComplete.run();
								System.out.println("チャンクの終了を確認");
								break;
							}
							//メソッド引数のonChunkにセットされているメソッドの呼び出し。
							onChunk.accept(chunk.getAnswer());
						}
					}
				} catch (Exception e2) {
					//チャンク読み込み中エラー
					throw new IllegalStateException("チャンク読み込みエラー:" + e2.getMessage());
				}
			}

		});

では、テスト実行

スクリーンショット 2025-07-06 8.38.12.png

ちょっと2重で完了メッセージ出してますが、OKです。

最後に

かなり長くなりましたが、これで通信処理についてはOKです。
次のUTで色々とチェックは必要ですがひと段落しました。
ここまでやってみた感想としてはGsonライブラリが楽すぎますね
自分がPHPでJson触った時は、確かリスト構造みたいに要素の位置とキーを指定して中身取り出していた気がします。
Dtoに放り込んで、getterで取れるのは簡単で良いですね。
次に懸念点になりますが、処理のためにここまで色々とライブラリを追加してきました。
それが最終的に「デスクトップアプリで実行する環境だと無理です。」みたいになりそうで怖いです。
できる限りシンプルな環境を心がけたいです。

以上、お付き合いありがとうございます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?