charlm-agent
5000 万パラメータ(52.6M)の文字単位の小さな言語モデルです。2 つのことができます。
- 役になりきった会話:短い人物設定(カード)を渡すと、その人物として雑談や簡単な案内に答えます。
- ゲームの行動選び:ゲームが「目標・状態・使える操作」を送ると、目標に近づく操作をタグで返します。
CPU だけで動きます。4bit 版は 33MB です。
どれを使えばいいか
| フォルダ | 中身 | 使いどころ |
|---|---|---|
agent13b/ |
会話 + 行動選び(最新)。agent12a に、教師モデルに「口調がどうあれ事実は正確に」と指示して書かせた会話 3,600 本(ダークファンタジー・友達ゲーム多め)を追加学習。名前の記憶やお店の時間などの事実の正答 80 → 85%、ダークファンタジーでの名前の記憶 23 → 57%、嫌いな贈り物への反応 54 → 69% | 迷ったらこれ |
agent12a/ |
1 つ前の版(agent11 が自分で作った返答の良いものだけで追加学習) | 比較用 |
agent11/ |
1 つ前の版 | 比較用 |
agent6/ |
2 つ前の版 | 比較用 |
npc4/ |
会話だけに絞った版 | 行動選びが要らず、会話の質を優先したいとき |
base/ |
事前学習だけの土台 | 自分で追加学習したいとき |
各フォルダの model_final.pt は通常版(210MB)、model_4bit.pt は 4bit 量子化版(33MB)です。
使い方
charlm_run.py と charlm_fold.py を同じ場所に置いて使います(PyTorch のみ必要)。
from charlm_run import Model
m = Model("agent13b/model_4bit.pt") # GPU があれば GPU、なければ CPU
# 会話: 最初のユーザー発話の前にカードを置き、空行で区切る
card = "You are Mira, a farmer in the village of Elm. You are cheerful and practical."
print(m.reply([card + "\n\nHi! What do you grow here?"], temp=0.7))
# 行動選び: GOAL / OPS / STATE を毎ターン送る。答えは {考えの筋道}<操作>
turn = ("GOAL: life >= 1\n"
"OPS: <a> life+1 gold-3 | <b> gold+2 (needs gold>=1) | <c> life-1\n"
"STATE: life=0 gold=7")
print(m.reply([turn], n=260)) # -> {life 0 >=1 up. up: a+1. a ok}<a>
reply(turns) の turns は、ユーザー → モデル → ユーザー … の順に並べた文字列のリストです。
行動選びの形式
GOAL: 変数 比較 数:達成すべき条件(>=<=><==、andで 2 つまで)OPS: <名前> 変数+数 変数-数 (needs 変数>=数) | ...:使える操作。名前は自由(学習で見ていない名前でも読めます)STATE: 変数=数 ...:今の状態。目標の変数を先頭に置くのが約束事です(agent11 は他の順番でもほぼ同じように読めます)- 答えの最後の
<名前>を実行し、新しい STATE を送る。目標を満たすと<none>が返ります
ゲームに組み込むときのコツ
- 目標は 1 つずつ渡す。「道具を作ってから売る」のような段取りはゲーム側で小目標に分けると、ほぼ確実に動きます。
- 使えない操作は一覧から外す。返ってきたタグがおかしければ、その操作を外して聞き直す。
- 例:
village.py(3 人の村人が市場を共有して暮らすシミュレーション)。小目標に分けた形で、4 日間・48 回の判断でゲームに弾かれた回数は 0、全員が所持金を増やしました。
成績(agent13b、4bit)
行動選び:学習で見ていない作り物の名前で、ゲームとやり取りしながら目標に届いた割合(各 100 問、目標の変数が先頭 / 並び順ばらばら)。
| レベル | 内容 | agent13b |
|---|---|---|
| 1 | 目標 1 つ | 99 / 99% |
| 2 | 関係ない操作・前提条件が混ざる | 93 / 92% |
| 3 | 先に前提を満たしてから使う(2 段) | 85 / 81% |
| 4 | 目標 2 つ、片方を壊さない | 81 / 76% |
住人テスト(3 種類のゲームの住人 128 人、学習で見ていない設定、1,544 問):名前を覚える・お店の時間などの事実の正答 85%(agent12a 80%、agent11 74%、agent6 78%)、採点役(Qwen3.5-4B)が「使える」とした返答 90%、ルール違反なし 96.5%(名前を忘れた 20 問、agent12a は 33 問)。ジャンル別の事実の正答:どうぶつの森風 96%、友達ゲーム風 87%、ダークファンタジー風 57%(agent12a は 23%)。同じ重みを 2 回採点すると事実の正答は ±8 ポイントぶれるので、それより小さな差は意味がありません。
会話の予測誤差(1 文字あたり、通常版、小さいほど良い):日常会話 0.827、NPC 0.730、案内 0.912、住民 1.075(agent12a は 0.822 / 0.724 / 0.909 / 1.094、agent6 は住人 1.37)。雑談の力は落ちていません。
作り方
base/:WikiText-103 と FineWeb-Edu を 63 億文字学習(16 層、幅 512、文脈 4096 文字、文字の種類 100)。- 会話:SmolTalk、SODA(人物設定つきの会話)、Qwen3.5/3.6 で作った NPC 会話と案内データで追加学習(
npc4/)。 - 行動選び:ルールで作った目標達成の会話(考えの筋道つき)を雑談と半々に混ぜて追加学習(
agent6/)。
学習データの出典と利用条件:WikiText-103(CC BY-SA)、FineWeb-Edu(ODC-By)、SmolTalk(Apache 2.0)、SODA(CC BY 4.0)、Qwen3.5/3.6(Apache 2.0)の生成データ。
限界
agent13b は事実を正しく言う代わりに、お店の時間を聞かれたときの答えが「It opens at 8 am and closes at 5 pm.」のように素っ気なくなることがあります(採点役はこれを「役になりきれていない」と減点します)。
小さなモデルなので、短いあいさつ、天気、プレゼントへの反応、お店の時間のような単純なやり取りは得意ですが、相談に乗るような開いた質問や、長い文になるとつじつまが合わなくなります。ダークファンタジーの住人は、名乗った名前を覚えていないことが多いです。返答をいくつか作って talk_rules.py(GitHub)で壊れたものを落とすと安定します。行動選びでは、3 段以上の段取りや、操作がどの値に効くかの読み違い、数の大小の判断ミスが残っています。