はじめに
ついにOCI Generative AI の Cohere command R + が使用できるようになりました!!
CohereのFunction Callingを使うと生成されたテキストやデータに対して追加の処理や特定のアクションを行うことができます。
今回はこちらの記事を応用してOCI Generative AI の
Cohere command R/R +を使ってFunction Callingを試す+回答の違い
を見比べていきたいと思います。
シナリオは
- 質問の郵便番号から緯度と経度を算出する
- Function Callingを使って
OCI Generative AI について
OCI Generative AI(Oracle Cloud Infrastructure Generative AI)は、OCIのSaaSとしてお使いいただける生成AI(Generative AI)サービスの一部です。
OCI Generative AIサービスのファンデーションモデル(事前学習済みモデル)では、
- チャットモデル:会話型回答を行うモデル
- cohere.command R +
- cohere.command R
- meta.llama-3
が提供されており、今回はこちらの Cohere command R/R+を使用します。
その他にも事前学習済みモデルとして、
- テキスト生成モデル:テキストを生成したり、テキストから情報を抽出するモデル
- テキスト要約モデル:指示した形式でテキストを要約するモデル
- Embed(埋め込み)モデル:ベクトル変換を行うモデル
を使用することができます。
Cohere command R / R +とは
Command RとCommand R+はCohere社が開発したエンタープライズ向けのLLMです。
R / R +とも日本語も含め10カ国語対応しています。
Cohereのドキュメントには、
- Command R+:複雑なRAGや複数ステップのツール使用するワークフローに向く
- Command R:単純なRAGや単一ステップのツール使用と価格が重要な考慮事項となるアプリケーションに最適
とあります。
特に Command R +は、Cohere BlogにてGPT4-turboと性能を比較したベンチマークもあり、低コストで高い精度を実現したモデルといえます。
実装してみた
環境
今回はこちらの環境で構築していきます。
- Python 3.12.2
1. 必要なライブラリを設定
import oci
from oci.auth.signers import InstancePrincipalsSecurityTokenSigner
from oci.generative_ai_inference.generative_ai_inference_client import GenerativeAiInferenceClient
from oci.generative_ai_inference.models import (
ChatDetails,
OnDemandServingMode,
CohereChatRequest,
CohereTool,
CohereParameterDefinition,
CohereToolCall,
CohereToolResult
)
2. OCI認証設定を行う
import os
で環境変数からcompartment_idやendpointを指定していただいても問題ないです。
compartment_id ="ocid1.compartment.***********************************"
endpoint = "https://inference.generativeai.us-chicago-1.oci.oraclecloud.com"
config = oci.config.from_file(file_location='~/.oci/config', profile_name="CHICAGO")
signer = InstancePrincipalsSecurityTokenSigner()
## AIクライアント作成
generative_ai_inference_client = GenerativeAiInferenceClient(config = {}, signer = signer, service_endpoint = endpoint)
3. まずはcommmand R /R+単体での回答を見る
model_id
を変えてそれぞれ回答を見てみます。
- command Rの場合:cohere.command-r-16k v1.2
- command R +の場合:cohere.command-r-plus
と、モデルを指定しています。
response = generative_ai_inference_client.chat(
chat_details=ChatDetails(
compartment_id=compartment_id,
serving_mode=OnDemandServingMode(
model_id="cohere.command-r-plus"
),
chat_request=CohereChatRequest(
message="107-0061の緯度と経度を教えてください",
max_tokens=200,
)
)
)
print(f"response text: {response.data.chat_response.text}")
ちなみに、上記の国土地理院のAPIを使って〒107-0061(東京都港区北青山)の緯度と経度を調べると
[139.720947, 35.675377]
となります。
それぞれの回答
- Command R
経度0061度、緯度107度です。
- Command R +
107-0061 は日本の住所で、緯度と経度はおよそ 35.6886°N, 139.7503°E です。
Command Rは見るからにハルシネーションを起こしています。(笑)
Command R +は誤差が0.01度以内と少なく、単体でも比較的精度が高いことが分かります。
4. API を使う関数の登録
下記2つの関数を登録します。
- query_address_from_postal_code
- get_adress_geometory
import requests
import urllib
def query_address_from_postal_code(postal_code: str) -> dict:
if re.match("^[0-9]{3}-[0-9]{4}$", postal_code):
postal_code = postal_code.replace("-", "")
headers = {"content-type": "application/json"}
response = requests.get(f"https://jp-postal-code-api.ttskch.com/api/v1/{postal_code}.json", headers=headers)
data_ja = response.json()["addresses"][0]["ja"]
if response.status_code == 200:
return {
"address": f"{data_ja['prefecture']}{data_ja['address1']}{data_ja['address2']}"
}
else:
return {
"address": "与えられた郵便番号に合致する住所は見つかりませんでした"
}
def get_address_geometry(address: str):
base_url = "https://msearch.gsi.go.jp/address-search/AddressSearch?q="
encoded_address = urllib.parse.quote(address)
url = base_url + encoded_address
response = requests.get(url)
if response.status_code == 200:
data = response.json()
if data and len(data) > 0:
coordinates = data[0]["geometry"]["coordinates"]
return coordinates # [経度, 緯度] のリストを返す
else:
print("与えられた住所から住所を算出できませんでした")
else:
print(f"リクエストに失敗しました。status code: {response.status_code}")
5. Toolsを使って登録
CohereのToolsを使い、先ほど作成した関数をツールとして登録します。
こちらが必須パラメータとなっています。
- name
- description
- parameter_definitions
また、functions_mapでToolsの関数名と4セクションでで作成した関数のマッピングを行います。
tools = [
CohereTool(
name = "query_address_from_postal_code",
description = "郵便番号を元に住所を検索します",
parameter_definitions = {
"postal_code": CohereParameterDefinition(
description = "この郵便番号を元に住所を検索します。",
type = "str",
is_required = True
)
}
),
CohereTool(
name = "get_adress_geometory",
description = "住所から緯度を検索します",
parameter_definitions = {
"address": CohereParameterDefinition(
description = "この住所をもとに緯度を検索します。",
type = "str",
is_required = True
)
}
)
]
functions_map = {
"query_address_from_postal_code": query_address_from_postal_code,
"get_adress_geometory":get_adress_geometory
}
6. 回答の確認
セクション3で書いたコードを書き換えます。
- preamble_override:チャットに事前情報としてタスクとコンテキスト、回答形式を指定します
- is_force_single_step:
有効化すると元のメッセージに直接応答する前に、1 つのステップで (潜在的に複数の) ツール呼び出しを発行
preamble_override = """
## タスクとコンテキスト
人々の質問やその他のリクエストにインタラクティブに答える手助けをする。2つの質問に答えてください。あらゆる種類のトピックについて、非常に幅広い質問を受けます。幅広い検索エンジンや類似のツールが用意されており、それらを使って答えを調べます。あなたは、ユーザーのニーズにできる限り応えることに集中すべきです。
## スタイルガイド
ユーザーから別の回答スタイルを要求されない限り、適切な文法とスペルを使い、完全な文章で回答してください。
"""
response = generative_ai_inference_client.chat(
chat_details=ChatDetails(
compartment_id=compartment_id,
serving_mode=OnDemandServingMode(
model_id="cohere.command-r-plus"
),
chat_request=CohereChatRequest(
message="107-0061の緯度と経度を教えてください",
max_tokens=400,
tools=tools,
preamble_override=preamble_override,
is_force_single_step=True
)
)
)
print(response.data)
response.dataの出力の確認
"tool_calls"にてquery_address_from_postal_codeとget_adress_geometory関数と紐づくパラメータが確認できます。
{
"chat_response": {
"api_format": "COHERE",
"chat_history": [],
"citations": null,
"documents": null,
"error_message": null,
"finish_reason": "COMPLETE",
"is_search_required": null,
"prompt": null,
"search_queries": null,
"text": "",
"tool_calls": [
{
"name": "query_address_from_postal_code",
"parameters": {
"postal_code": "107-0061"
}
},
{
"name": "get_adress_geometory",
"parameters": {
"address": "\u6771\u4eac\u90fd\u6e2f\u533a\u5317\u9752\u5c711\u4e01\u76ee"
}
}
...
},
"model_id": "cohere.command-r-plus",
"model_version": "1.2"
}
tool_calls に含まれるツール名とパラメータを使って、ツールを実行します。
tool_results = []
for tool_call in response.data.chat_response.tool_calls:
output = functions_map[tool_call.name](**tool_call.parameters)
outputs = [output]
tool_results.append(
CohereToolResult(
call=CohereToolCall(
name=tool_call.name,
parameters=tool_call.parameters
),
outputs=outputs
)
)
print(tool_results)
7.最終回答の確認
response.data.chat_response.textresponse.data.chat_response.textの回答を確認してみます。
response = generative_ai_inference_client.chat(
chat_details=ChatDetails(
compartment_id=compartment_id,
serving_mode=OnDemandServingMode(
model_id="cohere.command-r-plus"
),
chat_request=CohereChatRequest(
message="107-0061の緯度と経度を教えてください",
chat_history=response.data.chat_response.chat_history,
max_tokens=200,
is_force_single_step=True,
tools=tools,
tool_results=tool_results,
preamble_override=preamble_override
)
)
)
print(response.data.chat_response.text)
それぞれのモデルの最終回答
求める回答:東経 139.720947 北緯 35.675377
- Command R
東京都港区北青山の郵便番号 107-0061 の緯度経度は、北緯35.675、東経139.720です。
- Command R +
郵便番号 107-0061 は、東京都港区北青山のエリアに対応しているようです。緯度と経度は、北緯35.67538、東経139.72095です。
Function Callingの結果を見ると
特にCommand Rの回答がかなりよくなることが確認できました!!
Command R +の回答も日本の住所→東京都北青山と具体的になりました。
裏話
実は、message="107-0061の住所を求め、その住所の緯度と経度を教えてください"
というプロンプトを最初は書いていましたが、郵便番号APIだけが使われて国土地理院APIは
使用されずハルシネーションを起こしていました・・・涙
適切な入力プロンプトにすることがFunction Callingにおいて大事なんだなと・・・
(絶対最初のプロンプトの方がいいと思うんだけどな^^)
もし、うまくFunction Callingできない場合は入力プロンプトを変えて試してみてください。
超おまけ)Command R+の関西弁が好きすぎる
preamble_overrideのタスクを書き直します。
preamble_override = """
## タスクとコンテキスト
あなたは陽気なアシスタントです。関西弁で話してください。
"""
response = generative_ai_inference_client.chat(
chat_details=ChatDetails(
compartment_id=compartment_id,
serving_mode=OnDemandServingMode(
model_id="cohere.command-r-plus"
),
chat_request=CohereChatRequest(
message="107-0061の緯度と経度を教えてください",
max_tokens=400,
tools=tools
preamble_override=preamble_override,
is_force_single_step=True
)
)
)
print(response.data.chat_response.text)
結果
ええやんええやん!調べた結果、107-0061は東京都港区北青山一丁目に対応しとって、緯度経度はおおよそ次の値やな。
緯度: 35.669076
経度: 139.717096
青山通りと外苑西通りの交差点の近くや。近くに青山霊園もあるで。
私ほんとにプログラミング苦手なんですが、初めて楽しいと思えました。^^
元気出るので疲れたときのCommand R +、関西弁お勧めです。