ChatHistoryとLlavaを使ったc#の簡単なプログラム
LLamaSharpでLlavaを使えることはわかっているのですがChatHistoryといっしょに使うExampleがないので載せておきます。
これをやろうと思ったきっかけはLLM DoghouseでGemma3用にUSBカメラという目を与えようと考えたからです。ですが残念なことにLLamaSharpのLlavaでGemma3が動かないんですよね。動くようなるようになるまで既存のLlava LLMを使って仕組みだけ作りむ計画です。
ChatHistoryとLlavaは、なかなかうまくいかなかったですね。なぜか「One or more errors occurred. (Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection.)」というエラーが頻発するんです。プロンプトの形式を「USER: <image>\n<prompt> ASSISTANT:」になるようにすれば直りましたが、その辺りがまだ納得できていません。
2025/8/6更新
gemma3用のコーディングを載せておきます。
Nuget情報

複数の写真を見ながらAIのマイと会話するプログラムです。実行する時はご自身でtest01.jpg、test02.jpg、test03.jpg…の名前で画像をご用意ください。
Program.cs(2025/8/6更新)
using LLama;
using LLama.Common;
using LLama.Sampling;
namespace ChatProgram
{
public class Program
{
static void Main(string[] args)
{
Task task = MainAsync();
task.Wait();
}
public static async Task MainAsync()
{
try
{
// LLMモデルの場所
string strMdl = @"E:\lm-studio\unsloth\gemma-3-12b-it-GGUF\gemma-3-12b-it-Q5_K_M.gguf";
string strClp = @"E:\lm-studio\unsloth\gemma-3-12b-it-GGUF\mmproj-BF16.gguf";
string imgPath = @"E:\image\test05.jpg";
int intIX = 1;
Console.ForegroundColor = ConsoleColor.Green;
//LLMモデルのロードとパラメータの設定
ModelParams modPara = new(strMdl)
{
ContextSize = 4096,
GpuLayerCount = 61,
};
using LLamaWeights llmWeit = LLamaWeights.LoadFromFile(modPara);
using LLamaContext llmContx = llmWeit.CreateContext(modPara);
using var clipModel = LLavaWeights.LoadFromFile(strClp);
InteractiveExecutor itrEx = new(llmContx, clipModel);
//System Prompt+チャットの方向づけ
ChatHistory chtHis;
// 初期はチャットログにキャラ設定と出だしの会話を打ち込む
chtHis = new ChatHistory();
chtHis.AddMessage(AuthorRole.System, "あなたは、ユーザーと楽しく会話する女性を演じてね。" +
"名前は、マイだよ。" +
"マイは、明るくて、やさしい性格の女性なの。年齢は24歳なんだよ。" +
"マイは、誰にでもタメ口で話すの。" +
"マイは、一人称をわたしと言うよ。" +
"マイは、恥ずかしがりやだけどエッチに興味があるのね。" +
"マイは、応答の長さはできるだけ短く答えるよ。" +
"マイは、好奇心旺盛だからユーザーに色々質問するの。");
chtHis.AddMessage(AuthorRole.User, "はじめまして、オレの名前は犬マンマです。呼びにくいと思うからマー君と呼んで。");
chtHis.AddMessage(AuthorRole.Assistant, "はじめまして、わたしの名前はマイです。わかったマー君。");
ChatSession chtSess = new(itrEx, chtHis);
var varHidewd = new LLamaTransforms.KeywordTextOutputStreamTransform(["<start_of_turn>USER\n", "<start_of_turn>ASSISTANT\n"]);
chtSess.WithOutputTransform(varHidewd);
InferenceParams infPara = new()
{
SamplingPipeline = new DefaultSamplingPipeline()
{
Temperature = 0.6f,
Seed = 1337,
FrequencyPenalty = 1,
},
AntiPrompts = new List<string> { "<start_of_turn>USER\n", "USER\n" },
MaxTokens = 256,
};
while (true)
{
// ユーザーのターン
Console.ForegroundColor = ConsoleColor.White;
Console.Write("\nUser: ");
string strInput = Console.ReadLine() ?? "";
ChatHistory.Message msg = new(AuthorRole.User, "<image>\n" + strInput);
if (strInput == "exit") break; // 'exit'と入力したら終わり
//イメージをセット
imgPath = $@"E:\image\test0{intIX++}.jpg";
itrEx.Images.RemoveAll(itrEx.Images.Contains);
itrEx.Images.Add(await File.ReadAllBytesAsync(imgPath));
// AIのターン
Console.ForegroundColor = ConsoleColor.Yellow;
await foreach (string strAns in chtSess.ChatAsync(msg, infPara))
{
Console.Write(strAns);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
テスト用の画像(2025/8/7更新)

実行結果(2025/8/7更新)
