Alerter Configuration
The pgEdge AI DBA Workbench Alerter supports configuration through YAML files and command-line flags.
Configuration Precedence
The alerter applies configuration settings in the following order; later sources override earlier ones:
- Default values built into the application.
- Configuration file settings (YAML format).
- Command-line flag overrides.
The alerter overrides a configuration file setting only when you actually pass the corresponding flag; a flag you omit never overrides the file, even though the flag has a built-in default. A flag you do pass always wins, including when the value you give it happens to equal that flag's default.
Configuration File
The alerter searches for its configuration file in the following order:
- The path specified via the
-configflag. - The per-user config directory at
~/.config/pgedge/ai-dba-alerter.yamlon Linux (honouring$XDG_CONFIG_HOME),~/Library/Application Support/pgedge/ai-dba-alerter.yamlon macOS, and%AppData%\pgedge\ai-dba-alerter.yamlon Windows. /etc/pgedge/ai-dba-alerter.yaml(system-wide).
If -config is set and the file is missing, the
alerter exits with an error. If -config is not set
and none of the default locations contain a
configuration file, the alerter uses built-in defaults
silently. The alerter no longer searches the binary
directory or the current working directory. A SIGHUP
signal re-runs discovery on each reload, so a
configuration file installed at a default location
after startup is picked up on the next signal.
A complete example configuration file is available at ai-dba-alerter.yaml in the project repository.
Command-Line Flags
The alerter accepts the following command-line flags:
| Flag | Description | Default |
|---|---|---|
-config |
Path to configuration file | Auto-detected |
-debug |
Enable debug logging | false |
-db-host |
Database host | None |
-db-port |
Database port | None |
-db-name |
Database name | None |
-db-user |
Database user | None |
-db-password |
Database password | None |
-db-sslmode |
Database SSL mode | None |
In the following example, the alerter starts with debug logging and a custom configuration file:
./ai-dba-alerter -debug \
-config /etc/ai-workbench/alerter.yaml
In the following example, the alerter connects to a specific database without a configuration file:
./ai-dba-alerter \
-db-host db.example.com \
-db-name ai_workbench \
-db-user alerter \
-db-password secret
Configuration File Reference
The configuration file uses YAML format. The following sections describe all available options.
Datastore Connection (datastore)
The datastore section configures the connection to
the AI DBA Workbench PostgreSQL datastore.
| Option | Type | Default | Description |
|---|---|---|---|
host |
string | localhost |
Server hostname |
hostaddr |
string | None | Server IP address |
database |
string | ai_workbench |
Database name |
username |
string | postgres |
Database username |
password |
string | None | Database password |
password_file |
string | None | Path to password file |
port |
integer | 5432 |
Server port |
sslmode |
string | prefer |
SSL connection mode |
sslcert |
string | None | Client certificate path |
sslkey |
string | None | Client private key path |
sslrootcert |
string | None | CA certificate path |
The sslmode option accepts the following values:
disabledisables SSL encryption.allowattempts non-SSL first and falls back to SSL.preferattempts SSL first and falls back to non-SSL.requirerequires SSL without certificate verification.verify-carequires SSL and verifies the server certificate.verify-fullrequires SSL and verifies the certificate and hostname.
In the following example, the datastore section
configures a secure connection with certificate
verification:
datastore:
host: db.example.com
database: ai_workbench
username: ai_workbench
password_file: /etc/ai-workbench/password.txt
port: 5432
sslmode: verify-full
sslcert: /etc/ai-workbench/client-cert.pem
sslkey: /etc/ai-workbench/client-key.pem
sslrootcert: /etc/ai-workbench/ca-cert.pem
Connection Pool (pool)
The pool section configures the database connection
pool.
| Option | Type | Default | Description |
|---|---|---|---|
max_connections |
integer | 10 |
Max concurrent connections |
max_idle_seconds |
integer | 300 |
Idle connection timeout |
Threshold Evaluation (threshold)
The threshold section configures threshold-based
alert evaluation.
| Option | Type | Default | Description |
|---|---|---|---|
evaluation_interval_seconds |
integer | 60 |
Evaluation interval |
trigger_count |
integer | 2 |
Consecutive breaching samples that raise an alert; must be at least 1 |
clear_count |
integer | 3 |
Consecutive samples within the threshold that clear an alert; must be at least 1 |
The trigger_count and clear_count options stop an alert
from being raised and cleared repeatedly when a metric hovers
near its threshold. The alerter raises an alert only after
the threshold has been breached on trigger_count
consecutive metric samples, and clears an active alert only
after the metric has been back within the threshold on
clear_count consecutive samples. A sample is one collection
by the collector rather than one evaluation cycle; the
evaluator and the alert cleaner read the same sample several
times between collections, but each sample counts once. A
sample that breaches the threshold starts the clear count
again, and one that does not breach it starts the trigger
count again.
Setting both options to 1 restores the earlier behaviour of
raising and clearing on a single sample. The counts are held
in memory, so restarting the alerter starts them again from
zero; at worst a restart delays a raise or a clear by a few
samples. The counts do not apply to the metric_staleness and
probe_unavailable rules or to connection error alerts, which
do not judge metric samples.
Anomaly Detection (anomaly)
The anomaly section configures the tiered anomaly
detection system.
| Option | Type | Default | Description |
|---|---|---|---|
enabled |
boolean | true |
Enable anomaly detection |
Tier 1: Statistical Analysis
The anomaly.tier1 section configures z-score-based
statistical detection.
| Option | Type | Default | Description |
|---|---|---|---|
enabled |
boolean | true |
Enable tier 1 |
default_sensitivity |
float | 3.0 |
Z-score threshold |
evaluation_interval_seconds |
integer | 60 |
Evaluation interval |
clear_count |
integer | 3 |
Consecutive in-band samples before an anomaly alert clears |
The anomaly.tier1.clear_count option sets how many
consecutive samples of the metric behind an active anomaly
alert must score back inside the sensitivity band before the
alerter clears the alert. Only a newly collected sample
counts; tier 1 usually evaluates more often than the probe
collects, and an evaluation that re-scores a sample already
counted does not add to the count. The value must be at least
1; the alerter refuses to start, or to apply a reload, with a
smaller value. For a probe that collects every 300 seconds,
the default of 3 clears an alert about ten minutes after the
first in-band sample. A SIGHUP reload applies a new value
from the next evaluation.
Tier 1: Variance Floor, Warmup, and Z-Score Cap
Three additional blocks under anomaly.tier1 prevent the
detector from firing on baselines that have not yet stabilised.
These blocks are most relevant on young datastores; on a
datastore with one or two days of metric history, a baseline's
stored standard deviation can collapse far below the metric's
natural variation, producing z-scores in the thousands. The
variance floor and warmup gate suppress this failure mode, and
the z-score cap acts as defence in depth.
The anomaly.tier1.max_z_score option clamps the absolute
z-score symmetrically around zero before the sensitivity
comparison. The default is 100.0; any genuine outlier sits
well below this value, and the cap simply prevents a runaway
divisor from generating multi-thousand-sigma scores. Setting
max_z_score: 0 disables the cap.
| Option | Type | Default | Description |
|---|---|---|---|
max_z_score |
float | 100.0 |
Z-score clamp; 0 disables |
The anomaly.tier1.variance_floor block enforces a minimum
divisor on the z-score calculation. The effective standard
deviation is the larger of the raw stored value and a hybrid
floor; the floor itself is the larger of relative_pct times
the absolute baseline mean and absolute_floor. The relative
term dominates for non-zero metrics, while the absolute term
acts as a safety net when the mean approaches zero. Setting
both relative_pct: 0 and absolute_floor: 0 disables the
floor entirely; the detector then falls back to the existing
stddev == 0 guard.
| Option | Type | Default | Description |
|---|---|---|---|
relative_pct |
float | 0.05 |
Floor as fraction of abs(mean) |
absolute_floor |
float | 0.001 |
Absolute minimum stddev |
The anomaly.tier1.warmup block suppresses detection for
baselines that have not accumulated enough samples or enough
wall-clock observation time. Each period_type (all,
hourly, and daily) has its own pair of thresholds, and a
baseline is considered warm only when both are met. The all
baseline defaults require roughly one day of operation at the
60 second collection interval; the hourly and daily
defaults require enough span for each time bucket to have been
observed multiple times. Setting both min_samples: 0 and
min_span_hours: 0 for a given period_type disables warmup
suppression for that type.
| Option | Type | Default | Description |
|---|---|---|---|
all.min_samples |
integer | 100 |
Minimum sample count |
all.min_span_hours |
integer | 24 |
Minimum span in hours |
hourly.min_samples |
integer | 5 |
Minimum sample count |
hourly.min_span_hours |
integer | 120 |
Minimum span in hours |
daily.min_samples |
integer | 3 |
Minimum sample count |
daily.min_span_hours |
integer | 336 |
Minimum span in hours |
In the following example, the anomaly.tier1 section tightens
the variance floor and extends the all warmup window:
anomaly:
tier1:
max_z_score: 100.0
variance_floor:
relative_pct: 0.10
absolute_floor: 0.005
warmup:
all:
min_samples: 200
min_span_hours: 48
hourly:
min_samples: 5
min_span_hours: 120
daily:
min_samples: 3
min_span_hours: 336
Warmup suppressions are recorded at debug log level only; the
detector does not write a candidate row or an alert when it
skips a cold baseline, and a fall-through from a cold hourly or
daily row to a warm, less specific one is logged at the same
level. On the long-term test host, enable debug
logging in the alerter and inspect recent suppressions with
sudo journalctl -u ai-workbench-alerter.service --since 10m.
The log line names the connection, metric, period type, and
sample count, which is enough to confirm whether a missing
alert reflects warmup suppression or a genuinely quiet metric.
Tier 1: Per-Metric Floors
The anomaly.tier1.metric_floors map sets two floors for each
metric, so that a value which is unusual for a quiet baseline
but harmless in absolute terms does not raise an alert. Each
key is a metric name, and each entry accepts two options:
| Option | Type | Description |
|---|---|---|
min_value |
float | Value below which the metric is never anomalous; 0 disables |
min_stddev |
float | Minimum z-score divisor in the metric's units; 0 disables |
A value under min_value raises no anomaly candidate, and it
counts as in band when the alerter decides whether to clear an
open anomaly alert. The min_stddev option raises the divisor
of the z-score to at least the given value, so a small change
on a baseline that has never moved scores a small z-score; it
only takes effect whilst it exceeds 5% of the baseline mean,
the default relative variance floor. When the baseline has no
spread of its own, the alert severity is capped at warning;
threshold rules remain the way to raise a critical alert on an
absolute value.
The alerter has built-in floors for its standard metrics, for
example min_value: 50 and min_stddev: 5 for CPU usage
percent, and min_value: 10 and min_stddev: 5 for the
session count; the
anomaly detection guide
lists them all. An entry overrides a built-in floor field by
field, so an entry that sets only min_value keeps the
built-in min_stddev. The alerter refuses a negative or
non-finite value, or an empty metric name, and logs a warning
at startup and on each reload for an entry naming a metric
that anomaly detection does not score, such as a mistyped
name.
In the following example, the metric_floors map raises the
CPU value floor and disables both floors for the session
count:
anomaly:
tier1:
metric_floors:
pg_sys_cpu_usage_info.processor_time_percent:
min_value: 70
pg_stat_activity.count:
min_value: 0
min_stddev: 0
Tier 2: Embedding Similarity
The anomaly.tier2 section configures pgvector-based
similarity search for pattern matching.
| Option | Type | Default | Description |
|---|---|---|---|
enabled |
boolean | true |
Enable tier 2 |
suppression_threshold |
float | 0.85 |
Suppression threshold |
similarity_threshold |
float | 0.3 |
Similarity threshold |
Tier 3: LLM Classification
The anomaly.tier3 section configures LLM-based
classification for complex anomalies.
The following table describes the tier 3 options:
| Option | Type | Default | Description |
|---|---|---|---|
enabled |
boolean | true |
Enable tier 3 |
timeout_seconds |
integer | 30 |
Timeout for each LLM API call |
decision_suppress_threshold |
number | 0.5 |
Probability that a candidate is not a real issue at which the decision model suppresses it; must be greater than 0 and less than 1 whilst a decision provider is configured |
Tier 3 classifies each candidate with the reasoning model
unless llm.decision_provider selects a decision model.
With a decision provider, tier 3 asks the decision model
first and uses the reasoning model only as a fallback; the
Decision Model section
describes the behaviour. Tier 3 runs whilst it is enabled
and either a reasoning or a decision provider initialised at
startup.
The decision model returns the probability that an anomaly
is a real issue requiring attention. Tier 3 suppresses the
candidate when one minus that probability is at least
decision_suppress_threshold, and raises the alert
otherwise. The default of 0.5 suppresses a candidate that
the model judges more likely than not to be a false
positive; a higher value suppresses fewer candidates. The
alerter checks the value only when a decision provider is
configured, and ignores the option otherwise. Each provider
calibrates its probabilities differently, so tune the
threshold for the provider in use rather than carrying a
value across providers.
Each model call has its own timeout_seconds limit. A
candidate whose decision model call fails and then falls
back to the reasoning model can therefore take up to twice
the timeout.
Provider Health (provider_health)
The anomaly.provider_health section controls the system
alert that the alerter raises when calls to the embedding,
reasoning or decision provider keep failing. Whilst a
provider fails, anomaly detection runs in a degraded mode:
tier 2 compares no candidate with past anomalies, tier 3
raises each candidate that reaches the tier without LLM
classification, and the re-evaluation worker leaves
acknowledged anomaly alerts unchanged. Whilst the decision
provider fails, tier 3 classifies each candidate with the
reasoning model where one is configured, and otherwise
raises the candidate without classification.
The following table describes the provider health options:
| Option | Type | Default | Description |
|---|---|---|---|
failure_threshold |
integer | 3 |
Consecutive failed calls that raise the alert; must be at least 1 |
failure_rate |
number | 0.3 |
Share of the last failure_rate_window calls that raises the alert when they fail; must be greater than 0 and at most 1 |
failure_rate_window |
integer | 20 |
Number of recent calls the failure rate covers; must be between 1 and 1000. A small window can set a limit of one failed call, as failure_rate: 0.3 with a window of 3 does, so the first failure raises the alert before failure_threshold is reached |
The alerter counts consecutive failures separately for tier 2 embeddings, tier 3 classification, tier 3 decisions and re-evaluation, and for each provider. When one count reaches the threshold, the alerter raises a single warning alert for that tier and provider, and updates the alert's failure count as further calls fail. The next successful call to that provider resets the count and clears the alert, unless the failure rate described below keeps the alert open; tier 3 classification and re-evaluation share the reasoning provider, so a successful call from either clears the alerts of both. The decision provider serves tier 3 decisions alone, so no reasoning call clears its alert; a successful decision call clears it, as does a successful startup check of the decision provider, described below. After clearing an alert, the alerter does not raise it again for five minutes, so that a provider that fails intermittently cannot send a burst of notifications. A call cancelled before it completes counts as neither a failure nor a success, although a call that times out counts as a failure. A tier 3 or re-evaluation call also counts as a failure when the provider answers with a response that holds no decision the alerter can parse; a decision call counts as a failure when the model refuses to answer, gives no answer, or gives a probability outside the range 0 to 1. The failure is recorded whatever the alerter then does with the candidate or alert, which may be to act on a keyword found in the response or to take the tier's default decision.
When anomaly detection is enabled, the alerter also checks the providers of the enabled tiers at startup. A failed check raises the alert for its tier immediately, whatever the threshold or the five-minute wait; a failed reasoning check raises the tier 3 alert. A successful reasoning check clears both the tier 3 and the re-evaluation alerts. When tier 3 is enabled, the alerter also checks the decision provider by listing its models, which costs no tokens; a successful listing counts as a successful decision call, so it can clear the decision alert left open by a previous run. At startup the alerter also clears any open provider alert for a tier or provider that is no longer configured or enabled. See System Alerts for how the alert appears.
The failure rate catches a provider that fails a steady
share of its calls without failing consecutively, such as a
provider that rejects every other request. The alerter keeps
the outcomes of the last failure_rate_window calls for each
tier and provider, and sets a limit of failure_rate times
the window, rounded up; the defaults give a limit of 6 failed
calls out of 20. The alerter raises the alert when the
failed calls among those outcomes reach the limit, even
before the window is full, and the five-minute wait still
applies. A raised alert stays open whilst the failed calls
remain at or above the limit, and clears on the first
successful call that leaves fewer failed calls than the
limit. A run of consecutive failures that reached the
threshold counts as a single failed call once the provider
recovers, provided that single failed call leaves the provider
below the limit; an outage therefore clears on the first successful
call, as the consecutive count alone would. Setting
failure_rate to 1 leaves the consecutive count as
effectively the only rule that raises the alert, provided
failure_rate_window is at least failure_threshold.
In the following example, the alerter raises the alert after five consecutive failures, or once 10 of the last 40 calls have failed:
anomaly:
provider_health:
failure_threshold: 5
failure_rate: 0.25
failure_rate_window: 40
Baseline Calculation (baselines)
The baselines section configures baseline metric
calculation for anomaly detection.
| Option | Type | Default | Description |
|---|---|---|---|
refresh_interval_seconds |
integer | 3600 |
Refresh interval |
lookback_days |
integer | 15 |
Historical lookback in days |
The lookback also caps how old a baseline's earliest sample can
be, because each refresh reads only samples inside the window
and rewrites earliest_sample_at from them. A warmup tier whose
min_span_hours exceeds lookback_days multiplied by 24 can
therefore never become warm, and the alerter logs a warning on
each baseline cycle naming the tier. The default of 15 days
covers the daily tier's 336 hour span with a day to spare and
gives every weekday at least two occurrences in the window.
Correlation (correlation)
The correlation section configures alert correlation
across metrics.
| Option | Type | Default | Description |
|---|---|---|---|
window_seconds |
integer | 120 |
Correlation time window |
LLM Providers (llm)
The llm section configures LLM providers for tier 3
anomaly detection and embedding generation, and the
optional decision model for tier 3.
Each provider section names its own embedding model, and the alerter accepts any model name the chosen provider recognises rather than checking the name against a fixed list. The defaults in the tables below apply when the configuration leaves the model empty, and an OpenAI-protocol-compatible local model server can supply embeddings under whatever model name that server uses.
The configured embedding model must not produce vectors with more
than 4000 dimensions. The alerter stores anomaly embeddings as
halfvec(4000) and zero-pads each embedding to 4000 dimensions; 4000
is pgvector's HNSW index limit for the halfvec type. The alerter
does not check the width of a model at startup: when the model turns
out to be wider than 4000 dimensions, the alerter never truncates the
vector, but instead logs an error naming the vector width and the
limit for each anomaly candidate it processes, stores no embedding for
the candidate, and passes the candidate straight to tier 3. Watch the
alerter log after changing the embedding model, because the error
first appears when the first anomaly candidate reaches tier 2, not
when the alerter starts. Storing embeddings as halfvec requires
pgvector 0.7.0 or newer; upgrading an existing installation against
an older pgvector causes the embedding-column migration to fail.
The following table describes the provider selection options:
| Option | Type | Default | Description |
|---|---|---|---|
embedding_provider |
string | ollama |
Embedding provider |
reasoning_provider |
string | ollama |
Classification provider |
decision_provider |
string | None | Optional tier 3 decision provider: typesafe or openai; empty, none or disabled turns the decision model off |
max_tokens |
integer | 4096 |
Max output tokens per reasoning call |
Decision Model (decision_provider)
A decision model answers a yes or no question with the
probability of yes, rather than generating text. When
decision_provider names a provider, tier 3 asks the
decision model whether each candidate is a real issue that
requires attention, and applies
anomaly.tier3.decision_suppress_threshold to the
probability it returns. The decision model receives the
same context as the reasoning model, without the reasoning
model's instructions for a JSON response.
The following providers serve decision models:
typesafeuses the TypeSafe Jev models, configured in thellm.typesafesection; the default model isjev-latest.openaiuses the OpenAI Decisions API with the model inllm.openai.decision_model; the default model isgpt-6-luna.
The alerter sends the decision provider the tier 3 classification context for each candidate. The context holds fixed instructions about the task and about weighing past user feedback, together with:
- the metric name, value and z-score, the connection ID, the database name and the detection time.
- the baseline mean, standard deviation and period.
- the similarity, decision and metric name of up to five similar past anomalies.
- up to ten past acknowledgements of the same metric and connection, each with its note, its false positive flag, the username of the user who acknowledged it, the time of the acknowledgement, and the original alert's value, z-score and severity.
- the number, names and roles of cluster peer connections.
- the title, severity, server name and trigger time of each active or acknowledged alert on those peers.
The decision provider receives nothing beyond what the reasoning provider already receives. A decision provider such as TypeSafe may, however, be a third party that has not received the data before, so review the provider's data handling terms before enabling it.
The decision model applies to tier 3 classification only.
The re-evaluation of acknowledged anomaly alerts always uses
the reasoning provider, whatever decision_provider holds.
When the decision model fails for a candidate, tier 3 falls back to the reasoning model for that candidate. A failure includes an error, a timeout, a refusal to answer, a missing answer and a probability outside the range 0 to 1. When no reasoning provider is configured, tier 3 raises the candidate as an alert without classification, the same fail-safe that applies when the reasoning model fails. The candidate's tier 3 error records the decision model failure in either case.
A decision provider can serve tier 3 on its own, without a reasoning provider; the alerter then has no fallback and does not re-evaluate acknowledged alerts with an LLM. When the decision provider cannot be created at startup, for example because the TypeSafe API key is missing, the alerter logs a warning and runs tier 3 on the reasoning provider alone.
For a decision model verdict, the candidate's tier 3 result is a one-line summary rather than a model response, as in the following example:
Decision model typesafe/jev-latest: alert (P(real issue)=0.87, suppress threshold 0.50)
The tier3_source column of the anomaly_candidates table
records whether a decision or a reasoning model produced
the tier 3 outcome, and the tier3_model column records the
provider and model in the form provider/model. When every
model fails, the columns name the last model tried.
In the following example, the llm section selects the
TypeSafe decision model and keeps the Anthropic reasoning
model as the fallback:
llm:
reasoning_provider: anthropic
decision_provider: typesafe
anthropic:
api_key_file: /etc/ai-workbench/anthropic-api-key.txt
typesafe:
api_key_file: /etc/ai-workbench/typesafe-api-key.txt
decision_model: jev-latest
anomaly:
tier3:
decision_suppress_threshold: 0.5
The alerter creates the decision provider at startup only,
so changing decision_provider or the llm.typesafe
settings takes a restart. Turning the decision provider off
also takes a restart: after a reload that sets
decision_provider to none, tier 3 keeps using the
provider built at startup. A configuration reload does apply
a new decision_suppress_threshold; whilst the reloaded
configuration has no decision provider, the alerter skips
the threshold check and uses 0.5 in place of a value
outside the valid range.
Choosing a Decision Model
A spike in October 2026 compared the decision providers with the stored tier 3 decisions on 150 real candidates from the previous 30 days, sampled evenly between candidates stored as alerts and candidates stored as suppressed. The stored decisions came from the reasoning model, so the figures measure agreement with that model rather than accuracy against decisions that users labelled; no such labels existed. The reasoning model, Claude Haiku 4.5, agreed with the stored decisions on 91% of the candidates when run again. The spike results on issue #626 give the full data.
The following table compares the models at the default threshold of 0.5:
| Model | Agreement with stored decisions | Median latency | Cost per 1,000 decisions |
|---|---|---|---|
TypeSafe jev-latest |
89% | About 200 ms | About US$0.04 |
OpenAI gpt-6-luna decisions |
79% | About 250 ms | Not confirmed |
| Claude Haiku 4.5 reasoning | 91% | About 3 s | About US$1.93 |
The Jev model agreed with Claude Haiku 4.5 on 88% of the candidates, and never suppressed a candidate stored as an alert: every disagreement was an alert where the stored decision was to suppress. The OpenAI decision model suppressed some candidates that were stored as alerts. The costs use the published prices as of October 2026: Jev charges US$0.042 per million input tokens with no charge for output, and Claude Haiku 4.5 charges US$1 per million input tokens and US$5 per million output tokens. The spike could not confirm the OpenAI decision price from official pricing.
On this evidence, typesafe is the recommended decision
provider; openai is supported, but agreed less often and
was more willing to suppress real alerts. Raising
decision_suppress_threshold above 0.5 reduced agreement
in the spike. Because each provider calibrates its
probabilities differently, test any change to the threshold
against your own candidates for the provider in use.
Re-evaluation stays on the reasoning model because of the spike. In a synthetic test, the Jev model never cleared an alert whose acknowledgement note explained the anomaly, and no real acknowledgements were available to validate either decision model.
Output Token Budget (max_tokens)
The max_tokens option caps the number of output
tokens a reasoning model may generate for a single
call. The budget applies to the following reasoning
work:
- The tier 3 anomaly classification applies the budget to each candidate that the alerter examines.
- The re-evaluation of acknowledged alerts applies the budget to each alert that the alerter revisits.
The default value is 4096 tokens; a value less than
or equal to zero falls back to the default. Earlier
releases capped both calls at 500 tokens and offered
no way to change the limit.
A reasoning model counts its internal thinking tokens
against this same budget. A budget that suits a
conventional model can therefore leave no room for the
verdict itself. The model then returns a response that
contains no text, which the alerter treats as a tier 3
failure. A failed tier 3 classification fails safe and
raises the alert; the alerter records the cause in the
candidate's tier 3 error field. Raise max_tokens
when these failures appear; alternatively, select a
model that emits less reasoning.
In the following example, the llm section raises the
budget for a reasoning model that a local llama.cpp
server hosts:
llm:
reasoning_provider: openai
max_tokens: 8192
openai:
base_url: http://localhost:8080/v1
reasoning_model: qwen3-8b
Ollama Configuration
The llm.ollama section configures the local Ollama
provider.
| Option | Type | Default | Description |
|---|---|---|---|
base_url |
string | http://localhost:11434 |
Ollama server URL |
embedding_model |
string | nomic-embed-text |
Embedding model |
reasoning_model |
string | qwen2.5:7b-instruct |
Reasoning model |
OpenAI Configuration
The llm.openai section configures the OpenAI
provider.
| Option | Type | Default | Description |
|---|---|---|---|
api_key_file |
string | None | Path to API key file |
base_url |
string | https://api.openai.com/v1 |
OpenAI base URL |
embedding_model |
string | text-embedding-3-small |
Embedding model |
reasoning_model |
string | gpt-6-luna |
Reasoning model |
decision_model |
string | gpt-6-luna |
Decisions API model when decision_provider is openai |
The decision model shares the api_key_file and
base_url options with the OpenAI embedding and reasoning
models. A base_url that points at a local inference server
therefore also receives the decision requests, and the
server must implement the OpenAI Decisions API for them to
succeed.
The openai provider works with any server that
implements the OpenAI-compatible API. Set base_url
to point at a local inference server and set
embedding_model to the model that server provides.
The API key is optional when using a custom base URL.
The following local inference servers are compatible:
- Docker Model Runner uses
http://localhost:12434/engines/llama.cpp/v1as the default endpoint. - llama.cpp uses
http://localhost:8080/v1as the default endpoint. - LM Studio uses
http://localhost:1234/v1as the default endpoint. - EXO uses
http://localhost:52415/v1as the default endpoint.
In the following example, the llm.openai section
configures a local llama.cpp server:
llm:
reasoning_provider: openai
openai:
base_url: http://localhost:8080/v1
reasoning_model: my-local-model
A local server that hosts a reasoning model usually needs a larger output budget than the default. For details, see Output Token Budget.
Anthropic Configuration
The llm.anthropic section configures the Anthropic
provider.
| Option | Type | Default | Description |
|---|---|---|---|
api_key_file |
string | None | Path to API key file |
base_url |
string | https://api.anthropic.com/v1 |
Anthropic base URL |
reasoning_model |
string | claude-haiku-4-5 |
Reasoning model |
Gemini Configuration
The llm.gemini section configures the Google Gemini
provider. Model availability varies by Gemini API key
tier; run ListModels to verify which models a given
key can access.
| Option | Type | Default | Description |
|---|---|---|---|
api_key_file |
string | None | Path to API key file |
base_url |
string | https://generativelanguage.googleapis.com |
Gemini base URL |
reasoning_model |
string | gemini-3.8-flash |
Reasoning model |
embedding_model |
string | gemini-embedding-001 |
Embedding model |
When Gemini is the embedding provider, the
embedding_model must match the model that the KB
Builder used to produce the knowledgebase; the KB
Builder uses gemini-embedding-001.
Voyage Configuration
The llm.voyage section configures the Voyage
provider for embeddings.
| Option | Type | Default | Description |
|---|---|---|---|
api_key_file |
string | None | Path to API key file |
base_url |
string | https://api.voyageai.com/v1/embeddings |
Voyage base URL |
embedding_model |
string | voyage-3-lite |
Embedding model |
TypeSafe Configuration
The llm.typesafe section configures the TypeSafe
provider, which serves the Jev decision models. The alerter
uses TypeSafe only as a decision_provider; TypeSafe cannot
serve as an embedding or reasoning provider.
The following table describes the TypeSafe options:
| Option | Type | Default | Description |
|---|---|---|---|
api_key_file |
string | None | Path to API key file |
base_url |
string | https://api.typesafe.ai/v1 |
TypeSafe base URL |
decision_model |
string | jev-latest |
Decision model |
The TypeSafe provider always requires an API key, even
when base_url points elsewhere, such as at a proxy. The
alerter reads the key only from api_key_file, and does
not fall back to the TYPESAFE_API_KEY environment
variable.
Notifications (notifications)
The notifications section configures the
notification delivery system for sending alerts
through external channels.
| Option | Type | Default | Description |
|---|---|---|---|
enabled |
boolean | false |
Enable notifications |
secret_file |
string | None | Path to server secret |
process_interval_seconds |
integer | 30 |
Processing interval |
reminder_check_interval_minutes |
integer | 60 |
Reminder check interval |
max_retry_attempts |
integer | 3 |
Max retry attempts |
retry_backoff_minutes |
list | [5, 15, 60] |
Retry backoff schedule |
http_timeout_seconds |
integer | 30 |
HTTP request timeout |
http_max_idle_conns |
integer | 10 |
Max idle HTTP connections |
The secret_file option specifies a file containing
the same plain text secret used by the server
component. The alerter uses this secret to decrypt
notification channel credentials that the server
encrypted. The alerter and the server must reference
the same secret file.
In the following example, the notifications section
enables delivery with custom retry settings:
notifications:
enabled: true
secret_file: /etc/ai-workbench/ai-dba-server.secret
process_interval_seconds: 30
max_retry_attempts: 5
retry_backoff_minutes: [5, 15, 30]
http_timeout_seconds: 60
API Key Management
Store API keys for LLM providers in files with
restricted permissions. The alerter reads API keys
from the paths specified in the api_key_file
options of the openai, anthropic, gemini, voyage
and typesafe sections. When a configured key file cannot
be read, the alerter logs a warning and continues with the
next configured file, keeping each key that it reads
successfully. A provider that requires a key it did not
receive then fails to initialise.
In the following example, the commands create an API key file with secure permissions:
echo "sk-your-api-key-here" \
> /etc/ai-workbench/openai-api-key.txt
chmod 600 /etc/ai-workbench/openai-api-key.txt
The corresponding configuration references the key file:
llm:
embedding_provider: openai
reasoning_provider: openai
openai:
api_key_file: /etc/ai-workbench/openai-api-key.txt
embedding_model: text-embedding-3-small
reasoning_model: gpt-4o
Signal Handling
The alerter responds to Unix signals for operational control:
SIGINTandSIGTERMtrigger a graceful shutdown.SIGHUPreloads the configuration file without restarting the process.
A reload does not recreate the LLM providers, which the
alerter builds once at startup. Changing a provider, a
model, a base URL or an API key file, including
llm.decision_provider, therefore takes a restart; the
reloaded values of settings such as
anomaly.tier3.decision_suppress_threshold apply at once.
In the following example, the kill command reloads
the configuration:
kill -HUP $(pidof ai-dba-alerter)