Build agents
Knowledge and RAG
Tracon knowledge is retrieval, not hidden prompt injection. Operators ingest
documents into a named collection. An agent gets a code-defined
search_knowledge tool for one collection and asks for relevant chunks when needed.
Mental model: administration and retrieval are separate
Section titled “Mental model: administration and retrieval are separate”flowchart LR
accTitle: Knowledge ingestion and retrieval
accDescr: Documents become embeddings in PostgreSQL with pgvector, while an agent independently calls search_knowledge to retrieve relevant chunks.
D["Document text or chunks"] --> E["Embedding generator"]
E --> V["PostgreSQL and pgvector"]
Q["Diagnostic search HTTP API"] --> V
A["Agent with vector search enabled"] --> T["search_knowledge tool"]
T --> V
V --> H["Nearest chunks<br/>smaller distance is closer"]
The HTTP API owns ingestion, listing, deletion, and diagnostic search. The agent does not manage the knowledge base. It receives only the search tool.
Register the two required dependencies
Section titled “Register the two required dependencies”Knowledge becomes functional only when both dependencies exist:
- An
IVectorSearchStore. The built-in implementation comes fromUsePostgreSql(). - An
IEmbeddingGenerator<string, Embedding<float>>. The host chooses and registers it.
The sample below uses the OpenAI embedding adapter already used by the repository sample host:
using Tracon;using Microsoft.Extensions.AI;using OpenAI;
var openAiKey = builder.Configuration["Tracon:Providers:OpenAI:ApiKey"] ?? throw new InvalidOperationException("The OpenAI API key is missing.");
var tracon = builder.AddTracon() .UsePostgreSql(builder.Configuration.GetSection( TraconPostgreSqlOptions.SectionName)) .UseOpenAI(builder.Configuration.GetSection( OpenAIProviderOptions.SectionName));
builder.Services.Configure<TraconKnowledgeOptions>(options =>{ options.Dimensions = 1_536; options.ChunkSize = 1_000; options.ChunkOverlap = 100; options.MaxResults = 5; options.EmbeddingProvider = "openai";});
builder.Services.AddSingleton<IEmbeddingGenerator<string, Embedding<float>>>( new OpenAIClient(openAiKey) .GetEmbeddingClient("text-embedding-3-small") .AsIEmbeddingGenerator());A third setting is required: TraconPostgreSqlOptions.EnableKnowledge is false
by default (a managed PostgreSQL instance without permission to install extensions
should never see pgvector unless it asked for it). Turn it on wherever
PostgreSql is configured:
{ "Tracon": { "PostgreSql": { "EnableKnowledge": true } }}With it off, IVectorSearchStore never resolves and an agent definition that sets
EnableVectorSearch fails compilation with a clear error — see
Persistence.
Name the provider the generator calls in EmbeddingProvider, as a tenant egress
policy names it (openai, azure-openai, …). Every embedding call — an upload, a
search, and the agent’s search_knowledge query — checks the tenant’s egress policy
against this name before the text leaves the process, and refuses a tenant that has
its own credential for this provider, because the generator holds the setup key.
Without it, the name the generator reports is used, and that name does not tell
providers apart: the OpenAI and the Azure OpenAI generators both report openai. When
no name is known, a tenant with an egress policy is refused (403 on the endpoints; for
the agent’s tool, the model reads the refusal as the result of the call); a tenant without
one is not affected. Every setting can also come from the
Tracon:Knowledge section of configuration.
The embedding model in this example produces 1,536 dimensions. If you choose another
model, set Dimensions to its actual output size before the knowledge migration
set first runs.
Turning EnableKnowledge on applies one additional migration set: it runs
CREATE EXTENSION IF NOT EXISTS vector, creates the document_embeddings table, and
adds an HNSW cosine-distance index. The database role that applies migrations must be
allowed to create the vector extension, or an operator must install it first —
while the option stays off, none of this runs and no permission is needed.
Give an agent access to one collection
Section titled “Give an agent access to one collection”tracon.AddAgent(new AgentDefinition{ Name = "support", Instructions = "Use knowledge search before answering policy questions. Cite the source id.", Model = new ModelBinding { Provider = OpenAIProviderNames.ChatCompletions, Model = "your-current-model-name", }, Memory = new MemorySettings { EnableVectorSearch = true, VectorCollection = "support-policies", },});The compiler adds search_knowledge automatically. Do not add it to ToolNames.
When VectorCollection is empty, the agent name is used. The tool returns at most
TraconKnowledgeOptions.MaxResults chunks.
The tool runs through the same chain as a tool you register in code, before the query is embedded:
- Your
IToolAuthorizationHandleris asked first, with the tool namesearch_knowledgeand the effectExternal, because the text of the query leaves the process for the embedding provider. A handler that deniesExternalcalls also closes the knowledge base to the agent, and the model reads your reason as the result. The tool is not in the tool registry, soGET /api/toolsdoes not list it: write its name in your policy as fixed text. - Your
IToolArgumentsValidator, the tool timeout (Tracon:Tools:DefaultTimeout) and the output limit apply. A run whose budget is spent does not start the search, so it does not pay for the embedding. - The call line of
GET /api/runs/{id}/toolscarriesusage: the unittokens, the input token count of the query as the embedding provider reported it, and nocost. An embedding has no price in Tracon yet, so the amount never reaches a budget, a quota or the webhook total. The run’snonTokenCostlists the call underunsummedItemCount. A provider that reports no token count leaves the line out.
The console does not currently provide a knowledge-management screen or vector-memory fields in the agent editor. Use the knowledge HTTP API for ingestion and calibration, and use code or the agent management API to enable vector search.
This feature is separate from session history, file memory, and conversation memory (what one user told an agent in earlier sessions). Knowledge is what you loaded; conversation memory is what the user said. See Context and memory.
Ingest raw text
Section titled “Ingest raw text”The server accepts either text or chunks, never both and never neither. Raw text
is split and embedded on the server:
curl -sS -X POST \ http://localhost:5081/tracon/api/knowledge/support-policies/documents \ -H "Authorization: Bearer $TRACON_TOKEN" \ -H 'Content-Type: application/json' \ -d '{ "sourceId": "refund-policy-v3", "text": "Refunds are available within 30 days when the order is unused..." }'The response contains sourceId and chunkCount. Uploading the same sourceId
again replaces all old chunks in one PostgreSQL transaction.
For a pre-chunked pipeline, send chunks:
{ "sourceId": "refund-policy-v3", "chunks": [ { "index": 0, "content": "Refunds are available within 30 days.", "metadata": { "section": "eligibility" } } ]}When a chunk has no embedding, the server embeds its content. When an embedding
is supplied, Tracon writes it as-is after checking its length against the store
dimension.
Calibrate retrieval before blaming the prompt
Section titled “Calibrate retrieval before blaming the prompt”The diagnostic search endpoint performs the same embedding and vector lookup used by the agent tool:
curl -sS -X POST \ http://localhost:5081/tracon/api/knowledge/support-policies/search \ -H "Authorization: Bearer $TRACON_TOKEN" \ -H 'Content-Type: application/json' \ -d '{"query":"Can an unused order be returned after two weeks?","top":3}'Each hit contains sourceId, chunkIndex, content, distance, and optional
metadata. Results are ordered by ascending cosine distance. Smaller is closer.
If the right chunk is absent here, the agent never received it. Fix ingestion, chunking, the embedding model, or collection routing before changing the prompt. Every diagnostic search and every tool query makes an embedding call, so it has the latency and cost of that provider call.
Manage sources
Section titled “Manage sources”# Source ids only. An unknown collection returns an empty array.curl -H "Authorization: Bearer $TRACON_TOKEN" \ http://localhost:5081/tracon/api/knowledge/support-policies/documents
# Deletes all chunks of the source. Repeating it still returns 204.curl -X DELETE -H "Authorization: Bearer $TRACON_TOKEN" \ http://localhost:5081/tracon/api/knowledge/support-policies/documents/refund-policy-v3Deletion is immediate. Restoring a source requires another upload and another round of embedding calls.
Defaults and limits
Section titled “Defaults and limits”| Setting or rule | Default or behavior |
|---|---|
Dimensions |
1536 |
ChunkSize |
1000 characters |
ChunkOverlap |
100 characters |
MaxResults |
5 |
EmbeddingProvider |
Not set: the generator’s reported name is used |
| Chunking bounds | ChunkSize > 0 and 0 <= ChunkOverlap < ChunkSize |
| Raw text chunking | Character-based, then every chunk is embedded |
| Collection name | Letters, digits, underscores, and hyphens only |
| Source replacement | Same sourceId replaces all chunks in that collection |
| Distance | Cosine distance; smaller is closer |
| Tenant boundary | Every write and search is scoped to the current tenant |
| Built-in vector store | PostgreSQL only |
The HTTP search request can set top. Use a positive value; this endpoint does not
apply the 1..200 clamp used by paged list endpoints. The agent tool always uses
MaxResults. The HTTP contract does not expose a distance threshold; use returned
distances to calibrate relevance in your own ingestion and evaluation process.
A SQL Server or SQLite host can provide a custom IVectorSearchStore. Without a
store and an embedding generator, every knowledge endpoint returns 501. An agent
with EnableVectorSearch = true fails compilation instead of receiving an empty tool.
Production checklist
Section titled “Production checklist”- Use the same embedding model and dimensions for document and query embeddings.
- Treat an embedding-model change as a data migration. Re-embed the full collection.
- Tune character chunk size and overlap against real documents and retrieval evals.
- Use stable, URL-safe source ids. Re-upload is replacement, not an appended version.
- Separate collections when access or retrieval domains differ. Tenant scoping is automatic, but collection design is yours.
- Keep raw source documents outside Tracon if you need document version history. The knowledge table stores chunks and embeddings, not an immutable source archive.
Troubleshooting
Section titled “Troubleshooting”Knowledge endpoints return 501. Register UsePostgreSql() with EnableKnowledge = true and an IEmbeddingGenerator<string, Embedding<float>>. Any one missing is not
enough.
PostgreSQL startup fails around vector. EnableKnowledge = true is set but the
server has no pgvector. Install the extension first, or turn EnableKnowledge off
until it is available — the core migration set never touches vector.
Upload or search returns 403. The tenant’s egress policy does not name the
embedding provider, the tenant has its own credential for it, or the provider is not
named at all. Set Tracon:Knowledge:EmbeddingProvider and add that name to the
tenant’s policy. The message names the tenant and the provider.
Upload says the embedding length is wrong. The generator output and the migrated column dimension differ. Do not change only the option. Migrate the schema and re-embed the collection.
The agent compiles without search_knowledge in ToolNames. This is expected.
EnableVectorSearch adds the code-defined tool during compilation.
Search returns irrelevant chunks. Run the diagnostic endpoint. Check collection, source content, chunk boundaries, embedding consistency, and distance distribution. Prompt changes cannot recover a chunk that retrieval did not return.
A collection name returns 400. Use only letters, digits, underscores, and
hyphens. Spaces, slashes, and other punctuation are rejected.
Deleting and re-uploading is expensive. Both replacement and restoration require fresh embeddings. Avoid unstable source ids that cause unnecessary full replacement.
In the reference
Section titled “In the reference”- Knowledge HTTP API
TraconKnowledgeOptionsAPIMemorySettingsAPIIVectorSearchStoreAPIUploadDocumentRequestAPISearchKnowledgeRequestAPIUsePostgreSqlAPI
Read next
Section titled “Read next”- Context and memory — control the context sent to a model and the memory retained.
- Persistence — choose a store for data that must survive process restarts.