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です。