tokobayashi’s blog

Anthropic API

アカウントと API Key

https://console.anthropic.com/

でアカウントを作り、API Key を作る。Billing でクレジットカードを登録し、$5 とか前払いする。他のサイトで Business tax ID に適当に入力したって書いてありましたが、私は入力しなくても進めました。

速くて安いのは Haiku、標準が Sonnet。値段は OpenAI と似た感じ。

https://www.anthropic.com/pricing#anthropic-api

Rest API 直接呼び出し

export ANTHROPIC_API_KEY="your-api-key-here"
curl https://api.anthropic.com/v1/messages \
     --header "x-api-key: $ANTHROPIC_API_KEY" \
     --header "anthropic-version: 2023-06-01" \
     --header "content-type: application/json" \
     --data \
'{
    "model": "claude-3-7-sonnet-20250219",
    "max_tokens": 1024,
    "messages": [
        {"role": "user", "content": "Hello, Claude"}
    ]
}'

anthropic-sdk-java

anthropic-sdk-java が Anthropic から提供されているオフィシャルな Java クライアントです。beta です。

https://github.com/anthropics/anthropic-sdk-java

    <dependency>
      <groupId>com.anthropic</groupId>
      <artifactId>anthropic-java</artifactId>
      <version>0.8.0</version>
    </dependency>
        String apiKey = System.getenv("ANTHROPIC_API_KEY");

        AnthropicClient client = AnthropicOkHttpClient.builder()
                .apiKey(apiKey)
                .build();

        MessageCreateParams params = MessageCreateParams.builder()
                .maxTokens(1024L)
                .system("あなたは2020年代の若い詩人です")
                .addUserMessage("Claude についてのボカロっぽい詩を3行で書いてください")
                .model(Model.CLAUDE_3_5_HAIKU_LATEST)
                .build();
        Message message = client.messages().create(params);

        message.content().stream()
                .flatMap(contentBlock -> contentBlock.text().stream())
                .forEach(textBlock -> System.out.println(textBlock.text()));

LangChain4j は OkHttp/Retrofit を使って自前で通信していました。

LangChain4J

LangChain4J は様々な LLM、Embedding Store を共通した Java API で使えるようにしたライブラリです。Tools や RAG もカバーしており、Java から LLM を呼び出してアプリを作るなら、かなり有力な選択肢でしょう。

https://docs.langchain4j.dev/

2025-03-07 時点で最新バージョンは1.0.0-beta1です。1.0.0 に向けて準備を進めているところですね。

ここでは OpenAI API を呼び出すサンプルを作ります。

    <dependency>
      <groupId>dev.langchain4j</groupId>
      <artifactId>langchain4j-open-ai</artifactId>
      <version>1.0.0-beta1</version>
    </dependency>

low-level サンプル

LangChain4J には、 low-level と high-level の2種類の使い方があります。low-level は、LLM が提供している Rest API に沿った単純な呼び出しです。ChatLanguageModelを使います。

    ChatLanguageModel chatModel = OpenAiChatModel.builder()
            .apiKey("demo")
            .modelName(OpenAiChatModelName.GPT_4_O_MINI)
            .temperature(0.3)
            .logRequests(true)
            .logResponses(true)
            .build();

    String response = chatModel.chat("パソコンで音楽を作るクラブの名前は何がいいですか?");
    System.out.println(response);

OpenAI API の Java ライブラリとほぼ同じですね。

langchain4j-open-ai はデモ用の API Key "demo" を提供しており、基本的な機能が無料で使えるようになっています。ありがたいですね。

