[TERMINAL · SKILLS]
> mounting /skills...
> indexing skill manifests...
> linking agents: claude · codex · gemini · cursor
> ready.
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 0%
Use Cases/Build a Multi-Step AI Workflow with LangGraph

Build a Multi-Step AI Workflow with LangGraph

Chain multiple AI agents into a DAG-based workflow with parallel execution, state management, and human-in-the-loop approvals.

Data & AI#langchain#llm#rag#agents#ai-chains
Works with:claude-codeopenai-codexgemini-clicursor

Skills stack · 6 skills

Avg quality 87/100·All SAFE
>

langchain

v1.1.0

Build LLM-powered applications with LangChain. Use when a user asks to create AI chains, build RAG pipelines, implement agents with tools, set up document loaders, create vector stores, build conversational AI, implement prompt templates, chain LLM calls, add memory to chatbots, or orchestrate language model workflows. Covers LangChain 1.x (Python and TypeScript) with LCEL, create_agent, structured output, tool calling, retrieval, and production patterns.

87/100 quality
1.37× impact
SAFE· stale score
View skill
>

langgraph

v1.1.0

Build stateful, multi-step AI agents and workflows with LangGraph. Use when a user asks to create AI agents with complex logic, build multi-agent systems, implement human-in-the-loop workflows, create state machines for LLMs, build agentic RAG, implement tool-calling agents with branching logic, create planning agents, build supervisor/worker patterns, or orchestrate multi-step AI pipelines with cycles, persistence, and streaming.

80/100 quality
1.28× impact
SAFE· stale score
View skill
>

anthropic-sdk

v1.1.0

The Anthropic SDK (Python `anthropic`, TypeScript `@anthropic-ai/sdk`) calls Claude models through the Messages API. Use when a user asks to add Claude to an app, build a chatbot, stream Claude responses, give Claude tools (function calling), send images or PDFs, turn on adaptive thinking, count tokens, or pick a current Claude model id. Covers client setup, streaming, tool use, vision, thinking and effort, and current model ids and breaking changes.

83/100 quality
8.08× impact
SAFE· stale score
View skill
>

langflow

v1.0.0

Langflow is a visual builder for AI agents and workflows that serves every flow as a REST API endpoint and as an MCP tool. Use when a user asks to install or run Langflow, deploy Langflow with Docker Compose and PostgreSQL, run a flow through the Langflow API, pass tweaks or a session id, export or import flow JSON, create a Langflow API key, run a flow file without the server using lfx, connect Langflow flows to an MCP client, or upgrade Langflow without losing flows.

80/100 quality
4.85× impact
SAFE· stale score
View skill
>

dify

v1.0.0

Dify is an open-source platform for building LLM apps such as chatbots, agents, workflows and RAG knowledge bases, and it exposes every published app as a REST API. Use when a user asks to self-host Dify with Docker Compose, configure Dify environment variables, call a Dify app or workflow API, upload documents to a Dify knowledge base, export or import a Dify DSL file, run a Dify app from the terminal with difyctl, or upgrade and back up a Dify instance.

100/100 quality
3.44× impact
SAFE· stale score
View skill
>

voltagent

v1.0.0

VoltAgent is an open-source TypeScript framework for building AI agents with typed tools, persistent memory, supervisor/sub-agent teams, MCP tools, guardrails and suspendable workflows, served over a local HTTP API. Use when someone asks to "build an AI agent in TypeScript", "add tools and memory to an agent", "set up a supervisor with sub-agents", "pause a workflow for human approval", "connect an agent to an MCP server", or mentions VoltAgent, @voltagent/core or create-voltagent-app.

93/100 quality
7.80× impact
SAFE· stale score
View skill
$

Persona: You're automating a research-to-article pipeline: find sources, analyze them, draft content, fact-check, then publish — all orchestrated without babysitting each step.

Single LLM calls are fine. But real tasks need coordination: steps that depend on each other, steps that can run in parallel, human checkpoints before irreversible actions. LangGraph gives you a proper execution graph for all of this.

What You're Building

[Search] → [Scrape URLs] ──┐
                           ├→ [Analyze] → [Draft] → [Human Review] → [Publish]
[Fetch DB Context] ────────┘

Setup

bash
npm install @langchain/langgraph @langchain/anthropic @langchain/core zod

Define the Workflow State

typescript
// workflow/state.ts
import { Annotation } from '@langchain/langgraph';

export const ResearchState = Annotation.Root({
  topic: Annotation<string>(),
  searchResults: Annotation<string[]>({ default: () => [] }),
  scrapedContent: Annotation<string[]>({ default: () => [] }),
  dbContext: Annotation<string>({ default: () => '' }),
  analysis: Annotation<string>({ default: () => '' }),
  draft: Annotation<string>({ default: () => '' }),
  humanApproved: Annotation<boolean>({ default: () => false }),
  publishedUrl: Annotation<string | null>({ default: () => null }),
  errors: Annotation<string[]>({ default: () => [] }),
});

export type WorkflowState = typeof ResearchState.State;

Step Nodes

typescript
// workflow/nodes.ts
import { ChatAnthropic } from '@langchain/anthropic';

const model = new ChatAnthropic({ model: 'claude-opus-4-5' });

// Parallel: search web for sources
export async function searchNode(state: WorkflowState) {
  const results = await webSearch(state.topic, { maxResults: 10 });
  return { searchResults: results.map(r => r.url) };
}

// Parallel: fetch internal DB context
export async function dbContextNode(state: WorkflowState) {
  const context = await db.query(
    `SELECT summary FROM knowledge_base WHERE topic_match($1) LIMIT 5`,
    [state.topic]
  );
  return { dbContext: context.rows.map(r => r.summary).join('\n') };
}

