迭代、版本与报告¶
一个已经跑完的研究是一份结果——迭代绝不能悄悄毁掉它。 SocioVerse 用一条规则加一套机制来保证这一点。
规则:所有修改都走 /sv-iterate¶
对既有研究的任何修改——加一条政策广播、加几步、换人群规模、调一个
阈值——都经过 /sv-iterate。
不要手改产物再重跑
原地编辑产物再重跑,会把上一条轨迹无记录地覆盖掉。 「再加一个政策跑一轮」是一次迭代,不是一次编辑。
版本门之前:reference 守卫¶
study.yaml 带 reference: true 的研究——内置的示例模板与适配基线——是
只能 fork 的。这道守卫跑在版本门之前,并且直接绕过版本门:新版本、
原地、分支三个选项都会改动同一个 study_id,而这恰恰是共享基线最不能承受的。
所以对 reference 研究说「改一下这个」,结果是 fork:
- fork 得到新的
study_id(且reference: false),模板本身分毫未动; - 新 fork 没有历史可版本化,因此跳过版本门——它从
v1开始自己的版本线, 直接进入流水线。
原地编辑基线模板是一个罕见而慎重的选择,必须先给出明确、说全的警告:此后 所有由该模板派生的研究都会继承这次改动。非 reference 的工作研究不经这道守卫, 直接进版本门。
版本门¶
/sv-iterate 总是开口问(从不擅自决定)这次修改如何版本化:
| 选项 | 发生什么 |
|---|---|
| 新版本(推荐默认) | 现行目录快照到 versions/v<N>/——产物、代码、结果——然后以 v<N+1> 的身份迭代 |
| 原地修改 | 不做快照;当前输出丢失(经典的覆盖流,仅限主动选择) |
从 v<X> 分支 |
新版本以某个旧版本的产物为起点;versions.json 记录父子树,跨版本探索保持可导航 |
快照是不可变的本地历史(versions/ 与运行输出一样被 gitignore):
旧版本永不自动删除,凡是跑过的东西永远可查。
模式与 provider 的切换只能走新版本
决策 / 运行模式或 provider 的切换——scripted ↔ LLM、本地 ↔ 真 LLM、 确定性 ↔ 随机——根本不提供「原地」选项。之前那次运行就是科学对照基线: 做 scripted 干跑或本地模型跑一遍,意义正在于它要摆在真实运行旁边比较。 这类改动只提供「新版本」和「分支」。
复核门(待复核)¶
创建版本时必须说明流水线从哪里恢复:
create_version(..., resume_from_stage=...) 接收——
| 改动类型 | resume_from_stage |
|---|---|
| 从当前状态开新版本 | 受影响的最早阶段——新广播 → sv-build-environment;人群变化 → sv-build-population;决策逻辑变化 → sv-build-model |
| 从历史版本分支 | sv-init——分支是一次大的语境切换,一切都要重新复核 |
从该阶段往后,每一个沿用(carried)的阶段在 dashboard 上都显示为 待复核,直到你处理它。处理方式只有两种:
- 受影响的阶段 → 照常用它的
sv-*skill 重新撰写。 - 未受影响的阶段 → 用 schema 校验沿用下来的产物,确认它对新问题依然成立, 然后贴出它的叙述。正是这段叙述清掉「待复核」标记。
验证并叙述——不要重写未受影响的产物
「保险起见」重写一份沿用产物,会搅动它的 mtime,破坏 dashboard 对 沿用 / 陈旧的判定。校验它、说清它为什么依然成立,字节原样别动。
既没重写、也没验证的沿用阶段会一直保持待复核并扣住指针——所以流水线
永远不可能在还带着未复核阶段的情况下看起来已经完成。/sv-run 照旧在花钱前
确认。
热启动¶
当修改只影响第 J 步之后的轨迹——延长了时间跨度,或新干预落在第 J 步——完整重跑会为不可能改变的步数重付 LLM 成本。热启动(warm start)避免这笔浪费:
/sv-run 会从父版本存储的行为里回放 0..K 步——精确复现、零 LLM——
只为 K+1..n_steps 花钱。
适用条件
热启动适用于 interaction_rounds == 1 的从零构建(Path B)研究
——它们的 apply 是行为的纯函数。包装遗留引擎的研究和多轮交互研究
从第 0 步重跑;引擎宁可报错也不会错误地热启动。
多轮之所以被排除,不是回放代码做不到,而是存下来的东西不够:父版本的 面板里只持久化了最后一轮的行为,中间那些促成它的轮次无法重建。见 多轮交互。
报告¶
/sv-report 读 trajectory/study.duckdb(panel、metrics、events,
可选 messages),写出 reports/report.md 与 reports/figures/*.png。
报告是分析性的,不是数据堆砌,而且结构是被规定死的——一条 总–分–总 的弧线,
顺序如下:
| # | 章节 | 必须做到什么 |
|---|---|---|
| ① | 总起 · 结论速览 | 全程 2–4 条头条结论,每条一行,写成断言——「X 发生了,因为 Y」,而不是「我们模拟了 X」。这是正文接下来要证明的论点。 |
| ② | 轨迹表格与图 | 后面一切据以解读的描述性骨架 |
| ③ | 分 · 发现与解读 | 大致一个关键结果一条发现。每条断言紧挨着它所依据的具体数字或图,并且给出机制——那个「为什么」,从面板与分群里读出来 |
| ④ | 深层洞察 | 原始表格没有直说的、跨指标 / 跨分群的非显然结论——谁被隔离、谁被暴露,某个临界翻转,两个指标的背离——每条仍然扣在揭示它的数据上 |
| ⑤ | 总结 · 回扣与深挖 | 回扣那几条(现在已被正文挣到的)头条结论,并再深一层:它们对研究问题意味着什么、统摄它们的机制是什么、什么仍不确定。不引入无支撑的新论断。 |
| ⑥ | 改进建议与下一步 | 可操作的建议,以及下一轮 /sv-iterate 该测什么——试哪条政策、扫哪个参数、探哪个分群 |
| ⑦ | 数据基础与参考来源 | 放最后,由 grounding 侧车渲染 |
③ 的硬规则
没有数字的断言不写,没有断言撑着的数字也不写。
一条发现读起来是「满意度从 0.74 跌到 0.61(step 2 政策后,见
figures/satisfaction.png)」再接机制——而不是一段解读旁边扔一张表,
让读者自己去对。
最后一节由 grounding/grounding.json 渲染成一张管道表:
| 事实 | 值 | 依据 | 来源 |
|---|---|---|---|
| … | … | 实证 / 代理 / 假设 | title (url) |
外加 implementation_refs(建模参考)与 assumptions(声明的假设)。
method_notes 为 "stylized" 的研究只得到一行诚实的
「风格化模型,无真实世界锚点。」;fork 出来的研究会在这一节开头说明自己继承了
哪些锚点。见真实世界锚定与溯源。
用 SQL 问你自己的问题¶
报告是一种渲染;存储才是结果本身。study.duckdb 永远可查:
-- 单个 agent 的纵向视图
SELECT step, state, action_kind
FROM panel WHERE agent_id = '…' ORDER BY step;
-- 聚合轨迹
SELECT * FROM metrics ORDER BY step;
-- 什么在何时触发
SELECT step, note FROM events ORDER BY step;
-- 如果研究用了消息总线:谁说了什么
SELECT step, author_id, content FROM messages ORDER BY step;
收尾时还会在旁边导出 Parquet(panel.parquet、metrics.parquet),
方便 pandas / R 工作流。更多查询示例见 FAQ。