Douglas Qian
StreamlineAn AI-native government funding platform
2022 – 2025

I spent the first half of 2022 deep in the "how software can be used to solve climate" rabbit hole. I was in so deep, at one point I found myself contributing to ideas that only 200 people in the world at that time were thinking about. And in the depths of that, I met Helena.

We started Streamline to solve funding bottlenecks for critical climate solutions. It was a simple mission, and at the core of it was just a simple desire to the serve the people we considered our heros. After the IRA passed in 2022, it was clear that access to government funding would be critical for many climate organizations so that's where we started.

3 years later, we were supporting all types of customers from non-profits to city governments to climate tech companies to consultants like Deloitte. We helped our customers win $100+ million in grants, RFPs, tax credits, etc. and sold the core business & assets to one of our biggest customers and partners GrantEngine.

The bumpy road

But the focus of this is slightly different. I led product & engineering at Streamline as the CTO, and this is a reflection of our product journey and the weird twist and turns we took to get there.

We initially thought we would solve government funding in 1-2 years and move to solving a different part of the climate capital stack. But it was problem that kept on giving.

The more we worked on it, the more layers we uncovered. That's honestly the best kind of problem to work on.

LLMs were first good at writing

The IRA passed in late 2022 and the timing couldn't have been better. Around the same time, a startup called OpenAI released a research demo called ChatGPT.

After the initialy hype settled, what immediately followed in the 12-18 months was the first wave of AI-native product companies. I call this the "AI for writing" companies – Jasper, Copy.ai, Lex.page, etc.

Streamline became one of these companies as well because the biggest pain point we identified early on was the actual writing of the grant proposals.

The first insight from our initial user interviews was that writing the 3rd did not make writing the 4th one easier. Every time you do this, you start a new Google Doc that has no awareness of past proposal. So we asked ourselves "What would it look like for grant writing to be compounding?" What would that product look like?*

*Note: I don't have initial versions of everything I built so these are screenshots of the final product even though this story is being told somewhat chronologically.

The editor

The first product we built was an AI-powered editor for proposal writing. You start with a blank doc, and Streamline helps you generate an outline for the proposal based on who the funders are and what they are looking for this solicitation.

This is already hugely time saving. This step alone takes someone anywhere from hours to days. But at this point, there's nothing about your organization yet. It's just a template.

One of the core principles of the editor is flexiblity. An earlier version of the editor tracked responses as discrete blocks. The main benefit of this is that it's a well-defined workflow and lower learning curve. But it was very limiting.

We were inspired by Notion and the first release of Notion AI features. We wanted the AI to feel power yet seamless.

So on the 3rd rewrite of the document editor, we put all of our eggs in a floating menu that appears on selection with 4 key actions – Comment, Chat, Rewrite, and Answer.

With this approach we both get flexibilit (Chat & Rewrite) and a fast-track option (Answer). This was the "get shit done" mode, and most of our power users just spammed Answer. This felt like a very good sign.

And if you didn't like this, you could easily rewrite it using AI as well and iterate quickly.

Or if you want to slow down and do some planning or brainstorming with AI first, you can do that in our chat.

What defined our 3rd rewrite of the editor was less is more. We spent many hours debating on what was absolutely necessary, and after a ton of experimentation and iteration arrived at these 3 core AI UX primitives.

My experience building this was also my first time deeply internalizing the value of progressive disclosure. Making the product surface simpler made onboarding new users way easier. And a less intimidating initial experience allows people to spend more time with it and develop power users from just 3 fundamental building blocks.

A constantly evolving backend

Something that I struggled with at Streamline was keeping up with how quickly things were changing in AI. I naturally lean towards "exploit" more than "explore" so I deliberately surrounded myself with teammates who tended toward the opposite.

That being said, we also iterated on our backend A TON.

When we started, context windows were only 8k tokens. That's how RAG became the dominant industry pattern, and like many other companies our first first AI features were just extremely simple RAG apps.

And naturally as we built more product, we just tacked on more and more backend APIs to support them. Each looked slightly different and implemented by a different prior consensus of "this is how we should do things" by the zeitgeist.

By the time we were doing the 3rd rewrite, we realized that this mess we called the backend was hard to reason about and slowing us down.

More specifically, there were many common elements that needed to be shared but implementing them into each codepath and testing it was fragile and tedious.

A good example of this was search. We wanted robust source control in all parts of the editor – one that has access customers data, data we've gathered about the funder & the opportunity, and other public information that could be relevant (e.g. relevant studies, news about other similar programs, etc.).

It's just a little "+" but it was deceptively hard to make this work the same in all parts of the editor.

At the time we had just starting building our new endpoints as DAGs using LangGraph. One of the best parts of LangGraph was that it was highly composable. Graphs you defined can be invoked as subgraphs in other graphs.

This led to 2 revelations. First, commonly used elements like search can be built as subgraphs. One of the key benefits of this is that they can be separately QA'ed end-to-end. For search, we built a system using Typesense and Exa and developed our own evals on retrieval precision, recall, latency, etc. on customer documents.

Second, the entire backend can be backed by a single graph with different initial parameters. The different parameters influence how the control flow of the graph (ex: which sources were selected), but overall it reduced the amount of code we have to upgrade and maintain by 90%.

This led to more velocity but counterintuitively more quality as well because we were now concentrating engineering efforts on debugging a single cognitive flow.

The meta is still very much evolving still with the industry moving away from DAGs to agentic harnesses now (Claude Agents SDK, pi-mono), but I'd like to think that our approach was pretty solid back then (late 2024).

I don't know. It blew my mind at least.

Citations needed

In 2023, AI was so new that one of the biggest questions was "how can I trust this output?" And that was especially fair because hallucination was (and still is) one of the biggest fundamental limitations of LLMs.

And the 2 biggest UX patterns that improved trust with AI products are citations and reasoning traces. We implemented both in the editor.

But unlike most products, we saw citations as a first class citizens. That meant they couldn't be ephemeral - they needed to be persisted. And for WYSIWYG editors, that also meant clipdboard compatible so they can be copy and pasted.

In a 30-page grant proposal, scrolling to the bottom of the doc to see your citations was also frustrating. So we built inline highlighting on hover.

We had to figure out a lot of clever things to make this work. Similar to Perplexity and other citation-heavy products, we designed a custom syntax for parsing citations into inline glyphs during streaming. For us, this meant a Markdown + XML format that allowed us to leverage markdown libraries for rich text formatting but custom XML tags for parsing our own citations. Each citation comes with custom data that references database entries to fully resolve the original source.

For the custom highlighting, we built a custom Prosemirror mark. This was initially a custom Prosemirror node, but we realized a mark is actually what we want. Instead of it being an inline DOM node, it works more like a text decorator in the same way that bold or italic tags work.

Our goal was to build the best editor, and there's a long-tail of features you need to be at parity with Google Docs and Microsoft Word. Comments, text formatting, margins, images, etc.

Solving a recurring pain

The next problem we ran into: once someone applied for a grant, there was no need to use Streamline anymore. Most of our early customers were startups that only had the capacity to administer max 3-4 grants a year, which meant they frequently cancelled their monthly subscriptions after the first two months.

Even as we pushed into larger organizations and annual subscriptions, we realized that proposal writing is just inherently bursty. You go for long stretches were there's no activity, and then you have 8 weeks of work to cram into a 2 week sprint. And that's where we shined. One of our earliest customers applied to 2 Boston EV charging RFPs in 7 days and told us it just would not have been possible in a world without Streamline.

But fundamentally we needed to solve a recurring pain. And bonus if it could create more top-of-funnel for the main grant writing product.

So in October 2023 we decided to build Discover: an AI-native search engine for government funding.

At the time, our founding engineer Dhen did not have an approved work visa yet so I flew to London to work on this with him. We rented an Airbnb on the outskirts of London a week before Christmas to just crank on this together. Looking back, this is what startups are all about.

Our goals for launching this were simple:

  • 1. Simple. Current discovery channels (ex: email listservs) are extremely dense and text-heavy. We want an experience that delivered the best signal-to-noise ratio to separate the wheat from the chaff.
  • 2. Complete. Another big pain point was fragmentation. If you were a commpany based in New Jersey, you'd have to check 21 different federal & state agency sites to understand what you qualify for. We wanted to fix that.

So what did we do? We built an army of Typescript scrapers that scraped 96+ sources nightly for federal, state and local funding. Everything from the biggest sites like grants.gov to NYSERDA.

We tried a lot of AI web scraper products but they weren't good enough. I'm sure they will be one day, but even after MVP launch we tried once every 3 months just to temperature check. What ended up working best for us was contracting engineers from Africa on Upwork and refactoring this into a separate repo for contractors to submit PRs against.

Each scraper visited sites, grabbed the web content, and downloaded all the attachments. Each scraper ran its own docker container and uploaded the documents to S3, which then triggered serverless processing of each document with Unstructured. Solicitations can be 200+ pages long, so we stored each document in a Typesense collection so we can search over them later.

That was the infra. The fun stuff we built on top were the search & filters.

Specifically, the filters were interesting because we didn't just use scraped metadata. Solely relying on that had 2 problems: (1) the data was sparse and often not available and (2) the data was often outdated. What we did was we ran structured attribute extraction on the attachments as well to pull details like award size, due date, etc. into JSON fields in the search index. This increased the number of opportunities that filters could be meaningfully by 2x from just under 40% to about 80%.

As a user you can also see this in more detail on each page.

Each page shows you the information with the same UX patterns as the editor – inline citations, AI chat to learn more, etc.

Becoming a true platform

Moving upstream to support funding discovery opened a lot of doors for us. Like we had hoped, it worked hand in hand with the original product and created a natural sales funnel.

