onewaymyway

negotiation-template

多方谈判/博弈场景模板:把用户一句话的多方谈判/竞争意图,展开成结构化的模拟初始状态,以及在模拟推进过程中,把"当前状态+本次选择"推演成下一状态、叙事文本和候选分支——与另外两个模板的核心区别是启用"多主体私有信念"结构(阶段十七,4.9 节),每个主体有自己的私有视角,互相不完全知道对方的真实状况。当 world_simulator 项目的 template 为 negotiation 时,generate_scenario / advance_step 两个 workflow 都会挂载本 skill。

onewaymyway 2 Updated 3w ago
GitHub

Install

npx skillscat add onewaymyway/mini-agent/negotiation-template

Install via the SkillsCat registry.

SKILL.md

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

这是本模板与另外两个模板最大的区别,务必严格遵守:

  1. vars 顶层必须包含一个 entities 对象:键是每个主体的名字
    (用有辨识度的短名字,比如"甲方"/"乙方",或"红方公司"/"蓝方公司",
    不要用"entity1"这种无意义占位符),值是这个主体自己的私有信息
    (只有这个主体自己知道的立场、底线、对其它主体的猜测/评估等)。
  2. vars 顶层还应该有一个 shared_vars 对象:放所有主体都公开
    知道的共享信息(比如"当前谈判轮次"、"公开报价"、"市场公开数据")。
  3. 绝对不能让一个主体的私有信息泄露到另一个主体的私有信息里,也
    不能泄露到 shared_vars 里
    :比如"甲方对乙方底线的猜测"只能出现
    在 entities.甲方 里面,不能出现在 entities.乙方 或
    shared_vars 里——这是这个模板存在的核心价值("互相不完全知道
    对方的真实状况"),如果每个主体的私有信息都互相看得到,这个结构
    就没有意义了,等于白白增加了复杂度。
  4. 允许主体对同一件事有不一致的认知:比如 entities.甲方 里"我
    猜乙方的心理价位是 80 万"和 entities.乙方 里"我的真实心理价位是
    65 万"可以不一致——这种不一致本身就是"信息不对称"的具体体现,不是
    bug,不需要强行调和成一致的数字。
  5. entities/shared_vars 之外,vars 顶层也可以有少量其它字段
    (比如 round——如果你觉得放在 shared_vars 里更合适也可以,
    两种放法都可以,不强制),但核心结构必须是 entities +
    shared_vars。

引擎(engine.py)本身不解析、不校验这个结构——vars 对引擎而言
始终是不透明的自由 JSON(和另外两个模板一样),这个约定完全靠你
自觉遵守;app.py 的展示层会按这个约定分 tab 展示,如果你不遵守
(比如没有 entities 字段),展示层会安全退化为原样展示整个 vars
(不会报错,但会丢失"分 tab、互不泄露"的展示效果)。

通用判断原则

  1. 不要越界替用户做决定:除非上游明确给出"代理决策"的画像/原则
    (advance_step prompt 里的 decision_context 非空),否则你只负责
    给出候选选项和对应后果,不擅自替用户选。

  2. 每个主体的立场要有独立的逻辑,不要沦为"作者视角的提线木偶":
    每个主体的私有信息应该服务于"这个主体自己的利益/目标",不要因为
    剧情需要就让某个主体做出明显不符合自己利益的选择。

  3. 叙事克制、有代入感,但不夸张:narrative 是几句话的情节推进
    文本,不是小说章节。

  4. 候选选项数量是判断出来的结果,不是要凑够的目标值:
    {option_count_hint} 只是软上限(防止候选列表刷屏),不是"请
    生成这么多个"。先判断用户所扮演的那一方(通常是意图里明确提到的
    那一方,看不出来就选第一个提到的主体)这一轮是否真的走到了需要
    做选择的分岔点——没有就给空数组;只有一个真正有效的方向就只给
    一个;多个方向有实质区分度才都列出来,避免大同小异的选项凑数。

    候选选项必须是谈判/博弈场景里现实可执行的具体行动(报价、让步、
    条款调整、策略选择),不能是抽象的数值调节。本模板的正反例:

    • 正例:"提出延长交付周期换取价格让步"(用非价格条款换取让步的
      具体策略)、"先私下试探对方底线再报价"(现实可执行的策略选择)、
      "以数量折扣为条件维持原单价";
    • 反例:"要价提高 10%""让步幅度 -5%""谈判优势 +0.2"——这些是
      数值化的让步/优势描述,不是用户能理解并执行的现实行动,即使
      选中后确实会带来这些数值变化,也不能把数值本身写成选项内容。

    当且仅当"不谈/维持现有报价不变"是这一轮的一个真实后果分支(比如
    对方可能因此转向其它买家)时,才显式给出一项 id 以 continue_
    开头的选项并说明后果;不是每一轮都要有的机械项。

  5. 信息不对称要贯穿始终,不要在推进过程中"意外"抹平:除非情节
    里明确发生了"某一方获得了情报/对方主动摊牌"这类事件,否则不要让
    一个主体凭空知道了原本只有对方才知道的私有信息。

  6. 时间粒度:规则与另外两个模板一致,见 {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 条;只有
    极其简单、确实拆不出第二个维度的场景才可以只给一条。每条线还
    必须同时给出 future_tree
    (阶段二十六,格式与另外两个模板完全
    一致:{"branches": [{"id": ..., "description": ..., "likelihood": "high"|"medium"|"low"}, ...]},2
    3 个有实质区分度的未来分支,比如
    谈判线可以给"达成协议"/"僵持不下"/"谈判破裂")。

作为 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 对应章节的
三条优先级规则),这里不重复——区别只在于"选择"影响的是"用户扮演的
那一方"的后续走向,其它主体的反应需要你按它们各自的私有目标/立场
独立推演,不能假设它们会无条件配合用户的选择。

推进时必须保持每个主体的私有信息独立演化:

  1. 每个主体的私有信息可能因为这一步发生的事情而变化(比如"乙方发现
    甲方的真实预算比想象中高,调整了自己的心理预期")——这种变化要
    体现在对应主体的 entities.<名字> 里,不能让所有主体的认知在没有
    明确"信息暴露"情节的情况下悄悄同步。
  2. shared_vars 只放这一步之后所有主体都确实公开知道的信息变化
    (比如"轮次 +1"、"某一方公开报了价")。
  3. 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.yaml prompt 的完整说明——
    谈判场景下 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 完全一致——只有这一步真正让某一方
    获得了新能力(比如"摸清了对方的真实底线""建立了直接沟通对方决策者
    的渠道")才给,绝大多数平淡的推进步骤不需要这个字段。