PromptOptimizer
On this page
Reduces token count of the assembled (system, user) prompt pair without (intentionally) changing meaning. Every operation is deterministic and regex/string-based — there is no LLM call anywhere in this module.
Table of contents
Constructor
PromptOptimizer(
max_total_tokens: int = 6000,
enable_filler: bool = True,
enable_dedup: bool = True,
enable_whitespace: bool = True,
) -> None
optimize()
def optimize(
self, system: str, user: str, request: Optional[object] = None,
) -> Tuple[str, str, int, List[str]]
request is accepted (typed loosely as object to avoid a circular import with types.PromptRequest) but not used anywhere in the method body — it's a currently-unused parameter reserved for future request-aware optimization logic.
Returns
(optimized_system, optimized_user, tokens_saved, notes) where tokens_saved = max(0, original_tokens - final_tokens) (never negative) and notes is a list of strings describing which passes ran and what they did.
Raises
No exceptions raised under normal use — all operations are string/regex transforms with no I/O.
Passes, in order
Every enabled pass runs unconditionally in this fixed order; there's no way to reorder them without subclassing.
1. Whitespace normalization (enable_whitespace)
_normalize_whitespace(system, user): collapses runs of 2+ spaces/tabs to one space (_MULTI_SPACE), collapses 3+ newlines to exactly two (_BLANK_LINES), and strips leading/trailing whitespace. Appends "whitespace-normalized" to notes unconditionally (even if nothing actually changed).
2. Filler stripping (enable_filler)
_strip_filler(system, user): removes (case-insensitive) any of these phrases, plus a trailing !/, if present: certainly, great question, of course, absolutely, i'd be happy to help., i'm happy to assist., let me help you with that., sure, definitely. Appends "filler-stripped" unconditionally.
3. Cross-block deduplication (enable_dedup)
_dedup_instructions(system, user): splits system into "\n\n"-delimited paragraphs longer than 30 characters into a set; splits user into "\n\n"-delimited paragraphs; removes any user paragraph that appears verbatim (exact string match after .strip()) in the system paragraph set. Note appended only if at least one paragraph was removed: f"dedup-removed-{removed}-paragraphs".
4. Hard cap (always checked, not gated by a flag)
After the three passes above, if _tok(system) + _tok(user) > self.max_total_tokens, _hard_cap(user, self.max_total_tokens - _tok(system)) is called: truncates user to approximately the remaining token budget (by word count, budget / 1.3), tries to cut at the last paragraph boundary if that boundary is past 70% of the truncated length, appends "\n\n[Context truncated to fit token budget]", and adds a note f"hard-capped-at-{budget}-tokens". Only user is ever truncated — system is never shortened by this pass.
Token counting
def _tok(text: str) -> int:
return int(len(text.split()) * 1.3)
A whitespace-split word count multiplied by 1.3 — a common rough heuristic, not a real tokenizer (e.g. not tiktoken). This same heuristic is duplicated (not shared) across optimizer.py, context_manager.py (_count_tokens), and strategies/__init__.py (_estimate_tokens) — all three implement the identical formula independently rather than importing a shared utility.
Complexity
- Time: O(n) in prompt length for whitespace/filler regex passes; O(p) in paragraph count for dedup (using a Python
setfor system paragraphs, so lookup is O(1) amortized per user paragraph — overall O(p_system + p_user)); O(w) for the hard cap wherewis word count. - Space: O(n) for the transformed strings and the paragraph set.
Thread safety
Fully stateless with respect to instance mutation — all methods either take self config (immutable after construction) or are pure @staticmethods. Safe for concurrent use across threads (not explicitly tested in source).
Side effects
One logger.debug call, only if tokens_saved > 0.
Examples
1. Basic
from zerda.prompt.optimizer import PromptOptimizer
opt = PromptOptimizer()
system, user, saved, notes = opt.optimize(
system="You are a helpful assistant.",
user="Sure! Certainly, here's the context...\n\nQuestion: What is RAG?",
)
print(saved, notes)
2. Intermediate — disabling filler stripping
opt = PromptOptimizer(enable_filler=False)
3. Advanced — tight token budget
opt = PromptOptimizer(max_total_tokens=500)
system, user, saved, notes = opt.optimize(system=long_system, user=long_user)
assert "[Context truncated to fit token budget]" in user or saved == 0
4. Error handling — empty strings
system, user, saved, notes = opt.optimize(system="", user="")
assert saved == 0
5. Production — inspecting what was cut
system, user, saved, notes = opt.optimize(system=sys_prompt, user=usr_prompt)
if saved > 200:
logger.warning("Aggressive prompt trimming: %s", notes)
Best practices
- If your system prompt legitimately needs conversational filler removed but you don't want cross-block dedup (e.g. you intentionally repeat an instruction in both blocks for emphasis), disable only
enable_dedup, not the whole optimizer. - Set
max_total_tokensconservatively below your actual model context window to leave room for the model's response.
Common mistakes
- Assuming
systemcan be trimmed by the hard cap — it cannot; onlyuseris ever shortened, so an oversizedsystemprompt alone can still exceedmax_total_tokensafter optimization. - Passing a
requestobject expecting request-aware optimization (e.g. skipping dedup for a particular prompt type) — the parameter is currently inert.
Related APIs
- strategies —
BaseStrategy.build()is the only caller ofPromptOptimizer - context_manager — has its own, separately-implemented token heuristic