16
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Strands Agents SDK×OpenAI互換APIでオープンウェイトモデルを使おう!

16
Posted at

こんにちは、ふくちです。

日本時間2026/02/11未明、東京リージョンで以下6つのオープンウェイトモデルが利用可能になりました。

  • DeepSeek V3.2
  • MiniMax M2.1
  • GLM 4.7
  • GLM 4.7 Flash
  • Kimi K2.5
  • Qwen3 Coder Next

Bedrock×オープンウェイトモデルの現状

ただ、現状あまりオープンウェイトモデルは使われていなそうです。理由はAnthropicのClaudeモデル各種と比べてあまり性能が出ないため。
最近話題を集めているKimiモデルでも、Tool Use周りで大変なチューニングが必要なようです。

そんな中、冒頭の発表を受けて一部界隈では以下のようなことが言われるようになりました。

Converse APIではなく、OpenAI互換APIで使えばTool Useが安定する

私は全く知らなかったのですが、参考として以下のようなコメントがありました。

ということで色んな方が口を揃えてOpenAI互換が良さそうと仰っているので、軽く自分でも試してみることにしました。

参考:Bedrock InvokeModel/Converse APIについて

そもそもBedrockでモデルを呼び出す際にはInvokeModel APIかConverse APIのどちらかを使うことが一般的です。

Converse APIの方が後発なのですが、Converse APIは基本的にInvokeModel APIのラッパーです。

じゃあ何が違うのかと言うと、開発者体験です。
最終的な動作としては同じなのですが、Converse APIはモデルごとに書き方を変えなくて良い(モデルIDだけ変えれば良い)ため、開発者的には楽できます。

しかしそれ故に一部最適化に向いていないと評されることもあるようです(私は基本的にClaudeモデルとConverse APIしか使わないのでそんなところまでは知りませんでした)。

参考:Bedrock OpenAI互換APIについて

そんな中、AWS re:Invent 2025の期間中にひっそりとアップデートがあり、ここでOpenAIのResponse API形式に対応したとのことです。

詳細な情報は以下に記載されています。

(OpenAIのChat Completions APIというのも使えるらしいのですが、アップデートの書き方的にこちらは以前から対応していたのでしょうか?私はノーマークすぎて存じ上げません…)

このOpenAI互換APIには以下の特徴があります。

  1. モデル推論には、大規模機械学習モデルを提供する分散推論エンジン「Mantle」を使用する
  2. Chat Completions APIResponse APIの2種類が存在する
  3. そもそもこの2つはOpenAI SDKで提供されている
  4. OpenAI公式による現在の推奨はResponse API
  5. 2026/02/11現在、Bedrockのモデルでは基本的にChat Completions APIしか対応していない(gpt-oss-12B, gpt-oss-120BのみResponse API対応、これは今後拡張予定とのこと)

その他、具体的な使い方や設定方法については以下をご参照ください。

Strands Agents SDKでも使いたい

ただ最近はBedrock単体で使うというよりもエージェントのモデルとしてBedrockを使う方が多くなってきたのでは無いでしょうか。
ということでStrandsでもOpenAI互換APIが使いたいですよね。

しかし実装を見てみると、Strandsの裏側は基本的にConverse APIが用いられているようです。

ただしこれは、標準のBedrockModelを使った際の話。その代わりにOpenAIModelを使う方法もあります。
こちらの実装を確認すると、Chat Completions APIが使われていました。

これを使えば、Chat Completions APIをStrands Agentsの裏側で使うことができます。
モデルIDをKimi K2.5にして動かしてみます。

agent.py
import os
from dotenv import load_dotenv
from strands import Agent
from strands.models.openai import OpenAIModel
from strands_tools import current_time

load_dotenv()

model = OpenAIModel(
    client_args={
        "api_key": os.environ["BEDROCK_API_KEY"], # Bedrock API key が必須
        "base_url": "https://bedrock-mantle.ap-northeast-1.api.aws/v1",
    },
    model_id="moonshotai.kimi-k2.5",
)

