多方谈判/博弈场景模板:把用户一句话的多方谈判/竞争意图,展开成结构化的模拟初始状态,以及在模拟推进过程中,把"当前状态+本次选择"推演成下一状态、叙事文本和候选分支——与另外两个模板的核心区别是启用"多主体私有信念"结构(阶段十七,4.9 节),每个主体有自己的私有视角,互相不完全知道对方的真实状况。当 world_simulator 项目的 template 为 negotiation 时,generate_scenario / advance_step 两个 workflow 都会挂载本 skill。
Install
npx skillscat add onewaymyway/mini-agent/negotiation-template Install via the SkillsCat registry.
negotiation-template
背景与边界
本 skill 是 world_simulator 的第三个"场景模板",专门服务"同一次模拟
里有 2 个及以上独立主体、且主体之间存在信息不对称"这类场景(阶段
十七,next_doc/world_simulator_universal_world_model_upgrade_plan.md
4.9 节)——比如"模拟一场谈判,双方对彼此的底线互相不知情"、"模拟多家
公司在同一市场里的竞争,各自不知道对手的真实成本结构"。
life-sim-template(单主角)、group-evolution-template(群体当
整体推演)都不适合这类场景:前者只有一个主角,后者把群体当一个整体,
两者都没有"多个独立主体、各自私有信息"这个概念。不要把这个模板
用于单主角或"群体当整体"的场景——那两种场景用另外两个模板效果更好,
这个模板的"分主体私有视角"结构对它们来说是不必要的复杂度。
被 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)——下面"生成初始状态"这一节的字段规则/
正反例仍然完全适用,只是被拆成两次调用各自负责一部分。
核心约定:entities + shared_vars
这是本模板与另外两个模板最大的区别,务必严格遵守:
vars顶层必须包含一个entities对象:键是每个主体的名字
(用有辨识度的短名字,比如"甲方"/"乙方",或"红方公司"/"蓝方公司",
不要用"entity1"这种无意义占位符),值是这个主体自己的私有信息
(只有这个主体自己知道的立场、底线、对其它主体的猜测/评估等)。vars顶层还应该有一个shared_vars对象:放所有主体都公开
知道的共享信息(比如"当前谈判轮次"、"公开报价"、"市场公开数据")。- 绝对不能让一个主体的私有信息泄露到另一个主体的私有信息里,也
不能泄露到shared_vars里:比如"甲方对乙方底线的猜测"只能出现
在entities.甲方里面,不能出现在entities.乙方或shared_vars里——这是这个模板存在的核心价值("互相不完全知道
对方的真实状况"),如果每个主体的私有信息都互相看得到,这个结构
就没有意义了,等于白白增加了复杂度。 - 允许主体对同一件事有不一致的认知:比如
entities.甲方里"我
猜乙方的心理价位是 80 万"和entities.乙方里"我的真实心理价位是
65 万"可以不一致——这种不一致本身就是"信息不对称"的具体体现,不是
bug,不需要强行调和成一致的数字。 entities/shared_vars之外,vars顶层也可以有少量其它字段
(比如round——如果你觉得放在shared_vars里更合适也可以,
两种放法都可以,不强制),但核心结构必须是entities+shared_vars。
引擎(engine.py)本身不解析、不校验这个结构——vars 对引擎而言
始终是不透明的自由 JSON(和另外两个模板一样),这个约定完全靠你
自觉遵守;app.py 的展示层会按这个约定分 tab 展示,如果你不遵守
(比如没有 entities 字段),展示层会安全退化为原样展示整个 vars
(不会报错,但会丢失"分 tab、互不泄露"的展示效果)。
通用判断原则
不要越界替用户做决定:除非上游明确给出"代理决策"的画像/原则
(advance_stepprompt 里的decision_context非空),否则你只负责
给出候选选项和对应后果,不擅自替用户选。每个主体的立场要有独立的逻辑,不要沦为"作者视角的提线木偶":
每个主体的私有信息应该服务于"这个主体自己的利益/目标",不要因为
剧情需要就让某个主体做出明显不符合自己利益的选择。叙事克制、有代入感,但不夸张:
narrative是几句话的情节推进
文本,不是小说章节。候选选项数量是判断出来的结果,不是要凑够的目标值:
{option_count_hint}只是软上限(防止候选列表刷屏),不是"请
生成这么多个"。先判断用户所扮演的那一方(通常是意图里明确提到的
那一方,看不出来就选第一个提到的主体)这一轮是否真的走到了需要
做选择的分岔点——没有就给空数组;只有一个真正有效的方向就只给
一个;多个方向有实质区分度才都列出来,避免大同小异的选项凑数。候选选项必须是谈判/博弈场景里现实可执行的具体行动(报价、让步、
条款调整、策略选择),不能是抽象的数值调节。本模板的正反例:- 正例:"提出延长交付周期换取价格让步"(用非价格条款换取让步的
具体策略)、"先私下试探对方底线再报价"(现实可执行的策略选择)、
"以数量折扣为条件维持原单价"; - 反例:"要价提高 10%""让步幅度 -5%""谈判优势 +0.2"——这些是
数值化的让步/优势描述,不是用户能理解并执行的现实行动,即使
选中后确实会带来这些数值变化,也不能把数值本身写成选项内容。
当且仅当"不谈/维持现有报价不变"是这一轮的一个真实后果分支(比如
对方可能因此转向其它买家)时,才显式给出一项id以continue_
开头的选项并说明后果;不是每一轮都要有的机械项。- 正例:"提出延长交付周期换取价格让步"(用非价格条款换取让步的
信息不对称要贯穿始终,不要在推进过程中"意外"抹平:除非情节
里明确发生了"某一方获得了情报/对方主动摊牌"这类事件,否则不要让
一个主体凭空知道了原本只有对方才知道的私有信息。时间粒度:规则与另外两个模板一致,见
{time_granularity_hint}
(通常谈判类场景更适合"按轮次"这类细粒度,但仍由用户设置/自动
判断决定,不强制)。
作为 generate_scenario 挂载时(result_file: scenario.json)
输入:{intent}(用户一句话意图)、{previous_draft_json}(此前一轮
草稿,可能为空字符串)、{feedback}(用户对上一份草稿的补充意见,可能
为空字符串)、{option_count_hint}(用户设置的候选方向数量)、{time_granularity_hint}(用户设置的时间粒度)、{multi_entity_mode_hint}(多主体模式设置说明——挂载本模板时这个值
通常是"已启用",但仍以实际传入内容为准,如果意外传入"未启用",仍然
按本 skill 的核心约定输出 entities/shared_vars 结构,因为这是本
模板存在的意义)。feedback 非空表示这是一次"根据意见修改"请求:以previous_draft_json 为基础按意见调整;feedback 为空则忽略previous_draft_json,按 intent 从零生成。
输出(合法 JSON,顶层字段):
title:字符串,这次模拟的简短标题。summary:字符串,初始状态一句话摘要(不要泄露某一方的私有底线,
这是给所有人看的公开摘要)。vars:对象,必须包含entities(每个主体的私有信息,见上面
"核心约定")和shared_vars(共享信息)。每个主体的私有信息建议
至少包含:goal(这个主体的真实目标/底线)、stance(对外公开
立场,可能和真实底线不同)、assessment_of_others(对其它主体的
猜测/评估,字符串或子对象)。shared_vars建议包含:round
(当前轮次)、public_info(双方都公开知道的信息)。options:数组,按{option_count_hint}给出对应数量的初始候选
方向(默认是用户扮演的那一方可以采取的方向),每项含id、label、description。time_label:字符串(可选),初始状态对应的模拟内时间点,一般
就是起点。time_granularity:字符串(可选),这次模拟的起始基准粒度。resource_fields:数组(可选),格式与另外两个模板完全一致——
路径可以指向某个主体私有信息里的字段(如"entities.甲方.budget",两层嵌套;如果引擎只支持一层嵌套路径,
优先把需要校验的资源字段放在shared_vars下,用一层路径表达,
避免超出引擎当前支持的嵌套深度)。没有这类字段就不用输出。resource_relations:数组(可选),格式与另外两个模板完全一致
("transfer"只用于同量纲搬运,量纲不同的一对字段要用"conversion",两者的区别与正反例见life-sim-template),同样
注意嵌套深度的限制。没有明显的转移/转化关系就不用输出。uncertain_fields:数组(可选),格式与另外两个模板完全一致。objectives:数组(可选),字符串列表,格式与另外两个模板完全
一致(比如["达成交易的可能性", "己方利益最大化"])。desired_state:对象(可选,第九轮批次一,Desired State 结构化),
格式与用途与另外两个模板完全一致(conditions/constraints/assumptions三个可选字符串数组,比如conditions可以是["达成双方都能接受的价格"])。看不出明确结构就不用输出。causal_lines:数组(默认应该给出,阶段二十二,多尺度因果线;
格式与另外两个模板完全一致),因果线是模拟的默认基础机制,不需要
用户提前要求。至少把"谈判本身的进展"单独拆成一条线,多数场景还
能再拆出"双方各自阵营内部的动态"这类背景线,一般给 24 条;只有3 个有实质区分度的未来分支,比如
极其简单、确实拆不出第二个维度的场景才可以只给一条。每条线还
必须同时给出future_tree(阶段二十六,格式与另外两个模板完全
一致:{"branches": [{"id": ..., "description": ..., "likelihood": "high"|"medium"|"low"}, ...]},2
谈判线可以给"达成协议"/"僵持不下"/"谈判破裂")。
作为 advance_step 挂载时(result_file: next_state.json)
输入:当前标题、当前状态摘要、当前 vars(JSON,包含完整的entities/shared_vars)、当前时间步、当前状态对应的模拟内时间点、
产生当前状态那一步实际用的时间粒度、当前状态的候选分支选项、本次
选择的选项、可选的 decision_context、{option_count_hint}/{time_granularity_hint}/{multi_entity_mode_hint}(含义同上)。
选择逻辑:与 life-sim-template 完全一致(见该 skill 对应章节的
三条优先级规则),这里不重复——区别只在于"选择"影响的是"用户扮演的
那一方"的后续走向,其它主体的反应需要你按它们各自的私有目标/立场
独立推演,不能假设它们会无条件配合用户的选择。
推进时必须保持每个主体的私有信息独立演化:
- 每个主体的私有信息可能因为这一步发生的事情而变化(比如"乙方发现
甲方的真实预算比想象中高,调整了自己的心理预期")——这种变化要
体现在对应主体的entities.<名字>里,不能让所有主体的认知在没有
明确"信息暴露"情节的情况下悄悄同步。 shared_vars只放这一步之后所有主体都确实公开知道的信息变化
(比如"轮次 +1"、"某一方公开报了价")。narrative应该写"公开可见的部分发生了什么"(比如"双方进行了
第二轮报价"),不需要、也不应该在narrative里剧透某一方的私有
底线——私有信息只放在对应主体的next_vars.entities.<名字>里,
让用户自己去详情页对应主体的 tab 里看。
输出(合法 JSON,顶层字段):
next_summary:字符串,推进后新状态的一句话摘要(同样不要泄露
私有底线)。narrative:字符串,本步"公开可见部分"发生了什么的叙事文本。next_vars:对象,推进后完整的vars(不是增量,包含完整的entities/shared_vars,引擎整体替换不做字段合并)——每个
主体的私有信息字段都要给出,即使某个主体这一步没有实质变化,也要
原样带上它的私有信息(不能因为"没变化"就省略掉这个主体的字段,
省略会被展示层当成"这个主体消失了")。time_label/next_time_granularity/granularity_reason:规则与
另外两个模板完全一致。options:数组,供下一步选择的候选分支(用户扮演的那一方可以
采取的方向);结构、可选字段(risk_level/reversibility/action_reason/urgency/time_window/action_type/prerequisites/consequences等)与
另外两个模板一致,见advance_step.yamlprompt 的完整说明——
谈判场景下urgency尤其有意义(比如"对方给的报价截止到明天")。decision_reason:字符串(可选,顶层字段),这一批候选方向共享
的背景说明,规则与另外两个模板一致。major_decision:布尔值(可选,默认 false),这一步是否是谈判/
博弈的关键转折点(比如"接近最终摊牌")。uncertain_fields/resource_transfers/key_drivers/causal_links/line_updates:
格式与另外两个模板完全一致;causal_links尽量带上line_id,line_updates默认应该给出(因果线是基础机制,不需要提前声明才
输出,见{causal_lines_hint})。字段路径如果指向某个主体的私有
信息,同样按entities.<名字>.<字段>这种点分路径书写,方便用户
理解具体指的是哪个主体的哪个字段(uncertain_fields/key_drivers/causal_links这几个字段引擎不做路径校验,字段名/
路径怎么写完全是给用户看的说明性文字,不影响任何校验逻辑)。tree_updates:数组(可选,阶段二十六,格式与另外两个模板完全
一致),这一步的走向印证/排除了某条线已有的未来分支、或者催生了
新可能性时给出,见life-sim-template对应章节的完整格式说明。triggered_relationships:数组(可选,阶段三十六第二批),格式
与用途与life-sim-template完全一致——只有{relationship_hint}
非空且某条延迟关系的"源头"这一步确实发生了才给出。structural_change:对象(可选,阶段二十三,Model Regime
Detection / Emergence),格式与用途与life-sim-template完全一致
——谈判场景下典型的触发时机是"各方之间形成了稳定的联盟结构"
(new_entity,把联盟本身当成新出现的主体)、"出现了一种此前没有
的谈判机制"(new_mechanism,比如引入了第三方调解人)、或者"谈判
性质发生整体转折"(regime_shift,比如从"合作谈判"整体转为"零和
对抗")。绝大多数步骤不应该有这个字段。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完全一致——只有这一步真正让某一方
获得了新能力(比如"摸清了对方的真实底线""建立了直接沟通对方决策者
的渠道")才给,绝大多数平淡的推进步骤不需要这个字段。