DEV Community

HYUN SOO LEE
HYUN SOO LEE

Posted on

How I Built an Automated Korean Saju Content Pipeline with Claude Vision and Python

The Problem: Manual Fortune-Content at Scale Is Broken

Korean Saju (四柱, Bazi) content is one of the highest-engagement niches on YouTube Korea right now. But the production pipeline is almost entirely manual: a practitioner reads a chart, writes a script by hand, records, edits, and publishes — then repeats for every subject and every channel format.

When I started building Dalbit Namu (달빛나무), the goal was to automate everything except the core interpretive logic. The content domain is Saju. The engineering problem is: how do you take a Manse Calendar screenshot, extract structured chart data reliably, generate a 10-minute script that passes editorial QA, and reformat it for five different channels — without hallucinating chart details or making claims the data does not support?

This post documents the full pipeline, the prompt architecture, the QA layer, and the lessons learned. I use the 2026 Jimin (BTS) Saju analysis as the live production example because it was the most complex test case: unknown birth time, high-profile subject, strict editorial guardrails.


Pipeline Overview

[Input Form] → [Vision Extraction] → [Structured JSON] → [Interpretation Engine]
→ [Script Generator] → [QA Checker] → [Channel Formatter] → [Publish]

Five stages, each with its own failure mode.


Stage 1 — Vision Extraction: Reading the Manse Calendar

The Manse Calendar (萬歲曆) image is the single source of truth. Everything downstream depends on reading it correctly.

![Manse Calendar extracted from 02_manse_calendar_complete.png — Day Pillar 丁丑, Month Pillar 丙戌, Year Pillar 乙亥, Hour Pillar 丙午, Current Grand Luck 壬午, 2026 Annual Luck 丙午]

I pass the calendar screenshot directly to Claude claude-sonnet-4 Vision with a structured extraction prompt. The prompt enforces strict field-by-field output:

Extract the following fields exactly as displayed in the image.
Do NOT infer, translate, or substitute characters.
Output JSON only.

Fields:

  • name, gender, solar_birthday, birth_hour
  • four_pillars: [year, month, day, hour] × {heavenly_stem, earthly_branch}
  • ten_gods: [year, month, day, hour] × {stem_god, branch_god}
  • twelve_phases: [year, month, day, hour] × phase_name
  • spirit_stars: each star with pillar location
  • grand_luck: {stem, branch, stem_god, branch_god}
  • annual_luck_2026: {stem, branch, stem_god, branch_god}

The critical guardrail here: the prompt explicitly says "do NOT substitute." In early iterations, the model would silently correct what it thought were OCR errors — changing 劫財 (Robber, 겁재) to 比肩 (Friend, 비견) because they look similar in small font. Adding "If you are uncertain about a character, output the character you see followed by [UNCERTAIN]" reduced silent substitution errors by roughly 80%.

Extracted output for Jimin's chart (2026-05-07 run):

{
"name": "Jimin",
"gender": "male",
"solar_birthday": "1995-10-13",
"birth_hour": "unknown",
"four_pillars": {
"year": { "stem": "乙", "branch": "亥" },
"month": { "stem": "丙", "branch": "戌" },
"day": { "stem": "丁", "branch": "丑" },
"hour": { "stem": "丙", "branch": "午" }
},
"ten_gods": {
"year": { "stem_god": "편인(偏印)", "branch_god": "정관(正官)" },
"month": { "stem_god": "겁재(劫財)", "branch_god": "상관(傷官)" },
"day": { "stem_god": "비견(比肩)", "branch_god": "식신(食神)" },
"hour": { "stem_god": "겁재(劫財)", "branch_god": "비견(比肩)" }
},
"grand_luck": {
"stem": "壬", "branch": "午",
"stem_god": "정관(正官)", "branch_god": "비견(比肩)"
},
"annual_luck_2026": {
"stem": "丙", "branch": "午",
"stem_god": "겁재(劫財)", "branch_god": "비견(比肩)"
}
}

Key structural reads from this chart:

  • Day Master (日柱): 丁火 — the candle flame. Steady, internalized, long-burning.
  • Month Branch 戌 carries 상관(傷官, Hurting Officer) — strong expressive and independent execution energy.
  • Chart is 신강(身强, strong self): Fire element at roughly 45%, Earth at 30%. Robber (劫財) appears twice in heavenly stems, indicating high self-drive and a tendency toward independent standards.
  • Spirit stars present: 천덕귀인(天德貴人, Heavenly Noble) and 월덕귀인(月德貴人, Monthly Noble) — unexpected support arrives through trusted relationships. Also 귀문관살(鬼門關殺) at Day Branch 丑 and Hour Branch 午 — heightened sensitivity and perceptual sharpness.
  • Grand Luck 壬午(임오): 壬 stem carries 정관(正官, Direct Officer) — structure and accountability energy. 午 branch carries 비견(比肩, Friend) — self-assertion amplified.
  • 2026 Annual Luck 丙午(병오): 丙 stem is 겁재(劫財, Robber) — competitive drive, output surge. 午 branch is 비견(比肩) — doubles the self-assertion. Hour Branch 午 and Annual Branch 午 are identical, creating a rhythm-lock on daily life patterns.