// Runs after search: scrape content from URLs
export async function scrapeNode(state: WorkflowState) {
  const content = await Promise.allSettled(
    state.searchResults.slice(0, 5).map(url => fetchAndExtract(url))
  );
  const scraped = content
    .filter(r => r.status === 'fulfilled')
    .map(r => (r as PromiseFulfilledResult<string>).value);
  return { scrapedContent: scraped };
}

// Synthesize all gathered info
export async function analyzeNode(state: WorkflowState) {
  const response = await model.invoke([{
    role: 'user',
    content: `Analyze these sources about "${state.topic}":

INTERNAL CONTEXT:
${state.dbContext}

WEB SOURCES:
${state.scrapedContent.join('\n\n---\n\n')}

Identify key themes, data points, controversies, and gaps.`
  }]);
  return { analysis: response.content as string };
}

// Write the article
export async function draftNode(state: WorkflowState) {
  const response = await model.invoke([{
    role: 'user',
    content: `Write a 800-word article about "${state.topic}" based on this analysis:
${state.analysis}

Include specific data points and cite sources inline.`
  }]);
  return { draft: response.content as string };
}

// Human checkpoint — pause and wait
export async function humanReviewNode(state: WorkflowState) {
  // In production: send Slack/email with draft, wait for webhook
  console.log('\n=== DRAFT FOR REVIEW ===\n', state.draft);
  const approved = await promptUser('Approve this draft? (y/n): ');
  return { humanApproved: approved === 'y' };
}

// Publish only if approved
export async function publishNode(state: WorkflowState) {
  if (!state.humanApproved) return { publishedUrl: null };
  const url = await cms.publish({ content: state.draft, topic: state.topic });
  return { publishedUrl: url };
}

Build the Graph

typescript
// workflow/graph.ts
import { StateGraph, START, END } from '@langchain/langgraph';

export function buildWorkflow() {
  const graph = new StateGraph(ResearchState)
    // Add all nodes
    .addNode('search', searchNode)
    .addNode('dbContext', dbContextNode)
    .addNode('scrape', scrapeNode)
    .addNode('analyze', analyzeNode)
    .addNode('draft', draftNode)
    .addNode('humanReview', humanReviewNode)
    .addNode('publish', publishNode)

    // Parallel start: kick off search + DB fetch simultaneously
    .addEdge(START, 'search')
    .addEdge(START, 'dbContext')

    // Scrape waits for search results
    .addEdge('search', 'scrape')

    // Analyze waits for BOTH scrape and dbContext
    .addEdge('scrape', 'analyze')
    .addEdge('dbContext', 'analyze')

    // Linear from analysis onward
    .addEdge('analyze', 'draft')
    .addEdge('draft', 'humanReview')

    // Conditional: only publish if approved
    .addConditionalEdges('humanReview', (state) => {
      return state.humanApproved ? 'publish' : END;
    })
    .addEdge('publish', END);

  return graph.compile();
}

Error Handling and Retries

typescript
// Wrap nodes with retry logic
function withRetry<T>(fn: (state: T) => Promise<Partial<T>>, maxRetries = 3) {
  return async (state: T): Promise<Partial<T>> => {
    let lastError: Error;
    for (let i = 0; i < maxRetries; i++) {
      try {
        return await fn(state);
      } catch (err) {
        lastError = err as Error;
        if (i < maxRetries - 1) {
          await sleep(1000 * 2 ** i); // exponential backoff
        }
      }
    }
    // Fallback: record error in state, continue workflow
    return { errors: [...(state as any).errors, lastError!.message] } as Partial<T>;
  };
}

// Apply to unreliable nodes
.addNode('scrape', withRetry(scrapeNode))
.addNode('search', withRetry(searchNode, 2))

Stream Execution with Progress

typescript
// main.ts
const workflow = buildWorkflow();

const stream = await workflow.stream(
  { topic: 'quantum computing breakthroughs 2025' },
  { streamMode: 'updates' }
);

for await (const update of stream) {
  const [nodeName, state] = Object.entries(update)[0];
  console.log(`✓ Completed: ${nodeName}`);

  if (nodeName === 'draft') {
    console.log('Draft preview:', (state as any).draft?.slice(0, 200) + '...');
  }
}

const finalState = await workflow.invoke({ topic: 'quantum computing breakthroughs 2025' });
console.log('Published at:', finalState.publishedUrl);

Checkpointing for Long-Running Workflows

typescript
import { PostgresSaver } from '@langchain/langgraph-checkpoint-postgres';

const checkpointer = PostgresSaver.fromConnString(process.env.DATABASE_URL!);
await checkpointer.setup();

const workflow = buildWorkflow().compile({ checkpointer });

// Resume interrupted workflows by thread ID
const threadId = 'research-quantum-2025-03';
await workflow.invoke(
  { topic: 'quantum computing' },
  { configurable: { thread_id: threadId } }
);

What to Build Next

  • Dynamic branching: Use LLM to decide which steps to run based on topic type
  • Sub-graphs: Encapsulate the research loop as a reusable sub-workflow
  • Webhook-based human review: Replace CLI prompt with Slack approval button
  • Cost tracking: Log token usage per node to optimize expensive steps

Related Skills

  • langflow — build the same kind of multi-step flow visually, then run it over its HTTP API or expose it as an MCP server
  • dify — a self-hosted platform for the same workflows with a visual builder, knowledge bases and an API for each app
  • voltagent — choose it for a declarative createWorkflowChain (andAgent, andAll, andWhen) whose human-review step suspends and resumes over a built-in REST endpoint (/workflows/:id/executions/:executionId/resume) instead of wiring LangGraph interrupts and checkpointers yourself.

Related use cases