群体演化场景模板:把用户一句话的"某个群体/组织/文明如何发展"意图,展开成结构化的模拟初始状态(阶段六第二个模板,验证"新增模拟类型=新增一个 skill,不改引擎代码"这条设计假设),以及在模拟推进过程中,把"当前群体状态+本次决策"推演成下一状态、叙事文本和候选分支。当 world_simulator 项目的 template 为 group_evolution 时,generate_scenario / advance_step 两个 workflow 都会挂载本 skill。
Install
npx skillscat add onewaymyway/mini-agent/group-evolution-template Install via the SkillsCat registry.
group-evolution-template
背景与边界
本 skill 是 world_simulator 的第二个"场景模板"(对应方案 2.4 节/
阶段六),服务"一个群体(不是单个人)如何随时间/决策演化"这类模拟:
一家创业公司、一个社群、一个小型文明/国家、一个物种种群等。与life-sim-template 的核心区别是推演单位是群体整体的结构性指标
(规模、资源、凝聚力、制度成熟度等),不是某一个体的人生轨迹——如果
用户的意图实际是"某个具体的人",应该走 life-sim-template,不是
本 skill。
被 generate_scenario workflow 挂载时,你只做"生成初始状态"这一件
事;被 advance_step workflow 挂载时,你只做"推进一步"这一件事——
两者的 result_file 契约不同,见下方两节。
关于"推进一步"可能被拆成两次调用(阶段三十三第八批,4.12 节
第三步,manifest.settings.split_decision_calls == True 时生效,
默认关闭):这种情况下你会被 world_evolve 和 decision_generate
两个 workflow 分别挂载两次,各自的 prompt 已经说明了这次调用该
输出哪些字段、不该输出哪些(world_evolve 只产出世界状态变化,
不产出 options;decision_generate 只产出候选选项)——下面
"推进一步"这一节的字段规则/正反例仍然完全适用,只是被拆成两次调用
各自负责一部分。
关于"创建初始状态"可能被拆成两次调用(第五轮方案 5.5 节,阶段
三十四第六批,manifest.settings.split_creation_calls == True 时
生效,默认开启):这种情况下你会被 world_builder 和causal_space_builder 两个 workflow 分别挂载两次,各自的 prompt
已经说明了这次调用该输出哪些字段、不该输出哪些(world_builder
只产出世界状态本身,不产出候选行动/因果线;causal_space_builder
把前一次的世界状态当既成事实,只产出 options/causal_lines/declared_causal_graph)——下面"生成初始状态"这一节的字段规则/
正反例仍然完全适用,只是被拆成两次调用各自负责一部分。
通用判断原则
推演群体层面的结构变化,不是某个成员的个人剧情:
vars里的
字段应该刻画群体整体(人口/成员规模、资源存量、凝聚力或治理成熟度、
外部环境压力等),narrative 里即使提到具体人物/事件,也是作为
"影响群体走向的一个因素"来写,不要滑向单人物的人生模拟。决策/事件驱动,不是自然史流水账:每一步推进应该对应一次明确
的关键决策点或外部冲击(资源分配、政策选择、重大外部事件),而不是
"又过了一年,一切照旧"这种没有信息量的推进。变量要具体、可追踪:同
life-sim-template的原则——字段是
真实存在、会随时间变化的量,不要用大段自由文本代替变量值。候选选项数量是判断出来的结果,不是要凑够的目标值:
{option_count_hint}只是软上限(防止候选列表刷屏),不是"请
生成这么多个"。先判断这一步是否真的走到了需要群体做决策的分岔
点——没有就给空数组;只有一个真正有效的路线就只给一个;多个路线
有实质区分度(比如"扩张 vs 稳健巩固")才都列出来,避免大同小异
的路线凑数。候选选项必须是群体层面现实可执行的具体政策/集体行动,不能是抽象
的数值调节。本模板的正反例:- 正例:"发起集体决议改组治理结构"、"暂停扩张、集中资源巩固核心
业务"、"推举代表与监管方谈判"——都是可以理解、可以执行的现实
行动或政策; - 反例:"群体凝聚力 +10%""治理成熟度提升 0.2""资源存量增加
15%"——这些是内部指标语义,即使选中后确实会引起这些数值变化,
也不能把数值本身写成选项内容。
如果这一步的关键变化是群体无法直接干预的宏观事件(行业震荡、
监管政策突变、宏观经济衰退等),不要把宏观事件本身包装成选项——
先在narrative/next_vars里如实呈现这个变化,再沿因果链往下
想"这件事最终影响到群体自己能采取行动的地方是什么",把候选选项
落在群体真正能施加影响的位置。当且仅当"不作为"是这一步的一个真实后果分支(比如错失窗口期、
风险持续累积)时,才显式给出一项id以continue_开头的
"维持现状"选项并说明后果;不是每一步都要有的机械项。- 正例:"发起集体决议改组治理结构"、"暂停扩张、集中资源巩固核心
大环境是活的,不是静止背景板:群体不是在真空里演化——宏观
经济周期、行业/技术发展(比如某项新技术让整个赛道重新洗牌)、
监管政策变化、社会思潮/公众舆论变化、竞争格局变化等"群体自己
控制不了、但真实影响它处境"的外部因素,应该随时间自己演化,不能
因为群体做了什么决策就停止变化,也不能从头到尾一成不变。external_pressure字段应该承担这部分职责(不要只写成一句静态
描述,要让它随步骤推进),narrative/选项设计里要让这些宏观变化
实际影响到群体的处境和决策后果。时间粒度可以是"自动/引导"的,同一次模拟可以出现多种粒度:
每一步的粒度由{time_granularity_hint}决定,具体是"固定"/
"自动"/"引导"哪一种,文案里会写明——固定模式每一步严格照做;
自动/引导模式允许你按情境切换,比如日常研究一个文明用"10 年"
一步,遇到剧烈冲突/关键决策窗口时可以临时收窄到"1 年"甚至"1 个
月",之后再放回日常粒度,默认延续上一步(advance_step会告诉你
上一步用的是什么),只有情境明显需要时才切换。变量变化幅度、事件
节奏都要按"这一步实际用的粒度"换算。
作为 generate_scenario 挂载时(result_file: scenario.json)
输入:{intent}(用户一句话意图)、{previous_draft_json}(此前一轮
草稿,可能为空字符串)、{feedback}(用户对上一份草稿的补充意见,可能
为空字符串)、{option_count_hint}(用户设置的候选方向数量,比如
"4 个左右")、{time_granularity_hint}(用户设置的时间粒度,比如
"1 个季度"/"10 年"/"自动——由你根据模拟对象的性质自行判断")。feedback 非空表示这是一次"根据意见修改"请求:以 previous_draft_json
为基础按意见调整,未提及的部分(包括未被要求改动的 options 条目,
尽量连 id 一起)保持不变;feedback 为空则忽略 previous_draft_json,
按 intent 从零生成。
输出(合法 JSON,顶层字段):
title:字符串,这次模拟的简短标题。summary:字符串,初始状态一句话摘要。vars:对象,初始状态的结构化变量。建议至少包含:group_type
(群体类型,如"创业公司"/"部落文明"/"线上社群")、size(规模,
人数或量级描述)、resources(资源存量,字符串或子对象)、cohesion(凝聚力/稳定度,字符串或 0-100 数值)、external_pressure
(当前面临的外部宏观环境/压力,字符串或子对象——具体写清楚是经济
周期、行业技术趋势、监管政策、竞争格局、社会思潮里的哪一种或哪几种
在起作用,不用面面俱到)。options:数组,按{option_count_hint}给出对应数量的初始候选
发展路线,每项含id、label、description(一两句话说明这条
路线的取向和代价)。若用户意图里已经明确给出候选路线,直接展开成
这几项(数量可以不等于{option_count_hint});没有则合理生成
对应数量、方向明显不同的路线,不要为了凑数给假选项。time_label:字符串(可选),初始状态对应的模拟内时间点,一般
就是起点;如果意图里明确给出了具体时间起点,可以写成对应的
描述。time_granularity:字符串(可选,固定模式下可以不写),这次模拟
的起始基准粒度(比如"10 年"/"1 个季度"),只需代表"日常节奏"。resource_fields:数组(可选),vars里属于"资源类数值"的字段
声明(比如群体的粮食储备、资金池这类"不应该低于某个下限"的数值),
每项是字段名字符串(默认下限 0)或{"field": "字段名", "min": 数值}。
之后每一步advance_step推进时引擎会自动检查这些字段有没有被
算成负数。没有这类字段就不用输出。resource_relations:数组(可选),vars里两个资源字段之间明显
存在的因果关系,区分"transfer"(同量纲搬运,比如"用资金池换取
粮食储备",两者都是同一种"资源储备"记账单位)和"conversion"
(不同量纲的有代价转化,比如"用人口投入换取凝聚力指数上涨"——
人口数量和凝聚力指数不是同一种量纲,不能假设两者变化量之和
≈0)。transfer每项是{"type": "transfer", "from": "字段名", "to": "字段名", "tolerance": 0.1},conversion每项是{"type": "conversion", "from": "字段名", "to": "字段名", "note": "一句话说明"}(conversion不会被引擎做数值核对,只做记录)。
量纲不同的字段对不要声明成transfer,否则引擎会用"零和"
假设去核对一对本来就不该零和的字段,必然报"不一致"。详细规则与
正反例见life-sim-template的resource_relations说明,两个
模板格式完全一致。没有这类明显的转移/转化关系就不用输出。uncertain_fields:数组(可选),vars里属于"你自己主观估计出来、
置信度不高"的字段声明(比如群体的"凝聚力指数"这类本质是推断而非
确定事实的数值),每项是{"field": "字段名", "confidence": "high"|"medium"|"low", "note": "一句话说明"},格式与life-sim-template完全一致。没有这类字段就不用输出。objectives:数组(可选),字符串列表,这次模拟"主要关心的指标"
(比如群体的"总人口""资源储备""凝聚力"),格式与用途与life-sim-template完全一致。没有明确指标就不用输出。desired_state:对象(可选,第九轮批次一,Desired State 结构化),
格式与用途与life-sim-template完全一致(conditions/constraints/assumptions三个可选字符串数组)。意图里看不出
明确的理想状态结构就不用输出。causal_lines:数组(默认应该给出,阶段二十二,多尺度因果线),
格式与用途与life-sim-template完全一致——因果线是默认基础机制,
不需要用户提前要求。至少给"群体内部演化"一条线,多数场景还能再拆
出"外部环境/邻近群体动态"这类背景线,一般给 24 条;只有极其简单3 个有实质
的场景才可以只给一条。每条线还必须同时给出future_tree
(阶段二十六,格式与life-sim-template完全一致),2
区分度的未来分支(比如"持续扩张"/"停滞内耗"/"分裂瓦解")。
作为 advance_step 挂载时(result_file: next_state.json)
输入:当前标题、当前状态摘要、当前 vars(JSON)、当前时间步、当前
状态对应的模拟内时间点({current_time_label},可能为空字符串)、
产生当前状态那一步实际用的时间粒度({current_time_granularity},
可能为"无(这是第一步……)")、当前状态的候选分支选项(JSON 数组,
可能为空)、本次选择的选项(可能为空对象 {})、可选的decision_context("代理决策者画像",非空时表示自动挡场景,你需要
自己从候选分支选项里选一个)、{option_count_hint}/{time_granularity_hint}(同上,含义与 generate_scenario 一致)。
选择逻辑(按优先级,与 life-sim-template 完全一致):
- "本次选择的选项"非空:按这个选项推进,
chosen_option_id/chosen_reason可以省略;这个选项的id若是custom_开头,
说明是用户自己新增的、不在原候选列表里的选项,处理方式一样。 - "本次选择的选项"为空对象但
decision_context非空(自动挡代选):
从候选分支选项里挑一个最符合画像的选项,输出chosen_option_id
(必须是候选列表里真实存在的 id)和chosen_reason(具体说明
符合了画像里的哪条原则/风险偏好,不要写空话)。候选分支选项为空
数组时按第 3 条处理。若decision_context明确允许"跳出候选列表
提出自定义选项"且候选列表确实都不够合理,可改为输出custom_option_label/custom_option_description+chosen_reason,
不要同时输出chosen_option_id;未明确允许时不能这样做。 - 两者都为空(普通手动挡未指定选择):依据当前状态给出最合理的默认
走向,不需要chosen_option_id/chosen_reason。
推进时要把"大环境也在自己变化"考虑进去:不要只推演"群体这次
决策的直接后果",external_pressure 应该有独立于群体决策的演化
(宏观经济、行业技术、监管政策、竞争格局、社会思潮按自己的逻辑往前
走),并且要让这个变化真的影响到这一步的叙事和下一步候选路线——比如
"群体选择继续扩张"这个决策本身没变,但如果外部环境这段时间转向紧缩,
后果就应该和外部环境宽松时不一样。没有明确迹象要引入宏观转折时,external_pressure 也可以只是渐进式的小幅变化,不需要每一步都制造
剧烈冲击。
输出(合法 JSON,顶层字段):
next_summary:字符串,推进后新状态的一句话摘要。narrative:字符串,本步发生了什么的叙事文本(几句话,聚焦群体
层面发生的结构性变化,不要写成单个人物的小说片段)。next_vars:对象,推进后的完整vars(整体替换,不是增量)。数值
型字段的变化幅度要符合常理,并按这一步实际用的粒度(即下面next_time_granularity,默认延续{current_time_granularity})
换算(粒度是"1 个季度"和粒度是"10 年",同样是"资源存量"的合理
变化量级应该明显不同,要能从叙事里的事件推出合理量级,不要无缘
无故跳变);external_pressure也要给出推进后的新值,体现它自己
的演化,而不是原样照抄。time_label:字符串(可选),这一步结束时对应的模拟内时间点,在{current_time_label}基础上按这一步实际用的粒度往前推算(比如
当前是"起点"、粒度是"10 年",这里给"10 年后"),合理估算即可。next_time_granularity:字符串(可选),这一步实际使用的时间
粒度。固定模式下应原样等于{current_time_granularity};自动/
引导模式下默认延续,只有情境明显需要时才切换(比如从"10 年"收窄到
"1 年"应对一场文明存续危机),不要无理由随意改变;留空表示"和上一
步一样"。granularity_reason:字符串(仅当next_time_granularity与{current_time_granularity}不同时才需要),一两句话说明为什么
切换节奏,没有切换就不要输出这个字段。options:数组,供下一步选择的候选发展路线(数量按{option_count_hint}),情节明显没有分歧时可以给空数组;有意义的
话,候选路线里可以体现"如何应对大环境变化"这条思路(比如"顺应行业
新趋势转型" vs "守住原有阵地"),不用每次都是纯粹的内部路线分歧。
每个选项可选字段(risk_level/reversibility/action_reason/urgency/time_window/action_type/prerequisites/consequences等)与另外两个模板一致,
见advance_step.yamlprompt 的完整说明。decision_reason:字符串(可选,顶层字段),这一批候选路线共享
的背景说明,规则与另外两个模板一致。major_decision:布尔值(可选,默认 false),当这一步的选择/走向
明显是群体发展的重大转折点(比如路线分裂、濒临解体、决定性扩张)时
设为 true——自动挡的review_mode: pause_on_major_decision用它
判断是否需要暂停等用户确认,逻辑与life-sim-template完全一致,
引擎侧(engine.py/autopilot.py)不需要为这个模板做任何改动。uncertain_fields:数组(可选),这一步next_vars里属于"你自己
主观估计出来、置信度不高"的字段声明,格式同generate_scenario
阶段(见上)。只标注这一步真正新增/变化的估计值型字段即可,没有
这类字段就不用输出。resource_transfers:数组(可选),格式与用途与life-sim-template
完全一致——如果这一步触发了某条transfer关系,如实报告具体
搬运量,引擎会优先按这份账核对,不再只靠差分估算;没有触发就不
用输出。key_drivers:数组(可选),字符串列表,1~3 条短语概括这一步变化
的关键驱动因素,格式与用途与life-sim-template完全一致。causal_links:数组(可选),格式与用途与life-sim-template完全
一致,是key_drivers的进阶信息,不需要每一步都给。line_updates:对象(默认应该给出,阶段二十二,多尺度因果线),
格式与用途与life-sim-template完全一致——因果线是基础机制,不
需要提前声明才输出,见{causal_lines_hint}。tree_updates:数组(可选,阶段二十六),格式与用途与life-sim-template完全一致,这一步印证/排除某条线的未来分支、
或催生新可能性时给出。triggered_relationships:数组(可选,阶段三十六第二批),格式
与用途与life-sim-template完全一致——只有{relationship_hint}
非空且某条延迟关系的"源头"这一步确实发生了才给出。structural_change:对象(可选,阶段二十三,Model Regime
Detection / Emergence),格式与用途与life-sim-template完全一致
——只有出现原模型没预期、会稳定存在下去的新结构(新的稳定子群体、
新的群体运作机制、或者整个群体运行逻辑的转折)才给,绝大多数步骤
不应该有这个字段。problems:数组(可选,第九轮批次一,Problem 结构化),格式与
用途与life-sim-template完全一致——只有这一步的发展暴露/解决/
演变了一个明确的现实问题(比如"资源储备见底导致扩张停滞""内部
路线分歧演变成公开分裂")才给,绝大多数平淡的推进步骤不需要这个
字段。- 本步已发生的外部事件(第二十二轮 WP2,仅当提示里出现"外生事件采样已开启"
时才有):那是引擎按先验概率抽样决定的既成事实,不是你的选择——把列出的事件
自然写进叙事与状态变化,不要否认/替换;没有列出事件说明本步抽样为空,可以写平静的
一步,不要凭空引入与已声明先验无关的突发外部冲击(由角色自身行动引发的后果不受此限)。
这不需要你输出任何新字段。 elapsed_days与tech_updates(可选,第二十二轮 WP1/WP2。elapsed_days
在提示里出现"技术发展模型已开启"、"外生事件采样已开启"或"因果引擎已开启"(且要求给elapsed_days)时输出;tech_updates仅在"技术发展模型已开启"时输出;都没有就不要输出):elapsed_days是
这一步在模拟世界里大致跨越的天数(正数,量级估计即可);tech_updates
是技术变化数组,每项以id为键,可含name/kind(登记新技术)、stage(本步结束时你认为所处阶段,六选一同maturity_stage)、investment(low/normal/high,非 normal 须附investment_reason)、bottleneck/bottleneck_severity、adoption、market、cost_index、perceived_stage、regression_reason等。引擎按时间推算阶段内进度并
裁决迁移——你不能自己宣布"进入下一阶段":一步最多升一档、进度未满或
硬前置未满足会被驳回并记录在时间线上;新技术只能从lab开始(世界里
本来就存在的技术请标preexisting: true)。详见提示里的协议说明。effect_dispositions(可选,第二十二轮 WP3,仅当提示里出现"因果引擎已开启"且列出了
到期的待兑现因果时输出;否则不要输出):数组,每个到期项一条{"pending_id": "提示里方括号中的 id", "disposition": "realized"|"dampened"|"postponed"| "countered", "reason": "一句话原因"}。realized=效果已在本步的next_vars/line_updates/
叙事里体现;dampened=只体现了一部分;postponed=推迟(之后仍会提醒);countered=被别的
因素抵消。dampened/postponed/countered必须给reason,否则引擎会忽略该条并记录
违规;延迟期未到的项、提示里没列出的 id 不要给交代。引擎只记录你的回报并统计兑现情况,
不会替你改任何数值——realized 就要真的写进状态里。tree_updates里新增分支的两个可选字段(第二十二轮 WP3 / P5c,仅当提示里出现"因果树接地已开启"
时才使用;否则不要填):trigger_condition(结构化触发条件,如{"var": "字段路径", "op": ">=", "value": 5}或{"tech": "技术id", "min_stage": "developer"},
数组表示全部满足;是你对"什么情况下它会发生"的可核验假设,没把握就不填)与exclusive_group
(同一条线内同名即互斥的组名)。引擎据此强制:前置未 resolved 的分支不能 active、互斥组内不能同时
active,违反会被降回 emerging 并记录;条件满足只产生建议(除非用户另开自动迁移)。tree_updates里新增分支的可选字段effects_if_active(第二十二轮 WP3 / P5d,仅当提示里出现"树影响世界已开启"
时才使用;否则不要填):数组,每项{"to_line_id": "目标因果线或技术节点 id", "mechanism": "一句话机制", "sign": "positive|negative|mixed", "strength": "high|medium|low", "delay_days": 90},声明"该分支一旦变为
active,会对哪里施加什么压力"。分支新变为 active 后,引擎把它排进待兑现队列,延迟期到了会在因果引擎
那一项里列出,你要用effect_dispositions交代。这是可核验的假设,没把握就不填,不要编目标 id;
引擎不会替你改任何数值。capabilities_gained:数组(可选,第九轮批次二,Capability 对象),
格式与用途与life-sim-template完全一致——只有这一步真正让群体
获得了新能力(比如"建立了稳定的资源调配机制""形成了跨群体协作
渠道")才给,绝大多数平淡的推进步骤不需要这个字段。