Grounding an agent on documents
Give an agent durable knowledge it can retrieve across conversations: put the (chunked) document text in an AutessaDB table with a vectorized column, attach a semantic-query tool, and let the agent search it — NOT a per-run file attachment.
The decision: you want an agent to ANSWER FROM specific documents (a policy manual, a product spec, an FAQ) and keep that knowledge across every conversation — not just for one run. This is grounding (a.k.a. RAG), and it is a PATTERN, not a single SDK call.
The anti-pattern to avoid first
autessa.agents.generateFileUploadLink LOOKS like grounding, but it is NOT. It mints a
presigned upload URL for a document that is attached to a SINGLE agent/tool execution and
expires in ~300 seconds. The agent cannot retrieve that document on a later run, and it is
not searchable. Use it only for a one-shot "read this file for THIS run" case. For durable,
retrievable knowledge, use the pattern below.
The pattern (build it yourself, it is all first-class resources)
- A table to hold the knowledge. Create an AutessaDB table
(
autessa.autessadb.tables.create) with at least a LONG_TEXT column for the chunk text and a VECTORIZED text column so the text is embedded for semantic search (plus SHORT_TEXT columns for a title / source / section so answers can cite where they came from). One ROW per chunk — see chunking below. - Insert the chunks. Write the document text into rows with
autessa.autessadb.query.insert— one row per chunk, not the whole document in a single cell (a single giant cell retrieves poorly). Getting text OUT of an uploaded file: never transcribe a document by hand. If your environment provides an upload-ingestion tool (the copilot'singest_upload_to_table), use it to copy the uploaded file into a FILE column, then extract its text with aGET_FILE_CONTENT_FROM_TABLEtool and chunk THAT text into the knowledge rows. - Index rows you will search IMMEDIATELY. Vectorization is ASYNCHRONOUS — right after
an insert the embedding may not exist yet, so a semantic query run in the same script can
come back empty on a perfectly correct build. When you need to search rows you just
inserted, call
autessa.autessadb.query.indexRecordon them first. VERIFY ROW-COUNT FIRST (a plainautessa.autessadb.query.queryorgetDatathat the rows landed), THEN semantic-search — never conclude "grounding failed" from an empty semantic result on a just-inserted row. - A semantic-query TOOL over the table. Create an
AUTESSADB_SEMANTIC_QUERYtool (discover its config with the tool-type discovery funnel) with the tableId baked in as a fixed value and the search query as an@@{query}argument. Publish it. - Attach the tool to a SIMPLE agent. Create a SIMPLE agent, add the semantic-query tool to its tools, and instruct it to search the tool before answering. Now every conversation the agent has can retrieve from the documents.
Verify by EXECUTING, not just by building
Grounding is easy to get subtly wrong (empty embeddings, the tool pointed at the wrong column, the agent not actually searching), and none of that shows up from reading the resources back. So PROVE it by running it:
- Execute the semantic-query tool with a query whose answer lives only in the documents
and confirm it returns the relevant chunk(s) — remembering the async-index caveat above
(verify the row count first,
indexRecordjust-inserted rows, THEN search). agents.executethe SIMPLE agent with a test question answerable ONLY from the grounded documents (not from general knowledge), and check the transcript shows it searched the tool and answered from the retrieved passage — ideally citing the title/source column. If it answers without searching, or can't find a fact that is in the docs, the grounding is not wired right; fix it before calling the step done. The read-back (row counts, the tool's config, the agent's tool list) is the second half — it confirms what exists, but the execution above is what proves it WORKS.
Chunking long documents
Do NOT put a long document in one cell. Split it into reasonably-sized passages (e.g. by
section or a few paragraphs each), one passage PER ROW, each with its own vectorized text so
retrieval returns the RELEVANT passage rather than the whole document. Keep a title/source/
section column per row so the agent can cite the origin of what it retrieved. Add more
documents later the same way: insert new chunk rows (and indexRecord them if you will
search right away) — the agent picks them up automatically, no re-wiring.
Scope: basic tables + a SIMPLE agent
You build grounding with a BASIC AutessaDB table + a vectorized column + a semantic-query tool + a SIMPLE agent — that whole pattern is scriptable and is the tool for this job. Other table and agent types are outside your scope; do not propose or route the user to them. The pattern above is complete on its own.
See also
- data-placement — why the knowledge lives in an AutessaDB table.
- agents-tools-managers — creating the SIMPLE agent and attaching the tool.
- syncing-connector-into-table — the parse-wrap pattern if you classify/enrich chunks.