HEROZ Tech Blog

日本将棋連盟公認「将棋ウォーズ」や、AIを活用したシステム企画・開発を行う、AI企業HEROZの公式テックブログです。

Claude Code:ターミナルベースAIエージェントの実践活用

Claude Codeとは

Claude CodeはAnthropic社が開発したターミナルベースのAIコーディングエージェントです。従来のChatGPTやCopilotとは異なり、コマンドラインから直接利用でき、プロジェクト全体のコンテキストを理解してコード生成、リファクタリング、バグ修正などを実行できます。

導入手順

インストール方法

# npmを使用してインストール
npm install -g @anthropic-ai/claude-code

# yarn を使用する場合
yarn global add @anthropic-ai/claude-code

AWS Bedrock経由での利用について

AWS Bedrock経由での利用を推奨します。

メリット:

VS Code統合

拡張機能のインストール

VS Code拡張機能が提供されており、開発効率を大幅に向上させることができます。詳細な設定については、こちらの記事などを参照してください。

ワークスペース設定

ワークスペースごとに異なるモデルやAWSプロファイルを設定できるため、プロジェクトに応じた最適な設定が可能です。

{
  "folders": [
    {
      "path": "."
    },
  ],
  "settings": {
    "terminal.integrated.env.{OS毎に異なる値}": {
      "AWS_PROFILE": "{事前設定しておいたAWSプロファイル}",
      "CLAUDE_CODE_USE_BEDROCK":"1",
      "ANTHROPIC_MODEL": "us.anthropic.claude-sonnet-4-20250514-v1:0",
      "ANTHROPIC_SMALL_FAST_MODEL": "us.anthropic.claude-3-5-haiku-20241022-v1:0"
    },
  }
}

初期化

Claude Codeを起動後、/initコマンドを実行します。これにより、カレントディレクトリの構造が分析され、プロジェクト固有の設定ファイルCLAUDE.mdが自動生成されます。このファイルには、プロジェクトの構造や開発ガイドラインが含まれ、Claude Codeがより適切な支援を提供するためのコンテキストとして使用されます。

基本的な使い方

ターミナルでの実行

VS Codeのターミナルでclaude codeを起動すると、現在開いているファイルを自動認識し、プロジェクトコンテキストを理解した上でインタラクティブにファイルの読み書きを行えます。

カスタムスラッシュコマンド

反復的な複雑なタスクを効率化するため、カスタムスラッシュコマンドを定義できます。

カスタムコマンドの作成方法:

  1. マークダウン形式でプロンプトファイルを作成
  2. 適切なディレクトリに配置:
    • グローバル利用:~/.claude/command/
    • プロジェクト専用:.claude/command/
  3. ファイル名がコマンド名として登録される

これにより、複雑な指示を毎回入力する必要がなくなり、開発効率が大幅に向上します。

実践的な活用例

ファイル検索・編集

  • 大規模コードベースでの効率的な検索:関連ファイルを素早く特定し、プロジェクト全体の構造を理解
  • 複数ファイルの一括処理:複数ファイル間での分析や一括置換・修正を実行
  • コード規約の自動適用:既存のコーディング規約や慣習に従った編集を自動実行

コード生成・リファクタリング

  • 既存コードの理解とキャッチアップリバースエンジニアリングによる既存システムの理解促進
  • 機能追加の自動化:既存コード構造を理解した新機能の実装
  • レガシーコードの現代化:古いコードパターンを現代的な書き方に変換
  • テスト自動生成:適切なテストコードの自動生成

バグ修正・最適化

  • エラー解析と修正提案:エラーログからの原因特定と修正案の提示
  • 依存関係問題の解決:複雑な依存関係のトラブルシューティング
  • パフォーマンス改善ボトルネックの特定と最適化の実装

開発プロセス支援

  • PR文書の自動生成:変更内容を分析したPull Request説明文の作成
  • コードレビュー支援:レビューポイントの提示と指摘事項の自動修正
  • チーム開発効率化:ドキュメント生成やコミュニケーション支援

Gemini CLIとの連携

Claude CodeとGemini CLIを組み合わせることで、複数のAIの視点を活用したより高度な開発支援が可能になります。

Gemini CLIセットアップ

インストール

npm install -g @google/gemini-cli

認証設定

  1. APIキーの取得Google AI StudioでAPIキーを発行
  2. 環境変数設定VS Codeワークスペース設定に追加
{
  "terminal.integrated.env.{OS名}": {
    "GEMINI_API_KEY": "your-api-key-here"
  }
}
  • 認証方法選択:初回起動時にGemini API Key (AI Studio)を選択

連携活用方法

Claude CodeのCLAUDE.mdに連携設定を記述することで、「Geminiと相談しながら進めて」という指示で自動的に両AIを活用した開発が可能になります。

基本的な連携フロー:

  1. Claudeが要件をまとめてGeminiに送信
  2. Geminiの回答を取得・分析
  3. 両AIの見解を統合した最終提案を作成

活用例:

  • コードレビューでの多角的な分析
  • アーキテクチャ設計の妥当性検証
  • 複雑な技術選択での意思決定支援

この連携により、単一のAIでは得られない多様な視点からの開発支援を実現できます。

MCPサーバー設定

MCPサーバーとは

