Asking for an embedding
To search by meaning, your app needs each text's embedding.
ai.embed(text, as: V) asks the model that ai: { models: } calls
embedding for a text's embedding, a V. ai.embed_all(texts, as: V)
asks for the embeddings of a list of texts at once, in the same order.
The text is a value built on Text, or a Markdown, never one that may
be none. V is a vector type with dimensions:, as many as
embedding_dimensions: says the model makes. So a model that changes
length is caught before a column holds two kinds of vector. Here the
model runs on this machine:
application { connections: { local_model: { driver: Driver.openai_compatible url: config.model_url } } ai: { connection: local_model models: { default: config.drafting_model embedding: config.search_model } embedding_dimensions: 768 limits: { per_day: { tokens: 2000000 } } }}
/// A question's meaning, as the configured embedding model makes it.type QuestionMeaning = Vector { dimensions: 768}
Like ai.generate, it is an outside call only an action makes. The
action maps each of its three failures to a failure of the app's, of
kind unavailable:
use support/types { CustomerQuestion, QuestionMeaning }use support/failures { AssistantUnavailable }/// What a customer's question means, to search the help articles with.action meaning_of(asked: CustomerQuestion) fails AssistantUnavailable -> QuestionMeaning { return attempt ai.embed(asked, as: QuestionMeaning) { timed_out: AssistantUnavailable over_budget: AssistantUnavailable too_long: AssistantUnavailable }}
What the server is sent for "Does your sourdough contain sesame?":
POST http://localhost:8000/v1/embeddings{"input": ["Does your sourdough contain sesame?"], "model": "nomic-ai/nomic-embed-text-v1.5"}
-
Each vector is read as
V, throughjson.read's decoding. So a vector of another length, or one of zeros whereVcompares by cosine, never becomes aV. It means the model isn't the oneai:says, and the call ismisconfigured. The built-in driver never asks a model for fewer dimensions: it sendsmodelandinputalone. -
The failures:
timed_out: no answer withintimeout:, 5 seconds unless you write one. It is counted once in a route's deadline.too_long: a text longer than the model takes, or more than 100 texts in oneembed_all. Nothing is sent.over_budget: the call would pass a limit (Budgets).
ai.embed_allof no texts gives no vectors, without asking. -
In a test, the
aifake makes each embedding from the text's SHA-256 and the test's seed. The same text always gives the same vector of unit length, and two texts give vectors far apart, so a search for a text finds what was stored for the same text.fakes: { ai.embed: [...] }scripts the failures ofai.embedandai.embed_allcalls, in the order they are made:
use support/types { QuestionMeaning }use support/failures { AssistantUnavailable }test "the same question always means the same" { var asked: QuestionMeaning = attempt meaning_of("Does your sourdough contain sesame?") var asked_twice: QuestionMeaning = attempt meaning_of("Does your sourdough contain sesame?") var other: QuestionMeaning = attempt meaning_of("Do you deliver to Jersey?") assert asked == asked_twice assert ai.distance(asked, other) > 0.5}test "a question the model can't embed in time goes to a person" { fakes: { ai.embed: [timed_out] } assert meaning_of("Sesame?") fails AssistantUnavailable}
Markdown for agents: /docs/asking-for-an-embedding.md.Built from Jadpo main at a707efb.