import { icepick } from "@hatchet-dev/icepick";
import z from "zod";
import { planSearchTool } from "@tools/plan-search";
import { searchTool } from "@tools/search";
import { websiteToMdTool } from "@tools/website-to-md";
import { extractFactsTool } from "@tools/extract-facts";
import { judgeFactsTool } from "@tools/judge-facts";
import { judgeResultsTool } from "@tools/judge-results";
import { summarizeTool } from "@tools/summarize";
const ResearchInput = z.object({
query: z.string().describe("The research question or topic to investigate"),
});
const ResearchOutput = z.object({
summary: z.string().describe("Comprehensive research summary with citations"),
sources: z
.array(
z.object({
index: z.number(),
url: z.string(),
title: z.string(),
})
)
.describe("Sources used in the research"),
iterations: z.number().describe("Number of research iterations performed"),
});
export const researchAgent = icepick.agent({
name: "deep-research-agent",
inputSchema: ResearchInput,
outputSchema: ResearchOutput,
description: "Conducts comprehensive research with iterative refinement",
executionTimeout: "15m",
fn: async (input, ctx) => {
let allFacts: any[] = [];
let allSources: any[] = [];
let iteration = 0;
const maxIterations = 3;
while (iteration < maxIterations) {
iteration++;
ctx.logger.info(`Starting research iteration ${iteration}`);
// Plan searches based on existing knowledge gaps
const searchPlan = await planSearchTool.run({
query: input.query,
existingFacts: allFacts,
iteration,
});
// Execute planned searches
for (const searchQuery of searchPlan.queries) {
const searchResults = await searchTool.run({
query: searchQuery,
context: input.query,
});
// Convert websites to markdown and extract facts
for (const result of searchResults.results) {
if (allSources.some((s) => s.url === result.url)) continue;
const markdown = await websiteToMdTool.run({
url: result.url,
context: input.query,
});
const facts = await extractFactsTool.run({
content: markdown.content,
query: input.query,
sourceUrl: result.url,
sourceTitle: result.title,
});
allFacts.push(...facts.facts);
allSources.push({
index: allSources.length + 1,
url: result.url,
title: result.title,
});
}
}
// Evaluate if we have sufficient information
const factJudgment = await judgeFactsTool.run({
query: input.query,
facts: allFacts,
});
if (factJudgment.sufficient || iteration >= maxIterations) {
break;
}
}
// Generate comprehensive summary
const summary = await summarizeTool.run({
query: input.query,
facts: allFacts,
sources: allSources,
});
// Final quality check
const resultJudgment = await judgeResultsTool.run({
query: input.query,
summary: summary.summary,
sources: allSources,
});
return {
summary: summary.summary,
sources: allSources,
iterations: iteration,
};
},
});