Skip to content

starmorph/render-researcher-workflow

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

30 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Render Research Agent

An autonomous AI research agent that searches the web, analyzes content with Claude, and builds a living knowledge base — powered by Render Workflows.

Deploy to Render

Live demo →

Architecture Overview

Research Intelligence Feed

Contents: What you'll learn Architecture How it works Project structure Deploy Local development Environment variables Key patterns Tech stack Why Render? More templates Learn more

What you'll learn

  • How to define and orchestrate multi-step workflow tasks with @renderinc/sdk
  • The stateless workflow pattern: compute in workflows, persistence in callers
  • Fan-out parallelism with Promise.all() across search and summarization tasks
  • Blueprint + Dashboard hybrid deployment for workflow services
  • Building SSR pages with Hono JSX — no React, no frontend build step

Architecture

graph TD
    CRON["Cron Job (daily)"] -->|startTask| ORCH["orchestrate_research"]
    WEB["Web Service"] -->|startTask| ORCH
    ORCH --> EXA["search_exa"]
    ORCH --> HN["search_hackernews"]
    EXA --> SUM["summarize_source × N"]
    HN --> SUM
    SUM -->|return results| CRON
    CRON -->|persist| DB[(PostgreSQL)]
    WEB -->|read| DB

    style ORCH fill:#6c47ff,color:#fff
    style EXA fill:#2d3748,color:#fff
    style HN fill:#2d3748,color:#fff
    style SUM fill:#2d3748,color:#fff
Loading

Four services, one repo:

Service Type Blueprint? Role
researcher-web Web Service Yes Hono SSR reader UI + JSON API
researcher-cron Cron Job Yes Triggers the workflow daily via SDK
researcher-db PostgreSQL Yes Sources, insights, run history
researcher-workflow Workflow Dashboard Stateless research pipeline

Note: Workflows are created via the Render Dashboard, not render.yaml. The Blueprint deploys the web service, cron job, and database automatically. You'll create the workflow service separately in Step 2.

How it works

  1. The cron job fires daily at 1 PM UTC (or you trigger manually from the web UI)
  2. It calls startTask("researcher-workflow/orchestrate_research") via the Render SDK, passing the research topic and config, and polls for completion
  3. The workflow fans out: search_exa and search_hackernews run as parallel subtasks
  4. For each result, summarize_source calls Claude via Vercel AI SDK's generateObject to extract structured insights with guaranteed schema compliance
  5. Results flow back to the cron job, which deduplicates by URL hash and persists new sources + insights to Postgres
  6. The web service reads from Postgres and renders the knowledge base as server-side HTML

The stateless workflow pattern

Workflow tasks are pure compute — they receive arguments, do work, and return JSON-serializable results. No database connections, no side effects, no shared state. The caller (cron job or web service) owns persistence.

This pattern keeps workflows portable, testable, and cacheable. It matches the convention across all official Render workflow examples.

Project structure

render-researcher-workflow/
├── render.yaml                # Blueprint: web, cron, postgres
├── shared/
│   └── types.ts               # Cross-service type contracts
├── web/                       # Web + cron service package
│   ├── src/
│   │   ├── index.tsx          # Hono server with JSX SSR
│   │   ├── trigger.ts         # Cron entry point
│   │   ├── config.ts          # Zod env validation
│   │   ├── db/
│   │   │   ├── schema.ts      # Drizzle schema (3 tables)
│   │   │   └── client.ts      # Postgres client
│   │   └── pages/
│   │       ├── layout.tsx     # Dark-themed HTML shell
│   │       ├── home.tsx       # Insight cards with tags + confidence
│   │       └── status.tsx     # Run history + manual trigger
│   ├── drizzle.config.ts
│   ├── package.json
│   └── tsconfig.json
├── workflows/                 # Workflow service package
│   ├── src/
│   │   ├── main.ts            # orchestrate_research (root task)
│   │   ├── search.ts          # search_exa + search_hackernews
│   │   ├── summarize.ts       # summarize_source (Claude)
│   │   └── types.ts           # Re-exports from shared/
│   ├── package.json
│   └── tsconfig.json
└── .env.example

The web package sets rootDir: ".." in tsconfig.json to import from shared/types.ts. The workflows package uses rootDir: "src" with inlined types for a flat dist/ layout.

Deploy

Prerequisites

Step 1: Deploy the Blueprint

Click the Deploy to Render button above, or:

# Fork and clone the repo
git clone https://github.com/starmorph/render-researcher-workflow.git
cd render-researcher-workflow

Then create a new Blueprint in the Render Dashboard pointing to your repo. This deploys:

  • researcher-web — the Hono web service
  • researcher-cron — the daily cron trigger
  • researcher-db — managed PostgreSQL

Set these environment variables on the web and cron services:

Variable Value
RENDER_API_KEY Your Render API key
WORKFLOW_SLUG researcher-workflow (or whatever you name the workflow service)

DATABASE_URL is injected automatically by the Blueprint.

Step 2: Create the workflow service

In the Render Dashboard:

  1. Click New > Workflow
  2. Connect the same repository
  3. Set Root Directory to workflows/
  4. Build Command: pnpm install && pnpm run build
  5. Start Command: pnpm start
  6. Add environment variables:
