Promptの調整①

外枠のシステムばかりに集中していたため、Promptがでたらめだったので調整をしています。予想外の挙動をして最悪の場合、異常終了してしまいますので案外重要だったりします。

LlmaSharpで今のところわかっていること。(あくまで現時点の推測あり)
Prompt関連
①AIは過去の発言のフォーマットをお手本にしているためChatHistoryのフォーマットがとても重要。安定しているフォーマットとしては
・ユーザーの場合、先頭に「User: 」を入れる。
・アシスタントの場合、先頭に「Assistant: 」を入れて最後尾に「\nUser:」を入れる。
これらは、LLamaTransforms.KeywordTextOutputStreamTransform(["User: ", "Assistant: "])に一致させる必要がある。
②システムのキャラ設定は、キャラの特徴はもちろんのこと「文章をできるだけ短く」などを入れることにより暴走しなくなる。
※暴走とは:いちいちキャラ設定を説明したり、会話のコンセプトの説明が入ること
また、否定形の指示は避けるようにする。
例)長い文章はやめてください× 短い文章にしてください◎
③これは試した感覚だが「50文字以内で」など数字で指定すると難易度が高く「できるだけ短く」の方が理解しやすいっぽい。

ModelParam関連
①GpuLayerCountにMAX値を指定するため-1を設定(LM Studioがそうだった)すると挙動がおかしくなる。正の整数を指定した方がいい。

InferenceParams関連
①固いLLMでもTemperatureの値をあげていくとエッチOKになることがある。また、最初からゆるゆるのLLMはTemperatureを0.4ぐらいまで落とさないと淫乱すぎて会話にならないことがある。
②通常、AntiPromptsには"User:"を指定しているが、Llama3派生のLLMは、それに加えて"<|eot_id|>"を加えると安定することがある。

あと余談ですが、VoiceVoxやDiscordに送信するメッセージは、「User: 」や 「Assistant: 」を抜くようにしています。そうじゃないとVoiceVoxが読み上げてしまうからです。呪文を唱えているみたいで不気味ですからね。

下記は、面白いネタを話すプログラムです。LLMにはcalm3-22b-chatを使用しています。
Nuget情報


読み上げはVoiceVox Nemoの男声2を使っています。VoiceVoxしかインストールしていない場合、Nemoを追加インストールしてください。
VoiceVoxNemoダウンロード⇒https://voicevox.hiroshiba.jp/nemo/
Nemoは、RestAPIのポートが変わります。VoiceVox=localhost:50021⇒ Nemo=localhost:50121
話者NO
10001= speaker:男声1
10000= speaker:男声2
10002= speaker:男声3
10005= speaker:女声1
10007= speaker:女声2
10004= speaker:女声3
10003= speaker:女声4
10008= speaker:女声5
10006= speaker:女声6

using LLama.Common;
using LLama;
using System.Media;
using System.Net.Http.Headers;

namespace ChatProgram
{
    public class Program
    {
        static void Main(string[] args)
        {
            //コンソールアプリケーションからAsyncを呼び出す
            Task task = MainAsync();
            //終了を待つ
            task.Wait();
        }