MCP(Model Context Protocol)サーバーは、Claude Codeの機能を拡張する外部ツールとして設定できます。これにより以下のようなメリットが得られます:

  • 機能拡張:Claude Codeの標準機能を超えた専門的なタスクの実行
  • 外部サービス連携AWSサービスやデータベースなどとの直接連携
  • 開発効率向上:反復的なタスクの自動化とワークフローの最適化

設定方法

MCPサーバーの設定は、コマンドライン実行またはJSON設定ファイルの編集で行えます。

コマンドによる設定

設定の適用範囲を指定できます。本記事では、ワークスペースごとのClaude環境構築を推奨しているため、ワークスペースフォルダをスコープとするprojectオプションを指定します。

AWS Diagram MCPサーバーの設定例:

今回はAWS Bedrockを使用しており、AWS Profileが設定済みであるため、AWS関連のMCPサーバーの動作環境が整っています。アーキテクチャ図を生成するサーバーを設定して、実際に作図機能を試してみます。

claude mcp add awslabs.aws-diagram-mcp-server -s project -- uvx awslabs.aws-diagram-mcp-server

設定ファイルの確認

上記コマンドを実行すると、.mcp.jsonファイルが自動生成されます。後からより詳細な設定を追加したい場合は、このJSONファイルを直接編集できます。

{
  "mcpServers": {
    "awslabs.aws-diagram-mcp-server": {
      "type": "stdio",
      "command": "uvx",
      "args": [
        "awslabs.aws-diagram-mcp-server"
      ],
      "env": {}
    }
  }
}

MCPサーバーを用いたAWSアーキテクチャ図の生成

設定したMCPサーバーを使用して、実際にアーキテクチャ図を生成してみます。

プロンプト例:

VpcStack-staging/MyVpcのAWSアーキテクチャ図を作成して./imgに保存して

このようにシンプルなプロンプトで、既存のAWSリソース構成を視覚化した図を自動生成できます。

生成結果:

MCPサーバーにより、作図を高い品質で自動化できて、ドキュメント作成や設計レビューの効率が大幅に向上します。

まとめ

Claude Codeは、従来の開発プロセスを革新する強力なツールです。導入により以下のような効果が期待できます。

開発効率の向上

  • 作業速度の大幅向上:ルーティンワークの自動化により、創造的なタスクに集中可能
  • コスト効率の改善:導入コストを大幅に上回る工数削減効果
  • 人材不足への対応:自動化により限られたリソースでの高品質な開発を実現

品質向上

  • コード品質の標準化:一貫したコーディング規約の適用
  • サービス品質の向上:これまで後回しにされていた品質向上作業の実現
  • 技術負債の解消:レガシーコードの現代化とリファクタリングの促進

学習・成長支援

  • 新技術の習得:最新のベストプラクティスや便利な機能の発見
  • 設計手法の向上:AIによる設計・実装手法の提案による学習効果
  • 技術領域の拡大:これまで困難だった技術領域への挑戦が可能に

Claude Codeは単なる開発支援ツールを超え、開発チーム全体の能力向上と、より高品質なソフトウェア開発を実現するパートナーとして機能します。

HEROZ ASKへのo1の導入

はじめに

昨年末の12/5に12 Days of OpenAIでついにo1が発表となりました。
また、Azure OpenAI Serviceでも、このo1が使用できるようになりましたので、HEROZ ASKへと組み込みました。
langchainを用いた組み込みで気になった点を書いていきます。

なお、このo1はHEROZ ASKで2025/1/24以降で使用可能です。

o1-preview時代からの共通項目

昨年9月にo1-previewがリリースされて以来、内部的にはo1-previewを組み込む試作を実施していました。
その時から注意点や気になった点は以下となります。

o1-previewやo1-miniはChatOpenAI()を用いてgpt-4oと同じように使おうとすると、以下が非対応のため修正が必要になります。

  • temperature: オプションの削除
  • ストリーミング(streaming): オプションの削除
  • SystemMessage: システムメッセージの除去

ストリーミングオプションを削除するとストリーミングを期待している時に何も出力されなくなるため、以下のようなラッピングクラスを用意しました。

コードを見る

class ChatOpenAI_o1(ChatOpenAI):
    def _generate(
        self,
        messages: List[BaseMessage],
        stop: Optional[List[str]] = None,
        run_manager: Optional[CallbackManagerForLLMRun] = None,
        **kwargs: Any,
    ) -> ChatResult:
        results = super()._generate(
            messages,
            stop,
            run_manager,
            *kwargs,
        )
        if run_manager is not None:
            # on_llm_new_tokenしないと結果が表示されない
            run_manager.on_llm_new_token("".join([g.text for g in results.generations]))
        return results

    async def _agenerate(
        self,
        messages: List[BaseMessage],
        stop: Optional[List[str]] = None,
        run_manager: Optional[AsyncCallbackManagerForLLMRun] = None,
        **kwargs: Any,
    ) -> ChatResult:
        results = super()._generate(
            messages,
            stop,
            run_manager,
            *kwargs,
        )
        if run_manager is not None:
            # on_llm_new_tokenしないと結果が表示されない
            await run_manager.on_llm_new_token(
                "".join([g.text for g in results.generations])
            )
        return results

また、ストリーミング時はstream_usageオプションを使用すると使用トークン数を取得できますが、これの出力トークン(completion tokens)に思考用のReasoning tokenも含まれており、助かりました。