Stage 2 — Interpretation Engine: Prompt Architecture

Raw JSON alone does not produce usable content. The interpretation layer takes the structured data and applies classical Bazi logic to generate a scored analysis across four domains: career, wealth, relationships, health.

The prompt chain has three components:

2a. Structural Analysis Prompt
Feeds the JSON and asks for: body strength assessment (신강/신약), favorable elements (용신), element percentage breakdown, and interaction analysis (합·충·형, combinations and clashes).

2b. Annual Luck Overlay Prompt
Takes the structural analysis and overlays the 2026 annual luck (丙午) against the grand luck (壬午). Key interactions flagged automatically:

  • 午午 self-combination at Hour Branch and Annual Branch — daily rhythm amplification
  • 午戌 half-combination (반합) between Grand Luck branch and Month Branch — additional Fire accumulation
  • No 財星 (Wealth Star) in heavenly stems — wealth outcomes depend on management quality, not inflow volume

2c. Scoring Prompt
Generates domain scores (0–100) with explicit reasoning chains. Scores are not arbitrary — each is derived from a weighted formula based on: favorable element presence, clash/combination severity, spirit star influence, and grand luck compatibility.

For Jimin's 2026 chart: Career 72 / Wealth 58 / Relationships 65 / Health 63 / Overall 68.


Stage 3 — Script Generator

The script generator takes the interpretation output and produces a channel-specific long-form script. For the YouTube long-form (10-minute target), the structure is fixed:

[Opening — 40s] Hook + score framing
[Part 1 — 3min] Annual energy overview + strategic framing
[Part 2 — 3min] Career and output analysis
[Part 3 — 3min] Wealth / Relationships / Health + quarterly rhythm
[Closing — 30s] Summary + next content CTA

The generator prompt enforces tone guardrails as hard constraints, not soft suggestions:

HARD CONSTRAINTS:

  • No absolute language: 무조건/반드시/절대/확실히 are banned tokens
  • No anxiety-inducing framing: no "disaster," "crisis," "doomed"
  • No celebrity private life speculation beyond what chart data supports
  • Birth hour is unknown — zero inferences from Hour Pillar beyond what is structurally present
  • Every claim must cite a specific pillar, ten-god, or spirit star from the extracted JSON

The "cite your source" constraint is the most important one. It forces the model to ground every interpretive claim in the actual chart data. When I removed it in testing, the model would drift into generic fortune-telling language within two paragraphs.

Classical reference integration is handled by a separate retrieval step. For 丁火 day masters with strong Robber/Friend configurations, the system pulls the relevant passage from 적천수(滴天髓, Di Tian Sui): "比劫重重,須看財官之氣" — when Robber and Friend stack heavily, the quality of Wealth and Officer energy determines the outcome direction. This grounds the modern interpretation in classical framework without fabricating citations.


Stage 4 — QA Checker

Before any content reaches formatting, it passes through an automated QA layer with six checks:

Check Method Failure Action
Ten-god consistency Compare script mentions vs. extracted JSON Flag + human review
Absolute language scan Regex on banned token list Auto-reject, regenerate
Speculation detector Classifier for ungrounded personal claims Flag + human review
Birth-hour boundary Scan for Hour Pillar causal claims Auto-reject if found
Citation coverage Every domain claim must reference a pillar Flag uncited paragraphs
Tone classifier Fine-tuned classifier on Dalbit Namu tone corpus Score < 0.7 → regenerate

The QA layer catches roughly 15–20% of first-pass generations on complex charts (unknown birth hour, high-profile subjects, charts with multiple clashes). For straightforward charts it passes ~90% first-pass.

The hardest failure mode to catch: plausible-sounding but structurally wrong ten-god assignments. The model occasionally generates "편재(偏財, Indirect Wealth) in the Month Stem" when the actual month stem is 丙 carrying 겁재(劫財, Robber) relative to a 丁 day master. This is a logic error, not a language error, so regex cannot catch it. The solution was a dedicated ten-god verification module that recomputes all ten-god relationships from the extracted stems/branches independently and diffs against the script.


