Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run typecheck

build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run build
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,23 @@ Multi-agent system built on the Anthropic TypeScript SDK (`@anthropic-ai/sdk`).

## Architecture

A **coordinator** agent (Opus 4.8) orchestrates two specialist subagents via tool calls:
A **coordinator** agent (Opus 4.8) orchestrates three specialist subagents via tool calls:

- `delegate_research(topic)` → research subagent (Haiku 4.5 + `web_search`) returns a JSON summary with sources.
- `delegate_fact_check(topic)` → fact-checker subagent (Haiku 4.5) verifies findings for contradictions, unsupported claims, and gaps.
- `delegate_writing(topic)` → writer subagent (Opus 4.8 + adaptive thinking) produces the final markdown report.

Each subagent runs its own tool-runner loop. The coordinator decides when to fan out research and when to write.
Each subagent runs its own loop. The coordinator follows a research → verify → write workflow.

```
user request
│
▼
coordinator (Opus 4.8)
├── delegate_research ──▶ researcher (Haiku 4.5 + web_search)
├── delegate_research ──▶ researcher
└── delegate_writing ──▶ writer (Opus 4.8)
├── delegate_research ──▶ researcher (Haiku 4.5 + web_search)
├── delegate_research ──▶ researcher
├── delegate_fact_check ──▶ fact-checker (Haiku 4.5)
└── delegate_writing ──▶ writer (Opus 4.8)
```

## Setup
Expand All @@ -44,6 +46,7 @@ npm run research -- "TypeScript 5.7 changes"

- `src/client.ts` — shared Anthropic client + model IDs.
- `src/agents/researcher.ts` — research subagent (web search + structured summary tool).
- `src/agents/fact-checker.ts` — fact-checker subagent (verifies findings before writing).
- `src/agents/writer.ts` — writer subagent (adaptive thinking, no tools).
- `src/coordinator.ts` — top-level coordinator exposing subagents as tools.
- `src/index.ts` — CLI entry point.
65 changes: 44 additions & 21 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@anthropic-ai/sdk": "^0.88.0",
"zod": "^3.23.8"
"@anthropic-ai/sdk": "^0.105.0",
"zod": "^4.4.3"
},
"devDependencies": {
"@types/node": "^22.10.0",
"@types/node": "^26.0.0",
"tsx": "^4.19.0",
"typescript": "^5.7.0"
"typescript": "^6.0.3"
}
}
26 changes: 26 additions & 0 deletions src/agents/fact-checker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { client, FAST_MODEL } from "../client.js";

export async function factCheck(
topic: string,
findings: string[],
): Promise<string> {
const response = await client.messages.create({
model: FAST_MODEL,
max_tokens: 4000,
system:
"You are a fact-checker subagent. Given a topic and research findings, identify any contradictions, unsupported claims, or gaps. Return a JSON object with: { verified: string[], disputed: string[], gaps: string[] }. Be concise.",
messages: [
{
role: "user",
content: `Topic: ${topic}\n\nFindings to verify:\n${findings
.map((f, i) => `[${i + 1}] ${f}`)
.join("\n\n")}\n\nAnalyze these findings now.`,
},
],
});

return response.content
.filter((b) => b.type === "text")
.map((b) => (b.type === "text" ? b.text : ""))
.join("");
}
25 changes: 21 additions & 4 deletions src/coordinator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { z } from "zod";
import { client, MODEL } from "./client.js";
import { research } from "./agents/researcher.js";
import { writeReport } from "./agents/writer.js";
import { factCheck } from "./agents/fact-checker.js";

// Hand-off pattern: the coordinator exposes subagents as tools.
// Each tool call delegates to a specialized agent and returns its output.
Expand All @@ -23,6 +24,21 @@ const delegateResearch = betaZodTool({
},
});

const delegateFactCheck = betaZodTool({
name: "delegate_fact_check",
description:
"Delegate fact-checking to the fact-checker subagent. Call after research and before writing to verify findings. Returns verified/disputed/gaps analysis.",
inputSchema: z.object({
topic: z.string(),
}),
run: async ({ topic }) => {
if (findings.length === 0) {
return "Error: call delegate_research at least once before fact-checking.";
}
return await factCheck(topic, findings);
},
});

const delegateWriting = betaZodTool({
name: "delegate_writing",
description:
Expand All @@ -44,12 +60,13 @@ export async function runCoordinator(userRequest: string): Promise<string> {
max_tokens: 32000,
thinking: { type: "adaptive" },
output_config: { effort: "high" },
system: `You are a coordinator agent orchestrating two subagents:
system: `You are a coordinator agent orchestrating three subagents:
- delegate_research(topic): fan-out research queries; call multiple times in parallel for independent subtopics.
- delegate_writing(topic): once you have enough findings, produce the final report.
- delegate_fact_check(topic): after research, verify findings for contradictions, unsupported claims, and gaps.
- delegate_writing(topic): once findings are verified, produce the final report.

Plan first, then delegate. Return the final report verbatim.`,
tools: [delegateResearch, delegateWriting],
Workflow: research → fact-check → write. Return the final report verbatim.`,
tools: [delegateResearch, delegateFactCheck, delegateWriting],
messages: [{ role: "user", content: userRequest }],
});

Expand Down
Loading