Torrin Leonard
· 3 min read

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

  1. Stormwater design experience
  2. A qualified project lead
  3. Similar municipal clients

From the firm's past work

  1. Project sheet: pond retrofit
  2. CV: water resources lead
  3. Old proposal: drainage approach
  4. 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.

  1. 56%of the commits
  2. 64%of the lines added
  3. 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

  1. Project sheet.pdfStaff CV.pdfOld proposal.pdf
  2. OpenAI's APIturns text into vectors[0.12, 0.83, -0.40, …]
  3. Qdranta vector database

When someone asks for a proposal section

  1. RequestDescribe your team's stormwater experience on road projects, and who would lead it.
  2. stormwater designroad projectslead engineer CVQdrant
  3. drainage section, road proposalproject sheet: city road workCV: water resources lead
  4. Prompt tuned per task, default in code
  5. DraftOur 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.

  1. Project experienceSummarize the firm's past projects that fit this section.Lead with municipal road work, newest first.
  2. Staff biosDescribe each lead's role and experience.
  3. 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.

Sources

  • AI Proposal Writer, Equator Studios' product page.

  • Data and security, Equator Studios, on the app's use of OpenAI's API for file search.

  • Equator LDI, the app itself.

  • The commit, line and release counts are my own, counted in August 2026 while I worked there.