
REXT
At Revnix, our content teams kept drowning in the same cycle: research keywords, draft outlines, write posts, optimize for SEO, publish. I led the backend development of REXT to automate the entire pipeline while keeping humans in control of the important decisions. It's a FastAPI backend that orchestrates multi-step LangGraph workflows — a SERP engine analyzes top-ranking pages, an SEO engine structures the outline, and a content engine generates the full article with E-E-A-T injections and readability scoring. The key design choice was LangGraph's checkpoint persistence: every workflow step is resumable, so editors can review and tweak outlines before the system generates the final post. It's a full SaaS backend too — multi-tenant workspaces, RBAC, subscription billing via LemonSqueezy, a knowledge base with vector search (FAISS + sentence-transformers), and direct WordPress publishing. The whole thing runs async on PostgreSQL and Redis, with Alembic migrations and structured logging via Sentry.
Technologies
Key Highlights
- Resumable LangGraph pipelines — editors review and edit at any checkpoint before final publish
- Full SaaS backend: multi-tenant workspaces, RBAC, billing, vector search, and WordPress publishing
Engineering Challenges
Pausing a Running AI Workflow to Wait for a Human
LangGraph workflows run as a continuous chain of steps — once started, they keep going. But REXT needed to stop mid-pipeline, show an editor the generated outline, wait for their approval or feedback, and then resume exactly where it left off. The solution was LangGraph's interrupt() mechanism, which serializes the entire workflow state to PostgreSQL at the pause point. When the editor responds, the workflow is resumed from that snapshot with their feedback injected directly into the next prompt. A rejected outline includes the editor's reason, which gets passed back into the LLM so the next generation is informed by what was wrong — not just regenerated blindly.
Bridging FastAPI's Async World with LangGraph's Sync World
FastAPI is fully async — every route handler runs in an async event loop. LangGraph nodes, however, run in a thread pool (sync). This created a real problem in the E-E-A-T injection step, which needs to fetch a persona record from the database inside a LangGraph node. Calling the async database session from a sync thread would either deadlock or crash with 'Task attached to a different loop'. The fix was two separate database engines: an asyncpg-powered engine for FastAPI routes, and a psycopg3-powered sync engine for LangGraph nodes. For cases where an async context had to call into sync code, loop.run_in_executor() was used to safely hand off the work to a thread without blocking the event loop.
Stopping the LLM from Making Things Up
LLMs are confident even when they're wrong — they'll invent keywords, fabricate statistics, and produce outlines that sound good but are disconnected from what actually ranks. REXT tackled this at multiple layers. Every LLM call uses structured output via a Pydantic model, so the response is validated against a strict schema before anything downstream uses it. Keywords and competitor data are sourced from real SERP results, not generated by the model. A prompt validation guard asserts that no template variables like {topic} remain un-interpolated before the prompt is ever sent — failing fast rather than sending a broken prompt and getting a confused response. And when an outline is rejected, the editor's specific reason is fed back into the regeneration prompt, so the model knows exactly what to fix.