TrackerConfig
TrackerConfig is a Pydantic BaseModel that controls every aspect of the belief tracking pipeline. It is passed to the BeliefTracker constructor at initialization time. All parameters have sensible defaults — you only need to override the ones relevant to your use case.
pythonfrom beliefstate import BeliefTracker, TrackerConfig
# Minimal config — all defaults
tracker = BeliefTracker(config=TrackerConfig())
# Custom config — override what you need
config = TrackerConfig(
store_type="sqlite",
store_kwargs={"db_path": "beliefs.db"},
similarity_threshold=0.80,
enable_background_tasks=True,
max_beliefs=100,
)
Store Settings
These parameters determine where extracted beliefs are persisted. The store is the database backend that holds all subject-predicate-value triples for each user session. Choose a store based on your deployment model:
"sqlite"— Best for single-process apps, local development, and prototyping. Data persists in a single file. Zero setup."redis"— Best for multi-worker production deployments behind a load balancer. Shared state across processes with native TTL support.
| Parameter | Type | Default | Description |
|---|---|---|---|
store_type | str | "sqlite" | Storage backend to use. Accepted values: "sqlite" (uses aiosqlite for async disk-backed persistence) or "redis" (uses redis.asyncio for distributed caching). The tracker automatically instantiates the corresponding store class at initialization. |
store_kwargs | dict | {} | Keyword arguments forwarded to the store constructor. For SQLite: {"db_path": "beliefs.db"} sets the database file path (use ":memory:" for in-memory testing). For Redis: {"redis_url": "redis://localhost:6379/0"} sets the connection URI. If empty, stores use their built-in defaults. |
Detection Settings
These three thresholds control the contradiction detection pipeline — the two-stage filter that determines whether a newly extracted belief conflicts with, duplicates, or is independent of existing beliefs in the store. Tuning these values directly impacts the precision and recall of contradiction detection.
| Parameter | Type | Default | Description |
|---|---|---|---|
similarity_threshold | float | 0.82 | Stage 1 — Semantic gate. Minimum cosine similarity between embedding vectors of the new belief and each existing belief. Pairs below this threshold are skipped entirely (they are considered unrelated topics), avoiding unnecessary NLI judge calls. Lower values cast a wider net (more NLI calls, fewer missed contradictions). Higher values are stricter (fewer NLI calls, faster, but may miss subtle contradictions). |
contradiction_threshold | float | 0.70 | Stage 2 — Contradiction gate. Minimum score from the NLI judge model to classify two beliefs as contradicting each other. The judge returns a confidence score between 0.0 and 1.0 for the "contradiction" label. If contradiction_score >= contradiction_threshold, the resolver is invoked. Example: "lives in London" vs. "lives in Paris" would typically score ~0.95. |
entailment_threshold | float | 0.85 | Stage 2 — Deduplication gate. Minimum NLI entailment score to classify beliefs as semantic duplicates. If entailment_score >= entailment_threshold, the new belief is silently discarded (it's already represented). Example: "likes Python" and "enjoys Python programming" would score ~0.90 entailment. |
💡 Tuning Tip: Start with the defaults and observe your application's logs. If you see too many false contradictions, raise contradiction_threshold to 0.80. If valid contradictions are being missed, lower similarity_threshold to 0.75 to let more candidate pairs through to the NLI judge.
How the Two-Stage Filter Works
To avoid querying the expensive NLI judge on every single stored fact, BeliefState uses a two-tiered check:
-
Similarity Threshold (
similarity_threshold): Checks the cosine similarity between the vector embeddings of the new fact and existing stored facts. If the similarity is below this value (default 0.82), the facts are deemed conceptually unrelated, and the NLI check is skipped. -
Contradiction & Entailment Thresholds: The Natural Language Inference (NLI) model scores logical relation on a 0.0 to 1.0 scale:
- If
contradiction_score >= contradiction_threshold, the facts are treated as contradicting (e.g. "lives in London" vs. "lives in Paris"). - If
entailment_score >= entailment_threshold, the facts are duplicates (e.g. "likes Python" vs. "enjoys Python programming") and the new record is ignored or merged.
- If
Resilience Settings
These parameters control how the tracker handles LLM API failures. Since belief extraction runs in the background, transient API errors should be retried automatically rather than silently dropping beliefs. The retry mechanism uses exponential backoff with jitter to prevent thundering herd problems when the provider recovers.
| Parameter | Type | Default | Description |
|---|---|---|---|
retry_max_attempts | int | 5 | Maximum number of retry attempts for each LLM API call (extraction, judge, embedding). After this many failures, the call is abandoned and logged as a warning. Does not affect your application — only the background tracking pipeline. |
retry_min_wait | float | 2.0 | Minimum wait time (in seconds) before the first retry. Subsequent retries grow exponentially from this base value. |
retry_max_wait | float | 30.0 | Maximum wait time (in seconds) between retries. The exponential backoff is capped at this value to prevent excessively long waits. |
retry_multiplier | float | 2.0 | Exponential backoff multiplier. Each retry waits min(retry_min_wait * multiplier^attempt, retry_max_wait) seconds. With defaults: 2s → 4s → 8s → 16s → 30s. |
enable_circuit_breaker | bool | True | Enable the circuit breaker pattern for LLM API calls. When enabled, consecutive failures cause the breaker to trip OPEN, immediately rejecting subsequent calls without hitting the provider. This prevents wasting rate limits and latency on a downed provider. See Resilience for state machine details. |
circuit_breaker_failure_threshold | int | 5 | Number of consecutive failures required to trip the circuit breaker from CLOSED to OPEN state. Higher values tolerate more transient errors before tripping. |
circuit_breaker_recovery_timeout | float | 30.0 | Cooldown period (in seconds) after the circuit breaker trips. Once elapsed, the breaker enters HALF-OPEN state and allows a single probe request. If the probe succeeds, the breaker resets to CLOSED; if it fails, back to OPEN for another cooldown. |
Task & Dispatcher Settings
These parameters control how belief tracking tasks are executed. By default, tracking runs as fire-and-forget asyncio background tasks in the same event loop as your application. For production deployments that require task durability (surviving process restarts), you can offload to Celery or RQ workers.
| Parameter | Type | Default | Description |
|---|---|---|---|
enable_background_tasks | bool | True | When True, belief extraction runs in a fire-and-forget background task — your LLM response is returned instantly with zero added latency. When False, the entire extraction → detection → resolution pipeline runs synchronously inside your @tracker.wrap call, blocking your application until tracking completes. Only set to False for debugging or testing. |
task_dispatcher_type | str | "asyncio" | Controls which dispatcher backend runs tracking tasks. Options:
|
dispatcher_kwargs | dict | {} | Keyword arguments forwarded to the dispatcher constructor. For Celery: {"celery_app": celery_app}. For RQ: {"queue": rq_queue}. Ignored for asyncio/sync dispatchers. See Background Workers. |
Internal Provider Override
By default, the tracker uses the same LLM adapter as your application for all internal operations (extraction, contradiction detection, embeddings). You can override this to use a separate, cheaper model for background tracking via the Dual-Adapter pattern.
| Parameter | Type | Default | Description |
|---|---|---|---|
internal_provider | Optional[Any] | None | An explicit ProviderAdapter instance for the tracker's internal LLM calls (belief extraction, NLI judge). When set to None, the tracker reuses the main application adapter. Set this to a cheaper or local model (e.g., OllamaAdapter) to reduce costs while keeping your premium model for the user-facing app. Note: This can also be set directly via the internal_adapter parameter on the BeliefTracker constructor. |
embed_provider | Optional[Any] | None | An explicit ProviderAdapter instance dedicated to embedding generation. This allows you to decouple your chat model (e.g., Anthropic Claude which lacks embedding endpoints) from your embedding generation service (e.g., OpenAI or a local Nomic embedder). |
embed_model | Optional[str] | None | The model name to use specifically for embedding generation. Overrides any default embedding model inferred from the provider adapter. |
⚠️ Auto-detection Fail-Fast: If the tracker auto-detects an unrecognized LLM response type at runtime, it falls back to GenericAdapter. Since this generic fallback cannot perform extraction or generate embeddings, the tracker now immediately throws a ValueError upon initialization to prevent silent background failures.
Belief Storage & Injection
These parameters control how beliefs are selected and formatted when injecting context into your LLM prompts via get_context_prompt() or inject_context(). They determine which beliefs are surfaced, how many, and in what order.
| Parameter | Type | Default | Description |
|---|---|---|---|
max_beliefs | int | 50 | Maximum number of beliefs injected into the system prompt. Acts as a hard cap after all filtering and sorting. Prevents context window overflow when a user has accumulated many beliefs over long conversations. Increase for models with larger context windows (e.g., GPT-4 128k); decrease for smaller models. |
belief_sort_strategy | str | "confidence_recency" | Strategy for ranking beliefs when selecting the top max_beliefs. Options:
|
Belief TTL (Time-to-Live)
These parameters enable automatic pruning of old beliefs based on age. Useful for applications where user preferences change frequently and stale beliefs should be automatically cleaned up rather than manually managed.
| Parameter | Type | Default | Description |
|---|---|---|---|
enable_belief_ttl | bool | False | Enable automatic pruning of beliefs older than belief_max_age_seconds. When enabled, the tracker periodically scans the store and removes expired beliefs. Disabled by default because most applications want to preserve belief history indefinitely. |
belief_max_age_seconds | int | 86400 | Maximum age (in seconds) before a belief is eligible for pruning. Only effective when enable_belief_ttl=True. Default is 24 hours (86400 seconds). Set to 604800 for 7-day retention or 2592000 for 30-day retention. |
belief_ttl_check_interval | int | 3600 | How often (in seconds) the tracker runs its background pruning sweep to remove expired beliefs. Default is 1 hour (3600 seconds). Only effective when enable_belief_ttl=True. Lower values detect expired beliefs faster but increase database load. |
ℹ️ Pruning vs. Staleness: TTL pruning (enable_belief_ttl) permanently deletes old beliefs from the store. Staleness scoring (enable_staleness_scoring) only hides old beliefs from prompt injection — they remain in the store and can be queried. Use TTL for data hygiene; use staleness for prompt relevance.
Staleness Scoring
When a user resumes an old session after days or weeks, some beliefs may no longer be relevant (e.g., "USER is working on a project X" from 10 days ago). Staleness scoring deprioritizes old beliefs during prompt injection without deleting them, keeping the context prompt focused on recent, high-confidence information.
| Parameter | Type | Default | Description |
|---|---|---|---|
enable_staleness_scoring | bool | True | Enable staleness-based filtering during prompt injection. When active, each belief is assigned a score based on its confidence and recency. Beliefs scoring below staleness_threshold are excluded from the injected context. If all beliefs are too stale, the tracker falls back to injecting the top 5 most confident, recent beliefs as a safety net. |
staleness_threshold | float | 0.1 | Minimum staleness score required for a belief to be injected into the prompt. Beliefs scoring below this value are excluded. Computed using the formula below. |
Staleness Decay Formula
Each belief is scored using:
Staleness Score = Confidence / (Days Since Last Referenced + 1)
Examples:
- A belief with confidence 1.0, referenced today → Score =
1.0 / (0 + 1)= 1.0 ✅ (included) - A belief with confidence 0.8, referenced 3 days ago → Score =
0.8 / (3 + 1)= 0.2 ✅ (included) - A belief with confidence 0.5, referenced 10 days ago → Score =
0.5 / (10 + 1)= 0.045 ❌ (excluded, below 0.1)
Token-Aware Injection
For users with many accumulated beliefs, the formatted context prompt can grow very large, consuming a significant portion of the LLM's context window. Token-aware injection automatically selects the most relevant beliefs when the total would exceed a token budget, using cosine similarity with the current user message to rank relevance.
| Parameter | Type | Default | Description |
|---|---|---|---|
enable_token_aware_injection | bool | True | Enable intelligent belief selection based on token budget. When the formatted belief summary exceeds belief_budget_tokens, the tracker embeds the current user message and ranks beliefs by relevance (cosine similarity), selecting only the most relevant ones to fit the budget. Requires current_user_message to be passed to get_context_prompt() or inject_context(). |
belief_budget_tokens | int | 500 | Maximum number of tokens reserved for the belief context block in the system prompt. Token count is estimated using a fast heuristic: 1 token ≈ 4 characters. When the belief summary exceeds this budget, relevance-based filtering is triggered. Increase for models with large context windows; decrease if you need to reserve more space for the conversation history. |
Token-Aware Filtering Cascade
When enable_token_aware_injection=True and a current_user_message is provided, the filtering works as follows:
- Estimate — Format all candidate beliefs and count tokens using the
1 token ≈ 4 charsheuristic. - Check budget — If estimated tokens ≤
belief_budget_tokens, use all beliefs (no filtering needed). - Embed query — Generate a vector embedding of the current user message.
- Rank beliefs — Compute cosine similarity between the user message embedding and each belief's stored embedding.
- Select top-K — Fill beliefs from highest to lowest relevance until the token budget is reached.
Custom Prompts
BeliefState uses two LLM prompts internally for the tracking pipeline. The extraction prompt parses conversation text into structured beliefs. The judge prompt evaluates whether two beliefs contradict, entail, or are neutral to each other. You can override both via TrackerConfig to customize behavior for your domain.
Extraction Prompt
This prompt is sent to the internal adapter after each LLM conversation turn. It instructs the model to parse the response text and extract factual claims as structured JSON objects.
| Parameter | Type | Default | Description |
|---|---|---|---|
extract_prompt_template | str | (Built-in prompt) | Legacy/Fallback template for belief extraction. If overridden, it will be used for both user and assistant messages to preserve backward compatibility. |
extract_user_prompt_template | str | (Built-in prompt) | Template used to extract beliefs from user messages. Maps first-person pronouns to "USER". Must include the {response} placeholder. |
extract_assistant_prompt_template | str | (Built-in prompt) | Template used to extract beliefs from assistant messages. Maps first-person pronouns to "ASSISTANT" and second-person to "USER". Must include the {response} placeholder. |
pythonconfig = TrackerConfig(
extract_user_prompt_template="""Extract beliefs from user text: {response}""",
extract_assistant_prompt_template="""Extract beliefs from assistant text: {response}""",
)
ℹ️ Prompt Routing & Pronoun Mapping Bias: By default, BeliefState automatically routes user inputs to extract_user_prompt_template and assistant responses to extract_assistant_prompt_template. This ensures that first-person pronouns ("I", "my") are mapped correctly to "USER" or "ASSISTANT" respectively. If you customize extract_prompt_template directly, it overrides both to maintain legacy behavior.
Expected Output Format
The extraction prompt must instruct the model to return a JSON array where each object has these fields:
| Field | Type | Required | Description |
|---|---|---|---|
subject | str | Yes | Who/what this belief is about (e.g., "USER", "Python") |
predicate | str | Yes | The relationship or attribute (e.g., "lives in", "likes") |
value | str | Yes | The value of the claim (e.g., "Tokyo", "Python") |
confidence | float | Yes | Extraction confidence from 0.0 to 1.0 |
belief_type | str | No | "assertion" (default) or "update" (explicit correction of prior belief) |
source | str | No | "user" or "assistant" — who made the claim |
is_hypothetical | bool | No | true if the belief is conditional/speculative (excluded from prompt injection) |
Judge Prompt
This prompt is sent to the internal adapter during contradiction detection. It evaluates the logical relationship between two beliefs (a "premise" and a "hypothesis").
| Parameter | Type | Default | Description |
|---|---|---|---|
judge_prompt_template | str | (Built-in prompt) | Template for the NLI judge prompt. Must include both {premise} and {hypothesis} placeholders, which are replaced with the two belief texts being compared. |
pythonconfig = TrackerConfig(
judge_prompt_template="""Analyze these two claims.
Premise: {premise}
Hypothesis: {hypothesis}
Return {"relationship": "contradiction|entailment|neutral", "score": 0.0-1.0}"""
)
ℹ️ Required placeholders: {premise} — The existing stored belief. {hypothesis} — The new belief being evaluated. The model must return a JSON object with relationship and score keys.
Tips for Writing Custom Prompts
- Be explicit about output format: The tracker parses the model's output as JSON. Include format examples in your prompt.
- Subject normalization matters: The default prompt normalizes first-person pronouns to
"USER"and second-person to"ASSISTANT". If you override the extraction prompt, include similar normalization rules to ensure consistent subject keys in the store. - Value normalization: The default prompt normalizes dates (ISO 8601), currencies (ISO 4217), and numbers (digits only). This prevents near-duplicate beliefs like "has $5,000" vs "has USD 5000".
- Test with
enable_background_tasks=False: When iterating on prompt design, disable background tasks so you can synchronously inspect extraction results.