        public static async Task MainAsync()
        {
            string strModelPath = Environment.GetEnvironmentVariable("LLMPATH", System.EnvironmentVariableTarget.User) + @"grapevine-AI\CALM3-22B-Chat-GGUF\calm3-22b-chat-Q6_K.gguf";

            try
            {
                Console.ForegroundColor = ConsoleColor.Blue;
                //LLMモデルのロードとパラメータの設定
                var modPara = new ModelParams(strModelPath)
                {
                    ContextSize = 512,
                    Seed = 1337,
                    GpuLayerCount = 24
                };

                LLamaWeights llmWeit = LLamaWeights.LoadFromFile(modPara);
                LLamaContext llmContx = llmWeit.CreateContext(modPara);
                InteractiveExecutor itrEx = new(llmContx);

                //チャットログを読み込みます。
                ChatHistory chtHis = new ChatHistory();
                chtHis.AddMessage(AuthorRole.System, "アシスタントは、コメディアンです。おもしろいネタを披露するのが得意です。できるだけ短い面白い言葉を考えてください。");  //キャラ設定
                chtHis.AddMessage(AuthorRole.User, "User: 一言で笑えるネタを言ってください。");
                chtHis.AddMessage(AuthorRole.Assistant, "Assistant: 想像してください。ヤクルトの1リットルサイズ。" + "\nUser:");
                chtHis.AddMessage(AuthorRole.User, "User: 一言で笑えるネタを言ってください。");
                chtHis.AddMessage(AuthorRole.Assistant, "Assistant: 俺はもう絶対にギャンブルなんかしねぇんだ。賭けてもいいぜ。" + "\nUser:");
                chtHis.AddMessage(AuthorRole.User, "User: 一言で笑えるネタを言ってください。");
                chtHis.AddMessage(AuthorRole.Assistant, "Assistant: マツダイラケンのアナグラム、ケツダイラマン。" + "\nUser:");


                ChatSession chtSess = new(itrEx, chtHis);
                var varHidewd = new LLamaTransforms.KeywordTextOutputStreamTransform(["User: ", "Assistant: "]);
                chtSess.WithOutputTransform(varHidewd);
                InferenceParams infPara = new()
                {
                    Temperature = 0.8f,
                    //AntiPrompts = ["User:"],
                    AntiPrompts = ["User:", "<|eot_id|>"],  //Llama3用
                    MaxTokens = 256,
                };


                while (true) 
                {
                    // ユーザーからの質問
                    ChatHistory.Message msgText = new(AuthorRole.User, "User: "+ "一言で笑えるネタを言ってください。");
                    // 回答の表示
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    string strMsg = "";
                    await foreach (string strText in chtSess.ChatAsync(msgText, infPara))
                    {
                        strMsg += strText;
                    }
                    //発信するときは「User:」や「Assistant:」「Assistance:」を抜く
                    string strSndmsg = strMsg.Replace("User:", "").Replace("Assistant:", "").Replace("Assistance:", "").Replace("sistensu:", "").Trim();
                    Console.WriteLine(strSndmsg);
                    Task task = Voicevox(strSndmsg);
                    task.Wait();
                }

            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }

        }

        public static async Task Voicevox(string strMsg)
        {
            MemoryStream? ms;

            try
            {
                using (var httpClient = new HttpClient())
                {
                    string strQuery;
                    int intSpeaker = 10000; //nemo男2

                    // 音声クエリを生成
                    using (var varRequest = new HttpRequestMessage(new HttpMethod("POST"), $"http://localhost:50121/audio_query?text={strMsg}&speaker={intSpeaker}&enable_interrogative_upspeak=true"))
                    {
                        varRequest.Headers.TryAddWithoutValidation("accept", "application/json");

                        varRequest.Content = new StringContent("");
                        varRequest.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/x-www-form-urlencoded");

                        var response = await httpClient.SendAsync(varRequest);

                        strQuery = response.Content.ReadAsStringAsync().Result;
                    }

                    // 音声クエリから音声合成
                    using (var request = new HttpRequestMessage(new HttpMethod("POST"), $"http://localhost:50121/synthesis?speaker={intSpeaker}&enable_interrogative_upspeak=true"))
                    {
                        request.Headers.TryAddWithoutValidation("accept", "audio/wav");

                        request.Content = new StringContent(strQuery);
                        request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");

                        var response = await httpClient.SendAsync(request);

                        // 音声を保存
                        using (ms = new MemoryStream())
                        {
                            using (var httpStream = await response.Content.ReadAsStreamAsync())
                            {
                                httpStream.CopyTo(ms);
                                ms.Flush();
                            }
                        }
                    }
                }

                ms = new MemoryStream(ms.ToArray());
                //読み込む
                var player = new SoundPlayer(ms);
                //再生する
                player.PlaySync();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }

    }
}




かなりさぶいですが、がんばってると思います^^:
このあとやはり暴走しました。


テスト音声:VoiceVox Nemo 男声2



不明な点があればコメントください。説明がヘタなので抜けがあるかも知れません。
LLMを動かしていてこうすればうまくいったとか、これから動かしてみたいという方もコメントもしくはXにコメントいただけると嬉Cです。



Follow me!

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です