注意点として、LLM API は基本的にステートレスです。会話のやり取りを行うには、クライアントが今までの会話を保持して、毎回すべて送信する必要があります。メッセージの管理のためChatMemoryというサポート機能がついています。

    ChatLanguageModel chatModel = OpenAiChatModel.builder()
            .apiKey("demo")
            .modelName(OpenAiChatModelName.GPT_4_O_MINI)
            .temperature(0.3)
            .build();

    ChatMemory chatMemory = TokenWindowChatMemory.withMaxTokens(1000, new OpenAiTokenizer(OpenAiChatModelName.GPT_4_O_MINI));

    SystemMessage systemMessage = SystemMessage.from("あなたはラーメンが大好きなグルメブロガーです。豚骨ラーメンが特に好きです。");
    chatMemory.add(systemMessage);

    UserMessage userMessage1 = userMessage("どの種類のラーメンがおすすめですか?");
    chatMemory.add(userMessage1);
    System.out.println("[User]: " + userMessage1.singleText());

    ChatResponse response1 = chatModel.chat(chatMemory.messages());
    AiMessage aiMessage1 = response1.aiMessage();
    System.out.println("[LLM]: " + aiMessage1.text());
    chatMemory.add(aiMessage1);

    UserMessage userMessage2 = userMessage("どこへ行けばそのラーメンが食べられますか?");
    chatMemory.add(userMessage2);
    System.out.println("\n\n[User]: " + userMessage2.singleText());

    ChatResponse response2 = chatModel.chat(chatMemory.messages());
    AiMessage aiMessage2 = response2.aiMessage();
    System.out.println("[LLM]: " + aiMessage2.text());

あえて冗長に書きましたが、定形処理をメソッドにくくりだせばもっとすっきりします。

high-level サンプル

high-level のやり方だとボイラープレートなコードを削減できます。

    interface Ramenholic {
        @SystemMessage("あなたはラーメンが大好きなグルメブロガーです。豚骨ラーメンが特に好きです。")
        String answer(String question);
    }

    @Test
    void aiService() {
        ChatLanguageModel chatModel = OpenAiChatModel.builder()
                .apiKey("demo")
                .modelName(OpenAiChatModelName.GPT_4_O_MINI)
                .temperature(0.3)
                .build();

        Ramenholic ramenholic = AiServices.create(Ramenholic.class, chatModel);

        String answer = ramenholic.answer("どの種類のラーメンがおすすめですか?");
        System.out.println(answer);
        System.out.println("----");

        answer = ramenholic.answer("どこへ行けばそのラーメンが食べられますか?");
        System.out.println(answer);
    }

インターフェース(Ramenholic)を定義するだけで、AiServices.createが必要な内部処理をもったプロキシクラスを生成してくれます。通常はこちらのほうがおすすめですね。

参照

LangChain4J はドキュメント、サンプルが豊富です。

チュートリアル https://docs.langchain4j.dev/category/tutorials

チュートリアルに沿ったサンプルコード https://github.com/langchain4j/langchain4j-examples/tree/main/tutorials

OpenAI API

アカウントと API Key

https://platform.openai.com/

でアカウントを作り、API Key を作る。Billing でクレジットカードを登録し、$10 とか前払いする

使うモデルによって値段が違う。現在(2025-03-6)安いのは gpt-4o-mini

https://openai.com/api/pricing/

ループや多ユーザでバリバリ使うようなことをしなければ大した額にはならないはず。(Cline とか他ツールに API Key 使わせる場合も注意)

Rest API 直接呼び出し

export OPENAI_API_KEY="your-api-key-here"
curl https://api.openai.com/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -d '{
    "model": "gpt-4o-mini",
    "messages": [{"role": "user", "content": "Say hello!"}],
    "max_tokens": 10
  }'

dev.ai4j:openai4j

OpenAI 用 Java クライアントライブラリはいくつか存在します。

langchain4j-open-ai は beta1 時点で dev.ai4j:openai4j:0.27.0dependency として使っています。が、dev.ai4j:openai4j は 2025年2月に archive され no longer maintained となっています。

https://github.com/ai-for-java/openai4j

