Files
WulaFallenEmpireRW/.agent/workflows/ai_letter_auto_commentary.md
2025-12-28 16:25:20 +08:00

14 KiB
Raw Blame History

AI Letter Auto-Response System - 开发文档

概述

这个功能将使 P.I.A AI 能够自动监听游戏内的 Letter信封通知并根据内容智能决定是否向玩家发送评论、吐槽、警告或提供帮助建议。


功能需求

核心功能

  1. Mod 设置开关: 在设置中添加 启用 AI 自动评论 开关
  2. Letter 监听: 拦截所有发送给玩家的 Letter
  3. 智能判断: AI 分析 Letter 内容,决定是否需要回应
  4. 自动回复: 通过现有的 AI 对话系统发送回复

AI 回应类型

类型 触发场景示例 回应风格
警告 袭击通知、疫病爆发 紧急提醒,询问是否需要启动防御
吐槽 殖民者精神崩溃、愚蠢死亡 幽默/讽刺评论
建议 资源短缺、贸易商到来 实用建议
庆祝 任务完成、殖民者加入 积极反馈
沉默 常规事件、无关紧要的通知 不发送任何回复

技术架构

1. 文件结构

Source/WulaFallenEmpire/
├── Settings/
│   └── WulaModSettings.cs          # 添加新设置字段
├── EventSystem/
│   └── AI/
│       ├── LetterInterceptor/
│       │   ├── Patch_LetterStack.cs    # Harmony Patch 拦截 Letter
│       │   ├── LetterAnalyzer.cs       # Letter 分析和分类
│       │   └── LetterToPromptConverter.cs  # Letter 转提示词
│       └── AIAutoCommentary.cs         # AI 自动评论逻辑

2. 关键类设计

2.1 WulaModSettings.cs (修改)

public class WulaModSettings : ModSettings
{
    // 现有设置...
    
    // 新增
    public bool enableAIAutoCommentary = false;  // AI 自动评论开关
    public float aiCommentaryChance = 0.7f;      // AI 评论概率 (0-1)
    public bool commentOnNegativeOnly = false;   // 仅评论负面事件
    
    public override void ExposeData()
    {
        base.ExposeData();
        Scribe_Values.Look(ref enableAIAutoCommentary, "enableAIAutoCommentary", false);
        Scribe_Values.Look(ref aiCommentaryChance, "aiCommentaryChance", 0.7f);
        Scribe_Values.Look(ref commentOnNegativeOnly, "commentOnNegativeOnly", false);
    }
}

2.2 Patch_LetterStack.cs (新建)

using HarmonyLib;
using RimWorld;
using Verse;

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)
        {
            // 检查设置开关
            if (!WulaModSettings.Instance.enableAIAutoCommentary) return;
            
            // 异步处理,避免阻塞游戏
            AIAutoCommentary.ProcessLetter(let);
        }
    }
}

2.3 LetterAnalyzer.cs (新建)

namespace WulaFallenEmpire.EventSystem.AI.LetterInterceptor
{
    public enum LetterCategory
    {
        Raid,           // 袭击
        Disease,        // 疾病
        MentalBreak,    // 精神崩溃
        Trade,          // 贸易
        Quest,          // 任务
        Death,          // 死亡
        Recruitment,    // 招募
        Resource,       // 资源
        Weather,        // 天气
        Positive,       // 正面事件
        Negative,       // 负面事件
        Neutral,        // 中性事件
        Unknown         // 未知
    }

    public static class LetterAnalyzer
    {
        public static LetterCategory Categorize(Letter letter)
        {
            // 根据 LetterDef 分类
            var def = letter.def;
            
            if (def == LetterDefOf.ThreatBig || def == LetterDefOf.ThreatSmall)
                return LetterCategory.Raid;
            if (def == LetterDefOf.Death)
                return LetterCategory.Death;
            if (def == LetterDefOf.PositiveEvent)
                return LetterCategory.Positive;
            if (def == LetterDefOf.NegativeEvent)
                return LetterCategory.Negative;
            if (def == LetterDefOf.NeutralEvent)
                return LetterCategory.Neutral;
            
            // 根据内容关键词进一步分类
            string text = letter.text?.ToLower() ?? "";
            if (text.Contains("raid") || text.Contains("袭击") || text.Contains("attack"))
                return LetterCategory.Raid;
            if (text.Contains("disease") || text.Contains("疫病") || text.Contains("plague"))
                return LetterCategory.Disease;
            if (text.Contains("mental") || text.Contains("精神") || text.Contains("break"))
                return LetterCategory.MentalBreak;
            if (text.Contains("trade") || text.Contains("贸易") || text.Contains("商队"))
                return LetterCategory.Trade;
            
            return LetterCategory.Unknown;
        }
        
