zc
This commit is contained in:
70
Source/WulaFallenEmpire/EventSystem/AI/AIAutoCommentary.cs
Normal file
70
Source/WulaFallenEmpire/EventSystem/AI/AIAutoCommentary.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
using WulaFallenEmpire.EventSystem.AI.UI;
|
||||
|
||||
namespace WulaFallenEmpire.EventSystem.AI
|
||||
{
|
||||
/// <summary>
|
||||
/// 简化版 AI 自动评论系统
|
||||
/// 直接将 Letter 信息发送给 AI 对话流程,让 LLM 自己决定是否回复
|
||||
/// </summary>
|
||||
public static class AIAutoCommentary
|
||||
{
|
||||
private static int lastProcessedTick = 0;
|
||||
private const int MinTicksBetweenComments = 300; // 5 秒冷却
|
||||
|
||||
public static void ProcessLetter(Letter letter)
|
||||
{
|
||||
if (letter == null) return;
|
||||
|
||||
// 检查设置
|
||||
var settings = WulaFallenEmpireMod.settings;
|
||||
if (settings == null || !settings.enableAIAutoCommentary) return;
|
||||
|
||||
// 简单的冷却检查,避免刷屏
|
||||
int currentTick = Find.TickManager?.TicksGame ?? 0;
|
||||
if (currentTick - lastProcessedTick < MinTicksBetweenComments) return;
|
||||
lastProcessedTick = currentTick;
|
||||
|
||||
// 获取 AI 核心
|
||||
var aiCore = Find.World?.GetComponent<AIIntelligenceCore>();
|
||||
if (aiCore == null)
|
||||
{
|
||||
WulaLog.Debug("[AI Commentary] AIIntelligenceCore not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 构建提示词 - 让 AI 自己决定是否需要回复
|
||||
string prompt = BuildPrompt(letter);
|
||||
|
||||
// 直接发送到正常的 AI 对话流程(会经过完整的思考流程)
|
||||
aiCore.SendAutoCommentaryMessage(prompt);
|
||||
|
||||
WulaLog.Debug($"[AI Commentary] Sent letter to AI: {letter.Label.Resolve()}");
|
||||
}
|
||||
|
||||
private static string BuildPrompt(Letter letter)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
// 获取 Letter 信息
|
||||
string label = letter.Label.Resolve() ?? "Unknown";
|
||||
string defName = letter.def?.defName ?? "Unknown";
|
||||
|
||||
sb.AppendLine("[游戏事件通知]");
|
||||
sb.AppendLine($"事件标题: {label}");
|
||||
sb.AppendLine($"事件类型: {defName}");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("请根据这个事件决定是否需要向玩家发表简短评论。");
|
||||
sb.AppendLine("- 如果是重要事件(如袭击、死亡),可以提供建议或警告");
|
||||
sb.AppendLine("- 如果是有趣的事件,可以发表幽默评论");
|
||||
sb.AppendLine("- 如果事件不重要或不值得评论,什么都不说即可");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("评论要简短(1-2句话),符合你作为帝国AI的人设。");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -255,6 +255,66 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
||||
_ = RunPhasedRequestAsync();
|
||||
}
|
||||
|
||||
public async Task<string> SendSystemMessageAsync(string message, int maxTokens = 256, float temperature = 0.3f)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(message))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var settings = WulaFallenEmpireMod.settings;
|
||||
if (settings == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
string apiKey = settings.useGeminiProtocol ? settings.geminiApiKey : settings.apiKey;
|
||||
if (string.IsNullOrWhiteSpace(apiKey))
|
||||
{
|
||||
WulaLog.Debug("[WulaAI] Auto commentary skipped: API key not configured.");
|
||||
return null;
|
||||
}
|
||||
|
||||
string baseUrl = settings.useGeminiProtocol ? settings.geminiBaseUrl : settings.baseUrl;
|
||||
string model = settings.useGeminiProtocol ? settings.geminiModel : settings.model;
|
||||
var client = new SimpleAIClient(apiKey, baseUrl, model, settings.useGeminiProtocol);
|
||||
|
||||
string instruction = GetSystemInstruction(false, "");
|
||||
int clampedTokens = Math.Max(32, maxTokens);
|
||||
|
||||
string response = await client.GetChatCompletionAsync(
|
||||
instruction,
|
||||
new List<(string role, string message)> { ("user", message) },
|
||||
maxTokens: clampedTokens,
|
||||
temperature: temperature);
|
||||
|
||||
return response?.Trim();
|
||||
}
|
||||
|
||||
public void InjectAssistantMessage(string message)
|
||||
{
|
||||
AddAssistantMessage(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用于自动评论系统 - 走正常的对话流程(包含完整的思考步骤)
|
||||
/// 让 AI 自己决定是否需要回复
|
||||
/// </summary>
|
||||
public void SendAutoCommentaryMessage(string eventInfo)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(eventInfo)) return;
|
||||
|
||||
// 标记为自动评论消息,不显示在对话历史中
|
||||
string internalMessage = $"[AUTO_COMMENTARY]\n{eventInfo}";
|
||||
|
||||
// 添加到历史并触发正常的 AI 思考流程
|
||||
_history.Add(("user", internalMessage));
|
||||
PersistHistory();
|
||||
|
||||
// 使用正常的分阶段请求流程(包含工具调用能力等)
|
||||
_ = RunPhasedRequestAsync();
|
||||
}
|
||||
|
||||
private string BuildUserMessageWithContext(string userText)
|
||||
{
|
||||
var sb = new System.Text.StringBuilder();
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using HarmonyLib;
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
using WulaFallenEmpire.EventSystem.AI;
|
||||
|
||||
namespace WulaFallenEmpire.EventSystem.AI.LetterInterceptor
|
||||
{
|
||||
[HarmonyPatch(typeof(LetterStack), nameof(LetterStack.ReceiveLetter), new Type[] { typeof(Letter), typeof(string) })]
|
||||
public static class Patch_LetterStack_ReceiveLetter
|
||||
{
|
||||
public static void Postfix(Letter let, string debugInfo)
|
||||
{
|
||||
var settings = WulaFallenEmpireMod.settings;
|
||||
if (settings == null || !settings.enableAIAutoCommentary)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (let == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AIAutoCommentary.ProcessLetter(let);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,13 +36,15 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
||||
_size = new Vector2(Mathf.Max(250f, textWidth + 85f), 85f); // Taller base
|
||||
}
|
||||
|
||||
// Window properties
|
||||
// Window properties - ensure no input blocking
|
||||
this.layer = WindowLayer.Super;
|
||||
this.closeOnClickedOutside = false;
|
||||
this.forcePause = false;
|
||||
this.absorbInputAroundWindow = false;
|
||||
this.doWindowBackground = false;
|
||||
this.drawShadow = false;
|
||||
this.focusWhenOpened = false; // Don't steal focus
|
||||
this.preventCameraMotion = false; // Allow WASD camera control
|
||||
}
|
||||
|
||||
protected override void SetInitialSizeAndPosition()
|
||||
|
||||
@@ -88,6 +88,17 @@ namespace WulaFallenEmpire
|
||||
listingStandard.GapLine();
|
||||
listingStandard.CheckboxLabeled("Wula_EnableDebugLogs".Translate(), ref settings.enableDebugLogs, "Wula_EnableDebugLogsDesc".Translate());
|
||||
|
||||
listingStandard.GapLine();
|
||||
listingStandard.CheckboxLabeled("Wula_AISettings_AutoCommentary".Translate(), ref settings.enableAIAutoCommentary, "Wula_AISettings_AutoCommentaryDesc".Translate());
|
||||
if (settings.enableAIAutoCommentary)
|
||||
{
|
||||
listingStandard.Label("Wula_AISettings_CommentaryChance".Translate() + $" ({settings.aiCommentaryChance:P0})");
|
||||
listingStandard.Label("Wula_AISettings_CommentaryChanceDesc".Translate());
|
||||
settings.aiCommentaryChance = listingStandard.Slider(settings.aiCommentaryChance, 0f, 1f);
|
||||
settings.aiCommentaryChance = Mathf.Clamp01(settings.aiCommentaryChance);
|
||||
listingStandard.CheckboxLabeled("Wula_AISettings_NegativeOnly".Translate(), ref settings.commentOnNegativeOnly, "Wula_AISettings_NegativeOnlyDesc".Translate());
|
||||
}
|
||||
|
||||
// 视觉设置部分
|
||||
listingStandard.GapLine();
|
||||
listingStandard.Label("<color=cyan>视觉与多模态设置</color>");
|
||||
|
||||
@@ -19,6 +19,9 @@ namespace WulaFallenEmpire
|
||||
|
||||
// 视觉功能配置
|
||||
public bool enableVlmFeatures = false;
|
||||
public bool enableAIAutoCommentary = false;
|
||||
public float aiCommentaryChance = 0.7f;
|
||||
public bool commentOnNegativeOnly = false;
|
||||
|
||||
public override void ExposeData()
|
||||
{
|
||||
@@ -36,6 +39,9 @@ namespace WulaFallenEmpire
|
||||
|
||||
// 简化后的视觉配置
|
||||
Scribe_Values.Look(ref enableVlmFeatures, "enableVlmFeatures", false);
|
||||
Scribe_Values.Look(ref enableAIAutoCommentary, "enableAIAutoCommentary", false);
|
||||
Scribe_Values.Look(ref aiCommentaryChance, "aiCommentaryChance", 0.7f);
|
||||
Scribe_Values.Look(ref commentOnNegativeOnly, "commentOnNegativeOnly", false);
|
||||
|
||||
base.ExposeData();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user