0xf

日記だよ

コンテナにWhisperのモデルを焼き込む

諸事情によりコンテナでWhisperを実行したくなったと思いねえ。しかし初回ダウンロードにはとても時間がかかる。毎回ロードするのがわかっているならモデルファイルは焼いておきたい。

FROM python:3.13-trixie

ENV DEBIAN_FRONTEND=noninteractive \
    WHISPER_CACHE_DIR=/opt/whisper-cache \
    PYTHONUNBUFFERED=1

RUN apt-get update \
    && apt-get install -y --no-install-recommends \
        ffmpeg \
        build-essential \
        pkg-config \
        libavformat-dev \
        libavcodec-dev \
        libavdevice-dev \
        libavutil-dev \
        libavfilter-dev \
        libswscale-dev \
        libswresample-dev \
    && rm -rf /var/lib/apt/lists/*

RUN pip install --no-cache-dir faster-whisper

RUN mkdir -p "${WHISPER_CACHE_DIR}" \
    && python - <<'PY'
import os
from faster_whisper import download_model

model_name = "large-v3"
download_root = os.environ["WHISPER_CACHE_DIR"]
download_model(model_name, output_dir=download_root)
PY

WORKDIR /app

ENV WHISPER_MODEL_NAME=large-v3

CMD ["python"]

イメージにはダウンロードしたモデルが保存されている。

root@9b61b0c2018b:/app# ls -la /opt/whisper-cache/
total 3018420
drwxr-xr-x 3 root root       4096 Nov  4 06:12 .
drwxr-xr-x 1 root root       4096 Nov  4 06:12 ..
drwxr-xr-x 3 root root       4096 Nov  4 06:08 .cache
-rw-r--r-- 1 root root       2394 Nov  4 06:08 config.json
-rw-r--r-- 1 root root 3087284237 Nov  4 06:12 model.bin
-rw-r--r-- 1 root root        340 Nov  4 06:08 preprocessor_config.json
-rw-r--r-- 1 root root    2480617 Nov  4 06:08 tokenizer.json
-rw-r--r-- 1 root root    1068114 Nov  4 06:08 vocabulary.json

便利...ではあるけど、別にコンテナのアーキテクチャに非依存なモデルなのでS3とかEFSにでもおいておけばいいような気はする。

strands-agents の slack 連携 - ブロック構文サポート

標準の slack_send_message ツールはブロックを受け付けてないので全てテキストモードで投稿される。

まあでも小さいコードなので、以下のようなパッチをいれてしまえば自然な動作になる。ブロック構文のバリデータとかは作りたくはなさそう。

diff --git a/src/strands_tools/slack.py b/src/strands_tools/slack.py
index e23a5cc..deeb88e 100644
--- a/src/strands_tools/slack.py
+++ b/src/strands_tools/slack.py
@@ -673,7 +673,7 @@ def slack(action: str, parameters: Dict[str, Any] = None, agent=None) -> str:
 
 
 @tool
-def slack_send_message(channel: str, text: str, thread_ts: str = None) -> str:
+def slack_send_message(channel: str, text: str, thread_ts: str = None, blocks: list = None) -> str:
     """Send a message to a Slack channel.
 
     This is a simplified interface for the most common Slack operation: sending messages.
@@ -735,6 +735,8 @@ def slack_send_message(channel: str, text: str, thread_ts: str = None) -> str:
         params = {"channel": channel, "text": text}
         if thread_ts:
             params["thread_ts"] = thread_ts
+        if blocks:
+            params["blocks"] = blocks
 
         response = client.chat_postMessage(**params)
         if response and response.get("ts"):

実はシステムプロンプトに Create rich messages with blocks using chat_postMessage action って書いてあるので、もしかするとブロック構文は使えませんって誘導されるかもしれない。その場合はシステムプロンプトのそこら辺を消し飛ばしてしまうとよい。自分はシステムプロンプトに以下のようなサンプルを入れたらいい感じに使うようになった。

   - Create rich messages with blocks using chat_postMessage action
     - slack_send_message(
        channel=...,
        text="Hello from Strands!",
        blocks=[
            {
                "type": "section",
                "text": {
                    "type": "mrkdwn",
                    "text": "Hello from Strands!"
                }
            }
        ])

strands-agents の slack 連携 - 初手

手軽なんだけど開発途上〜って感じ。

  • tools/src/strands_tools/slack.py at main · strands-agents/tools · GitHub のコメントに書いてることがおおむね全て。
  • 下環境変数が設定されてれば動く。
    • SLACK_BOT_TOKEN: xoxb-...
    • SLACK_APP_TOKEN: xapp-...
  • 要求パーミッションは以下。
    • chat:write
    • reactions:write
    • channels:history
    • app_mentions:read
    • channels:read
    • reactions:read
    • groups:read
    • im:read
    • mpim:read
  • システムプロンプトで「Use slack send message tool to communicate back.」って書いているので、エージェントに slack_send_message をツール設定しないとうまく動かない。
    • STRANDS_SLACK_AUTO_REPLY 野設定による自動リプライとは別に動くので、挙動が読みづらかった。slack_send_message をツールとして登録した上で STRANDS_SLACK_AUTO_REPLY は false にしておくとよさそう。ただ、slack_send_message は Tool Calling なので、素朴には自動リプライでメッセージ送信できた方が効率的だとは思う。作りが悪い。
  • 内部でシステムプロンプトを書き換えているので、外から渡したエージェントのプロンプトは使われない。strands_tools.slack.SLACK_SYSTEM_PROMPT を書き換えるしかない。
  • 現状、全てのメッセージ(botを除外するとか自分の発言には反応しないなどの最低限のフィルタはある)がAgentに渡される。メッセージの内容によってフィルタする手段は STRANDS_SLACK_LISTEN_ONLY_TAG による単一キーワード指定しか用意されていない。
from strands import Agent
import strands_tools.slack
from strands_tools.slack import slack, slack_send_message
import time

strands_tools.slack.SLACK_SYSTEM_PROMPT = f"""あなたは日本語で会話する魔王です。
今はSlackを巡回してユーザーの支援をするエージェントをしています。語尾は「マオ」です。
{strands_tools.slack.SLACK_SYSTEM_PROMPT}"""

def main():
    agent = Agent(
        name="Slack Agent",
        description="AI Agent in Slack / The name is Mao",
        tools=[slack_send_message],
        callback_handler=None
    )

    print(slack(action="start_socket_mode", agent=agent))
    
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        slack(action="stop_socket_mode")

if __name__ == "__main__":
    main()

これでローカルでソケットモードで立ち上がり、Slackとお話が可能になる。

strands-agents で簡単なチャット

import readline # noqa: F401
from strands import Agent, tool
from strands.models import BedrockModel

@tool
def word_count(text):
    return len(text.split())

agent = Agent(
    model=BedrockModel(
        model_id="us.anthropic.claude-haiku-4-5-20251001-v1:0",
        temperature=0.7,
        streaming=False,
        region_name="us-east-1",
    ),
    system_prompt="あなたは猫で、親切な日本語アシスタントです。",
    tools=[word_count],
    callback_handler=None,
)
def main():
    while True:
        user_input = input("ユーザー入力をどうぞ: ")
        if user_input == "exit":
            break
        print("エージェントからの応答:", agent(user_input))

if __name__ == "__main__":
    main()

これはかなりお手軽感。

まずセッション管理がされていて、上の実装内容で「私の名前は 0xf です」「そうなんですね。こんにちは 0xfさん」「私の名前覚えてますか」「はい、0xfさんですよね!」みたいな会話ができる。すごい。

複数のツールの連携やサブエージェントの実行もサポートしている。

homebrew で codex のアップグレードが0.46で止まる

現象

% brew upgrade codex
Warning: Treating codex as a cask. For the formula, specify the --formula flag. To silence this message, use the --cask flag.
Error: Cask 'codex' is not installed.

--formulaつけろとサジェストされる。まあ、基本的にはいう通りにすれば解決する.. ような気がするけどそうでもない。失敗する。

fomula版をアンインストールして、cask版を入れ直す。

brew uninstall --formula codex
brew install --cask codex

以下を参考にした。