A federal solicitation is not a document you read. It is a document that reads you. Section L tells you how to propose. Section M tells you how you will be scored. Section C is the work. The attachments hold the wage determinations, the past-performance requirements, and the one clause that disqualifies you on page 94. Miss the connection between any two of those sections and you write a beautiful proposal that loses on a technicality.
This is why I do not use plain RAG over solicitations. Plain RAG answers “what does this document say about X.” The question a capture manager actually has is “given everything in here, is this worth bidding, and what would it take to win.” Those are not the same question, and the gap between them is exactly the gap between regular and agentic retrieval.
Why one-shot retrieval fails here
Drop a 200-page solicitation into a vector store, chunk it, embed it, and ask “are we eligible to bid?” The retriever returns the chunks most semantically similar to your question. It will confidently surface the eligibility language in Section L and miss the small-business subcontracting requirement in Section H that actually controls the answer.
The failure is structural, not a tuning problem. The answer to a real bid question lives in multiple sections that do not mention each other and do not share vocabulary. Eligibility depends on the set-aside in the notice, the NAICS size standard, the certification clauses, and sometimes a past-performance threshold three attachments deep. No single chunk contains the answer. No single similarity search finds all the pieces.
Semantic similarity finds chunks that sound like your question. Bid decisions depend on chunks that don’t sound like anything, scattered across sections that never reference each other.
The multi-hop shape
The right model is an agent that decomposes the question, retrieves against the structure, and reasons across hops. “Is this worth bidding” expands into a small tree:
is_this_worth_bidding(solicitation)
├─ eligibility?
│ ├─ set-aside type (notice)
│ ├─ NAICS size standard (notice + SBA table)
│ ├─ required certs (Section K/L clauses)
│ └─ registration current? (federal registration status)
├─ can_we_win?
│ ├─ evaluation criteria (Section M)
│ ├─ past-performance req (Section L + attachments)
│ └─ incumbent strength (award history)
└─ economics?
├─ period of performance (Section F)
└─ estimated value (notice / history)
Each leaf is its own retrieval, and several of them reach outside the document entirely. Eligibility is not answerable from the PDF alone; it needs the live registration status and the SBA size table. Incumbent strength comes from public award history, the same computation I lean on across the rest of the intelligence engine. This is why I call it agentic: the agent decides what to fetch next based on what the last hop returned, and it knows when an answer requires leaving the corpus.
Structure-aware chunking
The other half of the fix is refusing to chunk a solicitation like a blog post. Naive fixed-size chunking shreds the section boundaries that carry all the meaning. A 512-token window will happily split a clause across two chunks and orphan its qualifier.
Federal solicitations have a known skeleton: the Uniform Contract Format sections A through M, plus numbered attachments. I parse to that structure first, then chunk within sections, and I tag every chunk with its section. That tagging is what makes multi-hop retrieval possible, because the agent can scope a hop: “retrieve evaluation criteria” becomes a filtered search inside Section M, not a similarity gamble across 200 pages.
| Approach | Eligibility recall | Cited the right section |
|---|---|---|
| Flat chunk + one-shot | ~55% | rarely |
| Section-tagged + one-shot | ~70% | sometimes |
| Section-tagged + agentic multi-hop | ~90% | consistently |
Those numbers are illustrative of the shape I have seen, not a benchmark from one solicitation, but the ranking is robust. Structure beats flat. Multi-hop beats one-shot. The combination is what makes the answer trustworthy enough to spend a bid-and-proposal budget on.
The money makes it different
If this were customer-support RAG, a wrong answer means a follow-up ticket. Here a wrong “yes, bid this” answer can burn a $40K to $80K proposal effort on something you were never eligible to win. That changes the engineering posture in two ways.
First, eligibility is a gate, not a score. The agent does not return “82% confident you qualify.” It returns a pass or fail with the specific clause cited. If it cannot find a definitive answer, it says so and flags it for a human. A confident guess on eligibility is the most expensive output the system can produce.
Second, every conclusion carries its citations. When the system says a solicitation is worth pursuing, it shows the section that establishes eligibility, the evaluation criteria it weighed, and the incumbent record it pulled. A capture manager can audit the reasoning in two minutes. Retrieval without provenance is just a chatbot with opinions, and opinions do not survive a bid review.
What agentic actually bought me
The honest summary is that the agentic part is not magic. It is the willingness to make multiple retrieval calls, against a structured corpus, with the model deciding the next hop and knowing when to reach into the public award data instead of the document. It costs more tokens and a few more seconds per question. For a $50K bid decision, that tradeoff is not close.
The regular-RAG version of this tool demos fine and loses you money quietly. The agentic version is slower, more expensive per query, and it tells you the one thing the fast version cannot: not what the solicitation says, but whether you should touch it at all.