But it was still a bit clunky. If search was lead generation for government funding, then the final result is a set of leads. What were missing was lead qualification. Something that takes you from "this is interesting" to "this is a great fit" before you commit to writing a word.

The user journey (simplified)

See the problem is that these solicitations can be hundreds of pages long. And it's someone's job to carefully review it, understand the requirements, and determine whether or not it's worth pursuing.

As you can imagine, this is both a very error prone and time consuming process. When you get it wrong, you end up wasting more time attempting to apply for something that was never a great fit in the first place. So you break out the highlighter and go line by line – or you make an intern do it.

Processing tokens at this volume seemed like a perfect use case for AI. And thus the RFP Shredder was born – or Shredder for short.

The Shredder allowed you to upload solicitation files and get both (A) executive summaries for quick go/no-go decisions and (B) the ability to dive in really deep.

And as you can see, UX-wise the tradeoffs were very different from the 2 other products. This was very workflow-driven with a stepper and clear indication of what's next.

One of the most interesting parts of this was building the bounding box highlights for precise citations. At a high-level what we did was using PyMuPDF to parse PDFs into text spans with bounding boxes, We then ran post-processing on extracted attributes to generate citations that fed these text spans into parallelized worktrees that focused on very specific citation tasks.

It's one of those things that we had to build because companies like Reducto and Extend hadn't existed yet.

Each step of the shredder workflow focuses on a different reason why one might say "no" to disqualify an opportunity. But we display in a specific order.

The go/no-go waterfall

The theory was that most organizations answered these questions in a waterfall fashion. They always ask about due date and award size first because it's easy to say no based on that. It's a question of granularity as well.

It doesn't make sense to break down all the requirements in the final deliverable before you understand the eligibility restrictions. Because if you weren't eligible, that would have been a complete a waste of time.

Some of it is also falls on the spectrum of "what can you do with just the solicitation information" vs. "what do we also need your org data for". The latter usually comes later.

And this created a natural on-ramp into the editor as well. For us to perform an eligiblity check, it isn't enough to just know about your org. We need to know what you are proposing as this current project as well. So we have you fill out an eligibility questionnaire.

And while that's happening, we're getting a "head start" to your application (should you choose to write it) because all of that context exists in the same workspace and can eventually be carried over.

All of this might seem obvious, but trust me it wasn't.

What I'm proud of

We were always building towards a fundamentally different workflow that was reimagined with AI, but it wasn't clear until a few years in exactly what pieces we needed and and when.

All we knew is that the old way was definitely not going to work if we cared about getting the dollars into the hands of the people who can do something meaningful with it.

The old way without Streamline

We faced very different product & sales challenges getting each product off the ground, but they all served the same overarching goal. To make government funding a compounding exercise. To make writing every proposal easier than the last.

The new way with Streamline

I'm very proud of the work we did at Streamline. We existed at a moment where a lot of challenges in an AI-native products were novel and had no right answers. We were never a household name, but I'd like to think that we contributed in our own ways by visiting the Langchain team, giving feedback, and sharing very publicly about what we were building.

And none of it would have been possible without the team that we had. Everyone just genuinely cared so much about the product. About making it excellent – down to every single detail. It's so important to work with people who care. Very grateful for that.

What we shouldn't have built

Despite all of this, I have to be brutally honest with myself. There were a lot of things we built that we shouldn't have.

That's not easy for me to admit because my identity as a CTO was very much being the guy who could build it all. If you wanted something, I can probably rip it in a day.

But as the product team grew, I had reframe my role. From "the guy who can build any feature" to "the guy who can coach anyone into a great engineer." Internalizing this transition took some time, but it was the most profound transformations of my first company building experience.

It doesn't feel complete to talk about this work without sharing what I would have done differently.

  • Focus relentlessly on the value drivers -> We spent a lot of time chasing what was next, and often this came at the cost of upgrading and improving parts of the product that were core value drivers. If I were to go back, I would have built less new product and focused a lot more relentlessly on things that customers cared about.
  • Build what you can't buy -> We spent a lot of time tinkering with open source versions of things we depended and tried self-hosting TipTap, Typesense, etc. And every time it was hard, we'd think it was a skill issue and work even harder. But each time, we'd figure it out and still end up buying the cloud-hosted service a month in. And that was often the right decision - it didn't make our beer taste better. Even in the vibe coding era of free software, I think a lot of this still holds.
  • Invest in dev infra -> I was always skeptical in investing time in better dev infra. Why spend time on that instead of building more product? But I could not have been wrong. Agility requires safety, and almost every dev infra project we took on had an immediate payback.

Inspiration

Like all products, our work was heavily inspired by other products. Products we analyzed, talked about, and were inspired by:

  • Editor: Notion, Lex, Jenni
  • Discover: Zillow, Airbnb
  • Shredder: ChatPDF, ExplainPaper