Stage 5 — Channel Formatter

The same interpretation JSON feeds five different format templates:

YouTube Long-form (10min script) → Korean, warm narrative, 3-part structure
YouTube Shorts (60s script) × 3 → Single domain, punchy, hook-first
Dev.to (technical) → English, pipeline-focused, this article
Instagram Caption → 150 words, emoji-light, CTA to full video
Notion Internal Doc → Full JSON + reasoning chains, editorial use only

Each formatter has its own prompt with channel-specific constraints. The Dev.to formatter, for example, strips all fortune-telling framing and reframes the content as a technical case study. The Instagram formatter enforces a strict word count and bans any language that could read as a definitive prediction.

The [INFO_GRAPHIC] block in the long-form script is a structured data handoff to the motion graphics team:

{
"type": "score_card",
"subject": "Jimin 2026",
"scores": {
"overall": 68,
"career": 72,
"wealth": 58,
"relationships": 65,
"health": 63
},
"key_pillars": ["丁丑(日柱)", "丙午(세운)", "壬午(대운)"],
"highlight_stars": ["天德貴人", "月德貴人", "鬼門關殺"]
}

This eliminates the manual briefing step between script and design.


Lessons Learned

1. Vision extraction is the highest-leverage investment. Every downstream error traces back to a misread character in Stage 1. Spend disproportionate time on the extraction prompt and validation layer.

2. Guardrails need to be structural, not advisory. "Please avoid absolute language" does not work. Banned token lists, auto-reject triggers, and regeneration loops do.

3. Classical citations require retrieval, not generation. Asking the model to "cite a classical text" produces hallucinated citations. Build a retrieval index of actual classical passages keyed by chart pattern.

4. Unknown birth hour is a common case, not an edge case. Design the entire pipeline to handle it gracefully from the start. In Jimin's chart, birth hour is unknown — the Hour Pillar (丙午) is present in the Manse Calendar image as a system-generated estimate, not a confirmed time. The QA layer enforces that no causal claims derive from it.

5. Channel formatting is a first-class concern, not an afterthought. The same content that works for a 10-minute YouTube script actively fails as a Dev.to technical article if you just translate it. Each channel needs its own formatter with its own constraints.


Unexpected Finding: The Reversal Case (反轉)

The most technically interesting element in Jimin's 2026 chart is the 귀문관살(鬼門關殺) interaction. This spirit star sits at Day Branch 丑(축) and Hour Branch 午(오) — and the 2026 Annual Branch is also 午.

Standard interpretation flags 鬼門關殺 as a sensitivity amplifier. But in a 신강(身强) chart with strong Fire, the same star can function as a perceptual asset — heightened awareness of subtle signals in creative and performance work — rather than a liability. The automated system initially scored this as a negative health indicator. Human editorial review upgraded it to a nuanced dual-function note.

This is the case for keeping a human-in-the-loop on the QA layer for complex charts. The model applies pattern-matching; a practitioner applies contextual judgment.


Summary

  • Stage 1 (Vision): Claude Vision extracts chart data character-by-character with explicit no-substitution guardrails
  • Stage 2 (Interpretation): Three-prompt chain produces structural analysis, annual overlay, and domain scores grounded in extracted JSON
  • Stage 3 (Script): Channel-specific generators with hard-coded tone and citation constraints
  • Stage 4 (QA): Six automated checks including a ten-god verification module; ~15–20% first-pass rejection rate on complex charts
  • Stage 5 (Formatter): Five channel templates, each with independent constraints; INFO_GRAPHIC block as structured handoff to design

The Jimin 2026 analysis (Overall 68/100, Career 72, Wealth 58, Relationships 65, Health 63) was generated, QA-passed, and formatted across all five channels in under four minutes of compute time. Manual review and editorial sign-off added approximately 25 minutes.


If you are building content automation in a specialized domain — astrology, TCM, legal summaries, financial commentary — the core architecture transfers directly. The domain changes; the extraction → interpretation → QA → formatting pipeline does not.

Explore the Dalbit Namu Saju content system at runartree.com


This article describes a content automation pipeline. All Saju interpretations referenced are generated outputs used to test and validate the system. They represent one analytical framework among many and should not be taken as definitive predictions about any individual.


Project link

This article is based on an automated content workflow for a Korean Saju platform.

The key lesson is simple: generation alone is not enough. A useful publishing pipeline also needs formatting, QA, tracking links, and channel-specific editorial rules.


Bazi interpretation. Not medical, legal, or investment advice.

Top comments (0)