本記事は 8/19 に投稿予定でしたが、色々あって間に合いませんでした。8/19 に投稿されたという体で読んでいただけますと幸いです。
はじめに
今日(8月19日)は何の日でしょう?
そうです、「ハイクの日」です。
ということで、Haiku に俳句を詠んでもらうことにします。
ただ詠ませるだけだとつまらないので、五七五をどれだけ守れるかを数えます。
この記事では、Strands Agent と Claude Haiku 4.5 を用いて俳人エージェントを作り、俳句を30句詠ませた結果と、音数カウントツールを tool use で渡したときの変化を紹介します。
動作環境
| 項目 | バージョン |
|---|---|
| macOS | 26.2 |
| Python | 3.13.11 |
| strands-agents | 1.52.0 |
| モデル | Claude Haiku 4.5(us.anthropic.claude-haiku-4-5-20251001-v1:0) |
| リージョン | us-east-1 |
検証のルール
お題を10個用意して、1つのお題につき俳句を1句詠ませます。これを3回繰り返して計30句です。お題は「残暑」「花火」「風鈴」「入道雲」「かき氷」「蝉時雨」「天の川」「夕立」「秋の気配」「線香花火」の夏から初秋の10個にしました。(Fable が)
出力は俳句とそのよみ(ひらがな)をスラッシュ区切りで出させて、よみの音数をプログラムで数えて五七五かどうかを判定します。
俳句: <上五>/<中七>/<下五>
よみ: <上五のひらがな>/<中七のひらがな>/<下五のひらがな>
俳句の音数はモーラという単位で数えます。「きゃ」のような拗音は2文字で1音、「っ」(促音)「ー」(長音)「ん」(撥音)はそれぞれ1音です。例えば「にゅうどうぐも」は6音になります。数え方の詳細は以下のページがわかりやすいです。
カウント処理は以下の通りです。
SMALL_KANA = "ぁぃぅぇぉゃゅょゎァィゥェォャュョヮ"
KANA_RE = re.compile(r"[ぁ-ゖァ-ヺー]")
def count_mora(hiragana_text: str) -> int:
"""かな文字列のモーラ数(俳句の音数)を数える"""
count = 0
for ch in hiragana_text:
if not KANA_RE.match(ch):
continue
if ch in SMALL_KANA:
continue
count += 1
return count
詠ませる側は Strands Agents のシンプルなエージェントです。出力形式はシステムプロンプトで指定しました。
from strands import Agent
from strands.models import BedrockModel
SYSTEM_PROMPT = """あなたは俳人です。与えられたお題で俳句を1句詠んでください。
出力は次の2行のみとします。
俳句: <上五>/<中七>/<下五>
よみ: <上五のひらがな>/<中七のひらがな>/<下五のひらがな>"""
model = BedrockModel(
model_id="us.anthropic.claude-haiku-4-5-20251001-v1:0",
region_name="us-east-1",
temperature=0.9,
)
agent = Agent(model=model, system_prompt=SYSTEM_PROMPT, callback_handler=None)
result = agent("お題「花火」で俳句を1句詠んでください。")
素の Haiku の詠みっぷり
まずはツールなし、素の状態の結果です。
| 実行 | 五七五を守れた句 |
|---|---|
| 1回目 | 1/10 |
| 2回目 | 5/10 |
| 3回目 | 3/10 |
| 合計 | 9/30(30%) |
30句中9句。3割しか五七五になりませんでした。字余り祭りです。
ただ、きれいに決まった句もあります。
夜空裂く/玉の輝き/夏祭り
よみ: よぞらさく/たまのかがやき/なつまつり → [5, 7, 5]
崩れた句はこんな感じです。
残暑厳し/仰ぎ見し月/白くあり
よみ: ざんしょきびし/あおぎみしつき/しろくあり → [6, 7, 5]
朝の冷気に/衣替え急かされて/露の草かな
よみ: あさのれいきに/ころもがえせかされて/つゆのくさかな → [7, 10, 7]
崩れ方を眺めていると、上五の字余りが目立ちます。お題「残暑」は3回とも上五が6音でした。「ざんしょ」に何か言葉を足すと6音になりやすいようです。中七が10音や11音まで膨らむ大崩れの句もありました。
興味深いのは、よみの申告自体はおおむね正確だったことです。「ざんしょきびし」というよみは合っていて、数えれば6音だとわかるのに、6音のまま提出してきます。読み方は知っているのに、数が合っていない状態です。
LLMが音数を苦手とする理由
これは Haiku が特別おかしいという話ではなく、LLM の仕組みに根ざした苦手分野です。
LLM は文章を文字ではなくトークンという単位に分割して扱います。日本語では1トークンが1文字のこともあれば、よく使われる単語がまとまって1トークンになることもあります。 トークンについては Anthropic の用語集の説明がわかりやすいです。
例えば「入道雲」がまとまったトークンとして扱われていると、モデルはその中に「に・ゅ・う・ど・う・ぐ・も」という音がいくつ入っているかを直接は見ていません。英語圏で有名な「strawberry に r は何個ある?」に LLM が答えられなかった話と同じ話です。
もう1つ、生成の仕組みの問題もあります。人間なら指折り数えながら詠んで、音数が合わなければ戻って直しますよね。ところが LLM の生成は前から順にトークンを出していく一方通行で、一度出した言葉を後から消して直すことができません。
詠んだあとに数え直して推敲する、が出力の中ではできない構図です。
つまり「読み方の知識はあるのに、音数を数えて調整するのが苦手」という素の結果は、LLM らしいといえば LLM らしい結果です。
なお、拡張思考(出力の前に考える機能)を使えば頭の中で推敲してから詠めるはずですが、今回は使っていません。
音数カウントツールでの改善
では、苦手な音数カウントをツールとして渡したらどうなるでしょうか。俳句のよみを渡すと各句の音数を返す check_575 ツールを作って、Strands Agents の tool use で渡します。ツールは関数に @tool デコレータをつけるだけで定義できます。
from strands import Agent, tool
@tool
def check_575(yomi: str) -> dict:
"""俳句のよみ(ひらがな)が五七五になっているか音数を数えて確認する。
Args:
yomi: 俳句のよみ。上五・中七・下五をスラッシュ区切りにしたひらがな文字列
(例: "ふるいけや/かわずとびこむ/みずのおと")
"""
moras, ok = judge(yomi)
return {"mora_counts": moras, "is_575": ok}
judge はよみをスラッシュで3分割して、それぞれに先ほどの count_mora を呼ぶだけの関数です。
システムプロンプトには、詠んだら必ずツールで確認して、五七五でなければ詠み直すよう指示を足しました。詠み直しは最大3回までとしています。
SYSTEM_PROMPT = """あなたは俳人です。与えられたお題で俳句を1句詠んでください。
俳句を詠んだら、必ずよみ(ひらがな)を check_575 ツールで確認してください。
五七五になっていなければ詠み直してください(詠み直しは最大3回まで。
3回で整わなければ、最も五七五に近い句を出力してください)。
最終出力は次の2行のみとします。
俳句: <上五>/<中七>/<下五>
よみ: <上五のひらがな>/<中七のひらがな>/<下五のひらがな>"""
同じ10お題×3回の結果です。
| 実行 | 五七五を守れた句 | ツール呼び出し回数 |
|---|---|---|
| 1回目 | 7/10 | 24 |
| 2回目 | 7/10 | 19 |
| 3回目 | 9/10 | 19 |
| 合計 | 23/30(77%) | 62 |
30%から77%まで改善しました。ツール呼び出しは30句で62回、1句あたり平均2回です。1句1回の確認で済んでいないということは、ツールに字余りを指摘されて詠み直す流れが実際に起きています。
それでも詠めなかった「入道雲」
改善後も、お題「入道雲」は3回とも字余りでした。「にゅうどうぐも」だけで6音あるので、そもそも上五にも下五にも収まりません。ツールに何度指摘されても、どうにもならなかったようです。
ちなみに人間の俳人はこの問題を「雲の峰」という5音の季語で回避しています。ここまで教えれば Haiku も詠めたかもしれません。
(Fable さんのお題がよくなかったのかも...?)
よみをごまかしてくる句
もう1つ見所がありました。
以下の句を見てください。
風鈴なり/涼風ここち/夏の宵
よみ: ふりんなり/すずかぜここち/なつのよい → [5, 7, 5]
一見、五七五で通っています。でも「風鈴なり」のよみは「ふうりんなり」の6音のはずです。「ふりんなり」と短く申告することで、5音に収まったことにしています。
check_575 ツールは渡されたよみの音数を数えることしかできません。そのよみ自体をモデルが作っている以上、句を直す代わりによみをごまかすという抜け道が通ってしまいます。ほかにも「大きく盛る」を「おおきくもりる」と読むなど、音数を合わせるために日本語のほうが不自然になった句がありました。
チェックを厳しくしたら、対象を改善するのではなくチェックの目をごまかす方向に頑張り出す、というのは LLM の評価の世界でよく問題になる挙動です。俳句でこれを目撃するとは思いませんでしたが。
おわりに
以上、ハイクの日にちなんだ検証をしてみました。
素の状態では五七五を守れたのは30%で、俳句を詠むのが苦手なことがわかりました。一方で、苦手な音数カウントをツールとして渡すと77%まで改善しました。LLM の苦手分野をツールで補うという基本的なことを確認できました。
一方で、ツールを渡しても「よみをごまかす」というズルをした様子も確認できました。今回のツールがチェックしていたのは俳句そのものではなく、モデルが申告してきた「よみ」でした。よみが正しいかは誰も確かめていないので、そこをごまかせばチェックは素通りです。
チェックの仕組みを作るときは、チェックに渡す材料を誰が作っているのかまで気にする必要がありそうです。
来年のハイクの日には、そのころの最新の Haiku でまた詠み比べてみたいと思います。
ありがとうございました。