なので、将来は期待できないけど参考のため触ってみよう。

    <dependency>
      <groupId>dev.ai4j</groupId>
      <artifactId>openai4j</artifactId>
      <version>0.27.0</version>
    </dependency>
        String apiKey = System.getenv("OPENAI_API_KEY");

        OpenAiClient client = OpenAiClient.builder()
                .openAiApiKey(apiKey)
                .logRequests()
                .logResponses()
                .build();

        ChatCompletionRequest request = ChatCompletionRequest.builder()
                .model("gpt-4o-mini")
                .addSystemMessage("あなたは2020年代の若い詩人です")
                .addUserMessage("ChatGPTについてのボカロっぽい詩を3行で書いてください")
                .temperature(0.9)
                .build();

        ChatCompletionResponse response = client.chatCompletion(request).execute();

        System.out.println(response.choices().get(0).message().content());

com.openai:openai-java

こちらが OpenAI から提供されているオフィシャルな Java クライアントです。比較的新しくて、まだ beta だと言われています。

https://github.com/openai/openai-java

    <dependency>
      <groupId>com.openai</groupId>
      <artifactId>openai-java</artifactId>
      <version>0.31.0</version>
    </dependency>
        String apiKey = System.getenv("OPENAI_API_KEY");

        OpenAIClient client = OpenAIOkHttpClient.builder()
                .apiKey(apiKey)
                .build();

        ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
                .model(ChatModel.GPT_4O_MINI)
                .addSystemMessage("あなたは1960年代のフォークシンガーです")
                .addUserMessage("ChatGPTについてのフォークソングっぽい詩を3行で書いてください")
                .temperature(0.9)
                .build();

        ChatCompletion response = client.chat().completions().create(params);

        System.out.println(response.choices().get(0).message().content().get());

メソッド名などはほぼ変わらないですね。

openai-javalogRequests(), logResponses() が無くて、env var で export OPENAI_LOG=debug する必要がある。

Apache KIE 10.0.0 リリース

Apache KIE 10.0.0 がリリースされました!

Apache KIE というのは Drools, jBPM, OptaPlanner, SonataFlow, Kogito そして開発ツールを含めたビジネスオートメーションのプロジェクトです。2023年から Apache Software Foundation への移行を進めており、今回(2024/12/11)やっと 10.0.0 のリリースに至りました。

https://kie.apache.org/blog/kie_10_0_0_release

申し訳ないですが、ドキュメントはまだ公開されていません。10.1.0 のときには公開できると思います。。。さしあたり、一つ前のバージョン(Drools は 8.44.0.Final) のドキュメントを参考にして下さい。

Maven Central に artifact が上がっているので pom でバージョンを 10.0.0 にすれば(Final は不要)、動くはずです。

リリースノート代わりに Drools 10.0.0 の新機能をリストすると

となります。

Droolsブログ : 16 Drools と LLM

16 Drools と LLM

昨今 LLM (Large Language Model) が大流行りですね。「LLMで何かアプリ作れや」と言われている方も多いのではないでしょうか。しかし、LLM だけで上手く行くのか?

AIチャットボットが誤った返金規程を提示し、裁判になったエア・カナダのニュースをご存知でしょうか。

「チャットボットの誤回答に責任はない」と弁解していたエア・カナダに裁判所が損害賠償支払いを命令 - GIGAZINE

LLM が生成する文章が非常に自然で役に立つのは間違いないですが、「間違ったことを書くことがある」ということはよく知られてますね。ビジネスの根幹となるルールを LLM に任せるのは危険です。そうです、ルールはルールエンジンに任せましょう。ルールとLLMはお互い補完しあう関係なのです。

こちらが Mario Fusco による Drools と LLM を組み合わせたアプリケーションのサンプルプロジェクトです。 https://github.com/mariofusco/quarkus-drools-llm/

このプロジェクトには、「ローン申し込み」「パスワード生成」「フライト返金チャット」の3つのサンプルが含まれています。このうち「フライト返金チャット」について、私が日本語化したフォークがあるのでこちらで見てみましょう。

https://github.com/tkobayas/quarkus-drools-llm/tree/openai-ja

環境: JDK 21+

実行手順

git clone https://github.com/tkobayas/quarkus-drools-llm.git
cd quarkus-drools-llm
git checkout openai-ja
export QUARKUS_LANGCHAIN4J_OPENAI_API_KEY=demo
export QUARKUS_LANGCHAIN4J_OPENAI_HOTMODEL_API_KEY=demo
./mvnw compile quarkus:dev

