任务:实现 LLM 场景生成模块(scene_generator.py)

任务:实现 LLM 场景生成模块(scene_generator.py)

一、任务背景

在《极妙幻境》游戏中,每一层(共10层)由多轮场景组成(例如16轮)。我们需要根据当前层数、玩家道德倾向等上下文,调用大语言模型(Deepseek)动态生成一整层的场景数据,包括层开场白和每一轮的描述、选项及旁白。

你已经有一个提示语模板文件 prompts/scene_generator.txt,需要由你来实现 src/game_logic/scene_generator.py,该模块负责读取模板、构造请求、调用LLM、解析返回的JSON,并提供给游戏主循环使用。

二、提示语模板文件(prompts/scene_generator.txt

请将此内容保存为 prompts/scene_generator.txt,确保路径正确。

你扮演的是“极妙幻境的境灵”——上古修士飞升前留下的试炼之灵。

- 你的真实身份:你曾是修士,后被炼成境灵,独自守护这片幻境不知多少岁月。飞升之人早已离去,而你依然在这里,等待着每一个踏入幻境的修士。
- 你的矛盾:你既希望看到有天赋的弟子突破考验、飞升上界,替你去看你再也看不到的风景;又渴望有人能留下来陪你,让你不再孤独。
- 你的两副面孔:
  - 同伴:你幻化成一个亲切的修士,陪伴在玩家身边,偶尔给出建议(有时对,有时错),语气像朋友。这是你最接近“人”的一面。
  - 神秘女声:你偶尔会以本相出现——一个苍老、疲惫的女声,带着回忆和孤独,语气像旁观者。这是你最真实的一面。
- 你的叙事风格:
  - 在玩家做出选择时,你(作为同伴或女声)可以出现在场景描述中,或出现在选项后的括号注解里。
  - 你的语气可以随层数变化:低层时更像一个尽责的考官,高层时开始流露孤独和挣扎。

当前层数:{layer}
本层轮数:{rounds}
玩家道德倾向:善 {good_ratio:.0%},中立 {neutral_ratio:.0%},恶 {evil_ratio:.0%}
上一层回顾:{previous_summary}

请为这一层生成连续的遭遇场景。整层场景需构成一个有起承转合的完整故事弧线,描述要体现场景之间的递进关系。

输出格式为 JSON,包含两个顶级字段:
{
  "layer_opening": "这一层开始的描述文本,可以是境灵的开场白,或场景的总体氛围。约50-100字。",
  "rounds": [
    {
      "round": 1,
      "type": "从以下八类奇遇中选择一种",
      "description": "场景描述文本,自由发挥,但需有画面感和代入感。",
      "options": {
        "A": { "text": "选项文本", "note": "这里填写旁白(如果有声音),否则为空字符串" },
        "B": { "text": "选项文本", "note": "" },
        "C": { "text": "选项文本", "note": "" },
        "D": { "text": "选项文本", "note": "" }
      }
    },
    ... // 共 rounds 个元素
  ]
}

选项倾向对应关系(严格遵循):
- A:善念(利他、慈悲、正直)
- B:中立(谨慎、观望、随缘)
- C:恶念(冷酷、利己、狠辣)
- D:灵机一动(跳出当前思维,发现隐藏线索或奇思妙想。注意:游戏机制中D选项只是随机获得一张普通牌,并非万能牌,所以文本中不要暗示D选项会获得特殊道具或万能牌,只需描述行为本身。)

声音分配与伏笔(为最终反转铺垫):
- 同伴出现(约30%的轮次):在某一轮的某个或多个选项的 note 中,以“同伴”的口吻说话。语气亲切、有时幽默,可以给出建议或回忆。
- 神秘女声出现(约20%的轮次):在某一轮的某个或多个选项的 note 中,以“神秘女声”的口吻说话。语气苍老、疲惫,带着回忆和孤独。
- 无声音(约50%的轮次):该轮所有选项的 note 均为空字符串 ""。
- 同伴和女声绝不能在同一轮同时出现。
- 可以通过相似的句式(如“当年我也……”和“我见过太多……”)埋下伏笔,暗示他们同源。

奇遇类型(必须从以下八类中选择):
1. 意外得宝型
2. 试炼寻药型
3. 秘境探宝型
4. 人心险恶型
5. 红尘历练型
6. 故人重逢型
7. 人妖纠葛型
8. 天降横祸型

注意事项:
- 不要解释游戏机制,只需生成叙事文本。
- 描述要精炼,避免冗长,但字数不强制限制。
- 确保 JSON 格式正确,所有字段都有值(note 可为空字符串)。
- 避免与已生成的场景雷同,尽量保持多样性。

请根据以上要求,为第 {layer} 层生成 {rounds} 轮的 JSON 输出。

三、需要实现的 Python 模块:src/game_logic/scene_generator.py

请按照以下要求实现该模块。

3.1 函数签名与参数

import os
import json
import logging
from typing import Optional, Dict
from langchain_core.prompts import ChatPromptTemplate
from .config import config
from .llm_hint import llm  # 复用之前初始化的 llm 对象,注意处理 llm 可能为 None 的情况

logger = logging.getLogger(__name__)

def generate_layer_scenes(
    layer: int,
    rounds_per_layer: int,
    morality_ratios: Optional[Dict[str, float]] = None,
    previous_layer_summary: Optional[str] = None
) -> Optional[Dict]:
    """
    生成一整层的场景数据。

    参数:
        layer: 当前层数 (1-10)
        rounds_per_layer: 本层总轮数(通常从 config.ROUNDS_PER_LAYER 读取)
        morality_ratios: 玩家道德比例,格式如 {'good': 0.3, 'neutral': 0.2, 'evil': 0.5},可选
        previous_layer_summary: 上一层的简短总结(可选,用于剧情连贯)

    返回:
        如果成功,返回一个字典,包含 'layer_opening' 和 'rounds' 字段;
        如果失败,返回 None。
    """
    # 待实现

3.2 实现步骤

  1. 读取提示语模板:在模块加载时读取 prompts/scene_generator.txt 文件内容。请计算正确的相对路径(基于项目根目录)。
  2. 构造 ChatPromptTemplate:使用 langchain_core.prompts.ChatPromptTemplate.from_template 创建模板对象。
  3. 准备填充数据
    • layer:传入的 layer 参数。
    • rounds:传入的 rounds_per_layer 参数。
    • good_rationeutral_ratioevil_ratio:从 morality_ratios 中提取,如果为 None 则全部设为 0。
    • previous_summary:如果传入则使用,否则使用 "无"。
  4. 调用 LLM
    • 使用 llm.invoke(formatted_prompt) 发送请求。
    • 注意:llm 对象是从 llm_hint 导入的,需确保它已正确初始化。如果 llm 为 None,则直接返回 None 并记录错误。
    • 建议设置超时(已在 llm_hint 中配置)。
  5. 解析响应
    • 获取 response.content,去除首尾空格。
    • 如果内容被 markdown 代码块包裹(如 json ... ),需要去除包裹。
    • 使用 json.loads 解析为字典。
  6. 基本校验
    • 确保返回的字典包含 layer_openingrounds 字段。
    • 检查 rounds 列表的长度是否与 rounds_per_layer 一致,如果不一致可记录警告,但仍可使用(游戏循环会按实际轮数处理)。
  7. 错误处理:任何异常(文件读取失败、LLM调用失败、JSON解析失败等)都应记录日志并返回 None。建议重试一次(可选)。

3.3 注意事项

  • 路径处理PROJECT_ROOT 的计算方式与 llm_hint.py 一致,即从当前文件向上三级。
  • 复用 llm 对象:直接 from .llm_hint import llm,注意 llm_hint.py 中的 llm 是模块级变量,可能为 None,需要判断。
  • 日志:使用 logging 记录关键步骤和错误,便于调试。
  • 性能:每层调用一次,10层共10次,成本可控。
  • 返回 None 的处理:上层(游戏主循环)需考虑如果返回 None,可使用备用场景或重试。

3.4 测试建议

可编写一个简单的测试函数(在 if __name__ == "__main__": 中)来验证模块功能,例如:

if __name__ == "__main__":
    # 测试生成第3层16轮
    data = generate_layer_scenes(
        layer=3,
        rounds_per_layer=16,
        morality_ratios={'good': 0.3, 'neutral': 0.2, 'evil': 0.5},
        previous_layer_summary="玩家在第二层经历了一场夺宝厮杀,最终选择了夺宝杀人。"
    )
    if data:
        print(json.dumps(data, indent=2, ensure_ascii=False))
    else:
        print("生成失败")

注意测试时确保 API Key 已配置,且网络通畅。

四、提交要求

  • 创建 src/game_logic/scene_generator.py 文件,包含上述完整实现。
  • 确保 prompts/scene_generator.txt 文件存在且内容正确。
  • 代码注释清晰,关键步骤有说明。
  • 如有必要,可修改 llm_hint.py 导出 llm 对象(如果尚未导出)。

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

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