Building Equator's AI Proposal Writer From the First Commit
Engineering firms win work by answering RFPs. I made the first commit on Equator's AI proposal writer, which drafts those answers from a firm's own past work, and was its largest contributor. This is the part I built.
From March 2023 to September 2026 I was a software developer at Equator Studios in Kitchener. Equator makes mapping and site analysis software for civil engineering firms. It also has an AI proposal writer, Equator LDI, and I made its first commit in September 2023.
The problem it solves
A civil engineering firm wins most of its work by answering RFPs (requests for proposals). An RFP lists what the client wants and how each answer will be scored. To write a response, someone has to find the right past projects, the right staff CVs and the right paragraphs from old proposals, then fit them to this RFP's criteria.
The RFP asks for
Stormwater design experience
A qualified project lead
Similar municipal clients
From the firm's past work
Project sheet: pond retrofit
CV: water resources lead
Old proposal: drainage approach
Project sheet: city road rebuild
Example criteria and files
An RFP's criteria, and the pieces of a firm's past work that answer each one
Equator LDI takes the RFP and the firm's own past work, and drafts a response tailored to the RFP's requirements.
My part
I created the repository and was the main engineer through its first year. Equator is a small company, and at most three developers worked on the app at the same time. I stayed the largest contributor, with 56% of the commits and 64% of the lines added. I authored and merged 70 of its production releases, and I onboarded two developers onto the codebase.
56%of the commits
64%of the lines added
70production releases I authored and merged
My share of Equator LDI's commits and lines, and the releases I authored and merged
When a firm uploads its files
Project sheet.pdfStaff CV.pdfOld proposal.pdf
OpenAI's APIturns text into vectors[0.12, 0.83, -0.40, …]
Qdranta vector database
When someone asks for a proposal section
RequestDescribe your team's stormwater experience on road projects, and who would lead it.
drainage section, road proposalproject sheet: city road workCV: water resources lead
Prompt tuned per task, default in code
DraftOur team has designed stormwater management for municipal road reconstruction. Our water resources lead would run the work.Our team has designed stormwater management for municipal road reconstruction. Our water resources lead would run the work.
Example files, request, searches and numbers
A simplified sketch of the parts this post describes
Searching a firm's own documents
Before the app can write anything, it has to know what the firm has done. When a firm uploads its files, the app makes them searchable by meaning. It turns the text into embeddings with OpenAI's API and stores them in Qdrant, a vector database. An embedding is a list of numbers that stands for what a piece of text means, so text with similar meaning gets similar numbers.
Stormwaterculvert sizingstormwater pond retrofitdrainage study for a subdivisionRoadspavement rehabilitationintersection redesignmunicipal road reconstructionPeopleCV: water resources engineerCV: transportation leadCV: structural engineerBridgesbridge deck replacementstructural inspection reportculvert to bridge upgrade
Example snippets and searches
Similar meanings sit close together, and a search finds the snippets nearest to it
An earlier version stored the embeddings in ChromaDB. I moved them to Qdrant.
Finding the right material
The simple form of retrieval-augmented generation (RAG) turns the request into one embedding and fetches the closest chunks of text. That works for a short question. It works badly for a proposal section, which asks for several things at once, in wording a firm's old documents rarely use.
So I built query expansion. A model reads the request and writes several focused searches, each aimed at one part of it. The app runs them in parallel, merges the results and drops the duplicates.
RequestDescribe your team's experience designing stormwater management for municipal road reconstruction, and name who would lead the work.
Stormwaterculvert sizingstormwater pond retrofitdrainage study for a subdivisionRoadspavement rehabilitationintersection redesignmunicipal road reconstructionPeopleCV: water resources engineerCV: transportation leadCV: structural engineerBridgesbridge deck replacementstructural inspection reportculvert to bridge upgradethe whole requeststormwater designroad reconstructionwater resources CV
Example request, searches and snippets
One search for the whole request, or one search for each part of it
Tuning it for each firm and each task
I built tooling so the prompt behind each AI task could be tuned without a code release, with a default in the code as the fallback. I also built an experimental admin tool for fine-tuning a model on a firm's past proposals.
Project experienceSummarize the firm's past projects that fit this section.Lead with municipal road work, newest first.
Staff biosDescribe each lead's role and experience.
Executive summarySummarize the response in plain language.Keep it to one short paragraph.
Example tasks and prompts
Each task runs with its tuned prompt while one is live, or the default from the code
Looking back
I enjoyed my time at Equator and my work on this product. The way I build software has changed a lot since that first commit, mostly because of AI coding agents, and it was good to look back at the problems I worked on in Equator LDI and the solutions I built for them.