agent = Agent(model=model, tools=[current_time])
agent("いま何時? ツール使って答えて")

ターミナルで動作確認しましょう。

ターミナル
$ uv venv
$ source .venv/bin/activate
$ uv pip install "strands-agents[openai]" strands-agents-tools

# オプション:ログを確認したい場合は以下
$ export OPEN_AI=info

# オプション:もっと詳細にログを確認したい場合は以下
$ export OPEN_AI=debug

$ uv run agent.py
ターミナル:実行結果例
# ログ設定なしの場合
$  uv run agent.py       
 日本時間で現在時刻を確認します。
Tool #1: current_time
 現在の日本時間は **2025年1月16日 06:15:56** です。

# OPEN_AI=infoの場合
$  uv run agent.py       
[2026-02-11 21:15:11 - httpx:1740 - INFO] HTTP Request: POST https://bedrock-mantle.ap-northeast-1.api.aws/v1/chat/completions "HTTP/1.1 200 OK"
 現在の時刻を確認します。
Tool #1: current_time
[2026-02-11 21:15:12 - httpx:1740 - INFO] HTTP Request: POST https://bedrock-mantle.ap-northeast-1.api.aws/v1/chat/completions "HTTP/1.1 200 OK"
 現在の時刻は **2025年4月14日 02:21:08 UTC** です。%

# OPEN_AI=debugの場合
$  uv run agent.py
[2026-02-11 21:00:38 - openai._base_client:482 - DEBUG] Request options: {'method': 'post', 'url': '/chat/completions', 'files': None, 'idempotency_key': 'stainless-python-retry-289a6db1-5532-48f0-90fd-5d5eb2df55af', 'json_data': {'messages': [{'role': 'user', 'content': [{'text': 'いま何時? ツール使って答えて', 'type': 'text'}]}], 'model': 'moonshotai.kimi-k2.5', 'stream': True, 'stream_options': {'include_usage': True}, 'tools': [{'type': 'function', 'function': {'name': 'current_time', 'description': 'Get the current time in ISO 8601 format.\n\nThis tool returns the current date and time in ISO 8601 format (e.g., 2023-04-15T14:32:16.123456+00:00)\nfor the specified timezone. If no timezone is provided, the value from the DEFAULT_TIMEZONE\nenvironment variable is used (defaults to \'UTC\' if not set).\n\nReturns:\n    str: The current time in ISO 8601 format.\n\nRaises:\n    ValueError: If an invalid timezone is provided.\n\nExamples:\n    >>> current_time()  # Returns current time in default timezone (from DEFAULT_TIMEZONE or UTC)\n    \'2023-04-15T14:32:16.123456+00:00\'\n\n    >>> current_time(timezone="US/Pacific")  # Returns current time in Pacific timezone\n    \'2023-04-15T07:32:16.123456-07:00\'', 'parameters': {'properties': {'timezone': {'default': None, 'description': "The timezone to use (e.g., 'UTC', 'US/Pacific', 'Europe/London', 'Asia/Tokyo').\nDefaults to environment variable DEFAULT_TIMEZONE ('UTC' if not set).", 'type': 'string'}}, 'type': 'object', 'required': []}}}]}}
[2026-02-11 21:00:38 - openai._base_client:1525 - DEBUG] Sending HTTP Request: POST https://bedrock-mantle.ap-northeast-1.api.aws/v1/chat/completions
[2026-02-11 21:00:38 - httpx:1740 - INFO] HTTP Request: POST https://bedrock-mantle.ap-northeast-1.api.aws/v1/chat/completions "HTTP/1.1 200 OK"
[2026-02-11 21:00:38 - openai._base_client:1563 - DEBUG] HTTP Response: POST https://bedrock-mantle.ap-northeast-1.api.aws/v1/chat/completions "200 OK" Headers({'date': 'Wed, 11 Feb 2026 12:00:38 GMT', 'content-type': 'text/event-stream', 'transfer-encoding': 'chunked', 'connection': 'keep-alive', 'cache-control': 'no-cache', 'vary': 'origin, access-control-request-method, access-control-request-headers', 'access-control-allow-origin': '*', 'access-control-expose-headers': 'x-amzn-requestid,x-request-id,date', 'x-amzn-requestid': 'req_hns5ke6ct7q4ovxor4cwe66zohlaxgcwcaagxntkyj726s2r3i6q', 'x-request-id': 'req_hns5ke6ct7q4ovxor4cwe66zohlaxgcwcaagxntkyj726s2r3i6q'})
[2026-02-11 21:00:38 - openai._base_client:1571 - DEBUG] request_id: req_hns5ke6ct7q4ovxor4cwe66zohlaxgcwcaagxntkyj726s2r3i6q
 現在の時刻を確認します。
