「穿越到一人之下世界,觉醒王者气人系统,靠气人攒生气值抽英雄模板」——这个标题一眼看过去是网文钩子,不是技术项目。但把它当成一个工程问题来拆,里面其实塞满了非常典型的技术需求:一整套会随剧情滚动的数值面板,一套需要跨几十万字保持一致的角色卡,一套「抽卡」式的模板资源池,以及最容易翻车的长文本上下文管理。
这次我们不聊具体情节尺度,只聊怎么把这个「系统流」设定做成一条可运行、可复现、可批量产出的本地 AI 创作流水线。核心思路一句话概括:让大模型只负责叙事文本,所有数值、模板、抽卡结果、好感度、仇恨值全部交给程序算,模型无权修改账本。这样做的好处是,不管你写到第 10 章还是第 300 章,面板数字永远对得上,不会出现第一章还差 20 点生气值、第三章突然变成 3000 点的离谱情况。
整条流水线跑在本机就能完成,用 Ollama 或者 vLLM 起一个 OpenAI 兼容接口,Python 侧做编排、校验、落盘,显存占用按你选的模型规模走,7B 到 14B 量级的模型在消费级显卡上通常能跑,具体数字以本机实测为准。下面会带你完成环境准备、服务启动、设定库建模、章节生成、接口调用、批量任务和排错,中间给出的命令和代码都可以直接复制改路径使用。
这篇文章适合三类读者:想用本地模型做长篇小说辅助创作的个人作者;正在做多轮结构化输出、需要保证数值一致性的开发者;以及想了解 RAG 角色卡、滚动摘要、账本式状态管理怎么落地的人。如果你只是想看一段爽文,这里没有;如果你想把「系统流」做成一条能跑起来的产线,可以直接往下翻。
1. 核心能力速览
先把这条流水线拆成一张规格表,方便你判断要不要投入时间。需要提前说明的是,下表里涉及具体硬件数字的部分,都只给量级参考,最终占用必须按你本机的模型版本、量化方式、上下文长度实测,任何写死的显存数字都是不负责任的。
| 能力项 | 说明 |
|---|---|
| 项目类型 | 本地部署的长篇连载创作辅助流水线,围绕「系统面板 + 角色卡 + 数值账本」展开 |
| 核心思路 | 数值、抽卡、状态由程序计算;模型只产出叙事文本与结构化建议 |
| 主要功能 | 设定库管理、章节大纲生成、正文草稿生成、数值账本更新、角色一致性校验、批量出稿 |
| 推荐硬件 | 消费级独立显卡即可起步,模型规模从 7B 量级开始;无独显时可退到 CPU 推理或小参数模型 |
| 显存占用 | 不确定,需按实际模型版本、量化精度、上下文长度测试;上下文越长 KV Cache 越大 |
| 支持平台 | Windows、Linux、macOS,Python 侧逻辑跨平台一致 |
| 启动方式 | Ollama 本地服务、vLLM 服务、或任意 OpenAI 兼容推理服务 |
| 是否支持 API | 支持,走 OpenAI 兼容的 /v1/chat/completions 与 /v1/models |
| 是否支持批量任务 | 支持,用任务清单加断点续跑的方式实现 |
| 数据落盘 | 设定库、大纲、正文、账本、日志分目录管理 |
| 适合场景 | 个人创作辅助、设定一致性管理、长文本实验、结构化输出练手 |
从表里能看出,这套东西的技术含量不在「让模型写小说」,那部分今天的模型已经很擅长了。真正的难点集中在三块:第一是结构化输出,模型必须稳定吐出可解析的 JSON,不能今天给 Markdown、明天给一段带前缀的废话;第二是状态管理,几十万字连载里的人物关系、资源数值、伏笔回收,必须有一个外部账本兜底;第三是上下文预算,你不能把前 200 章原文全塞进 prompt,必须有摘要和检索。
这三点对应到工程上,就是 JSON Schema 校验、账本式状态机、以及滚动摘要加关键片段检索。后面的章节会按这个顺序逐个展开,每一块都会给可运行的代码。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 适用场景与使用边界
先说清楚这东西能干什么、不该干什么,避免后面走弯路。它最擅长的场景是「降低机械劳动」:比如把一份章节大纲扩写成 3000 字草稿、把上一章结尾的状态同步进账本、检查这一章有没有把某个人物的发色写错、生成十条候选桥段让你挑。这些活儿人工做要花时间,模型做得又快又稳定,属于典型的性价比区间。
它不适合的场景同样明确。第一,不适合直接产出成品商用文本。模型生成的段落普遍存在节奏拖沓、对话同质化、转折生硬的问题,尤其长段落里容易出现「说了很多但什么都没发生」的空转,必须人工重写。第二,不适合替代人工做价值观和合规判断。涉及暴力、亲密关系、敏感社会议题的内容,模型可能给出你不想要的方向,必须有人工审稿环节。
第三点必须单独强调:《一人之下》是有明确版权归属的作品,角色名、世界观设定都受保护。拿它做个人练习、私下实验没问题,但一旦涉及公开发布、商业变现、平台签约,就必须确认授权与平台规则,不能想当然。同人创作在不同平台的接受度差异很大,发布前务必逐条核对平台规范。同理,如果你用这套流水线处理真人素材、他人肖像、真实姓名,也需要取得明确授权。
最后是数据边界。整条流水线跑在本地,设定库和正文都落盘在你自己的机器上,这比把几十万字草稿贴到在线服务里安全得多。但要注意本地推理服务默认监听 127.0.0.1 就好,不要图方便改成 0.0.0.0 暴露到局域网甚至公网,一旦开了端口又没有鉴权,等于把你的草稿库公开。批量任务跑起来之后也要留意日志里有没有异常请求。
3. 环境准备与前置条件
环境这块不用搞太复杂,一套 Python 加一个本地推理服务就够了。Python 建议 3.10 以上,虚拟环境用 venv 或 conda 都行,重点是依赖装进隔离环境,别污染系统 Python。下面这套命令是通用模板,路径和版本号按你实际情况替换。
bash复制# 创建并激活虚拟环境(Linux / macOS)
python3 -m venv venv
source venv/bin/activate
# Windows PowerShell 用这两行
# python -m venv venv
# .\venv\Scripts\Activate.ps1
# 安装编排侧依赖
pip install --upgrade pip
pip install requests pydantic jsonschema rich
推理服务有两种常见选择。想省事就用 Ollama,安装完拉个模型就能起服务,默认端口 11434,自带 OpenAI 兼容接口。想要更细的并发控制和显存调度能力,就用 vLLM,起服务时能直接指定显存占用比例、最大上下文、并发数。两者对上层 Python 代码来说是透明的,因为都走同一套 /v1/chat/completions 协议,切换只改 base_url。
硬件侧的最低检查清单如下,都是通用项,不涉及具体型号:
- 显卡驱动与 CUDA 环境可用,
nvidia-smi能正常输出。 - 磁盘预留足够空间放模型权重,7B 量级量化后通常几个 GB,14B 及以上会明显更大。
- 端口不被占用,推理服务常用 11434、8000,编排侧脚本不占端口。
- 系统编码统一 UTF-8,Windows 下尤其注意,否则中文落盘容易出乱码。
- 如果走 CPU 推理,内存要留足,速度会明显慢于 GPU,批量任务要相应放宽超时。
还有一个容易忽略的前置条件:目录结构要在动手写代码前定好。建议至少分成五个目录,settings/ 放设定库,outline/ 放大纲,chapters/ 放正文,ledger/ 放数值账本,logs/ 放运行日志。这个划分看着琐碎,但等到你跑到第 50 章、需要回头查某一章账本快照的时候,会庆幸当初分了目录。
bash复制# 建议的初始目录结构,一条命令建好
mkdir -p settings outline chapters ledger logs prompts
4. 服务启动与连通性验证
先起推理服务,再做连通性验证,别急着写业务代码。用 Ollama 的话,装好之后拉一个指令跟随能力较好的模型,然后确认服务在跑。
bash复制# 拉取模型(模型名按你实际想用的替换)
ollama pull qwen2.5:7b-instruct
# 查看已拉取的模型列表
ollama list
# 服务默认监听 127.0.0.1:11434,确认进程存在
curl http://127.0.0.1:11434/v1/models
如果你用 vLLM,启动命令大致是这样,重点是几个参数要按显存实际情况调:--gpu-memory-utilization 控制占用比例,--max-model-len 控制最大上下文,这两个直接决定你能塞多长的 prompt。
bash复制# vLLM 启动示例,模型路径和参数按实际情况替换
python -m vllm.entrypoints.openai.api_server \
--model /path/to/your/model \
--served-model-name local-writer \
--host 127.0.0.1 \
--port 8000 \
--max-model-len 8192 \
--gpu-memory-utilization 0.85
服务起来之后,用一次最简单的中文请求验证整条链路,能拿到回复就说明协议通了。这一步不要跳过,很多后面排查半天的问题,其实在这一步就能发现,比如模型名写错、端口被占用、返回 404 说明路径不对。
python复制# verify_conn.py
import requests
BASE_URL = "http://127.0.0.1:11434/v1" # vLLM 换成 http://127.0.0.1:8000/v1
MODEL = "qwen2.5:7b-instruct" # vLLM 下换成 served-model-name
resp = requests.post(
f"{BASE_URL}/chat/completions",
json={
"model": MODEL,
"messages": [{"role": "user", "content": "用一句话说明你已经就绪。"}],
"temperature": 0.2,
"max_tokens": 64,
},
timeout=120,
)
print(resp.status_code)
print(resp.json()["choices"][0]["message"]["content"])
这里有个实践建议:把 base_url 和 model 名抽成配置,不要写死在每个脚本里。因为本地服务换端口、换模型是家常便饭,写死之后每次都要全局搜索替换,很容易漏。配置文件用 JSON 或 YAML 都行,读取一次缓存在内存里。
yaml复制# config.yaml
llm:
base_url: "http://127.0.0.1:11434/v1"
model: "qwen2.5:7b-instruct"
timeout: 180
temperature: 0.7
max_tokens: 2048
paths:
settings: "./settings"
outline: "./outline"
chapters: "./chapters"
ledger: "./ledger"
logs: "./logs"
5. 设定库与角色卡建模
这一步是整个项目的基石,做扎实了后面事半功倍。思路是把「世界观 + 角色 + 系统规则」全部结构化成 JSON,每个角色一张卡,卡片里只放稳定不变的字段,会变化的东西一律进账本,不进角色卡。角色卡一旦混入可变状态,就会出现「A 章好感度 30、B 章好感度 30 但描述是冷淡」这类自相矛盾。
角色卡建议包含这些字段:唯一 ID、称呼、外貌锚点、性格关键词、说话风格、当前关系状态、禁止出现的行为。其中「外貌锚点」和「说话风格」是控制一致性的关键,写具体一点,比如「粉色短发、常穿短外套」比「外貌出众」有用得多。字段太少模型会自由发挥,字段太多又会挤占上下文,一般十来个字段比较合适。
json复制{
"id": "char_001",
"name": "示例角色A",
"role": "核心配角",
"appearance_anchor": ["粉色短发", "常穿深色短外套"],
"personality": ["外冷内热", "说话带刺但行动可靠"],
"speech_style": "短句为主,少用敬语,情绪激动时会重复关键词",
"relationship": {
"to_protagonist": "初始保持距离",
"stage": "陌生"
},
"forbidden": ["不得主动示弱", "不得长篇独白"]
}
建议用 Pydantic 做一次建模,好处是加载时就能发现字段缺失、类型错误,而不是等到生成阶段才报奇怪的错。下面的模型定义可以直接用,字段按你的设定增删。
python复制# models.py
from typing import List, Dict
from pydantic import BaseModel, Field
class CharacterCard(BaseModel):
id: str
name: str
role: str = "配角"
appearance_anchor: List[str] = Field(default_factory=list)
personality: List[str] = Field(default_factory=list)
speech_style: str = ""
relationship: Dict[str, str] = Field(default_factory=dict)
forbidden: List[str] = Field(default_factory=list)
class SystemState(BaseModel):
"""系统面板状态,由程序维护,模型不得修改"""
chapter_index: int = 0
anger_points: int = 0 # 生气值余额
total_earned: int = 0 # 累计获得
draw_count: int = 0 # 抽卡次数
owned_templates: List[str] = Field(default_factory=list)
系统面板这块要特别强调一个设计原则:数值只由程序计算,模型只负责输出「本次触发了什么事件」,不输出具体数值增量。具体做法是让模型返回事件类型和强度等级,程序按预设表换算成实际数值。这样做的好处是,即便模型胡编,账本也不会被污染,你随时可以用脚本重算一遍校验。
json复制{
"anger_points": 128,
"total_earned": 460,
"draw_count": 3,
"owned_templates": ["模板_坚韧", "模板_洞察", "模板_疾行"],
"rules": {
"gain_per_event": {"minor": 5, "normal": 12, "major": 30},
"draw_cost": 100
}
}
把这几份文件放进 settings/ 目录,加载时统一校验,别用一个错一个。设定库版本化也很重要,建议每次大改前复制一份带日期的备份,比如 settings_20240101/,写到后期你会发现经常需要回滚某一版设定。
6. 章节生成流水线与结构化校验
这条流水线分四步走:生成大纲、拆场景、出草稿、更新账本。每一步都单独一次模型调用,不要图省事合成一次,合并之后模型很容易在中途丢约束。分步的代价是多几次请求,但稳定性和可调试性提升明显,出错时你能立刻定位到是哪一步的问题。
第一步生成大纲,要求模型输出严格 JSON 数组,每个元素是一个场景。这里用 JSON Schema 做约束,配合低温度参数,能显著降低格式崩坏的概率。
python复制# outline_step.py
import json
import requests
SCHEMA_HINT = """
只输出 JSON 数组,不要任何解释文字、不要 Markdown 代码块标记。
每个元素包含字段:scene_id(int)、goal(str)、conflict(str)、turn(str)、end_state(str)。
"""
def gen_outline(base_url, model, chapter_goal):
prompt = (
f"章节目标:{chapter_goal}\n"
"请拆成 3 到 5 个场景,保证每个场景都有明确冲突和转折。\n"
+ SCHEMA_HINT
)
resp = requests.post(
f"{base_url}/chat/completions",
json={
"model": model,
"messages": [{"role": "user", "content": prompt}],
"temperature": 0.4,
"max_tokens": 1024,
},
timeout=180,
)
content = resp.json()["choices"][0]["message"]["content"].strip()
return json.loads(content)
第二步出草稿时,把角色卡、上一章结尾摘要、本章场景列表一起塞进 prompt。这里的关键是给模型一个「事实清单」,明确告诉它哪些设定不能改。经验上,把约束写成「必须」和「禁止」两类短句,比写一大段描述有效得多。
python复制def build_chapter_prompt(scene, chars, prev_summary):
lines = [f"上一章结尾摘要:{prev_summary}", "", "本章必须遵守的设定:"]
for c in chars:
lines.append(
f"- {c['name']}:外貌{c['appearance_anchor']};"
f"性格{c['personality']};说话风格{c['speech_style']};"
f"禁止{c['forbidden']}"
)
lines += [
"",
f"本场景目标:{scene['goal']}",
f"核心冲突:{scene['conflict']}",
f"转折点:{scene['turn']}",
f"收尾状态:{scene['end_state']}",
"",
"要求:只写叙事正文,1200 到 1800 字,不要小标题,不要总结,不要写数值。",
]
return "\n".join(lines)
第三步校验,这一步最容易被省掉,但恰恰是保证质量的关键。至少要跑三类检查:长度是否在区间内、是否出现了角色卡里禁止的行为、是否凭空出现了新的数值。前两类可以用简单规则加关键词匹配,第三类用正则扫一遍数字就行。发现问题就带上错误原因重试一次,重试仍不通过就落盘标记为待人工处理,不要死循环重试。
python复制import re
def validate_chapter(text, min_len=800, max_len=2600):
issues = []
if not (min_len <= len(text) <= max_len):
issues.append(f"长度 {len(text)} 不在 [{min_len}, {max_len}] 区间")
if re.search(r"(生气值|好感度|积分).{0,4}[++]\s*\d+", text):
issues.append("正文中出现数值增减表述,应交给账本处理")
if text.count("我") > 60:
issues.append("第一人称使用过密,叙事可能单调")
return issues
第四步更新账本,用事件驱动的方式。模型这一步的输出限定为「事件类型 + 强度」,程序换算数值。这样即便模型判断失误,你也能用脚本重放全部事件,重新算出正确账本。这个能力在后期改规则时特别有用,比如你决定把「普通事件」从 12 点调到 15 点,只需要重放,不用手工改几十个文件。
python复制GAIN_TABLE = {"minor": 5, "normal": 12, "major": 30}
def apply_events(state, events):
for ev in events:
level = ev.get("level", "normal")
delta = GAIN_TABLE.get(level, 0)
state["anger_points"] += delta
state["total_earned"] += delta
return state
7. 接口 API 与批量任务编排
服务跑通之后,把单章流程包成函数,就可以做批量了。批量任务最容易踩的坑是「跑了一半挂了,不知道从哪继续」,所以设计上必须支持断点续跑:每个任务有唯一 ID,完成后写一个标记文件,启动时先扫标记跳过已完成项。
bash复制# 最基础的批量调用,确认接口在批量场景下也稳定
curl -s http://127.0.0.1:11434/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "qwen2.5:7b-instruct",
"messages": [{"role": "user", "content": "输出一个 3 场景的章节大纲,纯 JSON 数组"}],
"temperature": 0.4,
"max_tokens": 800
}'
任务清单用一个 JSON 文件描述,每项包含章节号、目标、状态,程序读入后逐项处理。这种「清单 + 标记」的模式比在内存里维护队列可靠,进程被杀之后重启还能接着跑。
json复制{
"tasks": [
{"id": "ch_001", "goal": "主角初次触发面板,建立基础冲突", "status": "done"},
{"id": "ch_002", "goal": "首次抽卡,引出模板能力", "status": "pending"},
{"id": "ch_003", "goal": "对手登场,制造资源压力", "status": "pending"}
],
"batch_size": 1,
"retry": 2,
"sleep_between": 2
}
编排脚本要处理好三件事:超时、重试、日志。超时按单次请求设,不要设成无限等;重试带上退避间隔,避免连续打服务;日志把请求 ID、耗时、token 数、校验结果都记下来,后期做性能分析全靠它。
python复制# batch_runner.py
import json
import time
import requests
from pathlib import Path
CFG = json.loads(Path("config.yaml").read_text()) if False else None # 占位,实际按你的配置加载方式改
def run_task(task, base_url, model, retry=2):
for attempt in range(retry + 1):
try:
resp = requests.post(
f"{base_url}/chat/completions",
json={
"model": model,
"messages": [{"role": "user", "content": task["goal"]}],
"temperature": 0.6,
"max_tokens": 2048,
},
timeout=240,
)
resp.raise_for_status()
return resp.json()["choices"][0]["message"]["content"]
except Exception as exc:
if attempt == retry:
raise
time.sleep(2 * (attempt + 1))
批量跑起来之后有个明显现象:第一批请求响应很快,越往后越慢,尤其当你把上下文越堆越长的时候。这不是服务坏了,是 KV Cache 占用上涨导致解码变慢。所以批量任务的前置设计里,一定要限制单次请求的上下文长度,超出就走摘要压缩,不要指望模型能吃下整本书。
8. 资源占用与性能观察
性能这块必须靠自己测,任何别人给的数字对你都只是参考。观察方式很简单,另开一个终端盯着显存,同时看请求耗时。三个指标最值得记录:单次请求耗时、显存峰值、每秒输出 token 数。
bash复制# 观察显存占用变化,注意看显存和 GPU 利用率两项
nvidia-smi --query-gpu=memory.used,memory.total,utilization.gpu \
--format=csv -l 2
影响性能的变量按经验排序大致是:上下文长度 > 批量并发数 > 模型规模 > 量化精度。上下文长度是最容易被低估的一项,很多人只觉得「多塞点原文效果更好」,结果显存直接爆掉或者速度掉到无法接受。更稳妥的做法是固定一个上下文预算,比如 4K 到 8K,超出部分用滚动摘要顶替。
滚动摘要的做法是每章结束后让模型压缩成 150 到 250 字的摘要,同时保留最近两章的完整文本。这样多轮下来,模型既知道前面发生了什么,又不会因为上下文爆炸而变慢。摘要本身也要校验长度,跑偏了就是无效压缩。
降低占用的具体手段有四个,都是工程上成熟的做法:一是换更小的模型,7B 量级在很多创作任务上已经够用;二是用量化版本,显存占用能明显下降,代价是细微的表达质量损失;三是降低 max_model_len,也就是限制最大上下文窗口;四是在 vLLM 下调整 gpu_memory_utilization,给它留出余量,别压到极限。
bash复制# 量化版模型拉取示例,模型标签按你实际可用版本替换
ollama pull qwen2.5:7b-instruct-q4_K_M
还有一个隐蔽问题:进程残留。批量任务异常退出后,Python 进程可能还挂在那里占着内存,推理服务也可能因为上一次请求没释放而变慢。定期用 ps 或者任务管理器看一眼,确认没有僵尸进程,端口没被旧进程吃掉。这个习惯能省掉大量「明明代码没改怎么突然不行了」的困惑。
9. 常见问题与排查方法
下面这些问题基本都是我在搭这套流水线时真实遇到过的类型,按现象、原因、排查、解决四列整理,可以直接当检查清单用。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 服务起不来,端口报错 | 端口被占用或上次进程未退出 | 查看端口占用与进程列表 | 换端口或结束残留进程 |
| 请求返回 404 | base_url 少了 /v1 或模型名写错 |
先请求 /v1/models 看列表 |
修正路径与模型名 |
| 模型输出不是合法 JSON | 温度过高或未给格式约束 | 打印原始返回内容查看 | 降低温度、加 JSON 约束、加重试 |
| 中文落盘乱码 | Windows 默认编码非 UTF-8 | 检查文件编码 | 统一用 encoding="utf-8" |
| 显存不足直接崩 | 上下文过长或并发过高 | 看显存峰值与 max_model_len |
降上下文、降并发、换量化版 |
| 批量任务中途卡住 | 单请求无超时或服务假死 | 看日志最后一条时间戳 | 加超时与退避重试 |
| 输出越来越慢 | 上下文累积、KV Cache 变大 | 对比不同章节的耗时 | 上滚动摘要、限制上下文预算 |
| 人物设定前后矛盾 | 角色卡未注入或字段太笼统 | 抽查 prompt 与角色卡 | 补外貌锚点、说话风格、禁止项 |
| 数值前后对不上 | 让模型直接输出数值 | 搜正文里的数字表述 | 改为事件驱动、程序算数值 |
| 校验一直不通过 | 长度阈值设得过窄 | 看实际字数分布 | 放宽区间或改分场景生成 |
举两个典型的排查过程。第一个是「服务起来了但请求一直超时」,第一反应往往是模型太大,实际上更常见的是 max_model_len 设得比模型原生支持还大,服务启动日志里其实已经有警告,只是被忽略了。排查顺序应该是先看启动日志,再看显存,最后才怀疑网络。
第二个是「JSON 解析失败率很高」。这时候不要急着换模型,先把返回的原始文本打印出来。十有八九是模型在 JSON 前后加了「好的,以下是」或者 Markdown 代码块标记。解决办法两个:prompt 里明确写「不要任何解释文字、不要代码块标记」,以及在代码里做一次容错提取,截取第一个 [ 到最后一个 ] 之间的内容再解析。两层保险加在一起,成功率会明显上升。
10. 最佳实践与使用建议
跑到后期你会发现,这套系统的价值不取决于模型多强,而取决于你有没有把状态管理做干净。所以第一条建议是:第一次上手不要直接开长篇,先用三章的小样本把整条链路跑通,确认大纲生成、草稿生成、校验、账本更新四步全都正常,再扩大规模。中途改架构的成本远高于一开始慢一点。
第二条是把人工审稿环节当成流程的一部分,而不是可选项。具体做法是在每章落盘后标记「待审」,人工确认后才标记「已定稿」,账本更新只认已定稿的章节。这样即便某一章模型写崩了,也不会污染后续状态,回滚起来只需要删除那一章。
第三条关于目录和命名。设定库、大纲、正文、账本、日志分开目录,文件名带章节号且补零,比如 ch_001.md。补零这件事看着小,等到你需要按名字排序的时候就知道好处了。日志里记录请求耗时和 token 数,方便后期做性能回归。
第四条是合规层面的硬要求。这套流水线处理的是创作文本,但如果你扩展到图像、语音、真人素材,就需要额外确认授权范围,尤其是涉及人脸、声音克隆这类能力时,必须取得当事人明确同意,并且只在测试环境验证。基于已有版权作品做同人,发布和商用前要逐条核对平台规则与授权情况,拿不准就不要发。
第五条是保留可复现的最小配置。把 config、角色卡、任务清单、账本这几个文件打包备份,标注清楚对应的模型版本和量化方式。半年后你想复现某次效果,没有这份快照基本做不到,因为模型更新、参数微调都会让结果漂移。这个习惯看着麻烦,实际是最省时间的一步。
11. 收尾:这套流水线最值得先跑什么
如果只挑一件事先做,我建议先做账本和角色卡,而不是先调模型。原因很实际:模型输出质量是波动的,你调来调去也不会有确定的上限;但账本和角色卡是确定性的,做对了就能长期受益,做错了随时能靠脚本重算。先把 settings/ 和 ledger/ 这两个目录的内容定下来,再拿三章试跑,你会发现问题少了一大半。
第二个值得优先验证的是结构化输出成功率。拿同一段任务跑二十次,统计有多少次返回合法 JSON、有多少次需要重试,这个数字直接决定你的批量任务能不能无人值守。成功率低于八成,就老老实实降温度、加约束、加容错解析,别急着扩规模。
最容易踩的坑不是技术坑,是「让模型管数值」这个偷懒的设计。一旦数值散落在正文里,后面每一次调整规则都要手工翻几十个文件,维护成本会指数上升。事件驱动加程序算账这个模式,前期多写几十行代码,后期省掉的是几十个小时。
往后可以继续扩展的方向有几个:把角色卡做成检索式的,按场景只注入相关角色,省上下文;把校验规则做成可配置的,不同题材用不同规则集;把批量任务做成带优先级的队列,先跑关键章节;再进一步,可以对特定文风做小规模微调,让输出更贴近你想要的节奏。这些都建立在一个前提上——状态管理先干净,再谈别的。
这套东西本质上不是「AI 帮你写小说」,而是「你用工程手段管住一个会漂移的生成器」。把它当成一个状态机加一个文本生成器来看,很多设计选择就顺理成章了。建议先把最小闭环跑通,再逐步加规则,别一上来就追求全自动。
