Proposals that sound like you, with retrieval
How BidCraft grounds every Upwork proposal in the freelancer's own past work, using Mistral embeddings, pgvector in Supabase and a streaming FastAPI endpoint.
Generic AI proposals lose. Clients read dozens of them, and the ones that read like a template get skipped. A good bid proves you can do the job by pointing to work you have already done. BidCraft, the proposal generator I built the backend for, does that automatically for every new job post.
Start with the evidence
Each freelancer adds reference projects. The backend embeds them with Mistral and stores the vectors in Supabase Postgres using pgvector. Keeping the vectors next to the rest of the data meant one database, one set of credentials and ordinary SQL for everything else.
When a job post comes in, the API embeds it and ranks the freelancer’s past work by cosine distance. The top matches become the evidence the proposal is built around.
Scope retrieval like it is a permission check
Similarity search is scoped to the user and to the selected profile. That is not an optimisation, it is correctness. A freelancer with a “web development” profile and a “data engineering” profile should never get one profile’s projects in the other’s bids, and nobody should ever see another user’s work. The filter belongs in the query itself, not in the code after it.
Assemble the prompt from parts the user controls
The retrieved projects are combined with stored memory (facts about the freelancer worth repeating) and the user’s own editable prompts. Making prompts editable was one of the best decisions on the project. Freelancers know their voice better than any default, and they can tune it without waiting on a developer.
Stream it
Bids are streamed token by token from a FastAPI endpoint. Total generation time is the same, but watching the proposal appear makes the wait feel far shorter, and the user can start reading straight away.
Screening questions are different. Those answers come back as structured output, so the frontend can place each answer next to its question without parsing free text.
Make revisions cheap
Nobody sends a first draft. Bids are versioned, users can ask for revisions, and the history reads like a chat. Keeping every version means a user can go back to the one they liked instead of trying to regenerate it.
Ship it small
The backend is packaged for AWS Lambda with Mangum, which suits bursty traffic: lots of activity when someone is applying for jobs, nothing for hours in between.
The takeaway
Most of the quality in a RAG product comes from what you retrieve and how you scope it, not from the model call at the end. Get the evidence right, let the user own the voice, and stream the result.
More detail is on the BidCraft project page.