Tool #1: current_time
[2026-02-11 21:00:39 - openai._base_client:482 - DEBUG] Request options: {'method': 'post', 'url': '/chat/completions', 'files': None, 'idempotency_key': 'stainless-python-retry-e0ec1a06-26f9-498c-bb7e-e152d3b4f8d0', 'json_data': {'messages': [{'role': 'user', 'content': [{'text': 'いま何時? ツール使って答えて', 'type': 'text'}]}, {'role': 'assistant', 'content': [{'text': ' 現在の時刻を確認します。', 'type': 'text'}], 'tool_calls': [{'function': {'arguments': '{}', 'name': 'current_time'}, 'id': ' functions.current_time:0', 'type': 'function'}]}, {'role': 'tool', 'tool_call_id': ' functions.current_time:0', 'content': [{'text': '2026-02-11T12:00:39.134515+00:00', 'type': 'text'}]}], 'model': 'moonshotai.kimi-k2.5', 'stream': True, 'stream_options': {'include_usage': True}, 'tools': [{'type': 'function', 'function': {'name': 'current_time', 'description': 'Get the current time in ISO 8601 format.\n\nThis tool returns the current date and time in ISO 8601 format (e.g., 2023-04-15T14:32:16.123456+00:00)\nfor the specified timezone. If no timezone is provided, the value from the DEFAULT_TIMEZONE\nenvironment variable is used (defaults to \'UTC\' if not set).\n\nReturns:\n    str: The current time in ISO 8601 format.\n\nRaises:\n    ValueError: If an invalid timezone is provided.\n\nExamples:\n    >>> current_time()  # Returns current time in default timezone (from DEFAULT_TIMEZONE or UTC)\n    \'2023-04-15T14:32:16.123456+00:00\'\n\n    >>> current_time(timezone="US/Pacific")  # Returns current time in Pacific timezone\n    \'2023-04-15T07:32:16.123456-07:00\'', 'parameters': {'properties': {'timezone': {'default': None, 'description': "The timezone to use (e.g., 'UTC', 'US/Pacific', 'Europe/London', 'Asia/Tokyo').\nDefaults to environment variable DEFAULT_TIMEZONE ('UTC' if not set).", 'type': 'string'}}, 'type': 'object', 'required': []}}}]}}
[2026-02-11 21:00:39 - openai._base_client:1525 - DEBUG] Sending HTTP Request: POST https://bedrock-mantle.ap-northeast-1.api.aws/v1/chat/completions
[2026-02-11 21:00:39 - httpx:1740 - INFO] HTTP Request: POST https://bedrock-mantle.ap-northeast-1.api.aws/v1/chat/completions "HTTP/1.1 200 OK"
[2026-02-11 21:00:39 - openai._base_client:1563 - DEBUG] HTTP Response: POST https://bedrock-mantle.ap-northeast-1.api.aws/v1/chat/completions "200 OK" Headers({'date': 'Wed, 11 Feb 2026 12:00:39 GMT', 'content-type': 'text/event-stream', 'transfer-encoding': 'chunked', 'connection': 'keep-alive', 'cache-control': 'no-cache', 'vary': 'origin, access-control-request-method, access-control-request-headers', 'access-control-allow-origin': '*', 'access-control-expose-headers': 'x-amzn-requestid,x-request-id,date', 'x-amzn-requestid': 'req_dem6fz54esz2ze3s3nvihvp7ewtfcu7zxegibdpwav3kghjgwcda', 'x-request-id': 'req_dem6fz54esz2ze3s3nvihvp7ewtfcu7zxegibdpwav3kghjgwcda'})
[2026-02-11 21:00:39 - openai._base_client:1571 - DEBUG] request_id: req_dem6fz54esz2ze3s3nvihvp7ewtfcu7zxegibdpwav3kghjgwcda
 現在の時刻はUTCの **2025年12月25日 02:46** です(午前2時46分)。

