← All projects

Selected work · 03

Blog Studio

A web app that turns SEO keywords into source-grounded B2B blog posts in English and German, with a human review step at every stage.

Highlights

What shipped

  • Built an end-to-end content pipeline: keyword import, web research, source review, article briefs, drafts, images, and publishing to a CMS.
  • Grounded every draft in human-approved sources only, so the model writes from vetted material rather than from memory.
  • Added a critique-and-revise loop and a separate guardrails check that flags unhedged claims, invented customers, and facts not found in the sources.
  • Tracked the cost of every external API call and enforced a monthly budget cap before any paid action runs.
  • Shipped it as a Docker container to Azure, deployed by GitHub Actions on every push, with backend, component and end-to-end test suites.

The story

Behind the build

Overview

Writing good B2B blog posts takes research, and language models are a tempting shortcut that invents facts. Blog Studio keeps the speed of generation but puts a person in control of what the model is allowed to use. It takes a list of SEO keywords and produces publish-ready articles in English, German, or both, through a bilingual interface that defaults to German.

The design rests on one rule: the writing model only ever sees sources a person has approved. Every stage before writing exists to find good sources and let someone vet them. Every stage after it exists to check that the draft stayed within them.

Research and source control

Keywords are imported from Excel and grouped into topics by their target URL. For each topic, Exa’s neural search runs twice, once for English sources and once for German ones from the DACH region. Each pass is steered towards in-depth guides, whitepapers and case studies, and away from product pages and social media. The topic itself is the main query, and its keywords are passed as variants of the same search.

That choice was tested rather than assumed. An earlier version ran one search per keyword, and the two approaches were compared side by side. Per-keyword search made many more paid calls and returned largely the same high-authority pages for every variant. The combined search gave the same breadth without the duplicates.

Before the German pass, all queries are translated in a single LLM call, with rules that keep the phrasing natural, for example “Plattform” rather than an English compound. A second LLM call strips navigation, cookie notices and other boilerplate from each scraped page, and falls back to the original text if it fails. Sources are deduplicated across both passes and across topics, so one page can serve several topics while being stored only once. The raw search requests and responses are kept on disk for auditing. A reviewer then approves or rejects each source in a dedicated inbox.

Planning

An optional competitor analysis fetches the pages that currently rank for a topic and extracts their structure without an LLM: headings, FAQ sections, comparison tables, checklists and title patterns. One LLM call then summarises what every ranking page covers, what none of them do, and which questions searchers actually ask.

The planner turns this into article briefs. Five angles are always included: an overview, a comparison, a business case, common mistakes and a buyer’s guide. The rest are chosen from what the keywords point to, such as industry verticals, audiences or specific use cases, and the prompt requires that no two briefs overlap. Each brief has a title, a section outline and the questions the article must answer.

Writing and review

For each brief, the approved sources are ranked by how many of the brief’s key terms they contain, and the best matches are passed to the writer in full together with the brand context. The writer is told to ground every claim in those sources and never to invent statistics or benchmarks.

A separate critique model then reviews the draft against a fixed rubric: keyword placement, structure and scannability, depth of the core sections, useful structured elements such as comparison tables or checklists, redundancy, source discipline, reader intent and how naturally the brand is brought in. It returns a verdict, a score, the main problems and concrete fixes. How strictly it judges SEO is a setting. The writer revises from this critique for as many rounds as the settings allow. A person can also leave feedback on any draft, and a revision treats that feedback as the priority and the automated critique as secondary. Every version is stored, so nothing is overwritten.

Finally, a guardrails pass checks the draft against the same source pool on three criteria: vendor claims are hedged rather than stated as fact, no customer or case study is named that the sources do not mention, and factual claims trace back to an approved source. The interface shows each result with a one-line reason instead of decorative checkmarks.

Images and publishing

Header images are generated to match the brand’s real photography. The app picks the brand photo whose tags best fit the brief, has a vision model describe its look, and uses that description to steer the image prompt. Another LLM call reads the finished draft and plans where images belong and whether each should be a real brand photo or a generated one. Approved drafts are converted to HTML and sent to a Webflow CMS collection as unpublished items, with a configurable field mapping so the app fits any blog template.

Engineering

Every external call, whether to the language model, the search API, the image model or the SEO data provider, is priced and logged. A hard monthly budget blocks any paid action once it is reached, and a lower per-draft threshold warns before a draft gets expensive. The app is company-agnostic: everything it knows about a brand comes from one editable context file, uploaded knowledge documents (PDF, Word, Excel or a public URL) and a folder of logos and photos.

The backend is FastAPI with SQLModel and Alembic migrations, running on SQLite locally and Postgres in production, with files in Azure Blob Storage. The front end is React and TypeScript with React Query and a custom German/English translation layer. The whole app ships as one Docker image to Azure App Service, rebuilt by GitHub Actions on every push. Tests cover service logic, API integration, React components and full browser flows with Playwright. All external APIs are mocked by default, so the test suite never spends money, and smoke tests against the real services are opt-in.

Keep exploring

More projects

All projects

Cross-Lingual RAG Evaluation

A controlled study of how much retrieval-augmented generation degrades when questions and documents are in different languages, and which fix closes the gap.

  • Python
  • RAG Architectures
  • Embeddings
  • Vector Search

qado.ai IDP: RAG and AI Agents for Procurement

My interdisciplinary project at the AI startup qado.ai: a modular RAG pipeline over contract data, a benchmark for its retrieval quality, and prototype agents for strategic purchasing.

  • Python
  • RAG Architectures
  • Embeddings
  • Vector Search

Job Hunt

A local job-search dashboard that writes tailored CVs, cover letters and application emails, and refuses to claim anything the applicant's own CV does not support.

  • Python
  • FastAPI
  • React
  • TypeScript

Application Optimizer

A command-line pipeline that scores job fit, tailors a LaTeX CV towards a posting's keywords, and writes a matching cover letter, delivered as two finished PDFs.

  • Python
  • LLM Pipelines
  • Prompting
  • LaTeX

Haushalt — Personal Finance Tracker

A local budgeting app that replaced a set of spreadsheets, with linked money pots, automatic capital-gains tax handling and a one-click desktop launcher.

  • Python
  • FastAPI
  • SQL
  • Database Design

Legacy Data Migration Platform

A relational data platform consolidating historically grown legacy records into a clean target structure, with a Dash front end and REST API for exploration.

  • Python
  • SQL
  • Dash
  • REST APIs

Machine Learning Pipeline

A supervised and unsupervised modelling pipeline in Python, covering feature engineering, hyperparameter tuning, and cross-validated evaluation.

  • Python
  • Machine Learning
  • Feature Engineering
  • Model Evaluation

In design

Automated Trading System

The design for an automated swing-trading system on Interactive Brokers, built around separate processes, shared backtest and live code, and safety rules that must exist before any real order.

  • Python
  • FastAPI
  • Backtesting
  • Risk Management

Questions?

Want to talk through the details?