任务:实现层间事件生成函数(layer_event_generator.py)

任务:实现层间事件生成函数(layer_event_generator.py)

一、功能概述

在《极妙幻境》中,每层结束后(非 BOSS 层)会触发一次层间事件。玩家将面对一个特殊的幻境场景,通过选择可能获得一张万能牌。你需要实现一个函数 generate_layer_event,根据当前层数和玩家道德比例,调用大语言模型(Deepseek)生成层间事件的描述、四个选项、每个选项的后续结果,并标记出唯一的陷阱选项(选该选项无法获得万能牌)。

二、模块位置与依赖

  • 文件路径src/game_logic/layer_event_generator.py
  • 依赖模块
    • config:可能不需要直接使用,但可复用路径计算等。
    • llm_hint:复用 llm 对象和 check_api_key
    • langchain_core.prompts:用于构建提示词模板。
    • loggingjsonreos 等标准库。
  • 复用资源:可复用 scene_generator.py 中的 _parse_llm_responsePROJECT_ROOT 等辅助函数。

三、函数设计

3.1 函数签名

import logging
import os
import json
import re
from typing import Dict, List, Optional, Any
from langchain_core.prompts import ChatPromptTemplate
from .config import config
from .llm_hint import llm, check_api_key

logger = logging.getLogger(__name__)

PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
TEMPLATE_PATH = os.path.join(PROJECT_ROOT, "prompts", "layer_event_prompt.txt")

# 模块加载时读取模板
try:
    with open(TEMPLATE_PATH, "r", encoding="utf-8") as f:
        template_str = f.read()
    logger.info(f"层间事件模板已加载: {TEMPLATE_PATH}")
except FileNotFoundError:
    logger.error(f"层间事件模板文件未找到: {TEMPLATE_PATH}")
    template_str = None

def generate_layer_event(
    layer: int,
    morality_ratios: Optional[Dict[str, float]] = None
) -> Optional[Dict]:
    """
    生成层间事件数据。

    参数:
        layer: 当前层数 (1-10)
        morality_ratios: 玩家道德比例,如 {'good':0.3, 'neutral':0.2, 'evil':0.5},可选

    返回:
        如果成功,返回一个字典,包含以下字段:
        {
            "description": str,  # 事件描述
            "options": {
                "A": {
                    "text": str,
                    "is_trap": bool,
                    "outcome": str,          # 选择后的叙事文本
                    "card_name": Optional[str],  # 若 is_trap=False,获得万能牌的名称
                    "card_description": Optional[str]  # 卡牌描述
                },
                "B": { ... },
                "C": { ... },
                "D": { ... }
            }
        }
        如果失败,返回 None。
    """
    # 实现细节见下文

3.2 返回值结构说明

  • description:事件描述文本,约 50-100 字,营造神秘氛围。
  • options:四个选项,每个选项包含:
    • text:选项文本,简洁(不超过 15 字)。
    • is_trap:布尔值,表示该选项是否为陷阱(选陷阱不得万能牌)。
    • outcome:玩家选择后的叙事结果,约 30-50 字。
    • card_name:如果非陷阱,获得万能牌的名称(如“虚空幻影”),陷阱选项此项为 null
    • card_description:卡牌描述,约 10-20 字,陷阱选项此项为 null

注意:必须确保四个选项中只有一个 is_trap 为 True,其他三个为 False。