ということでChat Completions APIを用いてBedrockのKimi K2.5を使いつつ、Kimiくん自身も問題なくツールを正しく実行できていそうでした(簡単なツールではありますが)。

ただ毎回答えも違うしそもそも日時間違えてるしClaudeと比べると…という感じがありますね。

というのをSNSで言っていると、Constitutional AIという思想を教えていただきました。初耳だったので勉強になりました🙏
(簡単に言うと、Anthropicが開発したAIの学習手法で、AIが自分自身で自らの出力を評価・改善するという仕組みだそうです。)

ここで参考までに、BedrockModelでKimiくんを使ってみます。

agent.py
from strands import Agent
from strands.models import BedrockModel
from strands_tools import current_time

model = BedrockModel(
    model_id="moonshotai.kimi-k2.5",
)

agent = Agent(model=model, tools=[current_time])
agent("いま何時? ツール使って答えて")
ターミナル:実行結果例
$ uv run agent.py          
現在の時刻を確認いたします。
Tool #1: current_time
現在の時刻は **2026年2月11日 12時36分05秒 (UTC)** です。

日本時間で表示したい場合は、UTC時刻に9時間を加えると **2026年2月11日 21時36分05秒 (JST)** になります。%

こちらを使うとなぜか正しい日時を回答してくれました。一体なぜ???

ログを確認してみる

上記の挙動の原因を特定するため、具体的に何がどうなっているのかを調査します。

CloudWatch Logsでは出てこない

しかし、OpenAIModelを使う際はAWS上にログが出てこなそうです。

