Skip to main content

Embeddings

An embedding is a list of numbers that stands for a piece of text. Two texts that mean similar things get similar numbers, which is what makes "find me documents about X" work when the documents never use the word X.

This page covers generating them. Storing and searching them is VectorDB.

The model

Model idOutputTask
qwen-embedding4096 numbers per inputDense text embedding

4096 is the dimension, and it is not a setting — it is a property of the model. An index built for a different width will reject these vectors, so the number has to match in both places. Read it from the platform rather than typing it:

platformctl platform limits -o json
Do not hardcode the dimension

The console once carried its own copy of this number, it was wrong, and every index created from that page was unusable. If you need the width in code, read EMBED_MODEL and ask the platform; do not paste 4096 into a config file.

Generate one

platformctl inference embed "a sailboat on the water"

Generate many at once

input takes an array. One request with 64 chunks is dramatically cheaper in round trips than 64 requests, and the results come back in the order you sent them:

import os
from openai import OpenAI

client = OpenAI(
base_url=os.environ["EMBED_BASE_URL"], # injected; do not write a URL
api_key=os.environ["MODEL_API_KEY"],
)

chunks = ["...", "...", "..."] # your document, already split
resp = client.embeddings.create(model=os.environ["EMBED_MODEL"], input=chunks)

vectors = [d.embedding for d in resp.data] # same order as `chunks`
Order is the contract, not index

data[i] corresponds to input[i]. Each item also carries index, which says the same thing — use whichever you like, but do not assume the server sorted anything.

Batch size is a trade: bigger batches mean fewer round trips and a larger request body. If a batch is refused for size, halve it and retry — the operation is stateless, so a retry costs nothing but time.

Chunking before you embed

An embedding of a whole 40-page document is an average of forty pages, and averages match nothing well. Split first.

A reasonable default that needs no tuning to be useful:

  • Split on structure — headings, then paragraphs — rather than every N characters. A chunk that ends mid-sentence embeds badly.
  • Aim for a few hundred words per chunk.
  • Overlap slightly (a sentence or two) so a fact that straddles a boundary is in both chunks.
  • Keep the source with the chunk — file name, heading, URL. You will want to cite it, and adding it later means re-embedding everything.

This is exactly the job an object-store-triggered function is for: a document lands in a bucket, the function splits it, embeds the chunks, and writes them to an index. Nothing has to be running when the file arrives.

Store them

The vector alone is not useful — you need to get back to the text. Write each vector to a VectorDB point with a payload:

points = [
{
"id": f"{doc_id}-{i}",
"vector": vec,
"payload": {"text": chunk, "source": doc_path, "heading": heading},
}
for i, (chunk, vec) in enumerate(zip(chunks, vectors))
]

A stable id derived from the document and chunk number means re-indexing the same document overwrites rather than duplicating — so a re-run is safe, and a document that changed does not leave its old chunks behind.

Use them from an agent

An agent's memory bank embeds automatically: everything committed to memory is indexed with qwen-embedding, and search_memory retrieves against it. You do not call this API for that.

You call it yourself when you are building your own corpus — product docs, tickets, a wiki — and exposing it to the agent as a tool or an MCP server.

Where to call it from

CallerBase URL to use
An agent, function or MCP server on the platform$EMBED_BASE_URL (injected)
Your laptop, or CI$CAI_API/v1/embeddings routes to Inference
An OpenAI SDK clientEither of the above as base_url

The public hostname does not route from inside a project. A workload that hardcodes one hangs rather than erroring, which is the worst way for this to fail — so read EMBED_BASE_URL.

Next steps