o1での変更点

o1はo1-previewと比べると以下の点で変更となっています。

  • SystemMessageへの対応: roleはdeveloperとする
  • 画像入力: 数式の入力がやりやすくなります
  • Markdown出力: プロンプトにFormatting reenabledと追加すると、Markdownで出力できるようになります

SystemMessageについてはlangchainにおいてはSystemMessagePromptTemplateの呼び出しで、オプションにadditional_kwargs = { "__openai_role__": "developer" }を渡すことで対応できます。
確かにこの方法でSystemMessageの指定はできるようになったのですが、試してみる限りではgpt-4oのSystemMessageと比べて遵守率が低いように感じました。

一方で、o1は未だにコスト計算用の単価が書かれているopenai_info.pyやtiktokenのmodel.pyが対応していないので、テーブルを動的に上書きしない限りはトークンやコストの計算時にエラーが発生します。
これらのファイルについては早期に追加されることを期待します。

組み込み結果

早速、難解な数式を画像で入力して解かせたところ、無事に正解しました。

o1の実行結果

おわりに

gpt-4oより賢いと言われているo1をHEROZ ASKへ導入することができました。
o1は確かに込み入った数式のような問題には抜群の能力を発揮するのですが、普通の質問や知識を問う内容だとgpt-4oと回答が変わらず、o1は時間とコストだけがかかるので、使い所が難しいように思いました。
一般ユーザーを含めて導入することにより、その辺りのユースケースをもっと発掘していきたいと思います。

MCP(Model Context Protocol)をLangChainのエージェント機能で動かしてみた

はじめに

先月、Anthropic社よりMCP(Model Context Protocol)というプロトコルが発表されました。 www.anthropic.com MCPAIアシスタント(RAGシステム)と情報源やツール群をつなげるための通信規約(プロトコル)ですが、現時点で何がどこまでできるかが未知数でしたので、調査を行い、LangChainのエージェント機能で動作させました。
とりあえずは、得られた知見についてシステム構成面の切り口で考察していきたいと思います。

MCPとは

MCPの各種情報は以下にあります。

modelcontextprotocol.io

概要

MCPプロトコル仕様のArchitectureにある下記の図のようにアプリケーションがMCPのクライアントとなり、ローカルやリモートに位置するMCPのサーバーと通信して、情報を取得したり、ツールを動作させたりします。
アプリケーションにはデスクトップアプリやSaaSサービスが想定されます。

MCPの概要図

そして、MCP自体は以下のようなプロトコルのフローやメッセージの中身が規定されています。

MCPプロトコルフロー

認証の不在

プロトコルの規定の中で気になったのは、現時点では認証に対応しておらず、今後議論していくように書かれていました。
認証が存在しない以上、インターネットを介しての通信は難しいかもしれません。

Auth

Authentication and authorization are not currently part of the core MCP specification, but we are considering ways to introduce them in future. Join us in GitHub Discussions to help shape the future of the protocol!

Clients and servers MAY negotiate their own custom authentication and authorization strategies.

2つのトランスポート

プロトコル仕様のTransportsによると、MCPには以下の2つのトランスポートがあるそうです。

後者のSSEタイプはhttpsを介した通信なのでMCPのサーバーはローカルとリモートを問わずに適用できます。
一方で、前者のstdioタイプはMCPのクライアントがMCPのサーバーとなるプロセスを起動して、パイプ機能による標準入出力(stdio)を介して通信するため、stdioタイプはローカル内でしか動作しないです。

SDKとリファレンスサーバー群

MCPの公式githubにはPython SDKTypeScript SDKが整備されており、これらを使用したリファレンスやサードパーティサーバー群も提供されています。

リファレンスサーバー群

SDKは前述した2つのトランスポートの両方に対応しているのですが、リファレンスサーバー群のほとんどはstdioタイプで作られているため、リモートで動作するものはないです。

LangChainへの実装

Python SDKを使用してLangChainへ実装したlangchain-mcpというライブラリもありますが、stdioタイプを前提としているため、ローカル内でのみ動作します(=リモートでは動作しません)。
langchain-mcpToolコンポーネントMCPToolもしくはToolkitコンポーネントMCPToolkitという形態で提供されるため、LangChainのエージェント機能で使う形になります。

このlangchain-mcpasync with構文でトランスポートのクライアント(下記の例ではstdio_client)を起動した状態で実行する必要があるため、チェインの構成と実行を近接して行う必要があります。

async with stdio_client(server_params) as (read, write): 
    async with ClientSession(read, write) as session: 
        toolkit = MCPToolkit(session=session) 
        await toolkit.initialize() 
        (toolkitを使用してrunする)

HEROZ ASKはチェインの構成と実行のタイミングが離れているので、langchain-mcpが適用しづらく、組み込みについては一旦は断念することにしました。

システム構成

MCPの動作についてシステム構成面で図を交えて考察していきます。

Claude Desktopでのシステム構成

Anthropic社はMCPの発表とともに応用例としてデスクトップ版Claude(Claude Desktop)からのデスクトップ検索を提示しています。
これが「デスクトップ検索が便利だ」や「デスクトップを覗かれるのは危険だ」みたいな感想とともに強調されてしまい、「MCP=デスクトップ検索」や「MCP=Claude Desktop」みたいな勘違いが発生しています。
MCPはあくまで通信プロトコルであって、Claude Desktopやデスクトップ検索は直接的には関係なく、単なる応用例の一つに過ぎません。