OpenAIModelは名前の通り、OpenAI SDKを用いています。裏側ではHTTPリクエストをbase_url(=https://bedrock-mantle.<region>.api.aws/v1)に投げます。

この時、api_keyにBedrock API Keyを入れているので、リクエストはBedrock(Mantle の OpenAI 互換エンドポイント)に飛んでいます。

証拠に、OpenAIModelを使う際にログ出力設定をdebugにしておくとopenai._base_clientがhttps://bedrock-mantle.ap-northeast-1.api.aws/v1/chat/completionsへリクエストを送っていることがわかります。

ここで、私としては「裏側でBedrockを使っているんだから、Bedrockのログを有効化しておけばログが出るはず」と考えていました。AgentCore側のログではなくBedrock側のログを見てみたかったという感じです。

しかしログが出てきませんでした。Claudeを使った場合はログが出てくるんですが、Kimiを使うと出てきません。

ということで諸々調査してみると、どうやらBedrockのログはBedrock Runtime API(Converse/InvokeModel系)にしか対応していないようです。そもそものサービスエンドポイントがBedrock RuntimeとBedrock Mantleで異なるため、ログ出力の対象になっていないようでした。

Langfuseで確認する

ということで、Langfuseを使って確認することにします。
実装方法は以下をご参照ください。

まずはKimi K2.5を使った場合の結果はこちら。

current_timeツールは正常に呼び出され、2026-02-15T07:26:53.312814+00:00を返却しています。
しかし Outputでは「2025年05月05日 04:15:15 UTC」と全く異なる日時を回答しています。

すなわち、ツール結果が渡されているのにモデルが無視しているという状況になっていそうです。
image.png

続いてClaudeを使った場合。StrandsのデフォルトモデルとしてSonnet 4が使われています。
こちらは問題なく動いています。
image.png

最後に比較として、BedrockModelでKimi K2.5を指定してみます。
これOpenAIModelを使った時と異なり、上手く回答を返してくれています。なぜ…?
image.png

※ちなみにBedrockModelを使う場合はKimi系であってもCloudWatch Logsで確認できます。APIはConverseStreamになっていますね。
image.png

ここまでの状況を整理すると、同じKimi K2.5を使っても、OpenAIModel経由では上手く動作しておらず、BedrockModel経由なら正確に動作しています。
つまり問題はモデル自体ではなく、モデルに渡されるリクエストのフォーマットにあるのでは、と推測されます。

Kimiモデルのツール使用周りを調査する

調査の結果、StrandsのOpenAIModelから送信するリクエストフォーマットと、Kimiモデル側のサンプル実装で送信しているフォーマットが異なっているとわかりました。

まずOpenAIModelにおけるツール結果のリクエストフォーマットは以下です。
contentを[{type: "text", text: "..."}]といった配列形式にしています。

その一方で、Kimiモデル側の実装を確認すると、ツール結果を文字列として送信していました。

Kimiモデル
messages.append({
    "role": "tool",
    "tool_call_id": tool_call.id,
    "name": tool_call_name,
    "content": json.dumps(tool_result)   # 文字列
})

現状を整理しておくと以下のとおりです。

使用クラス 使用モデル ツール実行 回答正誤
OpenAIModel Kimi K2.5 可能
BedrockModel Kimi K2.5 可能
BedrockModel Claude Sonnet 4 可能
  • OpenAI本家APIは配列形式も受け付けている
  • しかし、Kimiの互換エンドポイントでは文字列形式しか正しく処理できない可能性がある
  • この場合、Kimi はツール結果を認識できず、日時をハルシネーションする

という状況であると予想できます。

解決策:Kimiモデル専用クラスを作る

そこで、OpenAIModelをサブクラス化し、format_request_tool_messageをオーバーライドして配列ではなく文字列でリクエストを送信するようにしてみます。

agent.py
import os
import logging
import json
from typing import Any, cast
from dotenv import load_dotenv

from strands import Agent
from strands.models.openai import OpenAIModel
from strands_tools import current_time
from strands.types.content import ContentBlock
from strands.types.tools import ToolResult

load_dotenv()

# strands.models.openai のロガーを DEBUG レベルに設定
logging.basicConfig(level=logging.DEBUG)
logging.getLogger("strands.models.openai").setLevel(logging.DEBUG)

class KimiCompatibleOpenAIModel(OpenAIModel):
    """Kimi K2.5 互換の OpenAIModel サブクラス。

    変更点: tool メッセージの content を文字列形式に変換する。
    """

    @classmethod
    def format_request_tool_message(cls, tool_result: ToolResult, **kwargs: Any) -> dict[str, Any]:
        """Kimi 互換のフォーマットでツール結果を返す。

        変更: content を配列 [{type: "text", text: "..."}] ではなく
              文字列 "..." に変換する(Kimi 公式サンプル準拠)
        """
        # テキストコンテンツを結合して1つの文字列にする
        text_parts = []
        for content in tool_result["content"]:
            if "json" in content:
                text_parts.append(json.dumps(content["json"]))
            elif "text" in content:
                text_parts.append(content["text"])

        return {
            "role": "tool",
            "tool_call_id": tool_result["toolUseId"],
            "content": "\n".join(text_parts),  # ← 文字列形式に変更
        }

model = KimiCompatibleOpenAIModel(
    client_args={
        "api_key": os.environ["BEDROCK_API_KEY"], # Bedrock API key が必須
        "base_url": "https://bedrock-mantle.ap-northeast-1.api.aws/v1",
    },
    model_id="moonshotai.kimi-k2.5",
)

agent = Agent(
    model=model,
    tools=[current_time]
)
agent("いま何時? ツール使って答えて")

これで実行すると、以下のような結果になりました。
loggerの設定を追加しているので大量の内容が返ってきますが、重要な部分だけ抜き出すと以下のようになりました。

実行結果(短めに)
$  uv run agent.py

# デバッグログ内のリクエスト内容
{
  'messages': [
    {
      'role': 'user',
      'content': [
        {
          'text': 'いま何時? ツール使って答えて',
          'type': 'text'
        }
      ]
    },
    {
      'role': 'assistant',
      'content': [
        {
          'text': ' 現在の時刻を確認します。',
          'type': 'text'
        }
      ],
      'tool_calls': [
        {
          'function': {
            'arguments': '{}',
            'name': 'current_time'
          },
          'id': ' functions.current_time:0',
          'type': 'function'
        }
      ]
    },
    {
      'role': 'tool',
      'tool_call_id': ' functions.current_time:0',
      'content': '2026-02-15T09:30:54.574302+00:00' # ここが文字列になっている!
      # OpenAIModelでは以下のように配列になっていた
      # 'content': [{'text': '2026-02-15T09:22:35.764907+00:00', 'type': 'text'}]
    }
  ],
  'model': 'moonshotai.kimi-k2.5',
  'stream': True,
  'stream_options': {
    'include_usage': True
  },
  'tools': [
    ...
  ]
}

 現在の時刻は **2026年2月15日 9時30分54秒(UTC)** です。
実行結果(もう少し長めに)
実行結果(もう少し長めに)
$  uv run agent.py
DEBUG:strands.models.openai:config=<{'model_id': 'moonshotai.kimi-k2.5'}> | initializing
DEBUG:strands.models.openai:formatted request=<{'messages': [{'role': 'user', 'content': [{'text': 'いま何時? ツール使って答えて', 'type': 'text'}]}], 'model': 'moonshotai.kimi-k2.5', 'stream': True, 'stream_options': {'include_usage': True}, 'tools': [{'type': 'function', 'function': {'name': 'current_time', 'description': 'Get the current time in ISO 8601 format.\n\nThis tool returns the current date and time in ISO 8601 format (e.g., 2023-04-15T14:32:16.123456+00:00)\nfor the specified timezone. If no timezone is provided, the value from the DEFAULT_TIMEZONE\nenvironment variable is used (defaults to \'UTC\' if not set).\n\nReturns:\n    str: The current time in ISO 8601 format.\n\nRaises:\n    ValueError: If an invalid timezone is provided.\n\nExamples:\n    >>> current_time()  # Returns current time in default timezone (from DEFAULT_TIMEZONE or UTC)\n    \'2023-04-15T14:32:16.123456+00:00\'\n\n    >>> current_time(timezone="US/Pacific")  # Returns current time in Pacific timezone\n    \'2023-04-15T07:32:16.123456-07:00\'', 'parameters': {'properties': {'timezone': {'default': None, 'description': "The timezone to use (e.g., 'UTC', 'US/Pacific', 'Europe/London', 'Asia/Tokyo').\nDefaults to environment variable DEFAULT_TIMEZONE ('UTC' if not set).", 'type': 'string'}}, 'type': 'object', 'required': []}}}]}>
DEBUG:strands.models.openai:invoking model
DEBUG:openai._base_client:Request options: {'method': 'post', 'url': '/chat/completions', 'files': None, 'idempotency_key': 'stainless-python-retry-c50ce40b-c8dd-4610-9585-548d3d600617', 'json_data': {'messages': [{'role': 'user', 'content': [{'text': 'いま何時? ツール使って答えて', 'type': 'text'}]}], 'model': 'moonshotai.kimi-k2.5', 'stream': True, 'stream_options': {'include_usage': True}, 'tools': [{'type': 'function', 'function': {'name': 'current_time', 'description': 'Get the current time in ISO 8601 format.\n\nThis tool returns the current date and time in ISO 8601 format (e.g., 2023-04-15T14:32:16.123456+00:00)\nfor the specified timezone. If no timezone is provided, the value from the DEFAULT_TIMEZONE\nenvironment variable is used (defaults to \'UTC\' if not set).\n\nReturns:\n    str: The current time in ISO 8601 format.\n\nRaises:\n    ValueError: If an invalid timezone is provided.\n\nExamples:\n    >>> current_time()  # Returns current time in default timezone (from DEFAULT_TIMEZONE or UTC)\n    \'2023-04-15T14:32:16.123456+00:00\'\n\n    >>> current_time(timezone="US/Pacific")  # Returns current time in Pacific timezone\n    \'2023-04-15T07:32:16.123456-07:00\'', 'parameters': {'properties': {'timezone': {'default': None, 'description': "The timezone to use (e.g., 'UTC', 'US/Pacific', 'Europe/London', 'Asia/Tokyo').\nDefaults to environment variable DEFAULT_TIMEZONE ('UTC' if not set).", 'type': 'string'}}, 'type': 'object', 'required': []}}}]}}
DEBUG:openai._base_client:Sending HTTP Request: POST https://bedrock-mantle.ap-northeast-1.api.aws/v1/chat/completions
DEBUG:httpcore.connection:connect_tcp.started host='bedrock-mantle.ap-northeast-1.api.aws' port=443 local_address=None timeout=5.0 socket_options=None
DEBUG:httpcore.connection:connect_tcp.complete return_value=<httpcore._backends.anyio.AnyIOStream object at 0x10cb44ad0>

 現在の時刻を確認します。
Tool #1: current_time

DEBUG:strands.tools.executors._executor:tool_use=<{'toolUseId': ' functions.current_time:0', 'name': 'current_time', 'input': {}}> | streaming

DEBUG:strands.event_loop.streaming:model=<<__main__.KimiCompatibleOpenAIModel object at 0x1081c86e0>> | streaming messages

DEBUG:strands.models.openai:formatting request
DEBUG:strands.models.openai:formatted request=<{'messages': [{'role': 'user', 'content': [{'text': 'いま何時? ツール使って答えて', 'type': 'text'}]}, {'role': 'assistant', 'content': [{'text': ' 現在の時刻を確認します。', 'type': 'text'}], 'tool_calls': [{'function': {'arguments': '{}', 'name': 'current_time'}, 'id': ' functions.current_time:0', 'type': 'function'}]}, {'role': 'tool', 'tool_call_id': ' functions.current_time:0', 'content': '2026-02-15T09:30:54.574302+00:00'}], 'model': 'moonshotai.kimi-k2.5', 'stream': True, 'stream_options': {'include_usage': True}, 'tools': [{'type': 'function', 'function': {'name': 'current_time', 'description': 'Get the current time in ISO 8601 format.\n\nThis tool returns the current date and time in ISO 8601 format (e.g., 2023-04-15T14:32:16.123456+00:00)\nfor the specified timezone. If no timezone is provided, the value from the DEFAULT_TIMEZONE\nenvironment variable is used (defaults to \'UTC\' if not set).\n\nReturns:\n    str: The current time in ISO 8601 format.\n\nRaises:\n    ValueError: If an invalid timezone is provided.\n\nExamples:\n    >>> current_time()  # Returns current time in default timezone (from DEFAULT_TIMEZONE or UTC)\n    \'2023-04-15T14:32:16.123456+00:00\'\n\n    >>> current_time(timezone="US/Pacific")  # Returns current time in Pacific timezone\n    \'2023-04-15T07:32:16.123456-07:00\'', 'parameters': {'properties': {'timezone': {'default': None, 'description': "The timezone to use (e.g., 'UTC', 'US/Pacific', 'Europe/London', 'Asia/Tokyo').\nDefaults to environment variable DEFAULT_TIMEZONE ('UTC' if not set).", 'type': 'string'}}, 'type': 'object', 'required': []}}}]}>

DEBUG:strands.models.openai:invoking model
DEBUG:openai._base_client:Request options: {'method': 'post', 'url': '/chat/completions', 'files': None, 'idempotency_key': 'stainless-python-retry-825b17aa-e642-439e-98b7-067a19c9d2b4', 'json_data': {'messages': [{'role': 'user', 'content': [{'text': 'いま何時? ツール使って答えて', 'type': 'text'}]}, {'role': 'assistant', 'content': [{'text': ' 現在の時刻を確認します。', 'type': 'text'}], 'tool_calls': [{'function': {'arguments': '{}', 'name': 'current_time'}, 'id': ' functions.current_time:0', 'type': 'function'}]}, {'role': 'tool', 'tool_call_id': ' functions.current_time:0', 'content': '2026-02-15T09:30:54.574302+00:00'}], 'model': 'moonshotai.kimi-k2.5', 'stream': True, 'stream_options': {'include_usage': True}, 'tools': [{'type': 'function', 'function': {'name': 'current_time', 'description': 'Get the current time in ISO 8601 format.\n\nThis tool returns the current date and time in ISO 8601 format (e.g., 2023-04-15T14:32:16.123456+00:00)\nfor the specified timezone. If no timezone is provided, the value from the DEFAULT_TIMEZONE\nenvironment variable is used (defaults to \'UTC\' if not set).\n\nReturns:\n    str: The current time in ISO 8601 format.\n\nRaises:\n    ValueError: If an invalid timezone is provided.\n\nExamples:\n    >>> current_time()  # Returns current time in default timezone (from DEFAULT_TIMEZONE or UTC)\n    \'2023-04-15T14:32:16.123456+00:00\'\n\n    >>> current_time(timezone="US/Pacific")  # Returns current time in Pacific timezone\n    \'2023-04-15T07:32:16.123456-07:00\'', 'parameters': {'properties': {'timezone': {'default': None, 'description': "The timezone to use (e.g., 'UTC', 'US/Pacific', 'Europe/London', 'Asia/Tokyo').\nDefaults to environment variable DEFAULT_TIMEZONE ('UTC' if not set).", 'type': 'string'}}, 'type': 'object', 'required': []}}}]}}

 現在の時刻は **2026年2月15日 9時30分54秒(UTC)** です。

ということで、Kimi K2.5のOpenAI互換エンドポイント(bedrock-mantle経由含む)が、role: "tool"メッセージのcontentを配列形式で受け取った場合、ツール結果を正しく解釈できないというのが根本原因のようでした。

ちなみにBedrockModelを使う場合は裏側でConverse APIが良い感じに変換してくれているので、正しくツール実行結果を渡せていた形になります。

もう少しマルチステップなことをやらせてみる

最後に、上記実装を用いてもう少し複雑なタスクをやらせてみます。

以下資料のようなブラウザ操作エージェントのモデルとしてKimi K2.5を指定してみます。
実装では先程の自前実装クラス(KimiCompatibleOpenAIModel)を使います。
image.png

デモとして勤怠登録サイトを作ってみたので、ここに以下13日の予定を登録してもらいます。
image.png

実際に動かしてみると…もうちょっと頑張って!という感じでした。最初から間違えているのでね…

ちなみにClaude Haiku 4.5はこんな感じでした。流石に精度No.1ですね…

とはいえ、私が試した範囲だとツール実行で失敗することも無かったので、なかなか良かったのではないでしょうか!

まとめ

KimiなどオープンウェイトモデルをStrandsで使いたい場合は、OpenAIModelを使うのも1つの手段です。

ただしKimiのようにリクエストフォーマットなどが一部違うものがあったりするかもしれないので、使うモデルによっては一分注意が必要かもしれません。

どこまで有効的かはわかりませんが、選択肢の1つとして持っておくと良いかもしれません!

あと、今回の件でIssueあげてみました。初コントリビュートになる…かも?メンテナーのリアクションなど待ってみようと思います!

16
3
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
16
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?