AI游戏剧情生成:用Python脚本把事件与提示词串成可复用流程
做AI游戏剧情生成,最省事的起步方式不是先研究引擎插件,而是把剧情拆成“事件表 + 提示词模板 + 模型接口”三个部分,用一段Python脚本跑通。
这样每条剧情都能单独重跑、改提示词后立即对比效果,等流程稳定了再往Unity或Godot里接。
先定剧情结构,再碰模型
直接让模型“写一段游戏剧情”,结果往往散乱、角色对不上、分支无法落地。
比较稳的做法是先准备一张事件表,每行代表一个剧情节点,字段至少包括:
node_id:节点编号,例如forest_01scene:场景描述,例如“迷雾森林入口”characters:出场角色及关系player_choice:玩家选择或触发条件goal:本节点要达成的剧情目标
事件表可以先用CSV维护,字段不追求多,但要保证每个节点都有明确的目标和约束。
模型只负责把这一行扩写成可读的剧情文本,而不是自由发挥整个故事。
编写可复用的提示词模板
提示词模板建议单独存成文本文件,用占位符替换事件表内容。
下面是一份最小可用模板,放在 prompts/story_node.txt:
你是一名游戏剧情编剧。请根据以下节点信息,输出严格JSON,不要输出解释。
节点编号:{node_id}
场景:{scene}
出场角色:{characters}
玩家选择:{player_choice}
剧情目标:{goal}
输出格式:
{
"node_id": "原样返回",
"narration": "不超过120字的旁白",
"dialogue": [{"speaker": "角色名", "line": "台词"}],
"next_node_hint": "下一节点建议,若无则填null"
}
模板里固定“严格JSON”和字段名,是为了后续脚本能直接解析。
如果模型返回带Markdown代码块的文本,解析前要先去壳。
用Python调用模型接口
把事件表读入,逐行填充模板,调用兼容OpenAI格式的接口。
下面代码只依赖 requests,把 BASE_URL、API_KEY、MODEL 换成你的实际值:
import csv, json, os, requests
BASE_URL = os.environ["LLM_BASE_URL"]
API_KEY = os.environ["LLM_API_KEY"]
MODEL = os.environ.get("LLM_MODEL", "your-model-name")
def build_prompt(row):
with open("prompts/story_node.txt", encoding="utf-8") as f:
tpl = f.read()
return tpl.format(**row)
def call_model(prompt):
resp = requests.post(
f"{BASE_URL}/chat/completions",
headers={"Authorization": f"Bearer {API_KEY}"},
json={"model": MODEL, "messages": [{"role": "user", "content": prompt}], "temperature": 0.8},
timeout=60,
)
resp.raise_for_status()
return resp.json()["choices"][0]["message"]["content"]
with open("events.csv", encoding="utf-8") as f:
for row in csv.DictReader(f):
raw = call_model(build_prompt(row))
raw = raw.strip().removeprefix("```json").removeprefix("```").removesuffix("```").strip()
data = json.loads(raw)
with open(f"output/{row['node_id']}.json", "w", encoding="utf-8") as out:
json.dump(data, out, ensure_ascii=False, indent=2)
print("ok", row["node_id"])
运行前建好 output/ 目录,并把密钥放在环境变量里,不要写进脚本。
如果接口返回401,先检查 API_KEY 是否有效;
返回404,检查 BASE_URL 是否少了 /v1 或路径写错。
避坑:剧情生成最容易卡住的几个点
- 模型返回非JSON:在提示词里强调“不要输出解释”,并在代码中去掉代码块围栏;仍失败时把
temperature降到0.5左右重试。 - 角色名前后不一致:事件表里固定角色名,模板里要求原样使用,不要依赖模型自己起名。
- 分支无法落地:
next_node_hint只作为建议,最终节点关系仍以你的事件表为准,避免模型编出不存在的节点编号。 - 批量跑一半失败:给每个节点单独捕获异常并记录日志,失败节点重跑即可,不要整批重来。
验证生成结果是否可用
跑完后打开 output/ 下的JSON文件,逐项检查:node_id 是否与事件表一致、dialogue 是否为数组且每个元素都有 speaker 和 line、旁白是否超过字数限制。
可以用一段简单脚本做批量校验:
import json, glob
for path in glob.glob("output/*.json"):
data = json.load(open(path, encoding="utf-8"))
assert data.get("node_id"), path
assert isinstance(data.get("dialogue"), list), path
assert all("speaker" in d and "line" in d for d in data["dialogue"]), path
print("all passed")
校验通过后,把JSON按节点导入游戏引擎的对话系统,先在一个小场景里试跑,确认剧情衔接和角色说话顺序符合预期,再批量扩写。
AI游戏剧情生成的关键不在模型多强,而在于事件表是否清晰、提示词模板是否稳定、输出是否有校验,这三步做到位,后续换模型或换引擎都不用推翻重来。