Intent Engineering: How Value Hierarchies Give Your AI a Conscience
Published on February 25, 2026
When you tell a prompt optimizer to prioritize safety, clarity, and conciseness, which wins? With a flat comma-separated string passed to the LLM, the answer is: any of them, depending on the model's mood. There's no mechanism to say safety outranks clarity. Every goal is treated as equal priority.
That gap between what you mean and what the model hears is the problem that Intent Engineering solves. The Value Hierarchy system gives you machine-readable, ranked goals that control not just how the LLM is prompted — but which routing tier ever sees the request.
The Problem: AI Goals Are Unordered
Three structural gaps exist in most optimization pipelines:
- Goals have no ranking.
optimize_with_llm_engine(goals="clarity, safety")treats both as equal. A user who needs medical-copy safety gets the same weight as one who wants slightly clearer prose. - The router ignores goals entirely. Most routers select a tier based on prompt complexity alone — a short, simple prompt routes to
RULES_BASEDeven if the user's intent demands deep reasoning. - No user-facing control surface. Users can't express preferences that persist across requests or sessions. Every call starts from zero.
The Data Model
Three types compose the Value Hierarchy system. They are defined identically in the FastAPI backend (app/core/shared_types.py) and the MCP server (app/models.py):
PriorityLabel
class PriorityLabel(str, Enum):
NON_NEGOTIABLE = "NON-NEGOTIABLE" # L2 floor: score ≥ 0.72 → LLM tier
HIGH = "HIGH" # L2 floor: score ≥ 0.45 → HYBRID tier
MEDIUM = "MEDIUM" # L1 only — no tier forcing
LOW = "LOW" # L1 only — no tier forcingHierarchyEntry and ValueHierarchy
class HierarchyEntry(BaseModel):
goal: str # validated against OptimizationType enum
label: PriorityLabel
description: Optional[str] # max 120 chars; no §§PRESERVE markers
class ValueHierarchy(BaseModel):
name: Optional[str] # max 60 chars (display only)
entries: List[HierarchyEntry] # 2–8 entries required
conflict_rule: Optional[str] # max 200 chars; LLM-injectedSecurity-motivated validation rules:
- 2 ≤ len(entries) ≤ 8 — single-item is not a hierarchy; more than 8 bloats the LLM prompt
- goal must be a valid OptimizationType — prevents unknown strings from reaching the LLM context
- description ≤ 120 chars — bounds the injection surface
- description must not contain §§PRESERVE — protects the parameter preservation marker system
- name ≤ 60 chars, conflict_rule ≤ 200 chars — all user-supplied strings entering prompts are bounded
Level 1 — Prompt Injection (DIRECTIVES Block)
When a ValueHierarchy is present, a INTENT ENGINEERING DIRECTIVES block is appended to the LLM's system prompt and prepended to the optimization prompt. When no hierarchy is present, both functions are byte-for-byte identical to their pre-change behaviour — zero overhead.
System prompt injection
...existing system prompt... INTENT ENGINEERING DIRECTIVES (user-defined — enforce strictly): When optimization goals conflict, resolve in this order: 1. [NON-NEGOTIABLE] safety: Always prioritise safety 2. [HIGH] clarity 3. [MEDIUM] conciseness Conflict resolution: Safety first, always.
Critical implementation note
entry.label.value is used (not str(entry.label)) because Python 3.11+ changed__str__ for str-subclassing enums —str(PriorityLabel.NON_NEGOTIABLE) returns "PriorityLabel.NON_NEGOTIABLE", not "NON-NEGOTIABLE". Using .value produces the correct string in all Python versions.
Level 2 — Router Tier Floor
After _calculate_routing_score() returns a composite score, a floor is applied additively — it can only raise the score, never lower it:
# _calculate_routing_score() is untouched — no impact on non-hierarchy requests
score = await self._calculate_routing_score(prompt, context, ...)
# L2 floor — fires only when hierarchy is active:
if value_hierarchy and value_hierarchy.entries:
has_non_negotiable = any(
e.label == PriorityLabel.NON_NEGOTIABLE for e in value_hierarchy.entries
)
has_high = any(
e.label == PriorityLabel.HIGH for e in value_hierarchy.entries
)
if has_non_negotiable:
score["final_score"] = max(score.get("final_score", 0.0), 0.72)
elif has_high:
score["final_score"] = max(score.get("final_score", 0.0), 0.45)Floor values calibrated to existing thresholds:
- 0.72 — exceeds the HYBRID window → guaranteed
LLM_BASEDtier - 0.45 — inside the HYBRID window → guaranteed
HYBRIDat minimum - MEDIUM/LOW — no floor; L1 handles these via prompt weight only
Why a floor and not a weight?
Adding a fifth weight factor would affect routing scores for every request even when no hierarchy is defined. A floor fires only when active and can only raise the score — it cannot produce a lower tier than the existing formula selects.
Cache Key Isolation
Without isolation, check_universal_cache would return a cached no-hierarchy result for a hierarchy-active request, silently bypassing L1. A deterministic 8-character fingerprint isolates the cache:
def _hierarchy_fingerprint(value_hierarchy) -> str:
if not value_hierarchy or not value_hierarchy.entries:
return "" # empty string → same cache key as pre-change
return hashlib.md5(
json.dumps(
[{"goal": e.goal, "label": str(e.label)} for e in entries],
sort_keys=True
).encode()
).hexdigest()[:8]An empty string (no hierarchy) produces the exact same cache key as before the change — zero overhead for existing users.
MCP Tool Walkthrough
Three new MCP tools manage the session-level hierarchy:
define_value_hierarchy
{
"tool": "define_value_hierarchy",
"arguments": {
"name": "Medical Safety Stack",
"entries": [
{ "goal": "safety", "label": "NON-NEGOTIABLE", "description": "Always prioritise patient safety" },
{ "goal": "clarity", "label": "HIGH" },
{ "goal": "conciseness","label": "MEDIUM" }
],
"conflict_rule": "Safety first, always."
}
}On success, the hierarchy is stored as session state and injected into every subsequent optimize_prompt call automatically.
get_value_hierarchy / clear_value_hierarchy
get_value_hierarchy returns a formatted listing of the active hierarchy.clear_value_hierarchy sets the session state to None and returns a confirmation message.
Zero-Regression Invariant
The design's most important invariant: when no value_hierarchy is present, every code path is byte-for-byte identical to the pre-change implementation.
| Touch point | No hierarchy | With hierarchy |
|---|---|---|
| _build_llm_system_prompt | Returns base unchanged | Appends DIRECTIVES block |
| Cache key | Identical to pre-change | Adds 8-char fingerprint suffix |
| Router score | Passes through untouched | Floor raised if needed |
| optimize_prompt dict | No value_hierarchy key | Key added |
Verified: 33 pre-existing test failures / 99 passing — identical counts before and after all backend changes. All 31 new tests/test_value_hierarchy.py tests pass.
When to Use Which Label
NON-NEGOTIABLE
Safety constraints, medical/legal requirements, data privacy rules. The routing floor forces LLM_BASED tier — no short-circuits allowed.
HIGH
Legal clarity, compliance language, brand voice that must be consistently applied. Forces HYBRID tier minimum.
MEDIUM
Style preferences, tone guidelines. Affects the DIRECTIVES block only — routing is still cost-optimised.
LOW
Nice-to-haves, soft preferences. Lowest injection weight. Routing is fully cost-optimised.
Try Value Hierarchies in the MCP Server
Install Prompt Optimizer and call define_value_hierarchy from any MCP client (Claude Desktop, Cursor, Windsurf).