How the Cache Stability System Ensures Byte-Stable Prompts in Reasonix

Reasonix guarantees byte-stable prompts by separating static metadata from dynamic content, then deterministically serializing and hashing only the static portion to create cacheable, reproducible prompt prefixes.

The cache stability system in Reasonix is designed to solve a fundamental problem in LLM-based applications: caching responses requires identical byte sequences across runs, yet prompts often contain variable data like timestamps, file paths, or skill implementations that change between invocations. According to the esengine/DeepSeek-Reasonix source code, the system achieves this through architectural separation of static and dynamic prompt components.


Core Components of the Cache Stability System

System-Prompt Index: The Cache-Stable Prefix

In internal/skill/index.go, Reasonix builds a cache-stable prefix containing only immutable metadata: skill names and short descriptions. Crucially, skill bodies—which may execute arbitrary code and produce variable outputs—are deliberately excluded from this index.

The WriteIndex function in this file handles canonical serialization:

  • Entries formatted as name: description (one per line)
  • Deterministic newline separation
  • Explicit UTF-8 encoding
  • No discretionary whitespace

This format eliminates encoding ambiguities that could otherwise corrupt cache keys.

Tool Contract Static Definitions

File internal/tool/tool.go defines static tool contracts comprising name, arguments, and description. These contracts are computed once at startup and frozen thereafter. Unlike dynamic tool invocations, the contract representation never changes at runtime, making it safe to include in cache-stable prompts.

Deterministic Ordering Mechanism

In internal/skill/skill.go, all registry entries—skills, tools, and builtin plugins—maintain insertion order during registration. Before serialization, the collection is sorted to guarantee that identical logical sets always produce identical byte sequences regardless of initialization order.

Cache-Key Generation via Cryptographic Hashing

The canonical index string from BuildIndex() undergoes SHA-256 hashing to produce the final cache key. Because the input bytes are strictly deterministic, the resulting hash is byte-stable: the same skill/tool configuration always yields identical cache identifiers.


Runtime Prompt Assembly Process

The assembly flow in internal/sessioninbox/refs.go demonstrates how static and dynamic portions combine:

  1. Startup phase: Register all builtin tools (internal/tool/builtin/*) and user skills (internal/skill/*.go)
  2. Index construction: WriteIndex iterates the registry, emits name: description lines, sorts the slice
  3. Hash computation: SHA-256 digest becomes the cache-stable identifier
  4. Final composition: Static index string prepended to variable user prompt
  5. Cache lookup: Provider matches hash against stored responses; cache hit returns identical result for identical static prefix

This architecture ensures that only user-provided content can vary. Any modification to skill implementations, temporary paths, or timestamps remains isolated from the cache key calculation.


Practical Implementation Examples

Building a Cache-Stable System Prompt

package main

import (
	"context"
	"github.com/esengine/DeepSeek-Reasonix/internal/skill"
)

// Register a custom skill at init time
func init() {
	skill.Register(skill.Def{
		Name:        "hello",
		Description: "Returns a friendly greeting",
		Body: func(ctx context.Context, args map[string]string) (string, error) {
			return "Hello, " + args["name"] + "!", nil
		},
	})
}

func main() {
	// Build the cache-stable system-prompt prefix
	idx, err := skill.BuildIndex()
	if err != nil {
		panic(err)
	}
	// idx is byte-stable and cacheable
	// Example output:
	//   "builtin:bash: Execute a shell command
	//    builtin:git: Run a git operation
	//    hello: Returns a friendly greeting"
	_ = idx
}

Generating Deterministic Cache Keys

package main

import (
	"crypto/sha256"
	"encoding/hex"
	"github.com/esengine/DeepSeek-Reasonix/internal/skill"
)

func cacheKeyForPrompt(userPrompt string) string {
	staticPrefix, _ := skill.BuildIndex()          // deterministic bytes
	fullPrompt := staticPrefix + "\n" + userPrompt // final LLM prompt
	sum := sha256.Sum256([]byte(fullPrompt))
	return hex.EncodeToString(sum[:])
}

Repeated execution with identical registered skills produces the same cacheKeyForPrompt value, confirming byte-stability.


Source Files Implementing Cache Stability

File Purpose
internal/skill/index.go Constructs cache-stable prefix; provides WriteIndex and deterministic ordering
internal/skill/skill.go Handles registration; isolates dynamic bodies from static metadata
internal/tool/tool.go Defines static tool contracts for inclusion in stable prompts
internal/taskcontract/taskcontract.go Abstract contract layer contributing to byte-stable definitions
internal/sessioninbox/refs.go Demonstrates static/dynamic prompt concatenation

Summary

  • Static/dynamic separation in internal/skill/index.go excludes variable skill bodies from cache keys
  • Canonical serialization via WriteIndex eliminates formatting ambiguities
  • Deterministic ordering in internal/skill/skill.go ensures consistent byte sequences
  • SHA-256 hashing of the stable prefix creates reproducible cache identifiers
  • Runtime assembly in internal/sessioninbox/refs.go combines cached prefix with variable user input

Frequently Asked Questions

What makes a prompt "byte-stable" in Reasonix?

A byte-stable prompt produces identical byte sequences across multiple executions when given the same static configuration. Reasonix achieves this by strictly controlling which data enters the cache key calculation—only skill names, descriptions, and tool contracts are included, while dynamic implementations and variable runtime data are excluded.

Why exclude skill bodies from the cache-stable index?

Skill bodies contain executable code that may produce different outputs depending on context, timing, or external state. Including these in cache keys would cause cache fragmentation and misses. The skill.Def struct in internal/skill/skill.go explicitly separates Body (runtime logic) from Name and Description (static metadata), enabling selective inclusion.

How does deterministic ordering prevent cache inconsistencies?

Without explicit sorting, iteration order over maps or registration sequences could vary between runs due to concurrency or initialization timing. The sorting step in WriteIndex ensures that {skillA, skillB} and {skillB, skillA} registerings both serialize identically, collapsing equivalence classes to single cache entries.

Can the serialization format affect cache stability?

Yes. Reasonix uses a rigid format (name: description\n) with explicit UTF-8 encoding to prevent common instability sources: variable whitespace, platform-specific newlines, or encoding mismatches. Alternative approaches like JSON with default marshaling risk key ordering differences or pretty-printing variations.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →