semantic#
The semantic module performs LLM-based semantic similarity search. Rather than matching literal text like grep or words, semantic compares the meaning of a search phrase against the meaning of each entry. This lets a query for “user could not log in” match an entry that reads “authentication failure for account jsmith” even though the two share no words.
The module works on vector embeddings. At query time, semantic sends your search phrase to a configured embedding model and receives a vector back. Each entry in the pipeline is expected to already carry its own embedding vector in an enumerated value. The module computes the cosine similarity between the search vector and each entry’s vector, attaches that similarity as a score, and drops any entry scoring below a threshold.
Note
The semantic module requires an embedding model to be configured on the webserver. See Embedding Model Configuration below.
Prerequisites#
Two things must be in place before semantic can be used:
An embedding model must be configured on the webserver. The
[AI]section ofgravwell.confprovides the endpoint, model name, and optional token used to embed the search phrase. See Embedding Model Configuration.Entries must already have embedding vectors attached. The
semanticmodule does not generate embeddings for entries; it only generates one for your search phrase. Entry embeddings are normally produced at ingest time by the vector preprocessor, which calls an embedding model for each entry and attaches the result as an intrinsic enumerated value namedembeddings.
Because embeddings is an intrinsic enumerated value created at ingest, it must be pulled into the pipeline with the intrinsic module before semantic can read it:
tag=notes intrinsic embeddings | semantic "disk is full" | table
Syntax#
semantic [options] <search phrase>
The search phrase is a single string. Phrases containing spaces must be quoted.
Supported Options#
-vector <name>: Read each entry’s embedding vector from the named enumerated value. Defaults toembeddings, which is the name used by the vector preprocessor.-score <name>: Set the name of the enumerated value that receives the similarity score. Defaults toscore.-t <n>: Set the score threshold as a whole percentage from 0 to 100. Entries whose similarity score is belown/100are dropped. Defaults to 75.-p: Permissive mode. Pass through entries that have no embedding vector, whose vector cannot be parsed, or which cannot be scored, instead of dropping them or failing the query. Passed-through entries do not receive a score enumerated value.
Produced Enumerated Values#
Enumerated Value |
Default Name |
Description |
|---|---|---|
Score |
|
Float in the range -1.0 to 1.0 giving the cosine similarity between the search phrase and the entry. Higher is more similar. |
Only entries that meet or exceed the threshold receive a score, so every entry leaving semantic has a score at or above -t – with the exception of entries passed through by -p, which have no score at all.
Entry Handling#
Without -p, the module handles problem entries as follows:
Condition |
Result |
|---|---|
Entry has no vector enumerated value |
Entry is dropped |
Score is below the threshold |
Entry is dropped |
Vector is not a valid JSON array of numbers |
The query fails with an error |
Vector cannot be compared to the search vector, e.g. because it has a different number of dimensions |
The query fails with an error |
With -p, all four cases pass the entry down the pipeline unscored instead. This makes -p useful when only some of the data in a query carries embeddings, or when embeddings were generated inconsistently.
Choosing a Threshold#
Cosine similarity is a measure of the angle between two vectors, not a probability. A score of 1.0 means the two vectors point in exactly the same direction and 0.0 means they are unrelated. In practice, the useful range depends heavily on the embedding model in use; some models cluster all real text between 0.6 and 0.9, while others spread scores much wider.
The most reliable way to pick a threshold for your data and model is to start permissive, sort by score, and look at where the results stop being relevant:
tag=notes intrinsic embeddings | semantic -t 0 "disk is full" | sort by score desc | table score DATA
Then set -t to that cutoff for production queries, dashboards, and alerts.
Performance Considerations#
Embedding vectors can be large. A model producing 1024 dimensions yields an enumerated value on the order of 10KB to 20KB per entry, and because semantic runs on the webserver, every candidate entry’s vector must be sent from the indexers to the webserver before it can be scored. This makes semantic considerably more expensive than a text search over the same data. Consider filtering as much data out as possible before invoking the module.
Embedding Model Configuration#
semantic embeds the search phrase by calling an OpenAI-compatible /v1/embeddings endpoint. The endpoint is configured in the [AI] section of gravwell.conf on the webserver:
[AI]
Embedding-URL="https://ai.example.com/v1/embeddings"
Embedding-Model="qwen3-embedding:8b"
Embedding-Token="sk-abc123"
Directive |
Required |
Description |
|---|---|---|
|
Yes |
Full URL of the OpenAI-compatible embeddings endpoint. Setting this directive is what enables the |
|
Yes |
Name of the embedding model to request. Required whenever |
|
No |
Bearer token sent in the |
See the configuration parameters documentation for the full [AI] section reference. These directives are independent of the Enable directive, which controls only Logbot AI chat features; semantic search can be used with Logbot disabled.
Attention
The model configured here must be the same model that generated the entries’ embeddings at ingest time. Embeddings from different models are not comparable, and vectors of differing dimensions cannot be scored at all – a mismatch will cause the query to fail (or, with -p, to pass every entry through unscored).
Requests to the embedding endpoint are subject to a 10 second timeout. A single request is made per query when the query starts.
Examples#
Find log entries about a concept#
Search notes for anything semantically similar to a phrase, using the default threshold of 75:
tag=notes intrinsic embeddings | semantic "the customer wants a refund" | table DATA
Rank results by relevance#
Loosen the threshold and sort the strongest matches to the top:
tag=tickets intrinsic embeddings | semantic -t 40 "cannot connect to the VPN" | sort by score desc | table score DATA
Use a custom vector and score name#
Read vectors from an enumerated value named vec and write the similarity to relevance:
tag=docs intrinsic vec | semantic -vector vec -score relevance -t 60 "quarterly revenue growth" | sort by relevance desc | table relevance DATA
Keep unembedded entries in the pipeline#
In permissive mode, entries with no embedding vector continue down the pipeline unscored rather than being dropped:
tag=notes intrinsic embeddings | semantic -p -t 80 "authentication failure" | count
Combine text filtering with semantic scoring#
Narrow the candidate set with a cheap word match on the indexers, then score only what survives:
tag=syslog words error | intrinsic embeddings | semantic -t 65 "the service failed to start" | sort by score desc | table score DATA
Chart semantic matches over time#
tag=tickets intrinsic embeddings | semantic -t 70 "billing problem" | count | chart count