        public static bool ShouldComment(Letter letter)
        {
            var category = Categorize(letter);
            
            // 始终评论的类型
            switch (category)
            {
                case LetterCategory.Raid:
                case LetterCategory.Death:
                case LetterCategory.MentalBreak:
                case LetterCategory.Disease:
                    return true;
                    
                case LetterCategory.Trade:
                case LetterCategory.Quest:
                case LetterCategory.Positive:
                    return Rand.Chance(WulaModSettings.Instance.aiCommentaryChance);
                    
                case LetterCategory.Neutral:
                case LetterCategory.Unknown:
                    return Rand.Chance(0.3f); // 低概率评论
                    
                default:
                    return false;
            }
        }
    }
}

2.4 LetterToPromptConverter.cs (新建)

namespace WulaFallenEmpire.EventSystem.AI.LetterInterceptor
{
    public static class LetterToPromptConverter
    {
        public static string Convert(Letter letter, LetterCategory category)
        {
            var sb = new StringBuilder();
            
            sb.AppendLine("[SYSTEM EVENT NOTIFICATION]");
            sb.AppendLine($"Event Type: {category}");
            sb.AppendLine($"Severity: {GetSeverityFromDef(letter.def)}");
            sb.AppendLine($"Title: {letter.label}");
            sb.AppendLine($"Content: {letter.text}");
            sb.AppendLine();
            sb.AppendLine("[INSTRUCTION]");
            sb.AppendLine("You have received a game event notification. Based on the event type and content:");
            sb.AppendLine("- For RAIDS/THREATS: Offer tactical advice or ask if player needs orbital support");
            sb.AppendLine("- For DEATHS: Express condolences or make a sardonic comment if death was avoidable");
            sb.AppendLine("- For MENTAL BREAKS: Comment on the colonist's weakness or offer mood management tips");
            sb.AppendLine("- For TRADE: Suggest useful purchases or sales");
            sb.AppendLine("- For POSITIVE events: Celebrate briefly");
            sb.AppendLine("- For trivial events: You may choose to say nothing (respond with [NO_COMMENT])");
            sb.AppendLine();
            sb.AppendLine("Keep your response brief (1-2 sentences). Match your personality as the Legion AI.");
            sb.AppendLine("If you don't think this event warrants a response, reply with exactly: [NO_COMMENT]");
            
            return sb.ToString();
        }
        
        private static string GetSeverityFromDef(LetterDef def)
        {
            if (def == LetterDefOf.ThreatBig) return "CRITICAL";
            if (def == LetterDefOf.ThreatSmall) return "WARNING";
            if (def == LetterDefOf.Death) return "SERIOUS";
            if (def == LetterDefOf.NegativeEvent) return "MODERATE";
            if (def == LetterDefOf.PositiveEvent) return "GOOD";
            return "INFO";
        }
    }
}

2.5 AIAutoCommentary.cs (新建)

namespace WulaFallenEmpire.EventSystem.AI
{
    public static class AIAutoCommentary
    {
        private static Queue<Letter> pendingLetters = new Queue<Letter>();
        private static bool isProcessing = false;
        
        public static void ProcessLetter(Letter letter)
        {
            // 检查是否应该评论
            if (!LetterAnalyzer.ShouldComment(letter))
            {
                WulaLog.Debug($"[AI Commentary] Skipping letter: {letter.label}");
                return;
            }
            
            // 加入队列
            pendingLetters.Enqueue(letter);
            
            // 开始处理(如果还没在处理中)
            if (!isProcessing)
            {
                ProcessNextLetter();
            }
        }
        
        private static async void ProcessNextLetter()
        {
            if (pendingLetters.Count == 0)
            {
                isProcessing = false;
                return;
            }
            
            isProcessing = true;
            var letter = pendingLetters.Dequeue();
            
            try
            {
                var category = LetterAnalyzer.Categorize(letter);
                var prompt = LetterToPromptConverter.Convert(letter, category);
                
                // 获取 AI 核心
                var aiCore = Find.World?.GetComponent<AIIntelligenceCore>();
                if (aiCore == null)
                {
                    WulaLog.Debug("[AI Commentary] AIIntelligenceCore not found.");
                    ProcessNextLetter();
                    return;
                }
                
                // 发送到 AI 并等待响应
                string response = await aiCore.SendSystemMessageAsync(prompt);
                
                // 检查是否选择不评论
                if (string.IsNullOrEmpty(response) || response.Contains("[NO_COMMENT]"))
                {
                    WulaLog.Debug($"[AI Commentary] AI chose not to comment on: {letter.label}");
                }
                else
                {
                    // 显示 AI 的评论
                    DisplayAICommentary(response, letter);
                }
            }
            catch (Exception ex)
            {
                WulaLog.Debug($"[AI Commentary] Error processing letter: {ex.Message}");
            }
            
            // 延迟处理下一个,避免刷屏
            await Task.Delay(2000);
            ProcessNextLetter();
        }
        
