LLM 描述引擎, 第 5 部分:整合测试和行为冻结

2026年8月2日1 次浏览来源:Dev.to阅读原文

正文保留英文原文(机翻易破坏代码与排版),标题/摘要已提供中文

Before reading this: I'd recommend skimming Part 3's "Parser" section and Part 4's summary to understand how the parser outputs a .

This post assumes you already know the parser can turn into a struct.

I.

A Narrative Engine's Fourth Problem: How Do You Keep Behavior Stable?

The parser is written.

But it's code that gets maintained long-term — requirements change, formats expand, bugs get fixed.

Every change risks breaking existing behavior.

The tension here is: creators depend on stable behavior, while developers depend on freedom to change.

If every code change requires manually testing every known scenario, the developer will fear refactoring.

If you don't test, broken behavior reaches the creator — but the creator doesn't care that you refactored the parser.

The solution is to "freeze" parsing behavior: use a fixed set of contracts as watchdogs.

After every change, automatically compare parse results against expectations.

This is what integration tests do: take a fixed set of contracts as "watchdogs," run them after every change, and verify that behavior hasn't been accidentally altered.

II.

Golden File Testing: Freezing Parse Results The most straightforward approach: prepare a standard contract, parse it, serialize the result to JSON, and save it.

On every subsequent test run, compare the current parse result against that JSON file.

The project's is that standard contract.

Here's the test flow: On first run, is auto-generated.

Every subsequent run compares against it, reporting differences.

If the change is intended (e.g., a new field was added), running refreshes the Golden file.

The core value of this mechanism: the parser's behavior is "frozen." Any change must pass test validation — it can't silently alter parse results.

III.

Parse-Time Validation Parsing isn't just "reading text in" — it validates required fields during the parse itself.

If the character name is empty, errors out immediately: If a rule name, condition, or action is empty, errors out with the line number.

If parsing fails, the struct doesn't exist.

There's no state where "parsing succeeded but content is invalid" — this is another advantage of the hand-written parser over JSON/YAML.

JSON parsers don't validate semantic completeness — they only validate structural correctness.

IV.

Sliding-Window Aging Tests: Correct History Truncation The engine has a critical behavior: history can't grow indefinitely.

It must auto-truncate, keeping only the most recent N turns.

This test verifies that behavior: This test ensures history truncation is "whole-turn" rather than "per-record." If truncation were per-record, you could end up with only the "fate" input and no "assistant" response — half a turn of data, leading to incomplete context in the prompt block.

Whole-turn truncation preserves history integrity — either a full turn (fate + assistant) is retained, or it's discarded entirely.

V.

Error Scenario Testing: Ensuring Precise Error Messages Beyond the "happy path," integration tests cover the error path — ensuring all kinds of malformed input produce correct errors, with line numbers and block names included: These tests ensure error messages never degrade to — the kind of error we set out to eliminate in Part

1.

VI.

The Cost Golden files need manual review on first generation or update (checking that the content is correct) Error-scenario tests need to cover as many edge cases as possible Each new block type requires updating test cases But the payoff is: you can refactor fearlessly.

As long as all tests pass, behavior hasn't changed.

VII.

Summary Integration tests are the project's "watchdogs." They freeze the parser's behavior, and every change must pass their validation.

Four things make up this test suite: Golden file tests: freeze the parse results of a standard contract Parse-time validation: check required fields and completeness during parsing Sliding-window aging tests: ensure history is truncated by whole turns Error-scenario tests: guarantee error messages are precise down to the line number With this system in place, the engine can evolve confidently — no fear of breaking things, because the tests will catch it.

Next, we enter the engine's runtime.

The core problem: once you have a , how do you drive the LLM to generate narrative that follows the rules?

The answer is the sandwich prompt structure — putting format constraints at both the top and bottom, with context in the middle — to completely eliminate parenthetical stage-direction drivel.

GitHub: https://github.com/yuelinghuashu/mephisto

分享