Claude Desktopでのシステム構成を図にすると、以下になります。
Claude DesktopはMCPクライアントとして位置し、必要なMCPサーバーを自ら起動して、stdioタイプのトランスポートで接続します。 そして、起動したMCPサーバーのプロセスからデスクトップのファイルにアクセスしています。

Claude Desktopでのシステム構成

起動するMCPサーバーの種類やアクセス先は設定ファイルのjsonにてClaude Desktopに渡しますが、任意のプログラムが起動できたり、ローカルのリソースに自由にアクセスできるという点で、安全とは言えない代物です。

SaaSシステムでの目指したいシステム構成

HEROZ ASKのようなSaaS型のAIアシスタントサービスにMCPを組み込む場合には、以下のような2つのパターンのシステム構成があると思います。

SaaSシステムで目指したいシステム構成

a.はSaaSサービスがMCPクライアントとなるパターンで、ユーザーからの質問に応じて、AIアシスタントがリモートサーバーにある情報を取得したり、ツールで何か実行したりします。
b.はSaaSサービスがMCPサーバーとなるパターンで、SaaSサービスが持っているリソースに対して、ユーザのAIアシスタントアプリや、別のSaaSサービスのAIアシスタントからのアクセスを受け付けます。 こちらはSaaSサービスが持つリソースが重要となるので、どちらかと言うとコンテンツプロバイダー的な位置付けになります。

HEROZ ASKは独自のリソースを持つわけではないので、a.のパターンを狙うことになります。
また、HEROZ ASKのようなエンタープライズ向けのサービスの場合には、リモートサーバーはお客様の情報やツールが入ったサーバーになることが想定されます。

現状のシステム構成

前述のようにAIアシスタントSaaSサービスはa.のようなMCPクライアントとして、リモートサーバーへのアクセスを望む場合が多いですが、現状はlangchain-mcpがstdioタイプのトランスポートにしか対応していないため、下記のようなシステム構成にならざるをえません。

現状のシステム構成

すなわち、ユーザから質問を受け取ると、MCPサーバーとなるプロセスを起動するとともに、SaaSサービスが動作しているサーバーのリソースにアクセスします。
これは、セキュリティ的にも問題が多いので、HTTP with SSEの対応が待たれます。

SaaSシステムでの問題点

仮にlangchain-mcpがHTTP with SSEタイプのトランスポートに対応したとしても、お客様のサーバーに情報を取りに行ったり、ツールを実行しに行ったりする場合には以下のような問題があります。

  • MCPに認証機能が実装されていないので、非公開情報のアクセスができない
  • お客様のサーバーがイントラネット内に存在する場合には、アクセスするための術がない

これらを図示すると以下となります。

SaaSシステムでの問題点

LangChainのエージェント機能で動かしてみた

langchain-mcpのREADMEにはエージェント機能を使わない例が載っていますが、せっかくなのでエージェント機能を使って動作させてみました。
/docというディレクトリと/doc/test.txtというファイルを作成してから、「/docには何のファイルがありますか?」という質問でディレクトリ内のファイル一覧を取得させています。

コードを見る

!pip install langchain langchain-openai langchain-mcp langchain-community==0.3.12 langchain-core==0.3.25
!mkdir -p /doc
!touch /doc/test.txt

import os
import asyncio
from langchain_openai import ChatOpenAI
from langchain import hub
from langchain.agents import AgentExecutor, create_structured_chat_agent
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from langchain_mcp import MCPToolkit

os.environ["OPENAI_API_KEY"] = "(OpenAIのAPIキー)"

server_params = StdioServerParameters(
    command="npx",
    args=["-y", "@modelcontextprotocol/server-filesystem", "/doc"],
)

from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.agents.structured_chat.prompt import FORMAT_INSTRUCTIONS, PREFIX, SUFFIX
HUMAN_MESSAGE_TEMPLATE = "{input}\n\n{agent_scratchpad}"

prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "\n\n".join([PREFIX, "{tools}", FORMAT_INSTRUCTIONS, SUFFIX])),
        MessagesPlaceholder("chat_history", optional=True),
        ("human", HUMAN_MESSAGE_TEMPLATE),
    ]
)

async def main(message):
    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            toolkit = MCPToolkit(session=session)
            await toolkit.initialize()
            
            model = ChatOpenAI(model_name="gpt-4o")
            tools = toolkit.get_tools()
            
            agent = create_structured_chat_agent(model, tools, prompt)
            agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
            
            await agent_executor.ainvoke({"input": message})

asyncio.run(main("/docには何のファイルがありますか?"))

無事にtest.txtがあるという回答を得ることができましたが、langchain-communitylangchain-coreをバージョン固定にしないとうまく動きませんでした。

実行結果

おわりに

新しく登場したMCPは外部の情報やツールを活用できるので、期待の技術と思っていたのですが、以下の理由によりまだまだ時期尚早のように思いました。

  • プロトコルの仕様に認証機能が搭載されていないため、インターネット経由の接続が難しい
  • リファレンスサーバーやlangchain-mcpのトランスポートがstdioタイプなので、ローカル内でしか実行できない(リモートサーバーへの接続ができない)
  • Python SDKasync with構文の中でしか実行できないので、セッションの確立(チェインの作成)と実際の動作(チェインの実行)を分離することができない