        private static void DisplayAICommentary(string response, Letter originalLetter)
        {
            // 方式1: 作为小型通知显示在 WulaLink 小 UI
            var overlay = Find.WindowStack.Windows.OfType<Overlay_WulaLink>().FirstOrDefault();
            if (overlay != null)
            {
                overlay.AddAIMessage(response);
            }
            
            // 方式2: 作为 Message 显示在屏幕左上角
            Messages.Message($"[P.I.A]: {response}", MessageTypeDefOf.SilentInput);
        }
    }
}

实现步骤

阶段 1: 基础设施 (预计 1 小时)

  1. WulaModSettings.cs 添加新设置字段
  2. 在设置 UI 中添加开关
  3. 添加对应的 Keyed 翻译

阶段 2: Letter 拦截 (预计 30 分钟)

  1. 创建 Patch_LetterStack.cs Harmony Patch
  2. 确保 Patch 正确注册到 Harmony 实例
  3. 测试 Letter 拦截是否正常工作

阶段 3: Letter 分析 (预计 1 小时)

  1. 创建 LetterAnalyzer.cs 分类逻辑
  2. 创建 LetterToPromptConverter.cs 转换逻辑
  3. 测试不同类型 Letter 的分类准确性

阶段 4: AI 集成 (预计 1.5 小时)

  1. 创建 AIAutoCommentary.cs 管理类
  2. 集成到现有的 AIIntelligenceCore 系统
  3. 实现队列处理避免刷屏
  4. 添加 SendSystemMessageAsync 方法到 AIIntelligenceCore

阶段 5: UI 显示 (预计 30 分钟)

  1. 决定评论显示方式WulaLink UI / Message / 独立通知)
  2. 实现显示逻辑
  3. 测试显示效果

阶段 6: 测试与优化 (预计 1 小时)

  1. 测试各类 Letter 的评论效果
  2. 调整评论概率和过滤规则
  3. 优化提示词以获得更好的 AI 回应
  4. 添加速率限制避免 API 过载

需要添加的翻译键

<!-- AI Auto Commentary Settings -->
<Wula_AISettings_AutoCommentary>启用 AI 自动评论</Wula_AISettings_AutoCommentary>
<Wula_AISettings_AutoCommentaryDesc>开启后P.I.A 会自动对游戏事件(袭击、死亡、贸易等)发表评论或提供建议。</Wula_AISettings_AutoCommentaryDesc>
<Wula_AISettings_CommentaryChance>评论概率</Wula_AISettings_CommentaryChance>
<Wula_AISettings_CommentaryChanceDesc>AI 对中性事件发表评论的概率。负面事件(如袭击)总是会评论。</Wula_AISettings_CommentaryChanceDesc>
<Wula_AISettings_NegativeOnly>仅评论负面事件</Wula_AISettings_NegativeOnly>
<Wula_AISettings_NegativeOnlyDesc>开启后AI 只会对负面事件(袭击、死亡、疾病等)发表评论。</Wula_AISettings_NegativeOnlyDesc>

注意事项

  1. API 限流: 需要实现请求队列和速率限制,避免短时间内发送过多请求
  2. 异步处理: 所有 AI 请求必须异步处理,避免阻塞游戏主线程
  3. 用户控制: 提供足够的设置选项让用户控制评论频率和类型
  4. 优雅降级: 如果 AI 服务不可用,静默失败而不影响游戏
  5. 内存管理: 队列大小限制,避免积累过多未处理的 Letter

预期效果示例

场景 1: 袭击通知

[Letter] 海盗袭击!一群海盗正在向你的殖民地进发。
[P.I.A] 检测到敌对势力入侵。需要我启动轨道监视协议吗?

场景 2: 殖民者死亡

[Letter] 张三死了。他被一只疯狂的松鼠咬死了。
[P.I.A] ...被松鼠咬死?这位殖民者的战斗技能令人印象深刻。

场景 3: 贸易商到来

[Letter] 商队到来。一个来自外部势力的商队想要与你交易。
[P.I.A] 贸易商队抵达。我注意到你的钢铁储备较低,建议优先采购。

依赖项

  • Harmony 2.0+ (用于 Patch)
  • 现有的 AIIntelligenceCore 系统
  • 现有的 WulaModSettings 系统
  • 现有的 Overlay_WulaLink UI

文档版本: 1.0 创建时间: 2025-12-28