2026年3月27日金曜日

localAIでのRakuten AI 2.0 mini

結構試行錯誤の末になんとなくまともに動くようになった感じです。(contextの大きさなども含め動くだけの設定ですが。)

色々と試行錯誤してみると細かいところでlocalAIの中身がわからないのでソースを見ながら調整していました。

おもに.yamlファイルのtemplateを調整していたのですが、結局のところ何も考えなければ、

options:
 - use_jinja: true

と設定すればllama-cppでgguf内の定義体からtokenizer.chat_templateをつかってプロンプトをいい感じに扱うようになりました。

ただ、この設定にするとdebugメッセージで実際のトークン内容が見えなくなってしまいます。それと、システムプロンプトに設定したものがうまく反映されているのかどうかもわかりませんでした。(少なくても反応してない感じ)

結果 .yaml の template を調整する必要になるのですが、基本的にはtokenizer.chat_templateを基に考えればいいはず。

localAIのドキュメント(https://localai.io/advanced/model-configuration/index.html)を見ると.yamlのサンプルに

name: my-llm-model
description: A high-performance LLM model
backend: llama-stable

:
:

template:
chat: |
{{.System}}
{{range .Messages}}
{{if eq .Role "user"}}User: {{.Content}}
{{else if eq .Role "assistant"}}Assistant: {{.Content}}
{{end}}
{{end}}
Assistant:

と言う物が載っていたのでこのまま行けるんじゃ?と思ったのですが、この形式はllama-cppでは使えませんでした。(仕様が変わったのか、バックエンドにllama-stableを使えば行けるのかわかりませんが)

試行錯誤の末出来上がったものがこちら。

stopwords:
- "USER:"
- "ASSISTANT:"
template:
  chat_message: |-
    {{- if eq .MessageIndex 0 }}
      {{- if eq .Role "system" }}
        {{- trim .Content }}
      {{- else }}
        {{- "A chat between a curious user and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the user's questions." }}
      {{- end }}
    {{- end }}
    {{- if eq .Role "user" }}USER: {{ trim .Content }}
    {{- else if eq .Role "assistant" }}ASSISTANT: {{ trim .Content }}</s>
    {{- end }}
  chat: |-
    {{- regexReplaceAll "\n(USER:|ASSISTANT:) " (regexReplaceAll "\nASSISTANT: " .Input " ASSISTANT: ") " $1 " }}
    {{- " "}}ASSISTANT:
  completion: |-
    {{- .Input }}
roles:
  user: "user"
  system: "system"
  assistant: "assistant"

途中gemeiniさんに相談したりいろいろしてみましたが、localAIのプロンプトのやり取りと、Rakuten AI 2.0 mini Instructがどのように反応するのか見てみないといけなかったので、最終的にlocalAIのソースと、実際に動かしながら結構強引にやってます。なので細かい例外はこの際目をつぶることにしました。

Rakuten AI 2.0 mini (Instruct)のtokenizer.chat_templateから見える実際のテキストストリームはこのような形になります。

最初は

「USER: [UserInput] ASSISTANT:」もしくは、システムプロンプトがあれば
「[SystemPrompt] USER: [UserInput] ASSISTANT:」

それ以降はこんな感じ。
「[SystemPrompt] USER: [UserInput1] ASSISTANT: [ModelOutput1]</s> USER: UserInput2] ASSISTANT: [ModelOutput2]</s> ~ USER: [UserInput] ASSISTANT:」

という形式でやり取りするという事。改行(\n)は行いません。

</s>があって<s>がないのが気になった(というか色々調整して途方にくれましたw)のでRakutenAI-2.0-mini-instruct-Q8_0.ggufのmetadataをみると、tokenizer.ggml.add_bos_tokenがtrueになっているので<s>は付加されるという事のようです。実際にどのようにbosが追加されているのか不明なのでいろいろ試してみたところ、bosが無い状態でトークンが来ると自動でbosを付加している感じでした。結果としてchat_templateで示している会話の1つの質疑ー>応答のタイミングで区切ること会話が安定していました。 

まぁ</s>がなくてもいいような気はしています。ただ、改行が含まれるとかなり会話がおかしなことになります。

最初は普通にテンプレートを書き換えても、1メッセージ(User側の入力やAssistant側の出力)単位で必ず改行されているのでそれが普通だと思っていたのですが、どうも安定しないので結構強引に改行を消すと会話が成立しやすくなりました。