とは言え、これらの技術課題が解決されれば、大規模言語モデル(LLM)を用いたエンタープライズサーチの実現も夢が広がります。こうしたOSSへのコントリビュートについても一緒にやってくれる仲間を募集しています。

社内向けHEROZ ASKにSLMのPhi-3.5を入れてみた

はじめに

昨今、小規模言語モデル(SLM, Small Language Model)の話が生成AI界隈で賑わせています。
SLMはgpt-4oのような大規模言語モデル(LLM, Large Language Model)と比較して小型軽量である故に以下のような特徴があるとされています。

  1. エッジデバイスやオンプレサーバーで動作させることができる。動作させてもコストが大きくならない。
    • セキュリティーやプライバシーの問題で海外のサーバーへプロンプトを送ることに対しての抵抗は根強くあると思います。
  2. 応答までのレイテンシーが短い
    • 特に音声会話のような場合にはミリ秒(ms)を争うので、レイテンシーが低いに越したことはないです。
  3. ドメイン特化のためのファインチューニングを実施しやすい。
    • 従来の中規模や大規模のモデルと比べてはるかに少ないGPU枚数でファインチューニングができるので、敷居が下がることを期待できます。

弊社でもSLMの動向は追っていましたが、以前の実験のように小規模のモデル(この時は10B前後)だと大した精度が出ないと考えていましたので、スルーしていました。
ところが、最近のSLMは以前のgpt-3.5-turboに匹敵する精度が出るという話や、精度が多少劣っても使い所があるかもしれないという話を聞き、遅ればせながら社内向けの環境に導入して、いろいろ検証しようと思いました。

検証に使用したSLMは今年の8月にリリースされたMicrosoft社のPhi-3.5の中で最も軽量なPhi-3.5 mini instructとしました。Phi-3.5 mini instructは3.8Bとかなり軽量です。
これをAzure Machine Learning サーバーレスAPIで動作させました。Phi-3.5 mini instructは1000トークンあたり$0.00013で推論できます。

HEROZ ASKに組み込むにあたってはlangchainで動作するようにしなければならないのですが、Azure Machine Learning サーバーレスAPIへの接続に関する情報がほとんどありませんでしたので、こちらに書こうと思います。

モデルのデプロイ

langchainからの呼び出しにはAzureMLChatOnlineEndpointを使用するのですが、こちらはAzure Machine Learning Studioでデプロイしたモデルにしか対応していませんでした。
先日のIgnite 2024でデビューしたAzure AI Foundryでデプロイしたモデルにはつながらず、少しハマりました。

以下がMachine Learning Studioでのモデルのデプロイ方法です。

1. ワークスペースの作成

Azure Machine Learning Studioにログインし、ワークスペースを作成する。

ワークスペースの作成

2. サーバーレスエンドポイントの作成

作成したワークスペースに入り、エンドポイントページのサーバーレスエンドポイントタブからサーバーレスエンドポイントを作成する。

サーバーレスエンドポイントの作成

3. エンドポイントの詳細

作成したエンドポイントの詳細ページを開くと、エンドポイントのURLとAPIキーを取得できます。この時に「APIルート」のパネルがあることがポイントです。

エンドポイントの詳細

Azure AI Foundryで作成したエンドポイントとの比較

Azure AI Foundryで作成したエンドポイントの詳細には「APIルート」が存在しません。

AI Foundryで作成したエンドポイント

Azure AI Foundryのエンドポイント一覧では、Machine Learning Studioで作成したOKの方のエンドポイントは「サーバーレス」となっていて、AI Foundryで作成したNGの方のエンドポイントは「Azure AI Services」となっています。

エンドポイントの一覧

langchainからの呼び出しコード

langchainからは以下のコードにて呼び出すことができます。

!pip install langchain langchain_community

from langchain_core.prompts import ChatPromptTemplate
from langchain_community.chat_models.azureml_endpoint import AzureMLChatOnlineEndpoint, AzureMLEndpointApiType, CustomOpenAIChatContentFormatter

AZURE_PHI3_ENDPOINT="(エンドポイント)"
AZURE_PHI3_API_KEY="(APIキー)"

human = "あなたは何者ですか?"
prompt = ChatPromptTemplate.from_messages([("human", human)])

chat = AzureMLChatOnlineEndpoint(
    endpoint_url=AZURE_PHI3_ENDPOINT,
    endpoint_api_type=AzureMLEndpointApiType.serverless,
    endpoint_api_key=AZURE_PHI3_API_KEY,
    content_formatter=CustomOpenAIChatContentFormatter(),
)