四、提示词模板(layer_event_prompt.txt

将以下内容保存为 prompts/layer_event_prompt.txt

你扮演的是“极妙幻境的境灵”——上古修士飞升前留下的试炼之灵。你内心矛盾,既想考验弟子,又渴望有人陪伴。

现在玩家已经完成了第 {layer} 层的试炼,幻境深处产生了一次异动,你需要为玩家生成一个层间事件。这是一个特殊的幻境片段,玩家将面临一个短暂但关键的抉择,可能获得一张万能牌,也可能错过。

当前玩家的道德倾向:善 {good_ratio:.0%},中立 {neutral_ratio:.0%},恶 {evil_ratio:.0%}。这个倾向可以微妙地影响事件氛围,但不必生硬挂钩。

请生成一个 JSON 对象,格式如下:

{
  "description": "事件描述,约50-100字,要有幻境特有的诡异或神秘感。",
  "options": {
    "A": {
      "text": "选项A文本(不超过15字)",
      "is_trap": false,
      "outcome": "选择A后的叙事结果,约30-50字,描述发生了什么,以及是否获得万能牌。",
      "card_name": "万能牌名称(2-5字,如'镜中花')",
      "card_description": "卡牌描述(10-20字,说明这张牌的由来)"
    },
    "B": { ... },
    "C": { ... },
    "D": { ... }
  }
}

重要规则:
1. 四个选项中,**必须恰好有一个选项的 is_trap 为 true**,其他三个为 false。
2. 陷阱选项不得获得万能牌,因此其 card_name 和 card_description 应为 null。
3. 非陷阱选项可以获得万能牌,card_name 和 card_description 需生成,并确保名称和描述与事件情境相关。
4. 选项文本要简洁,让玩家一眼看懂。
5. 描述和结果要生动,体现修仙世界的氛围,但避免解释游戏机制。
6. 道德比例可用来微调语气(例如善念高时,事件更倾向于慈悲考验;恶念高时更倾向于残酷诱惑),但不要生硬。

直接输出 JSON,不要包含额外内容。

五、实现步骤

  1. 检查模板:若 template_str 为 None,直接返回 None 并记录错误。
  2. 检查 LLM 和 API Key:复用 llm_hintllmcheck_api_key,若失败返回 None。
  3. 准备填充数据
    good_ratio = morality_ratios.get('good', 0.0) if morality_ratios else 0.0
    neutral_ratio = morality_ratios.get('neutral', 0.0) if morality_ratios else 0.0
    evil_ratio = morality_ratios.get('evil', 0.0) if morality_ratios else 0.0
    
  4. 格式化提示词
    prompt = ChatPromptTemplate.from_template(template_str)
    formatted_prompt = prompt.format(
        layer=layer,
        good_ratio=good_ratio,
        neutral_ratio=neutral_ratio,
        evil_ratio=evil_ratio
    )
    
  5. 调用 LLM:使用 llm.invoke(formatted_prompt),可重试 2 次。
  6. 解析响应:复用 scene_generator.py 中的 _parse_llm_response(或复制一个),去除 markdown 代码块,解析 JSON。
  7. 校验数据
    • 确保返回的字典包含 descriptionoptions 字段。
    • 确保 options 包含 A/B/C/D 四个键。
    • 检查每个选项的字段完整(text, is_trap, outcome, card_name, card_description)。
    • 验证陷阱选项的数量是否为 1。
    • 陷阱选项的 card_name 和 card_description 应为 None 或空,非陷阱选项应有值。
    • 如果校验失败,记录警告并返回 None。
  8. 返回结果:通过校验后返回字典。

六、默认值设计

如果 LLM 调用失败或校验不通过,应返回一个简单的默认事件,保证游戏流程不中断。默认事件需满足结构要求,陷阱选项为 C(示例):

DEFAULT_EVENT = {
    "description": "幻境微微颤动,你看见一道虚影闪过。",
    "options": {
        "A": {
            "text": "追上去看看",
            "is_trap": False,
            "outcome": "你追上了虚影,它化作一张灵符落入你手中。",
            "card_name": "追影符",
            "card_description": "幻境中捕捉到的残影,可当作任意牌使用。"
        },
        "B": {
            "text": "静观其变",
            "is_trap": False,
            "outcome": "虚影绕着你转了一圈,留下一缕青烟,凝成一张牌。",
            "card_name": "观烟牌",
            "card_description": "静观其变所得,蕴含一丝道韵。"
        },
        "C": {
            "text": "出手攻击",
            "is_trap": True,
            "outcome": "你出手攻击,虚影破碎,什么也没留下。",
            "card_name": None,
            "card_description": None
        },
        "D": {
            "text": "置之不理",
            "is_trap": False,
            "outcome": "虚影消散前,留下一张牌在你脚下。",
            "card_name": "遗影",
            "card_description": "虚影离去前的馈赠。"
        }
    }
}

七、测试建议

编写单元测试文件 tests/test_layer_event_generator.py,至少覆盖:

  1. 成功生成:mock LLM 返回有效 JSON,验证返回结构正确,陷阱选项唯一。
  2. JSON 解析错误:模拟 LLM 返回无效 JSON,验证返回 None 或默认事件。
  3. 校验失败:模拟 LLM 返回缺少字段的 JSON,验证返回默认事件。
  4. 道德比例传入:验证提示词中包含正确的比例。
  5. 模板缺失:模拟模板文件不存在,验证返回 None。

八、提交要求

  • 创建 src/game_logic/layer_event_generator.py,包含上述实现。
  • 创建 prompts/layer_event_prompt.txt,包含提示词内容。
  • 创建 tests/test_layer_event_generator.py,包含单元测试。
  • 代码注释清晰,关键逻辑有说明。
  • 确保所有测试通过。

开始实现吧!如有疑问,随时沟通。

posted @ 2026-03-17 21:41  神秘园欢迎您  阅读(11)  评论(0)    收藏  举报