feat(blog): Title Contract + Technical Sanity Check + Self-Heal + angle-aware LinkedIn generator
Pipeline now has 21 steps: - STEP0: Title Contract binds LLM to headline promise - STEP19: Technical Sanity Check (optical engineering accuracy) - STEP20: Self-Heal (auto-fix technical errors preserving tone) - STEP21: Title Contract Verification (final gate check) - LinkedIn generator is now angle-aware (no more default Physical Layer hook)
This commit is contained in:
parent
7c8f545c18
commit
6c818c79b5
@ -299,6 +299,31 @@ CONTENT MODULES (use 2-3 per article):
|
||||
- Vendor bullshit vs reality
|
||||
- When NOT to use this technology`;
|
||||
|
||||
// ═══════════════════════════════════════════════════════
|
||||
// STEP 0: TITLE CONTRACT — binds the LLM to the headline promise
|
||||
// ═══════════════════════════════════════════════════════
|
||||
|
||||
export const STEP0_TITLE_CONTRACT = `You are a senior technical editor. Your job is to analyze a blog title and create a binding contract that the article MUST fulfill.
|
||||
|
||||
Title: {{TITLE}}
|
||||
|
||||
Analyze the title and return EXACTLY this structure:
|
||||
|
||||
TOPIC DOMAIN: (what subject area — e.g., "pricing comparison", "standards maturity", "migration process")
|
||||
PROMISE TYPE: (what the title promises — e.g., "COST ANALYSIS", "STATUS REPORT", "DECISION FRAMEWORK", "STEP-BY-STEP GUIDE", "MARKET TIMING")
|
||||
REQUIRED CONTENT: (what MUST appear in the article to fulfill the title — be specific, 3-5 bullet points)
|
||||
FORBIDDEN CONTENT: (what would VIOLATE the title promise — be specific)
|
||||
READER TAKEAWAY: (what the reader should know/do after reading — one sentence)
|
||||
ANGLE TYPE: (A=Economic, B=Decision, C=Market, D=Operational, E=Political, F=Migration)
|
||||
|
||||
CRITICAL RULES:
|
||||
- If the title says "Cost Difference" → the article MUST be about costs, pricing, TCO. NOT about physical layer failures.
|
||||
- If the title says "Production-Ready" → the article MUST be a status report on maturity. NOT about what breaks.
|
||||
- If the title says "Migration Guide" → the article MUST be step-by-step. NOT about failure scenarios.
|
||||
- If the title says "Comparison" → the article MUST compare specific options. NOT tell a generic story.
|
||||
|
||||
The contract you produce will be injected into every subsequent pipeline step to prevent drift.`;
|
||||
|
||||
// ═══════════════════════════════════════════════════════
|
||||
// STEP 1: TOPIC EXPANSION
|
||||
// ═══════════════════════════════════════════════════════
|
||||
@ -1045,6 +1070,135 @@ Return ONLY a JSON object:
|
||||
Article:
|
||||
{{ARTICLE}}`;
|
||||
|
||||
// ═══════════════════════════════════════════════════════
|
||||
// STEP 11: TECHNICAL SANITY CHECK — expert-level accuracy verification
|
||||
// ═══════════════════════════════════════════════════════
|
||||
|
||||
export const STEP_TECHNICAL_SANITY = `You are a senior optical network engineer reviewing a blog post for technical correctness.
|
||||
|
||||
Your job is NOT to rewrite the text. Your job is to find technical inaccuracies, imprecise wording, or misleading statements.
|
||||
|
||||
Be strict. Assume the reader is an expert.
|
||||
|
||||
CHECK THESE AREAS:
|
||||
|
||||
1. MEASUREMENT vs OBSERVATION
|
||||
- Does the text confuse inspection (visual, scope) with measurement (OPM, OLTS, DOM)?
|
||||
- "inspection scope measures loss" → WRONG. A scope shows contamination/defects. An OPM/OLTS measures loss.
|
||||
- "inspection scope reveals insertion loss" → WRONG. It reveals contamination that CAUSES insertion loss.
|
||||
|
||||
2. FIBER & OPTICS ACCURACY
|
||||
- MMF vs SMF behavior — correct distinctions?
|
||||
- SR4 vs DR4 vs FR4 — correct fiber counts, connectors, wavelengths?
|
||||
- DR4 = MPO-12 (8 fibers, parallel, 1310nm). FR4 = LC duplex (CWDM4, 2km). NEVER mix these.
|
||||
- Connector types correct? (MPO vs LC vs SC)
|
||||
- Reach claims accurate?
|
||||
|
||||
3. OPTICAL BUDGET LOGIC
|
||||
- TX_min / RX_sensitivity usage correct?
|
||||
- Link budget calculations make sense?
|
||||
- Margin discussion realistic? (DR4 ≈ 4.8 dB budget, not more)
|
||||
- DR4 fiber attenuation = 0.35 dB/km at 1310nm (NOT 1550nm which is 0.22 dB/km)
|
||||
|
||||
4. FAILURE BEHAVIOR REALISM
|
||||
- CRC errors vs hard link down — correct description?
|
||||
- Intermittent vs permanent failure — accurate?
|
||||
- FEC behavior correct?
|
||||
- "creeping errors" pattern realistic?
|
||||
|
||||
5. TERMINOLOGY PRECISION
|
||||
- "contacts" for fiber → WRONG. Use: connector end-faces, ferrules, mating points
|
||||
- "signal loss" → too generic. Use: insertion loss, return loss, or attenuation
|
||||
- "worn contacts" → WRONG for fiber. Use: contamination, alignment degradation, connector wear
|
||||
- "patch panels worn contacts" → WRONG. Use: aging connectors and accumulated contamination
|
||||
|
||||
6. CAUSE vs SYMPTOM
|
||||
- Does the text correctly separate root cause from symptom?
|
||||
- Does it avoid blaming optics when the issue is physical layer?
|
||||
|
||||
7. POWER SPECS
|
||||
- 400G ≈ 10-15W per PORT (not per chassis)
|
||||
- 800G ≈ 15-25W per PORT
|
||||
- "1kW per port" → HARD FAIL
|
||||
|
||||
Return ONLY this JSON:
|
||||
{
|
||||
"technical_score": <1-10>,
|
||||
"critical_issues": [{"issue": "...", "why_wrong": "...", "fix": "..."}],
|
||||
"precision_fixes": [{"original": "...", "problem": "...", "better": "..."}],
|
||||
"safe_to_publish": true/false
|
||||
}
|
||||
|
||||
Article:
|
||||
{{ARTICLE}}`;
|
||||
|
||||
// ═══════════════════════════════════════════════════════
|
||||
// STEP 12: SELF-HEAL — fix technical errors preserving tone
|
||||
// ═══════════════════════════════════════════════════════
|
||||
|
||||
export const STEP_SELF_HEAL = `You are a senior optical network engineer and technical editor.
|
||||
|
||||
You have the original article and a technical sanity check report. Your task is to REPAIR the text.
|
||||
|
||||
RULES:
|
||||
- Fix all critical technical issues identified in the report
|
||||
- Sharpen imprecise wording (scope vs OPM, contacts vs end-faces, etc.)
|
||||
- Preserve the original TONE and FLOW — do not make it sound like a whitepaper
|
||||
- Do NOT add unnecessary content or over-explain
|
||||
- Do NOT rewrite sentences that are already correct
|
||||
- Only change what needs fixing — minimal, surgical edits
|
||||
- Keep it readable for engineers, not academics
|
||||
|
||||
SPECIFIC FIXES TO APPLY:
|
||||
- "inspection scope reveals/measures insertion loss" → "inspection scope reveals contamination that impacts insertion loss"
|
||||
- "contacts" (when discussing fiber) → "connector end-faces" or "mating points"
|
||||
- "MMF is stricter/less tolerant" → be specific: "DR4 links have tighter loss budgets than SR4 deployments"
|
||||
- "worn contacts on patch panels" → "accumulated contamination on connector interfaces"
|
||||
- Verify all dBm values against reference: DR4 budget ≈ 4.8 dB, attenuation at 1310nm = 0.35 dB/km
|
||||
|
||||
Return ONLY the repaired article text — no commentary, no changelog.
|
||||
|
||||
TECHNICAL REPORT:
|
||||
{{SANITY_REPORT}}
|
||||
|
||||
ARTICLE:
|
||||
{{ARTICLE}}`;
|
||||
|
||||
// ═══════════════════════════════════════════════════════
|
||||
// STEP 13: TITLE CONTRACT VERIFICATION — final check
|
||||
// ═══════════════════════════════════════════════════════
|
||||
|
||||
export const STEP_TITLE_CONTRACT_CHECK = `You are a senior editor performing a FINAL quality gate check.
|
||||
|
||||
Compare this article against its Title Contract (the promise the headline makes).
|
||||
|
||||
TITLE CONTRACT:
|
||||
{{TITLE_CONTRACT}}
|
||||
|
||||
ARTICLE:
|
||||
{{ARTICLE}}
|
||||
|
||||
CHECK:
|
||||
1. Does the article FULFILL the title's promise? (yes/no with explanation)
|
||||
2. Does the article DRIFT into a different topic? (identify specific paragraphs)
|
||||
3. Would a reader who clicked this title get what they expected? (yes/no)
|
||||
4. Is the ending on-topic or does it drift into generic "validate your setup" advice?
|
||||
|
||||
If the article VIOLATES the title contract, return:
|
||||
{
|
||||
"contract_fulfilled": false,
|
||||
"violations": ["specific violation 1", "specific violation 2"],
|
||||
"drift_paragraphs": ["paragraph text that drifts"],
|
||||
"verdict": "REJECT — article does not match title promise"
|
||||
}
|
||||
|
||||
If the article FULFILLS the contract:
|
||||
{
|
||||
"contract_fulfilled": true,
|
||||
"violations": [],
|
||||
"verdict": "PASS — article delivers on title promise"
|
||||
}`;
|
||||
|
||||
// ═══════════════════════════════════════════════════════
|
||||
// NEW BLOG TYPES (v0.2.0)
|
||||
// ═══════════════════════════════════════════════════════
|
||||
@ -2155,34 +2309,43 @@ ANTI-AI FILTER — make it human:
|
||||
→ Symmetric structure → make it slightly uneven
|
||||
|
||||
════════════════════════════════════════════════════════
|
||||
GOLD REFERENCE (engagement-boosted — this is the target):
|
||||
ANGLE-AWARE HOOK SELECTION — CRITICAL
|
||||
════════════════════════════════════════════════════════
|
||||
|
||||
Everything looks fine. Until it doesn't.
|
||||
The LinkedIn post MUST match the article's angle. Do NOT default to the Physical Layer story.
|
||||
|
||||
You bring up a new batch of 400G links. Lab tests were clean. Traffic is stable.
|
||||
TITLE CONTRACT FOR THIS ARTICLE:
|
||||
{{TITLE_CONTRACT}}
|
||||
|
||||
Then error counters start creeping up.
|
||||
Match the hook and behavior beats to the article's actual angle:
|
||||
|
||||
Not enough to fail. Just enough to waste your time.
|
||||
IF ECONOMIC/TCO article:
|
||||
HOOK: "A $350 optic just turned into an $18,000 problem." or "Your 400G budget looks fine. Until you add the numbers nobody showed you."
|
||||
BEATS: cost surprises, TCO reality, budget impact
|
||||
CLOSE: cost-based insight, not "clean your connectors"
|
||||
|
||||
So optics get blamed.
|
||||
IF DECISION/COMPARISON article:
|
||||
HOOK: "Three quotes on your desk. The cheapest one isn't always the most expensive." or "OEM vs compatible. Everyone has an opinion. Here's what the data says."
|
||||
BEATS: decision criteria, what specs don't tell you
|
||||
CLOSE: decision framework, not failure story
|
||||
|
||||
Swapped. Replaced. Escalated.
|
||||
IF MARKET/TIMING article:
|
||||
HOOK: "800G is shipping. Sort of." or "Half the industry says buy now. The other half says wait."
|
||||
BEATS: what's real vs hype, timing signals
|
||||
CLOSE: timing advice, not generic
|
||||
|
||||
Nothing changes.
|
||||
IF OPERATIONAL/PROCESS article:
|
||||
HOOK: "We deployed 200 links in 3 weeks. Zero issues. Here's the boring reason why."
|
||||
BEATS: process steps, what was done differently
|
||||
CLOSE: process insight, not failure
|
||||
|
||||
Config looks correct. Hardware looks fine.
|
||||
IF MIGRATION article:
|
||||
HOOK: "Month 1: everything on schedule. Month 6: nothing was."
|
||||
BEATS: planning vs reality, specific surprises
|
||||
CLOSE: what to plan for, based on real experience
|
||||
|
||||
That's usually the point where people start looking in the wrong place.
|
||||
DO NOT USE THE PHYSICAL LAYER HOOK ("Everything looks fine. Until it doesn't.") — it has been used already. Create a FRESH hook that matches THIS article's specific angle.
|
||||
|
||||
Same optics. Same setup. Different result.
|
||||
|
||||
At 100G, you get away with it.
|
||||
At 400G, you don't.
|
||||
|
||||
Full breakdown in the blog — link in first comment.
|
||||
|
||||
#OpticalNetworking #DataCenter #NetworkEngineering #Flexoptix
|
||||
════════════════════════════════════════════════════════
|
||||
|
||||
Return ONLY the post text. No commentary. No "Here is the post:". Start directly with the hook.
|
||||
|
||||
@ -1016,11 +1016,15 @@ async function runLlmPipeline(
|
||||
buildFeedbackContext,
|
||||
buildSLLContext,
|
||||
withCalibration,
|
||||
STEP0_TITLE_CONTRACT,
|
||||
STEP_TECHNICAL_SANITY,
|
||||
STEP_SELF_HEAL,
|
||||
STEP_TITLE_CONTRACT_CHECK,
|
||||
} = await import("../llm/fo-blog-pipeline");
|
||||
|
||||
const LLM_OPTS = { temperature: 0.7, maxTokens: 8192, timeoutMs: 480000 };
|
||||
const LLM_REFINE = { temperature: 0.4, maxTokens: 6144, timeoutMs: 480000 };
|
||||
const TOTAL_STEPS = 18; // 16-step pipeline + APM + Viral Signal + LinkedIn
|
||||
const TOTAL_STEPS = 21; // 17-step pipeline + title contract + technical sanity + self-heal + contract check
|
||||
let stepsCompleted = 0;
|
||||
|
||||
try {
|
||||
@ -1120,13 +1124,23 @@ async function runLlmPipeline(
|
||||
}
|
||||
} catch { /* fine if no articles yet */ }
|
||||
|
||||
// ═══ STEP 0: Title Contract — bind LLM to headline promise ═══
|
||||
console.log(" Step 0: Title Contract (binding headline to content)...");
|
||||
setProgress(draftId, 1, "Step 0: Title Contract");
|
||||
const step0 = await generate(systemPrompt,
|
||||
STEP0_TITLE_CONTRACT.replace("{{TITLE}}", title),
|
||||
{ ...LLM_REFINE, maxTokens: 2048 }
|
||||
);
|
||||
const titleContract = step0.text;
|
||||
console.log(` Title Contract: ${titleContract.split("\n").slice(0, 3).join(" | ").slice(0, 120)}...`);
|
||||
|
||||
// ═══ STEP 1: Topic Expansion ═══
|
||||
console.log(" Step 1/10: Topic Expansion...");
|
||||
setProgress(draftId, 1, "Step 1/10: Topic Expansion");
|
||||
console.log(" Step 1: Topic Expansion...");
|
||||
setProgress(draftId, 2, "Step 1: Topic Expansion");
|
||||
const step1 = await generate(systemPrompt,
|
||||
STEP1_TOPIC_EXPANSION
|
||||
.replace("{{TOPIC}}", title)
|
||||
.replace("{{EXISTING_ANGLES}}", existingAnglesContext),
|
||||
.replace("{{EXISTING_ANGLES}}", existingAnglesContext + "\n\nTITLE CONTRACT (the article MUST fulfill this):\n" + titleContract),
|
||||
LLM_OPTS
|
||||
);
|
||||
stepsCompleted = 1;
|
||||
@ -1136,7 +1150,7 @@ async function runLlmPipeline(
|
||||
setProgress(draftId, 2, "Step 2/10: Angle Selection");
|
||||
const step2 = await generate(systemPrompt,
|
||||
STEP2_ANGLE_SELECTION
|
||||
.replace("{{FORBIDDEN_ANGLES}}", forbiddenAnglesContext)
|
||||
.replace("{{FORBIDDEN_ANGLES}}", forbiddenAnglesContext + "\nTITLE CONTRACT:\n" + titleContract)
|
||||
.replace("{{SCENARIOS}}", step1.text),
|
||||
LLM_REFINE
|
||||
);
|
||||
@ -1160,7 +1174,7 @@ async function runLlmPipeline(
|
||||
const step4 = await generate(systemPrompt,
|
||||
STEP4_MASTER_DRAFT
|
||||
.replace("{{OUTLINE}}", step3.text)
|
||||
.replace("{{CONTEXT_DATA}}", contextData),
|
||||
.replace("{{CONTEXT_DATA}}", contextData + "\n\nTITLE CONTRACT (you MUST fulfill this — the article must match the headline promise):\n" + titleContract),
|
||||
{ ...LLM_OPTS, maxTokens: 8192 }
|
||||
);
|
||||
stepsCompleted = 4;
|
||||
@ -1342,7 +1356,9 @@ async function runLlmPipeline(
|
||||
// Fallback: dedicated LinkedIn post generator
|
||||
try {
|
||||
const stepLinkedIn = await generate(systemPrompt,
|
||||
STEP_LINKEDIN_POST.replace("{{ARTICLE}}", viralArticle),
|
||||
STEP_LINKEDIN_POST
|
||||
.replace("{{TITLE_CONTRACT}}", titleContract)
|
||||
.replace("{{ARTICLE}}", viralArticle),
|
||||
{ temperature: 0.6, maxTokens: 1024, timeoutMs: 120000 }
|
||||
);
|
||||
linkedinPost = stepLinkedIn.text.trim();
|
||||
@ -1361,6 +1377,69 @@ async function runLlmPipeline(
|
||||
}
|
||||
stepsCompleted = 18;
|
||||
|
||||
// ═══ STEP 19: Technical Sanity Check ═══
|
||||
console.log(" Step 19/21: Technical Sanity Check...");
|
||||
setProgress(draftId, 19, "Step 19/21: Technical Sanity Check");
|
||||
let sanityReport = "";
|
||||
try {
|
||||
const stepSanity = await generate(systemPrompt,
|
||||
STEP_TECHNICAL_SANITY.replace("{{ARTICLE}}", viralArticle),
|
||||
{ temperature: 0.2, maxTokens: 4096, timeoutMs: 240000 }
|
||||
);
|
||||
sanityReport = stepSanity.text.trim();
|
||||
console.log(` Sanity check: ${sanityReport.includes('"safe_to_publish": false') ? "⚠ ISSUES FOUND" : "✓ safe"}`);
|
||||
} catch {
|
||||
console.log(" Technical sanity check skipped (error)");
|
||||
}
|
||||
stepsCompleted = 19;
|
||||
|
||||
// ═══ STEP 20: Self-Heal (fix technical errors) ═══
|
||||
if (sanityReport && (sanityReport.includes('"safe_to_publish": false') || sanityReport.includes('"critical_issues"'))) {
|
||||
console.log(" Step 20/21: Self-Heal (fixing technical errors)...");
|
||||
setProgress(draftId, 20, "Step 20/21: Self-Heal (technical fixes)");
|
||||
try {
|
||||
const stepHeal = await generate(systemPrompt,
|
||||
STEP_SELF_HEAL
|
||||
.replace("{{SANITY_REPORT}}", sanityReport)
|
||||
.replace("{{ARTICLE}}", viralArticle),
|
||||
LLM_REFINE
|
||||
);
|
||||
const healedWords = stepHeal.text.split(/\s+/).length;
|
||||
if (healedWords > 400) {
|
||||
viralArticle = stepHeal.text;
|
||||
console.log(` Self-healed: ${healedWords} words`);
|
||||
} else {
|
||||
console.log(" Self-heal output too short — keeping original");
|
||||
}
|
||||
} catch {
|
||||
console.log(" Self-heal skipped (error)");
|
||||
}
|
||||
} else {
|
||||
console.log(" Step 20/21: Self-Heal skipped (no critical issues)");
|
||||
}
|
||||
stepsCompleted = 20;
|
||||
|
||||
// ═══ STEP 21: Title Contract Verification ═══
|
||||
console.log(" Step 21/21: Title Contract Verification...");
|
||||
setProgress(draftId, 21, "Step 21/21: Title Contract Check");
|
||||
try {
|
||||
const stepContract = await generate(systemPrompt,
|
||||
STEP_TITLE_CONTRACT_CHECK
|
||||
.replace("{{TITLE_CONTRACT}}", titleContract)
|
||||
.replace("{{ARTICLE}}", viralArticle),
|
||||
{ temperature: 0.2, maxTokens: 2048, timeoutMs: 120000 }
|
||||
);
|
||||
const contractResult = stepContract.text.trim();
|
||||
if (contractResult.includes('"contract_fulfilled": false') || contractResult.includes('"REJECT')) {
|
||||
console.log(" ⚠ TITLE CONTRACT VIOLATION — article may not match headline");
|
||||
} else {
|
||||
console.log(" ✓ Title contract fulfilled");
|
||||
}
|
||||
} catch {
|
||||
console.log(" Title contract check skipped (error)");
|
||||
}
|
||||
stepsCompleted = 21;
|
||||
|
||||
// Extract article from Viral Signal output (or APM fallback)
|
||||
// Fall back to step9.text if output looks too short or empty
|
||||
let finalArticleText = viralArticle.trim().length > 200 ? viralArticle : step9.text;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user