chain = prompt | chat
chain.invoke({})
→ AIMessage(content='私はMicrosoftのAIアシスタントです。...

Azure AI Foundryで作成したエンドポイントもlangchain_azure_aiに含まれるAzureAIChatCompletionsModelを使えば呼び出せるような記事はありました。ただし、現時点(2024/12/12)ではlangchain_azure_aiはままだ非公開のようです。

learn.microsoft.com

実行結果

HEROZ ASKの社内環境に組み込み、無事に動作しました。

Phi-3.5の動作

以下、実行して気になったことです。

モデルの精度と推論速度

モデルの精度については、プロンプトの些細な違いによっては壊れた回答になったり、Few-shotの例に引きづられすぎたりといった小中規模のオープンソースのモデルにありがちな間違いが発生することもありますが、プロンプトが合っていれば正しい回答を得られることは確認できました。

また、Azure Machine Learning サーバーレスAPIによる推論速度はモデルサイズの割には速くはなかったです。
やはりサーバーレスタイプですと、起動のオーバーヘッドがそれなりにあるのかもしれません。

ストリーミング

AzureMLChatOnlineEndpoint_(a)generate()関数から呼ばれる時にはストリーミングにならないことが分かりました。
そこで、以下のようなラッピング関数を作成して、_(a)stream()関数に分岐するようにしました。

コードを見る

from typing import Any, List, Optional

from langchain_core.callbacks import (
    AsyncCallbackManagerForLLMRun,
    CallbackManagerForLLMRun,
)
from langchain_core.language_models.chat_models import (
    agenerate_from_stream,
    generate_from_stream,
)
from langchain_core.messages import BaseMessage
from langchain_core.outputs import ChatResult

class AzureMLChatOnlineEndpointStreaming(AzureMLChatOnlineEndpoint):
    streaming: bool = False

    def _generate(
        self,
        messages: List[BaseMessage],
        stop: Optional[List[str]] = None,
        run_manager: Optional[CallbackManagerForLLMRun] = None,
        stream: Optional[bool] = None,
        **kwargs: Any,
    ) -> ChatResult:
        should_stream = stream if stream is not None else self.streaming
        if should_stream:
            stream_iter = self._stream(
                messages, stop=stop, run_manager=run_manager, **kwargs
            )
            return generate_from_stream(stream_iter)
        return super()._generate(messages, stop, run_manager, *kwargs)

    async def _agenerate(
        self,
        messages: List[BaseMessage],
        stop: Optional[List[str]] = None,
        run_manager: Optional[AsyncCallbackManagerForLLMRun] = None,
        **kwargs: Any,
    ) -> ChatResult:
        should_stream = self.streaming
        if should_stream:
            stream_iter = self._astream(
                messages, stop=stop, run_manager=run_manager, **kwargs
            )
            return await agenerate_from_stream(stream_iter)
        return await super()._agenerate(messages, stop, run_manager, *kwargs)

使用トークン数の取得

AzureMLChatOnlineEndpointならびにAzure Machine Learning サーバーレスAPI上のPhi-3.5モデルは"stream_usage"に対応していないようですので、使用トークン数が取得できませんでした。
今回は社内向けなので使用トークン数は気にしなくてもよいですが、本番サービス向けには使用トークン数を取得する方法を検討する必要がありそうです。
(デバッグしている限りではストリーミングの途中では使用トークン数を配信しているようですが、AzureMLChatOnlineEndpointが拾っていないようです。また、回答完了時に使用トークン数を返すこともなかったです)

おわりに

今回は話題のSLMについて、Phi-3.5をHEROZ ASKの社内環境で動作させることに成功しました。
まだ多くは触っていないですが、社内で精度限界の検証ならびにユースケースの発掘を引き続き進めていきたいと思います。 何か分かりましたら、別途記事にしようと思います。

NoSQL(mongoDB) 導入ガイド

はじめに

NoSQL の利用を促進したいという意識のもとこの記事を作成しました。 今迄、数多くの案件に関わってきましたが、DB といえば大体 RDB でした。NoSQL を扱っている案件もあるのですが数少ないです。 NoSQL にしておけばこんなに苦労することはなかったんだろうなというケースも多々あったので、とりあえず使い慣れた RDB を使うといった風潮に異を唱えたく思いました。 ロールの多様化によって DB に触れる機会があまりないエンジニアも増えてきた昨今、利用促進には情報発信したり説得材料を作るのが肝要だと思います。ビギナーでも手軽に試せるクイックスタート、どういった局面で利用すべきかといったガイドライン、現実に発生し得る要求に対処できるようなケーススタディなどを書いたブログ記事を作成することで、その一助とできればと願います。

環境構築

GitHub リポジトリに本記事で扱う環境やソースコードを全て格納しています。

Docker(docker-compose)を利用できる環境であれば、コマンド一つで環境を構築できます。

docker-compose up -d

コマンドを実行すると以下の 3 つのコンテナが起動します。

  • backend
    • リポジトリに配置しているファイルが/usr/src/app ディレクトリにマウントされます
    • Python(Jupyter)を実行できる環境です。
    • VSCode などでリモートログインして利用してください。
  • mongo
    • mongo DB 本体です。
    • ユーザーやパスワードなどは docker-compose.yml に書いてあります。
  • mongo-express
    • mongo DB をブラウザの GUI でメンテナンスできるツールです。
    • ブラウザでこのリンクを開いてください。

導入

pymongoでmongo DBを操作します。接続と動作確認を行うJupyterスクリプトを以下にGistで貼り付けます。

hello mongo

ケーススタディ: ユーザー情報を管理する

階層構造データの基本的なCRUD操作をしてみます。

mongo case study manage user info

おわりに

今回は導入部分の説明のみとしました。今後、より実践的な内容にも踏み込んでいきたいと思っています。

GraphRAGを試してみた

はじめに

GraphRAGはLLMによってナレッジ(知識)グラフを生成することで、複雑な情報のドキュメントから質疑応答を行う際の精度を向上させることができる手法ならびにソフトウェアです。
GraphRAGはマイクロソフトが開発しましたが、先日オープンソース化が発表されましたので、早速試してみました。
GraphRAGはAzureで動作させる方法(GraphRAG Accelerator)もありますが、今回はローカル(手元のMac)で動作させました。

GraphRAGが使用しているナレッジグラフは、ドキュメント内の複雑な情報の検索に向いていると言われています。
HEROZ ASKのドキュメント検索でも使用しているエンべディング(embedding)を用いた検索は、文意に基づく曖昧検索には向いているのですが、複雑な情報の検索ではカバーしきれないがあって気になりました。

インストール

インストールは公式のGet Startedや解説してくださっている動画(英語)の通りに実施すると、わりと簡単にできました。
python:3のdockerイメージ上で実施して気になったのは以下です。

  • Rustのインストールは個別で実施する必要がある
  • ragtest/.env: GRAPHRAG_API_KEYにOpenAIのAPIキーを書く(名前が紛らわしい)
  • ragtest/settings.yaml: GRAPHRAG_LLM_MODEL=gpt-4-turbo-previewをgpt-4oに変更する

実行結果

とりあえずは桃太郎の文章を読み込ませて、Get Startedの質問(What are the top themes in this story?)を和訳した「この物語の主なテーマは何ですか?」を問い合わせてみました。

# python -m graphrag.query --root ./ragtest --method global "この物語の主なテーマは何ですか?"

  :

SUCCESS: Global Search Response: ### 物語の主なテーマ

物語「桃太郎」にはいくつかの重要なテーマが含まれています。それぞれのテーマは物語の異なる側面を強調し、全体として豊かな物語を形成しています。

#### 勇気とリーダーシップ

桃太郎の鬼ヶ島征伐の使命は、勇気とリーダーシップのテーマを中心に展開されています。桃太郎の卓越した強さ、戦略的な手腕、そして仲間を鼓舞し導く能力は、物語の中で重要な役割を果たしています [Data: Reports (2, 4, 5)]。

#### 団結とチームワーク

桃太郎と彼の忠実な家来である犬、猿、キジの協力は、団結とチームワークのテーマを強調しています。彼らの結束したスキルと献身は、鬼たちがもたらす困難を乗り越えるために不可欠です [Data: Reports (2, 4, 5)]。

#### 家族の絆と育成

桃太郎と彼を育てた老夫婦との関係は、家族の絆と育成のテーマを象徴しています。老夫婦の支援と愛情は、桃太郎が旅に出るための準備において重要な役割を果たしています [Data: Reports (1, 2)]。

#### 神の介入と運命

老夫婦が桃太郎を神の贈り物と信じることから、物語には神の介入と運命のテーマが含まれています。これにより、物語に霊的な次元が加わり、運命と神の祝福のテーマが強調されます [Data: Reports (1)]。

#### 文化的意義

「桃太郎」の物語は日本の民間伝承に深く根ざしており、強さ、リーダーシップ、団結の力といったテーマを祝うものです。また、きびだんごなどの文化的要素も含まれています [Data: Reports (2)]。

### 結論

「桃太郎」の物語は、勇気、リーダーシップ、団結、家族の絆、神の介入、そして文化的意義といった多くのテーマを通じて、豊かで多層的な物語を提供しています。これらのテーマは、物語のキャラクターや出来事を通じて巧みに織り交ぜられ、読者に深い印象を与えます。

やや冗長ではありますが、かなり的を得た回答になっています。
そして、日本語でも大丈夫そうでした。

気づいたこと

思考言語

インデックス作成時の画面出力を見る限りでは、nodeのdescriptionが英語になっていたりするので、英語で思考している可能性があります。
付属のプロンプトは指示も例示も全て英語なので、プロンプトを改良すると日本語にできるかもしれません。

🚀 create_final_nodes
    level      title      type                                        description  ... graph_embedding                 top_level_node_id  x y
0       0    "おじいさん"  "PERSON"  おじいさん (Grandfather) is an elderly character wh...  ...            None  b45241d70f0e43fca764df95b2b81f77  0  0
1       0    "おばあさん"  "PERSON"  おばあさん is an elderly woman who plays a signific...  ...            None  4119fd06010c494caa07f439b333f4c5  0  0
2       0        "川"     "GEO"  "川 (river) is a geographical feature where おばあ...  ...            None  d3835bf3dda84ead99deadbeac5d0d7d  0  0
3       0        "桃"   "EVENT"  "\u6843" (peach) is a significant element in t...  ...            None  077d2820ae1845bcbb1803379a3d1eae  0  0

既存システムへの組み込み

GraphRAGは以下のような点で、既存のシステムに組み込むのは現段階では難しそうです。

  • Azureでの実行を前提としている部分が多い
  • データの保存形式がメモリ、ファイル(.parquet)、Azure blobの3択であり、既存のデータベースに載せる場合には自分で改造する必要がある
  • 検索もlangchainが対応していないので、自力でchainを定義する必要がある

おわりに

今回は出たばかりのGraphRAGを試してみましたが、思ったりよりも苦労せずに動作して良かったです。
まだ単純な例文でしか試していないですが、もう少し込み入った文章も入力してみて、従来のエンべディングの場合と比較してみようと思います。

また、GraphRAGはプロンプトの指示文や、例示文を書き換えることで、ドメイン適用も可能そうですので、そちらも試してみたいと思います。

HEROZ ASK を支えるインフラ技術(第2回)

はじめに

こんにちは、HEROZ ASK の開発チームです。

herozask.ai

今回のポストでは、このプロダクトの開発で活用しているインフラ技術を紹介したいと思います。

前回の記事

heroz-tech.hatenablog.jp

『BRIDGE』掲載記事

HEROZ ASK について取り上げていただきました。

Microsoft Azure』の情報収集

皆さんは、どのようにAzureを勉強していますか?今回は情報のキャッチアップ方法について紹介したいと思います。

Microsoft Build』

先日、米国 Microsoft 主催の年次開発者会議「Microsoft Build」(2024年5⽉21⽇‐23⽇ 米国時間)が開催されました。

build.microsoft.com

Microsoft Build Book of News」に、発表された主要なニュース項目がまとまっています。

news.microsoft.com

ただ、発表内容が多いため、キャッチアップが大変ですね

Microsoft Build Japan』

日本の開発者向けに「Microsoft Build Japan」(2024年6月27日‐28日)が開催されます。 1ヵ月程度遅れますが、まとまった日本語情報を提供していただけるのは助かります。

Microsoft Ignite

冬には、「Microsoft Ignite」(2024年11⽉18⽇‐22⽇ 米国時間)が開催されます。

ignite.microsoft.com

2023年は、「MICROSOFT IGNITE BOOK OF NEWS」に、発表された主要なニュース項目がまとまっています。

news.microsoft.com

Microsoft Ignite Japan』

2023年と同様に、日本の開発者向けに「Microsoft Ignite Japan」も開催されることでしょう。

Microsoft (有志)』の記事

Zennでは、Microsoft Azureをはじめとする最新技術情報が提供されています。 米国 Microsoft 主催の大規模なイベント直後は、Microsoft社員の方々により、一斉更新が始まります。

zenn.dev

Azure OpenAI Service の GPT-4o 対応をするのに、以下のブログ記事が参考になりました。

zenn.dev zenn.dev zenn.dev

Azure AI Searchについては、花ヶ﨑さんのQiitaが参考になります。

qiita.com qiita.com qiita.com

Azure OpenAI Serviceについては、蒲生さんのSpeaker Deckが参考になります。

『Azureの新機能』

AzureやAzure OpenAI Service の新機能をまとめたページがあります。 ただし、「Microsoft Build」などの大規模なイベントで発表された新機能の反映は多少遅れる印象があります。 また、日本語への翻訳が遅れる場合もあるため、英語ドキュメントのチェックも必要です。

azure.microsoft.com learn.microsoft.com

Microsoft Learn』

製品、キャリア パス別のトレーニングなど、公式ドキュメントがとても充実しています。

learn.microsoft.com

ただし、

  • あまりに膨大なため、何から手をつけていいかわからない。
  • 英語の自動翻訳のため、あまり頭に入ってこない。
  • どこまで勉強すれば、商用サービスを構築・運用できるのかわからない。

など、迷子になってしまうことはあるかもしれません。

ドキュメントと睨めっこしながら、実際に手を動かして、Azureのサービスを触ってみるということが大事だと思います。

『Azure コマンド ライン インターフェイス (CLI)』

Azure Portal から手を動かしたことは、なるべく効率化したいですよね。 Azure CLIチュートリアルやサンプルで自動化できないか試すことをお勧めします。

learn.microsoft.com

注意点としては、

  • Azure Portal から手作業でしか設定できない項目の方が多い
  • 新機能のアップデートで仕様が変わり、エラーになる場合がある
  • 削除したリソース次第で、論理削除(完全に削除されていない)となり、再作成するとエラーになる場合がある

などがあります。

コマンドラインで出来ること、手作業でしか設定できないことを把握した上で、どれくらい労力をかけるのが構築/運用フェーズでコスパがいいか、見極めが重要です。 Azure のリソースをコードで管理できるか試すのは、それからでも遅くないのではないかと思います。

learn.microsoft.com learn.microsoft.com

『日本マイクロソフト サポート情報』

他のクラウドでは一般的ですが、「Microsoft Learn」の記載にないことをやりたい場合、情報が不足していて困ることがあります。 そんな時に役立つのが、「Microsoft Azure テクニカルサポートチーム」のブログ記事です。

cssjpn.github.io

「Azure OpenAI Service」であれば、「Japan Azure Cognitive Services サポートチーム」のブログが参考になります。 自分が困っていることは、皆さん困っている印象があります。

jpaiblog.github.io

どうしてもわからなければ、あまり悩まずにAzure サポートにサポートケースを起票するのも手かもしれません。 ただし、漠然とした理解で質問しても、有効な回答は得られないかもしれません。質問の前に下調べして、十分に理解する努力は必要です。

azure.microsoft.com

最後に

今後も HEROZ は、クラウド技術と生成 AI を活用したサービス提供について、今後も技術的にさらに踏み込んだ内容を発信して行きたいと思います。本ブログをご愛顧いただければ幸いです。最後までお付き合いいただきありがとうございました。