## Why an automation?

I recently started blogging, more specifically I have my interest in technical blogging. I enjoy going through papers, articles and GitHub repos which spike my interest. Hence leading me to the idea of posting about the same. But I realize, technical blogging can be **more than just a medium to share my interests, it can be a powerful way to build up my personal brand.**

Speaking of creating a personal brand, a lot goes into it, but in my simple view point it is **only requires two things:**
- High value and high impact content
- A great distribution strategy

**Social media is the "great distribution strategy".**

But writing content optimized for social media, more specifically LinkedIn and X were **not the areas I wanted to spend a lot of time in** and also me **being an engineer, I was attracted to automating it away.**

---
## How it works?

### *Central Idea*
At the heart of this project is a **Langgraph workflow** that automates my content pipeline, which means, right from the idea stage to the draft and publish stage of my social media posts (not my blogs), the workflow takes care of the entire generation.

- _What kind of posts I want?_ 
- _What type of post I need depending on day of the week?_ 
- _Is the content of the post legitimate and factual?_
- _Does the post make any claims with no basis?_
- _Does the post state any statements without proper citations?_

### *Why Langgraph?*
After using multiple tools for creating agents like **LCEL, Crew AI and DSPY**. I felt Langgraph was extremely easy to follow in terms of the workflow as it **maps very well to flowcharts and UMLs** we encounter on daily basis. Also, I am familiar with Langgraph as **I used it extensively at my internship** to build agentic systems. 

### *Flowchart*

![post automation flow](/images/blog/langgraph/post-automation-flow.png)


### *Here’s a breakdown of the process:*

1. **Capture & Research**
    
    - The workflow starts by capturing a raw idea (`capture_idea`).
        
    - It then pulls in notes and references from Obsidian (`obsidian_research`), ensuring the idea is grounded in prior research.
2. **Planning & Teaser Generation**
    
    - The `planner_agent` structures the idea into a roadmap.
        
    - Depending on the phase, the system may generate an early **teaser post** (`teaser_generator`) to share on Monday as a preview.
3. **Drafting the Blog**
    
    - If we’re in the drafting phase, the `blog_drafter` creates a long-form draft.
        
    - Once the final blog URL is available, the system scrapes the published content (`scraper`) and builds a concise summary (`summarizer`).
4. **Social Media Post Creation**
    
    - Using the blog summary, the workflow generates tailored LinkedIn posts (`final_post_generator`) and X/Twitter posts (`x_generator`).
5. **Validation & Review**
    - Posts are validated for structure, tone, and platform-fit (`validator`).
        
    - If issues are found, they are sent for peer review (`peer_reviewer`) and, if necessary, refined by the `content_improver`.
        
    - The workflow limits improvements to 3 iterations to avoid endless loops.
        

6. **Self-Evaluation & Recovery**
    - Before finalizing, posts undergo a self-check (`self_evaluator`).
        
    - If errors occur (e.g., missing blog URL, broken logic), a `recovery_agent` steps in to fix or gracefully exit.
        
7. **Completion**
    
    - Once validated, the system outputs **ready-to-publish posts** for LinkedIn and X.
        
    - If human oversight is required (e.g., controversial phrasing or ambiguous context), the workflow pauses and flags it for manual review.