Variable Value
ANTHROPIC_API_KEY Your Anthropic API key
EXA_API_KEY Your Exa API key
  1. Click Deploy Workflow

Once deployed, the cron job will trigger your first research run at the next scheduled time. Or hit the "Trigger Run" button on the /status page.

Local development

Workflow tasks

Use the Render CLI to run the workflow dev server locally:

cd workflows
pnpm install
render workflows dev -- pnpm start

In a separate terminal, verify your tasks registered:

render workflows tasks list --local

You should see four tasks: orchestrate_research, search_exa, search_hackernews, and summarize_source.

Web service

The web service requires a PostgreSQL connection:

cd web
pnpm install
cp ../.env.example .env    # Fill in DATABASE_URL
npx drizzle-kit push       # Create tables
pnpm run dev               # Starts on http://localhost:3000

Environment variables

Variable Services Required Description
ANTHROPIC_API_KEY Workflow Yes Claude API key for summarization
EXA_API_KEY Workflow Yes Exa neural search API key
DATABASE_URL Web, Cron Yes Postgres connection string (auto-injected by Blueprint)
RENDER_API_KEY Web, Cron Yes Render API key for triggering workflow runs
WORKFLOW_SLUG Web, Cron Yes Name of your workflow service (e.g., researcher-workflow)
RESEARCH_TOPIC Web, Cron No Search topic (default: agentic AI frameworks MCP autonomous workflows)
PORT Web No Server port (default: 3000)

Key patterns

Parallel fan-out with subtasks

The orchestrator task calls search and summarization subtasks in parallel. Each subtask runs in its own isolated container with independent retry logic:

// workflows/src/main.ts
const searchPromises = [
  Promise.resolve(searchExa(topic, config.lookbackHours)),
  Promise.resolve(searchHackerNews(topic, config.lookbackHours)),
];
const allSources = (await Promise.all(searchPromises)).flat();

const summaryPromises = topSources.map((source) =>
  Promise.resolve(
    summarizeSource(source.title, source.url, source.content, topic),
  ),
);
const summaries = await Promise.all(summaryPromises);

task() returns TResult | Promise<TResult>, so wrap calls in Promise.resolve() for Promise.all() compatibility.

Structured LLM output with Zod

The summarize_source task uses Vercel AI SDK's generateObject with a Zod schema to guarantee the LLM returns valid, typed data — no prompt engineering for JSON formatting, no runtime parsing:

// workflows/src/summarize.ts
const result = await generateObject({
  model: anthropic("claude-sonnet-4-20250514"),
  schema: insightSchema, // Zod schema → guaranteed compliance
  prompt: `Analyze this article...`,
});
return result.object; // Fully typed InsightSummary

Triggering workflows with startTask + polling

Both the cron job and web endpoint use startTask followed by polling — the SDK's runTask uses SSE internally, which has a known event-name mismatch in v0.5.1. The polling pattern is more reliable:

// Start the task (returns immediately)
const started = await render.workflows.startTask(
  "researcher-workflow/orchestrate_research",
  [topic, config],
);

// Poll until terminal status
let result;
for (let i = 0; i < 120; i++) {
  await new Promise((r) => setTimeout(r, 2000));
  const status = await render.workflows.getTaskRun(started.taskRunId);
  if (status.status === "succeeded") { result = status; break; }
  if (status.status === "failed") { throw new Error(`Workflow failed`); }
}

The cron job exits with code 1 on failure so Render marks the run as failed. The web endpoint catches errors, persists them to the runs table, and redirects to /status.

Tech stack

Layer Choice Why
Runtime Node.js, TypeScript Render Workflows SDK is TypeScript-native
Web framework Hono ~14kb, JSX SSR without React or a build step
ORM Drizzle Type-safe SQL, zero-codegen schema
LLM Claude Sonnet via Vercel AI SDK generateObject for guaranteed structured output
Search Exa + HN Algolia Neural search + community signal
Validation Zod Shared schemas between LLM output and env config
Infra Render Blueprint One-click deploy for web, cron, and Postgres

Why deploy AI agents on Render?

Capability What it means
Durable Execution Tasks retry automatically with configurable backoff. Your agent never loses progress mid-run.
Scale to Zero Pay nothing when idle. Per-second billing when running — no reserved instances.
Sub-Second Cold Starts Each task runs in an isolated container that spins up in under a second. No cold start tax.
Parallel Fan-Out Scatter work across hundreds of concurrent tasks with Promise.all(). The platform handles orchestration.
Built-in Observability Logs, metrics, and per-task run status in the Dashboard — no Datadog or Grafana setup required.
Infrastructure as Code Define your web service, cron job, and database in one render.yaml and deploy with a click.

Read more about Render Workflows →

More workflow templates

Deploy these to see other Render Workflows patterns in action:

Template What it demonstrates
Data Processor Parallel processing of 400K records using hash-based sharding
SEO Audit Parallel page crawling with 5-point SEO analysis
Voice Agent LiveKit voice AI with backend workflow processing
Webhook Workflow Async webhook processing without blocking responses
Blog Thumbnail Generator Parallel AI image generation across multiple models

Learn more

About

An autonomous AI research agent powered by Render Workflows — search, analyze, and synthesize web content into a living knowledge base

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors