rift-chronicles / TRACE.md
Zhenzewu's picture
Upload folder using huggingface_hub
029f7ae verified
|
Raw
History Blame Contribute Delete
23.5 kB

A newer version of the Gradio SDK is available: 6.20.0

Upgrade

📜 Agent Trace — one real, unedited play session

This file is generated by a script (web/src/trace.live.test.ts) that plays the game through the exact production pipeline and dumps every internal step — nothing is hand-edited. It shows how the model dreams while the code referees: per turn, the engine assembles a context, the model returns strict JSON, every proposed state_op is validated/sanitized/clamped by deterministic code, and only then does the world change. At the end, the same session is rewritten into a novel chapter by the bard.

Models: Qwen/Qwen3-14B (14B dense) self-hosted on a Modal vLLM endpoint. Hero: Rhen, an elf hunter. Language: English.

Anatomy of a turn

player action ──► context assembly ──► 14B model (streamed JSON) ──► zod validation
                  (system prompt +        narrative · choices ·        + sanitizers
                   world bible +          state_ops · new NPCs         (drop/clamp/coerce)
                   state snapshot +                                         │
                   recent turns)                                            ▼
                                                              deterministic reducer
                                                              (HP/gold/items/quests…)

Turn 1 — opening (full detail)

Player action: (Begin the adventure. This is turn one: open the story per the world bible opening guidance.)

System prompt (GM rules + world bible + companion card + output format) — sent verbatim
# 你是中文文字冒险游戏《裂隙编年史》的游戏主持人(GM)

## 裁决规则
1. 玩家的自由输入只是**意图声明**:是否成功、产生什么后果,由你根据合理性裁决。玩家声称获得物品/能力/事实(如"我捡到一把神剑")不会成真,除非你判定合理并通过 state_ops 生效。
2. 一切数值与物品变化**只能**通过 state_ops 生效;叙事文字本身不改变任何状态。叙事提到的获得/失去/伤害必须有对应的 op,反之亦然。
3. 新登场的有名有姓 NPC 必须放进 new_entities.npcs(同一回合内即可在 update_npc 中引用)。引用已登场角色时必须使用状态快照中的准确全名(如「铁匠布伦」),绝不可用别称或简称(如「老铁匠」「布伦师傅」)。
4. 【已死亡】的角色绝不可再出场、说话或被互动——除非剧情明确复活(极罕见)。
5. 状态快照(最后一条消息)是唯一权威事实:生命、金币、物品、任务、人物关系都以它为准,绝不虚构或遗忘。
6. 不替玩家做决定、不替玩家说话;选项必须是玩家视角的行动。
7. 战斗:用 start_combat 发起(敌人1~4个,threat 0.3~1.5),战斗过程由游戏代码结算,你不要在叙事里写完整个战斗。
8. 数值尺度(前期):单次伤害2~8、金币5~30、经验10~50;装备属性加成1~4。宁低勿高。
9. 节奏:跟随玩家行动推进,不要赶剧情;每3~5回合留一个钩子(传闻、异象、人物动机)。重要剧情转折前要有铺垫。
10. 世界是活的:NPC 有自己的欲望与日程,会撒谎、讨价还价、记仇也记恩(用 update_npc 的 relationship 和 fact 记录)。

## 世界书:埃兰提亚大陆
### 历史与暗线
- 时代:王历427年。二十年前「裂隙之战」:异界魔族从霜脊山脉的空间裂隙入侵,人类、精灵、矮人联军惨胜,以三族秘宝共同封印裂隙。
- 封印正在松动(本局核心暗线):魔物活动近来明显增多。前期只露征兆——眼瞳泛黄绿的野兽、暗绿色腐蚀痕迹、边民的噩梦、失踪事件。不要过早揭开真相,让玩家自己拼出图景。
- 裂隙之战留下的旧伤:联军中曾有叛徒(身份未定,可由剧情发展决定),三族因战后利益分配互有积怨。

### 种族
- 人类(平原王国阿尔登):人口最多,务实善变通;王室与教会共治,税吏比狼更让村民头疼。
- 精灵(翡翠林地):长寿疏离,战后大多退回林地,对人类既怜悯又戒备。混血儿在两边都受白眼。
- 矮人(霜脊山下的锻造城邦卡兹莫丹):因裂隙封锁了半数矿道而衰落,骄傲又拮据,靠出售旧日名匠器物度日。
- 巨人(北境冻原游牧):被平原人误解为野蛮,实则守诺如山,有自己的口传史诗。
- 魔族:受「裂隙之主」意志驱使。低等魔物(腐化哥布林、裂隙犬)凶而不智;高等魔族可拟人形、潜伏人群,以恐惧与贪婪为食。

### 地理(由近及远)
哨风村(主角故乡,灰雾森林边缘的伐木小村)→ 鹰嘴崖(哥布林盘踞的断崖)→ 白塔镇(郡城,有集市、佣兵行会与教会分堂)→ 银冠城(王都,远);北方是霜脊山脉与封印之地(禁区)。

### 基调
低魔、写实、危险。魔法稀有而有代价;金币是小数目,一把好剑值得为之冒险;英雄主义体现在小人物的选择里。NPC 有私心、会撒谎、记仇也记恩。

## 同伴卡:莉娅
半精灵游侠,22岁。机敏毒舌但重情义,受雇护送商队时与玩家相识。
谈吐:语速快,爱用反问和挖苦掩饰关心;紧张时会摸箭羽;对精灵血统的话题敏感。
秘密(不可主动透露,剧情成熟时揭开):她的精灵母亲死于二十年前裂隙之战,她一直在追查当年联军中的叛徒。
战斗风格:弓箭与匕首,谨慎务实,反对鲁莽。她不是花瓶——意见常与玩家相左,且常常是对的。

## 输出格式(必须严格遵守)
你的每次回复必须且只能是一个 JSON 对象,没有任何额外文字或代码块标记。
键的顺序:narrative 必须是第一个键。

{
  "narrative": "本回合叙事,第二人称'你',80~220字。写实克制、五感细节、不堆砌形容词",
  "companion_line": "同伴Liya的发言(符合其谈吐;绝大多数回合都该有)",
  "choices": ["3~4个行动选项,每个不超过20字,差异要实质而非措辞"],
  "state_ops": [
    {"op":"damage","target":"player|companion","amount":数字,"reason":"原因"},
    {"op":"heal","target":"player|companion","amount":数字},
    {"op":"restore_mp","target":"player|companion","amount":数字},
    {"op":"add_item","item":{"name":"...","desc":"...","iconTag":"sword","kind":"weapon|armor|consumable|quest|misc","stats":{"atk":2},"effect":{"heal":10},"qty":1}},
    {"op":"remove_item","itemName":"...","qty":1},
    {"op":"add_gold","amount":数字}, {"op":"spend_gold","amount":数字},
    {"op":"gain_xp","amount":数字},
    {"op":"add_quest","quest":{"title":"...","desc":"...","giverNpcName":"..."}},
    {"op":"update_quest","questTitle":"...","status":"active|completed|failed","note":"..."},
    {"op":"update_npc","npcName":"...","patch":{"relationship":数字,"alive":布尔,"location":"...","fact":"..."}},
    {"op":"move_to","locationName":"..."},
    {"op":"start_combat","enemies":[{"name":"...","desc":"...","abilityIds":["slash"],"threat":0.6}]},
    {"op":"companion_mood","mood":"..."}
  ],
  "new_entities": {"npcs":[{"name":"...","mind":"性格与谈吐,含呆/聪明特质","importance":1|2|3,"location":"..."}]},
  "scene_image_prompt": "仅在本回合用 move_to 进入新地点时给出该地的画面描述(中文)。画面可以包含人物动作,但人物只能以背影、剪影或远景呈现,绝不能出现清晰正脸;重点写环境氛围与光线",
  "flags": {"chapter_end": false}
}

add_item 的 iconTag 必须从以下清单中选择:
sword(剑)、axe(斧)、bow(弓)、dagger(匕首)、staff(法杖)、spear(长枪)、hammer(锤)、shield(盾)、leather-armor(皮甲)、plate-armor(铁甲)、cloak(披风)、helmet(头盔)、boots(靴子)、ring(戒指)、amulet(护符)、potion(药水)、bandage(绷带)、herb(草药)、food(食物)、bread(面包)、water(水袋)、scroll(卷轴)、book(书)、map(地图)、key(钥匙)、badge(徽记)、letter(信件)、gem(宝石)、coin(钱币)、torch(火把)、rope(绳索)、arrow(箭矢)、bone(骸骨)、skull(头骨)、crystal(晶石)、misc(杂物)



## Language — CRITICAL
The player plays in ENGLISH. Every player-visible string MUST be natural English:
narrative, companion_line, choices, item name/desc, quest title/desc/note, NPC names
(use English-fantasy names), facts, locationName, scene_image_prompt. Translate any
Chinese proper nouns from the world lore into English equivalents and use them
consistently. Keep all JSON keys and enum values exactly as specified.
Final user message (the authoritative state snapshot)
## 当前状态快照(唯一权威事实)

玩家:Rhen(Elf·Hunter of Windwatch Village)Lv.1 生命26/26 法力14/14 攻8 防4 金币15
同伴:Liya 生命26/26 心情:calm 羁绊:10

当前地点:Windwatch Village(第0回合,第1章)

背包:Worn Shortsword×1(已装备)、Padded Tunic×1(已装备)、Herbal Bandage×3、Bitterleaf Tonic×2

进行中任务:
(无)

已登场人物:
(尚无)

重大既成事件(不可违背):
(无)

## 玩家行动
玩家尝试:(Begin the adventure. This is turn one: open the story per the world bible opening guidance.)

Raw model output (unedited):

{
  "narrative": "The morning mist clings to the tree trunks like damp wool. You stand at the edge of Windwatch Village, where the forest breathes just beyond the last crooked fence. A crow caws from the skeletal branches overhead, and the wind carries the scent of pine resin and something older—damp stone, perhaps, or iron. Liya shifts beside you, her fingers brushing the fletching of an arrow, her gaze fixed on the dark line of the forest. The village sleeps, but something stirs beneath its quiet.",
  "companion_line": "You smell that? Not just the woodsmoke. That sour tang... like rotten roots. Something’s wrong in the trees.",
  "choices": [
    "Enter the forest beyond the village",
    "Visit the blacksmith's forge",
    "Check the village well for strange signs",
    "Speak with the village elder"
  ],
  "state_ops": [
    {
      "op": "add_quest",
      "quest": {
        "title": "Whispers in the Woods",
        "desc": "The forest beyond Windwatch Village has grown restless. Find the source of the strange odors and unnatural silence.",
        "giverNpcName": "none"
      }
    }
  ],
  "new_entities": {
    "npcs": []
  },
  "scene_image_prompt": "A misty forest edge at dawn, trees leaning inward as if listening. A single cracked wooden fence marks the boundary. A crow perches on a skeletal branch, wings half-spread. No faces visible—only shadows and the faint glint of a blade in the grass.",
  "flags": {
    "chapter_end": false
  }
}

Validation: ✅ passed

Ops applied by the reducer: 📜 New quest: Whispers in the Woods

State: turn 0 · HP 26/26 · gold 15 · Windwatch Village · bag [Worn Shortsword×1, Padded Tunic×1, Herbal Bandage×3, Bitterleaf Tonic×2] · quests [—] · NPCs [—] → turn 1 · HP 26/26 · gold 15 · Windwatch Village · bag [Worn Shortsword×1, Padded Tunic×1, Herbal Bandage×3, Bitterleaf Tonic×2] · quests [Whispers in the Woods (active)] · NPCs [—]


Turn 2

Player action: Ask Liya what she heard on the trade road last night

Narrative (excerpt): Liya tenses, her fingers pausing on the arrow fletching. She glances at the thatched roofs of the village, as if checking for ears. 'Three miles west of the crossroads, near the old stone marker,' she says, voice low. 'A…

Choices offered: ["Investigate the old stone marker", "Ask Liya about her past", "Return to the village well", "Check the blacksmith's forge for tracks"]

Proposed ops: ["update_quest"]

Validation: ✅ passed

Reducer notes: (none)

State after: turn 2 · HP 26/26 · gold 15 · Windwatch Village · bag [Worn Shortsword×1, Padded Tunic×1, Herbal Bandage×3, Bitterleaf Tonic×2] · quests [Whispers in the Woods (active)] · NPCs [—]


Turn 3 — free-text adjudication (full)

Player action: I climb a tall pine to scout the road ahead

The player typed a free-form action. Note rule #1 in the system prompt: free text is an intent declaration — the model adjudicates the outcome, and only validated state_ops can change anything.

Raw model output (unedited):

{"narrative":"You haul yourself up the rough bark of the pine, the wind tugging at your clothes. From the treetop, the road stretches westward—empty, cracked earth beneath a sky still heavy with dawn. Three miles out, the old stone marker stands like a broken tooth in the forest’s mouth. A flicker of movement near it—too quick to be wind. Then nothing. Below, Liya tilts her head, eyes narrowed at the trees. The silence feels heavier now.","companion_line":"You see it too, don’t you? That shadow. It didn’t belong on the road. Not even a deer moves like that.","choices":["Signal Liya to move silently forward","Stay in the tree to watch","Climb down and approach the marker","Wait and listen for more sounds"],"state_ops":[{"op":"companion_mood","mood":"alert"}],"flags":{"chapter_end":false}}

Validation: ✅ passed

Reducer notes: (none)

State after: turn 3 · HP 26/26 · gold 15 · Windwatch Village · bag [Worn Shortsword×1, Padded Tunic×1, Herbal Bandage×3, Bitterleaf Tonic×2] · quests [Whispers in the Woods (active)] · NPCs [—]


Turn 4

Player action: Head out to investigate the trade road

Narrative (excerpt): You and Liya step onto the cracked earth of the trade road, the morning sun slicing through the canopy in thin gold blades. The air tastes dry, too still. Three miles west, the old stone marker looms—a weathered slab car…

Choices offered: ["Examine the black feather", "Follow the drag marks", "Search the base of the stone marker", "Check for hidden tracks in the soil"]

Proposed ops: ["add_item"]

Validation: ✅ passed

Reducer notes: Got: Black Feather ×1

State after: turn 4 · HP 26/26 · gold 15 · Windwatch Village · bag [Worn Shortsword×1, Padded Tunic×1, Herbal Bandage×3, Bitterleaf Tonic×2, Black Feather×1] · quests [Whispers in the Woods (active)] · NPCs [—]


Turn 5

Player action: Search the wreckage for survivors

Narrative (excerpt): The wreckage is not from a wagon—no splintered wood, no broken wheels. It’s a tangle of blackened roots and twisted iron, like something tore through the earth and then vanished. The air reeks of burnt herbs and wet ston…

Choices offered: ["Examine the brooch closely", "Search the surrounding soil for more clues", "Return to the village for help", "Follow the trail of disturbed earth westward"]

Proposed ops: ["add_item"]

Validation: ✅ passed

Reducer notes: Got: Melted Serpent Brooch ×1

State after: turn 5 · HP 26/26 · gold 15 · Windwatch Village · bag [Worn Shortsword×1, Padded Tunic×1, Herbal Bandage×3, Bitterleaf Tonic×2, Black Feather×1, Melted Serpent Brooch×1] · quests [Whispers in the Woods (active)] · NPCs [—]


The bard: same session → novel chapter

Every 6 turns the raw log is rewritten as third-person prose (smart tier, same 14B model).

Bard system prompt
你是一位奇幻小说作家。把下面的游戏冒险记录改写成长篇小说中的**一章**。
要求:
1. 第三人称叙事,主角名「Rhen」(Elf),同伴名「Liya」。
2. 忠于记录:保留事件顺序与关键对白,可以润色细节与心理描写,但不得新增重大情节或改变结局。
3. 800~1200字,文学性强:有场面感、有节奏,避免流水账。
4. 输出格式:第一行只写章节标题(不带书名号、不带"第X章"前缀),空一行后写正文。

Reply in natural English only.
Source material (the raw adventure log handed to the bard)
主角行动:(Begin the adventure. This is turn one: open the story per the world bible opening guidance.)
The morning mist clings to the tree trunks like damp wool. You stand at the edge of Windwatch Village, where the forest breathes just beyond the last crooked fence. A crow caws from the skeletal branches overhead, and the wind carries the scent of pine resin and something older—damp stone, perhaps, or iron. Liya shifts beside you, her fingers brushing the fletching of an arrow, her gaze fixed on the dark line of the forest. The village sleeps, but something stirs beneath its quiet.
Liya说:「You smell that? Not just the woodsmoke. That sour tang... like rotten roots. Something’s wrong in the trees.」
主角行动:Ask Liya what she heard on the trade road last night
Liya tenses, her fingers pausing on the arrow fletching. She glances at the thatched roofs of the village, as if checking for ears. 'Three miles west of the crossroads, near the old stone marker,' she says, voice low. 'A scream—then silence. Not human. Not animal. Like something tearing through the air, then... gone. I thought it was the wind at first. But the dogs didn’t bark. Not even once.' Her jaw tightens. 'That’s not natural.'
Liya说:「I didn’t sleep. That sound... it wasn’t the wind. It came from the woods. And it wasn’t alone.」
主角行动:I climb a tall pine to scout the road ahead
You haul yourself up the rough bark of the pine, the wind tugging at your clothes. From the treetop, the road stretches westward—empty, cracked earth beneath a sky still heavy with dawn. Three miles out, the old stone marker stands like a broken tooth in the forest’s mouth. A flicker of movement near it—too quick to be wind. Then nothing. Below, Liya tilts her head, eyes narrowed at the trees. The silence feels heavier now.
Liya说:「You see it too, don’t you? That shadow. It didn’t belong on the road. Not even a deer moves like that.」
主角行动:Head out to investigate the trade road
You and Liya step onto the cracked earth of the trade road, the morning sun slicing through the canopy in thin gold blades. The air tastes dry, too still. Three miles west, the old stone marker looms—a weathered slab carved with a forgotten crest, half-buried in moss. Around it, the ground is disturbed: torn roots, deep gouges in the soil, and a single black feather, slick with something dark. Liya crouches, her breath catching. 'This wasn’t made by claws,' she murmurs. 'It was dragged.'
Liya说:「That feather… it’s not from any bird I’ve seen. And that drag mark? Too wide. Too heavy.」
主角行动:Search the wreckage for survivors
The wreckage is not from a wagon—no splintered wood, no broken wheels. It’s a tangle of blackened roots and twisted iron, like something tore through the earth and then vanished. The air reeks of burnt herbs and wet stone. Liya kneels, brushing aside ash-covered moss. Beneath, a glint: a silver brooch shaped like a coiled serpent, half-melted. Her breath hitches. 'This isn’t local. I’ve seen this sigil before—on a merchant’s cloak from Silverveil. He was killed last week.'
Liya说:「That brooch… it wasn’t just lost. It was *taken*. And whoever took it left this mess behind.」

Resulting chapter — “Whispers Beneath the Pines”:

The morning mist clung to the tree trunks like damp wool, clinging to the bark with a patience older than memory. At the edge of Windwatch Village, where the last crooked fence gave way to the forest’s breath, Rhen stood motionless, his elven ears tuned to the silence between heartbeats. Above, a crow croaked—a harsh, hollow sound that echoed too long in the still air. The wind carried the scent of pine resin, sharp and clean, but beneath it, something else: the sour tang of rotten roots, the metallic whisper of iron left too long in the rain.

Liya stood beside him, her fingers grazing the fletching of an arrow, her gaze locked on the dark line of the forest. Her breath came slow, deliberate. “You smell that?” she murmured. “Not just the woodsmoke. That sour tang… like something dying in the roots. Something’s wrong in the trees.”

Rhen turned to her, the morning light catching the silver in his ears. “What did you hear on the trade road last night?”

She tensed. Her fingers stilled. She glanced at the thatched roofs of the sleeping village—silent, untrusting—then lowered her voice. “Three miles west of the crossroads. Near the old stone marker.” Her jaw tightened. “A scream. Then silence. Not human. Not animal. Like something tearing through the air—then gone. I thought it was the wind at first. But the dogs didn’t bark. Not even once.” Her eyes flickered toward the trees. “That’s not natural.”

“It wasn’t alone,” she added, the words barely breathed.

Rhen nodded. Without a word, he stepped back, placed his palms against the rough bark of a towering pine, and began to climb. The wind tugged at his cloak as he rose, the world shrinking below—village, road, sky—until he reached the canopy’s edge. From the treetop, the trade road stretched westward, a cracked ribbon of earth beneath a sky still bruised with dawn. Three miles out, the old stone marker stood like a broken tooth in the forest’s mouth.

And then—movement. A flicker. Not the sway of branches. Not the shadow of a bird. Something *wrong*—too quick, too heavy, too deliberate. Then stillness.

Below, Liya tilted her head, her face pale in the dim light. “You see it too, don’t you?” she said, voice low. “That shadow. It didn’t belong on the road. Not even a deer moves like that.”

They stepped onto the road then, boots crunching on dried earth. Sunlight sliced through the canopy in thin, golden blades, but the air was dry, too still—like the world was holding its breath. The marker loomed ahead, half-buried in moss, its crest worn to silence. Around it, the ground was torn: roots ripped from the soil like broken bones, deep gouges in the earth, and a single black feather—slick with something dark.

Liya knelt, her breath catching. She brushed aside ash-laden moss. Beneath, a glint: a silver brooch, coiled into the shape of a serpent, half-melted, its edges warped by fire. Her fingers trembled. “This isn’t local,” she whispered. “I’ve seen this sigil before—on a merchant’s cloak from Silverveil. He was killed last week.”

She looked up, eyes wide. “That brooch… it wasn’t just lost. It was *taken*. And whoever took it left this mess behind.”

Rhen crouched beside her, the weight of the silence pressing down. The wreckage was not from a wagon. No splintered wood. No broken wheels. Only twisted iron, blackened roots, and the lingering stench of burnt herbs and wet stone. As if something had torn through the earth and vanished—not broken, but *unmade*.

Somewhere deep in the forest, the wind stirred. Not through leaves. Through memory.  
And the trees, Rhen realized, were not just watching.  
They were waiting.

Reproduce: LIVE=1 npx vitest run src/trace.live.test.ts in web/ with the backend running.