**The workflow code snippet:**
```python
def create_workflow():
    workflow = StateGraph(AutomationState)

    workflow.add_node("capture_idea", capture_idea)
    workflow.add_node("obsidian_research", process_obsidian_content)
    workflow.add_node("planner_agent", planner_agent)
    workflow.add_node("teaser_generator", teaser_generator)
    workflow.add_node("blog_drafter", blog_drafter)
    workflow.add_node("scraper", scrape_blog_content)
    workflow.add_node("summarizer", generate_blog_summary)
    workflow.add_node("final_post_generator", generate_linkedin_posts)
    workflow.add_node("x_generator", generate_x_posts)
    workflow.add_node("validator", validate_posts)
    workflow.add_node("peer_reviewer", peer_review_agent)
    workflow.add_node("content_improver", content_improver_agent)
    workflow.add_node("self_evaluator", self_evaluator)
    workflow.add_node("recovery_agent", recovery_agent)

    workflow.set_entry_point("capture_idea")

    workflow.add_edge("capture_idea", "obsidian_research")
    workflow.add_edge("obsidian_research", "planner_agent")

    workflow.add_conditional_edges(
        "planner_agent",
        should_generate_teaser,
        {
            "teaser_generator": "teaser_generator",
            "planner_agent": "planner_agent",
            "scraper": "scraper",
        },
    )

    workflow.add_conditional_edges(
        "teaser_generator",
        should_generate_blog_draft,
        {
            "blog_drafter": "blog_drafter",
            "planner_agent": "planner_agent",
            "scraper": "scraper",
        },
    )

    workflow.add_conditional_edges(
        "blog_drafter", should_scrape_blog, {"scraper": "scraper", "END": END}
    )

    workflow.add_edge("scraper", "summarizer")
    workflow.add_edge("summarizer", "final_post_generator")
    workflow.add_edge("final_post_generator", "x_generator")

    workflow.add_conditional_edges(
        "x_generator",
        should_validate_or_end,
        {"validator": "validator", "recovery_agent": "recovery_agent", "END": END},
    )

    workflow.add_conditional_edges(
        "validator",
        should_improve_or_evaluate,
        {
            "peer_reviewer": "peer_reviewer",
            "self_evaluator": "self_evaluator",
            "recovery_agent": "recovery_agent",
        },
    )

    workflow.add_conditional_edges(
        "peer_reviewer",
        should_improve_or_end,
        {
            "content_improver": "content_improver",
            "self_evaluator": "self_evaluator",
            "recovery_agent": "recovery_agent",
        },
    )

    workflow.add_edge("content_improver", "validator")

    workflow.add_conditional_edges(
        "self_evaluator",
        should_loop_or_end,
        {"validator": "validator", "recovery_agent": "recovery_agent", "END": END},
    )

    workflow.add_edge("recovery_agent", END)

    return workflow.compile()
```

### *Entire code for my automation:* [<u>GitHub</u>](https://github.com/capybara-brain346/post-automation.git)

---
## Closing Thoughts
It was very fun building out this automation. Obviously it can't be deemed a boon for my productivity in such a short period of time, but I will be keeping a **close eye on gains (or losses) as a result of this automation, which I certainly will be posting about.**


## **Share This Post**

<div className="mt-8 flex flex-col items-center gap-6">
  <div className="linkedin-embed w-full max-w-lg">
    <iframe
      src="https://www.linkedin.com/embed/feed/update/urn:li:share:7374264313940869120?collapsed=1"
      height="400"
      width="100%"
      frameBorder="0"
      allowFullScreen
      title="LinkedIn Post - SHAP Values for GBTs"
      className="mx-auto rounded-lg shadow-md"
    />
  </div>
  <div className="not-prose">
  <TwitterEmbed tweetId="1968637987341705271" username="piyush_yip">
    <blockquote className="twitter-tweet">
      <p lang="en" dir="ltr">
        Earlier this week I shared how the real bottleneck wasn’t writing blogs, it was rewriting them into LinkedIn/X posts.
        <br />
        So I built an automation with LangGraph to solve that.
        <br />
        A 🧵:
        <a href="https://t.co/PhvIFatv6H">https://t.co/PhvIFatv6H</a>
      </p>
      &mdash; Piyush Choudhari (@piyush_yip){" "}
      <a href="https://twitter.com/piyush_yip/status/1968637987341705271?ref_src=twsrc%5Etfw">
        September 18, 2025
      </a>
    </blockquote>
  </TwitterEmbed>
</div>
</div>