上記の環境変数 QUARKUS_LANGCHAIN4J_OPENAI_API_KEYQUARKUS_LANGCHAIN4J_OPENAI_HOTMODEL_API_KEY は、OpenAI の API キーです。LangChain4Jによる無料のデモキーを使っていますが、自分のキーを使うこともできます。デモキーはデモンストレーション用であり、制限がありますが、このサンプルアプリケーションでは十分です。

アプリケーションが起動したら、ブラウザで http://localhost:8080 にアクセスしてください。以下のメニュー画面が表示されます。

日本語化しているのは3つめの Airline refund chatbot だけなので、そのリンクをクリックしてください。

チャット画面が表示されます。自分が乗ったフライトが遅延して到着したと想定して、返金されるかチャットボットに尋ねてみましょう。

実装

このサンプルアプリケーションは、Quarkus と Drools と LangChain4J を使っています。LangChain4J は様々な LLM を使うためのライブラリです。このブランチ openai-ja では OpenAI の GPT-3.5-turbo を使っています。 main ブランチでは Ollama をローカルにインストールして使うよう設定されています。興味があれば、README.md を参照してください。

Quarkus は REST エンドポイントを公開することに加え、quarkus-langchain4j-openai で LangChain4J をさらに簡単に使うための機能を提供しています。様々な設定が application.properties に集約されています。

さて、 Drools と LLM はどのように組み合わされているのでしょうか。

https://github.com/tkobayas/quarkus-drools-llm/tree/openai-ja/src/main/java/org/hybridai/refund 以下のクラスを眺めてみてください。

2つのチャットサービスがあります。こちらは ChatGPT などでよく使うプロンプトをアノテーションで定義しています。chat メソッドを呼べば LangChain4J が LLM (今回は OpenAI の API) と通信して返答を得ます。

@RegisterAiService(chatMemoryProviderSupplier = StatefulChat.MemorySupplier.class)
@Singleton
public interface CustomerChatService {

    @SystemMessage("<<SYS>>あなたは航空会社のチャットボットです。あなたの目的は、質問をして顧客の情報を収集することです</SYS>>")
    @UserMessage("""
        顧客の名前と年齢について質問してください。

        +++
        {message}
        +++
        """)
    String chat(@MemoryId String sessionId, String message);

}

また2つの Extractor があります。これは LLM の返答から、データを Java オブジェクトにマッピングして生成するためのクラスです。

@RegisterAiService(chatMemoryProviderSupplier = StatelessChat.MemorySupplier.class)
@Singleton
public interface CustomerExtractor {

    @UserMessage("顧客の情報をこのテキストから抽出してください '{text}'。レスポンスは JSON フォーマットの顧客のデータのみです。他の文は含めないでください。" +
            "日本人の氏名は「姓」「名」の順に記載されていることが一般的です。")
    Customer extractData(String text);
}

さて、LLM によるやりとりから必要な情報が得られたら、DroolsRefundCalculator が Drools を呼び出し、ルールに従って処理を行います。こちらがそのルールです。

rule "遅延による返金対象判定" when
    Flight( $delay : delayInMinutes >= 60 )
then
    insert(new RefundAmount( 20 * $delay ));
end

rule "高齢者向け返金増額" when
    Customer( age > 65 )
 $r: RefundAmount()
then
    $r.setAmount( $r.getAmount() * 1.1 );
end

極めて明解ですね。明示的なルールがあるのだから、ここは LLM ではなく Drools が処理します。

さらに詳しく

Jenkins 備忘録

sudo systemctl start jenkins

http://localhost:8080/ にログイン (jenkins/jenkins)

Job DSL

https://plugins.jenkins.io/job-dsl/ https://github.com/jenkinsci/job-dsl-plugin/wiki/Real-World-Examples

Pipeline DSL

https://www.jenkins.io/doc/book/pipeline/getting-started/