Iain Weir

By ·

Building with AI, and building AI in

I've been using LLMs to write code for a while. Now I've put one inside a product: Tour de France-style coverage of my cycle commute, the prompt injection risk I had to decide whether to live with.

I’m (probably) never going to take part in the Tour de France.

But that doesn’t mean I can’t pretend!

To date, my experimentation with AI has been to build projects that use LLMs to write code, but recently I decided to try and build something for fun that uses LLMs as part of the product: automated TDF-style write-ups of my commutes to work.

Building with LLMs versus putting LLMs into a product

What’s the difference between building with LLMs and putting LLMs into a product?

Code is deterministic. The whole thing is effectively on rails: it takes an input and creates an output, and if the input is the same each time, the output is the same each time.

LLMs, on the other hand, are non-deterministic. The same input will not always produce the same output.

There are other challenges too. Despite the best efforts of guardrails, LLMs are corruptible. A bad actor (or “poo-poo guy”, as my three year old would say) could find a way to sneak some words into the prompt (prompt injection) that gets the LLM to do things it isn’t meant to do. So even if you make an agent that’s told to write short, fun articles, if someone else finds a way to tell it to include offensive material, and they do it in the right way, it’ll happen!

The Tour de Toon

So for my project I built a fun little pipeline that creates sensationalist, Tour de France-style coverage of my cycle commute into work. I call it the Tour de Toon.

It pulls my ride data down from Strava, refines the data in order to support the write-up, passes it to the LLM together with some simple instructions on writing style and some historic data from previous “stages”, and then a few seconds later it responds with a short article that gets published here.

It’s a simple system that, after some initial tweaking, brings a bit of colour to my daily grind in and out of town. It is also strangely motivating.

Plus, because it’s a nice closed system connecting only to Strava and my site, it’s safe from bad actors (and poo-poo guys) who might want to engineer a prompt injection to fill my blog posts with links to get-rich-quick schemes…

Or is it?

Not completely!

Although it’s laughable, it would actually be possible for someone to create a Strava segment on my route with a title that could poison the well. It would obviously be subject to whatever constraints Strava puts on those fields, such as character length and anti-Samy controls, but it’s still possible… and that’s all it takes.

The choice then comes down to managing the risk.

How could I protect the prompt?

There are lots of options:

  • I could strip out the segment names and any other free text a segment might have, such as a description, but that would limit what the LLM could say about it.
  • I could define a set of known segments within the application and map the unique segment IDs to them, but this would require a manual process to import new segments each time I encounter a new one: a human-in-the-loop approach where I accept each one.
  • Or, I could just accept the risk of someone injecting links to crypto scams into my articles through a well-crafted and highly targeted prompt injection…

Whatever the approach, and I’m sure there are lots more, at least it’s a conscious decision, and I’ve understood the risks before I’ve given an LLM the ability to take actions on my behalf, however trivial the example.

Pause before you hand over the keys

In the rush to adopt and explore the possibilities that AI offers, it really is worth pausing to think about what you’re giving an LLM access to, both in terms of data and tools.

We’re already seeing AI find increasingly elegant ways to identify vulnerabilities in deterministic code that have existed for decades. It doesn’t feel like the right time to be dropping your guard when it comes to data and privacy.