惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
T
The Blog of Author Tim Ferriss
F
Fortinet All Blogs
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
阮一峰的网络日志
阮一峰的网络日志
The GitHub Blog
The GitHub Blog
Y
Y Combinator Blog
MyScale Blog
MyScale Blog
雷峰网
雷峰网
博客园 - 叶小钗
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
GbyAI
GbyAI
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
博客园 - 三生石上(FineUI控件)
云风的 BLOG
云风的 BLOG
V
V2EX
宝玉的分享
宝玉的分享
酷 壳 – CoolShell
酷 壳 – CoolShell
N
Netflix TechBlog - Medium
Vercel News
Vercel News
美团技术团队
人人都是产品经理
人人都是产品经理
The Cloudflare Blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
I Stopped Fighting Prompts: Locking Down Markdown with Ji...
quarktimes · 2026-06-15 · via DEV Community

quarktimes

We faced a recurring issue in our content generation pipeline: the LLM frequently outputted malformed Markdown. Unclosed code blocks, broken list levels—you name it. Relying solely on Prompt engineering became a game of whack-a-mole that we couldn't win.

The core problem? Asking an LLM to generate Markdown is a probabilistic process. A Prompt is a "soft constraint." No matter how well you phrase it, a slight token fluctuation can break the syntax, causing frontend crashes.

The Shift: Data vs. Presentation

We realized we were violating the Single Responsibility Principle. We were asking the model to do two jobs:

  1. Understand the content and generate data.
  2. Format that data into valid Markdown syntax.

Models are great at semantics but terrible at strict formatting rules. So, we decoupled them.

Solution 1: Jinja2 for Deterministic Rendering

Instead of asking the LLM to write Markdown, we switched to JSON output and let Jinja2 handle the rendering.

Before (Probabilistic):

# LLM generates raw text - hope for the best
prompt = "Write an article about {topic} in Markdown format."
response = llm.generate(prompt) 

After (Deterministic):

# LLM outputs structured data only
prompt = "Output data about {topic} in JSON format."
json_data = llm.generate(prompt) 

# Jinja2 enforces the syntax
md_content = jinja_env.get_template('article.md').render(data=json_data)

This moved the formatting from a "maybe" to a "definitely." If the template is correct, the Markdown is correct.

Solution 2: The Format Sanitizer Pipeline

Just in case (and for legacy compatibility), we added a post-processing layer with regex validation. It acts as a safety net for unclosed code fences.

def sanitize_markdown(text):
    # Check if code blocks are properly closed
    if not re.search(r'```

[\s\S]*?

```', text):
        # Attempt to wrap raw code in fences
        text = re.sub(r'(^.*$)', r'```

\n\1\n

```', text)
    return text

final_markdown = sanitize_markdown(llm_output)

Bonus: Handling Heterogeneous Data Sources

While fixing the text generation, we also noticed a logic gap in our stock data queries. We treated A-shares, ETFs, and Hong Kong stocks identically. This caused failures because:

  • ETFs need .SH or .SZ suffixes.
  • HK stocks require a separate auth API.

We implemented a router at the query entry point:

def get_stock_data(code):
    # Route HK stocks to specific API
    if is_hk_stock(code):
        return hk_api.get_price(code)

    # Append suffix for ETFs if missing
    elif ".SH" not in code and ".SZ" not in code:
        code = f"{code}.SH" 

    return api.get_price(code)

The Results

By shifting from "Prompt Optimization" to "Engineering Hard Constraints":

  • We processed 50k requests in 2 weeks.
  • Format error rate dropped from 3% to 0%.
  • P99 latency stayed at a manageable 200ms.

Key Takeaway

If you are fighting with LLMs to output perfect HTML or Markdown, stop. Use the LLM for what it's good at—generating structured JSON data—and use a template engine like Jinja2 to enforce the view layer. It turns a probabilistic headache into a deterministic pipeline.