Inspiration

Every freelancer knows this feeling: a client says "I need a website," and now you're stuck asking a dozen clarifying questions, guessing at a price, hearing "that's too much," and going back and forth for an hour you'll never get paid for. I've done this dance more times than I'd like to admit, and at some point I just thought ,why am I the one doing this part? Scoping and pricing and haggling is exhausting, and it's also not really what makes me a good developer. So I built PactAI to do that part for me. A client types out what they want in plain English, and from there the AI takes over ,figures out the scope, prices it fairly, negotiates the actual back-and-forth, and only comes back to me once there's a deal on the table worth reviewing.

What it does

The client just talks to it, no forms. PactAI asks the kind of questions I'd actually ask like what's the project, what platform, what features, what's the timeline, what's the budget ....and pieces together the real requirements from that conversation. Once it knows enough, it prices the work. I can set my pricing either as a straight hourly rate, or as a range per platform where the AI picks a number based on how complex the project actually is, instead of me manually estimating hours for every single feature. Then the actual negotiating happens. If the client lowballs, it counters ,gradually, not by jumping straight to my rock-bottom price. If they change the timeline or narrow the platform mid-conversation, it recalculates honestly instead of just making something up. And it never tells the client what my actual floor price is, which sounds obvious but took real work to get right. Once both sides land on a number, it writes up a proposal and drops it in front of me to accept or reject. The AI does the negotiating. I still make the final call.

How we built it

It's a FastAPI backend, but I didn't want one giant prompt trying to do everything — that felt like a recipe for inconsistent, hard-to-debug behavior. So instead there's a handful of smaller pieces that each do one job: A discovery agent asks conversational questions and pulls structured data out of whatever the client types back. A pricing/timeline setup turns that into an actual number, either from hours times rate, or from where the project lands on a complexity scale within a price range I set. A separate NLU step reads each message from the client and figures out what they actually mean , are they making an offer, changing the timeline, agreeing, asking a question ,using the LLM itself rather than trying to catch every possible phrasing with keywords. And the piece that actually talks to the client is fed only real numbers, nothing it's allowed to invent on its own. On top of all of that sits something I ended up calling the reply guard , it checks every message the AI is about to send against the numbers that were actually calculated, and if something doesn't line up, it swaps in a safer, correct response instead. More on why that exists below. The frontend's React and Vite and Tailwind, and I leaned into a "ledger and wax seal" look for it — felt right for something that's fundamentally about deals and trust.

Challenges we ran into

Honestly, the negotiation logic is where almost all my time went, and where I learned the most. My first attempt at figuring out whether a client had agreed to a price was just checking for words like "okay" or "deal." It broke constantly. Someone would type "okay but can we extend the timeline" and the system would happily lock in a deal that was never actually agreed to, because "okay" was in there. Typos made it worse , "okhay" instead of "okay" would sometimes match, sometimes not, depending on exactly how the regex was written. I eventually gave up trying to out-pattern-match human conversation and just had the LLM classify what was actually being said. That fixed more bugs in one shot than every regex patch combined. The scarier problem was the model occasionally just... making up a price. Not wildly, not obviously wrong , it would say something plausible-sounding that simply wasn't grounded in anything I'd actually calculated. Once it even stated my exact floor price out loud, which is precisely the number I never wanted the client to see. That's what pushed me to build the reply guard ,I stopped trusting the prompt alone to keep the model honest about numbers, and started checking its output in code before it ever reached the client. There was also a point where extending a timeline mid-negotiation would reset the whole conversation's price back to square one, which felt completely broken from the client's side — like the AI had just forgotten everything that had been discussed. I had to build a way to carry the negotiation's progress forward even when the underlying numbers changed, so it felt like one continuous conversation instead of starting over every time something shifted. And then, late in the process, I realized profiles and negotiations and proposals weren't actually separated by account , a second user would just see the first user's data. It worked fine with one account the whole time I was testing solo, which is exactly how that kind of bug hides until it doesn't.

Accomplishments that we're proud of

Getting a negotiation to actually hold together across many rounds, remembering what was already offered instead of contradicting itself, is the thing I'm happiest with. It's also the thing that was hardest to get right. I'm proud that the AI genuinely never leaks its own floor price, and that every number it says back to a client is something I can trace back to an actual calculation. And I'm proud there's a real end-to-end loop here ,a client can talk to it, negotiate with it, and I get a real proposal to accept or reject at the end, recycle bin and all.

What we learned

The biggest thing: telling an LLM to behave a certain way and actually guaranteeing it will are two different things, especially once real numbers are involved. The parts of this system I trust most aren't the cleverest prompts ,they're the plain, boring Python checks sitting quietly behind the LLM, catching it when it drifts. Letting the model handle understanding language, while keeping code in charge of the facts, ended up being the combination that actually worked.

What's next for PactAI

Real authentication, right now accounts are kept separate by trusting an email the frontend sends along, which is enough for a demo but not something I'd want handling real users. I'd also like the AI to handle multi-option scope tradeoffs more gracefully , right now if it offers "$X with this feature, or $Y without it," a vague "let's do that" from the client can be genuinely ambiguous, and I want that resolved cleanly instead of guessed at. And the LinkedIn/Upwork/Fiverr integrations are already sitting in the UI as "coming soon" I'd like them to actually be soon.

Built With

Share this project:

Updates