My Portfolio Assistant Was Retrieving Code. I Measured It, Then Fixed It.
Two fixed question sets, one honest correction to my own numbers, and what I found when I finally looked at what my assistant was retrieving.
Why I measured
The assistant on this site answers questions about my work, and I had never measured whether it retrieved the right passages. I wrote a fixed set of 28 recruiter-style questions, each with a piece of text that must appear in the retrieved chunks, such as "Ask Kindred" for a question about Rare Days. It is a retrieval check, not an answer-quality check: it tells me whether the right evidence reached the model, not whether the model used it well.
The baseline
The right chunk was in the top 3 for 14 of 28 questions (50%) and in the top 10 for 19 (68%). Worse, 74% of everything retrieved looked like code. The trace panel I added to the chat showed passages like an import line for a logo image.
Two causes
The index held raw source files. My indexer embedded my site's data files as text, in 500-character slices, so most chunks were imports, brackets and file paths rather than sentences about my work.
I had misread the threshold. The function's match setting is a maximum cosine distance, not a minimum similarity. My 0.75 meant "similarity at least 0.25", which let weak matches through. A setting of 0.3 returned nothing at all, which is how I found out.
The fix
I rebuilt the index from prose: the profile text, the case studies, the blog and the project list. Each chunk starts with its own context, such as "Case study: Ask Kindred. Speed, uptime and cost.", so it makes sense when retrieved alone. The first pass reached 61% in the top 3 and 93% in the top 10, and the two misses taught me something. A generic "What Bhimesh owned" chunk was matching unrelated questions, because the search adds my name to every query, so I took the name out of generic labels. And I had packed 13 different requirements into a few large chunks, so I split them to one each.
The same 28 questions, before and after
Then I tightened the settings
With clean chunks I could ask how few the model needs. A maximum distance of 0.70 with 8 chunks gave the same results as 0.75 with 10, and sends 27% fewer chunks to the model. Tighter than that started losing answers.
Chunks sent to the model across the 28 questions
Then I checked on questions I had not tuned against
Tuning against the same 28 questions flatters the result, so I wrote 30 new ones afterwards, in different phrasing, including informal and misspelled ones, and ran them without changing anything. The right chunk was in the top 3 for 70% and in the top 8 for 90%. That is the number to trust, and it is lower than 93% and 100%.
The three misses were informative. Two were missing content: the index had my jobs but no ordered career timeline, so "what did he do before Rare Days?" had nothing to match, and the countries I have visited were not indexed at all. I added one chunk for each. The third, a question about how the assistant avoids making things up, is a plain retrieval miss that I have not fixed.
Right chunk retrieved, by question set
Refusing what is not about me
A fixed question set also told me how the assistant handles things that are not about me. Real questions never scored below 0.415 for their best match, and unrelated ones (weather, trivia, code help, requests to ignore its instructions) scored 0.39 at most, many at zero. That gap is narrow, so I set the cutoff at 0.34, well clear of real questions. Below it the assistant replies with a fixed message and does not call the model at all, which is cheaper and cannot answer from training data. It catches 12 of 16 unrelated questions. The other four are left to a stricter rule in the prompt.
What this does not show
Both question sets were written by me, so neither is independent. The check looks for a piece of text in the retrieved chunks, so it cannot tell me whether an answer was good. The index is also rebuilt by a script I run, so it can drift from the site until I run it again.
What I would tell another team
Look at what you actually retrieve. A chat that sounds fluent hides a bad index. The trace panel is what made the problem visible.
Check what a parameter means before you tune it. I had been reading a distance as a similarity.
Index prose, not source files. And give every chunk its own context.