ざっくりと説明すると、.yamlのtemplate内のchat_message/chat/completionのテンプレートで定義されたもので整形し、backend(llama-cpp)へ送られます。 

localAI側はlocalAI内で扱っているデータをChatMessageTemplateData構造体に設定し、.yamlのtemplate内のchat_messageのテンプレートで定義されたもので整形します。すべてのメッセージを一纏めにした後、chatのテンプレートで整形し、最終的にcompletionで整形するようになっています。(ドキュメントを鑑みると、現状では1メッセージ単位から組み立てたうえで、chatで整える形になっているが、もともとは一回で整形していただけの様です。)

今回問題となるは chat_massageで整形されたものがchatで整形される前に1メッセージごとに改行コードが強制的に付加されている点です。どこかにパラメータがあれば良かったのですが、コード内を見る限りそのような分岐は見当たり…ありましたねw

現時点のコードでは    LocalAI/core/templates/evaluator.goの200行目から

	joinCharacter := "\n"
	if config.TemplateConfig.JoinChatMessagesByCharacter != nil {
		joinCharacter = *config.TemplateConfig.JoinChatMessagesByCharacter
	}

に定義されていてconfig.TemplateConfig.JoinChatMessagesByCharacterはyamlかjsonで取ってくるような雰囲気がLocalAI/core/config/model_config.goにあります。 

なので早速設定して、少し変更しました。

stopwords:
- "USER:"
- "ASSISTANT:"
template:
  chat_message: |-
    {{- if eq .MessageIndex 0 }}
      {{- if eq .Role "system" }}
        {{- trim .Content }}
      {{- else }}
        {{- "A chat between a curious user and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the user's questions." }}{{ " " }}
      {{- end }}
    {{- end }}
    {{- if eq .Role "user" }}USER: {{ trim .Content }}
    {{- else if eq .Role "assistant" }}ASSISTANT: {{ trim .Content }}</s>
    {{- end }}
  chat: |-
    {{- .Input }}
    {{- " "}}ASSISTANT:
  completion: |-
    {{- .Input }}
  join_chat_messages_by_character: " "
roles:
  user: "user"
  system: "system"
  assistant: "assistant"

強引な部分が消せましたw

ただ、ここまでやってもシステムプロンプト的なものを設定しても思った以上に効果が無かったりします。運次第ですが。

そんな感じでRakuten AI 2.0 miniに触れてみたのですが、基本的な感触としては会話と言うより情報端末に対して問いかける感じで、とにかくはっきり問いかけないとダメな感じです。何も用事がない場合は Rakuten AI 2.0 mini 側から話しのネタを提示してユーザー側に問いかけさせるようにする感じです。

なので、「こんにちは」「あなたの名前は?」「私の名前は~です」といった質疑応答に関係のない話題に対してとても不安定な回答をしたり、内部で混乱のもととなり、例えば

👨「こんばんは」
🤖「こんにちは!どのような話題についてお話したいですか?」
👨「ニュースについて教えて
🤖「こんにちは!今日のニュースです。* 経済:日本のGDP成長率が…

と言ったように2回挨拶を繰り返したり、しかも「こんばんわ」に対して「こんにちは!」とかちょっとアレな感じになったりします。

また、矛盾が生じると頻繁にエコーチャンバーを起こし、簡単に暴走します。2回程度で収まればいいのですが、contextが溢れるまで繰り返すことの方が多いです(笑)なのでチャットでは強制停止機能が必須です。

とくにRakuten AI 2.0 miniでは素直に冒頭から「ニュースについて教えて」と問いかけるのが正解です。

世間話をするために名前を付けようとしましたが、なかなか上手く行きませんでしたし…。会話はできるのですが、自分自身とユーザーの区別が明確に分かれていない状態のようでした。

結果的にRakuten AI 2.0 miniでできることは持っている知識に対して教えてもらうというぐらいでしょうか。現状ではこれを中心に何かできるとは思えないですね。

倫理的な保護機能はあるのですが、少し言い方を変えるだけで開示されたりするので、理論的と言うより機械的に保護されているレベルにとどまっている感じです。

言語に関しては英語と日本語となっていますが、ハングル文字も認識してるし、スペイン語も出力してきたりするので、適当に言語は扱える感じです。(ただ出力されたものをみるとそこまで流暢ではなさそうでした)まぁ日本語と英語が相互に扱えるだけで十分ですが。

 

今回苦労したおかげでテンプレートについていろいろ知ることができたのは唯一の救いかな? 

0 件のコメント:

コメントを投稿