We run an AI assistant over a construction company's SharePoint: 865 sites, about 400,000 documents. For a year it answered from PDF and Word only. This month we added Excel and PowerPoint. The extraction took an afternoon. The citation format took the real thinking, and that is what this post is about.
The problem with "see Budget_2026.xlsx"
An answer from a document assistant is only worth something if the reader can check it. For PDF that is easy: cite the page, the reader opens page 37 and compares. For a workbook, a link to the file is not a citation. Twelve sheets, four thousand rows, and the reader is expected to find the one cell the model used. Nobody does that. They either trust the number blindly or ignore the assistant. Both are bad.
So the rule we set: every citation must let a person verify the answer in seconds. Page for PDF. Sheet and row range for Excel. Slide number for PowerPoint.
What a spreadsheet chunk looks like
We read workbooks with openpyxl in read-only mode and walk each sheet row by row. Rows are grouped into chunks, and each chunk carries where it came from:
chunk = {
"text": "...", # the rows, rendered as text
"source_name": "Budget_2026.xlsx",
"sheet": "Q3",
"row_from": 118,
"row_to": 142,
"doc_version": "4.0",
"modified": "2026-09-30T08:12:00Z",
}
Three details that mattered in practice:
-
Header rows travel with every chunk. A row like
45 | 1.2 | 2026-09-30means nothing without the column names. We repeat the sheet's header row at the top of each chunk, so the model seesrate | factor | valid_fromnext to the values. -
Rows, not cells. A cell reference like
Q3!C119is precise but useless for a human: the reader still has to find column C. A row range is enough to land on the right place, and the header tells the rest. - A ceiling per sheet. Some sheets are data dumps with 200,000 rows. We stop at 5,000 rows per sheet. Those sheets are rarely where a person's question lives, and indexing them makes every other answer slower.
For PowerPoint the slide number plays the role of the page: title, text frames, tables and notes are read slide by slide, and the citation names the slide.
The citation in the answer
The answer shows the source name, the sheet and the rows, and a link that opens the file in SharePoint. If content approval is enabled on the library, the approval status is shown too, because a draft workbook and an approved workbook can contain different numbers for the same thing.
This is the same contract we already had for PDF: document, version, exact location, and, through the signed-in user's own permissions, only documents that person may open.
Why bother
Because a fluent answer from the wrong place is worse than no answer. A reader who can check in seconds will check, and will catch the model when it picks the draft or the old version. A reader who cannot check will either stop trusting the tool or, worse, keep trusting it.
I wrote up the full set of checks we apply before an answer can be trusted, with the SharePoint and Graph details behind each one: Copilot, SharePoint and 400,000 construction documents: four checks before you trust an AI answer.
The assistant is Answergrove, built at SIA KSJ in Latvia. It runs alongside Microsoft 365 Copilot and does not need a licence per user.
Top comments (0)