Files
ai/gateway/src/tools/platform/web-explore-agent.tool.ts

68 lines
3.1 KiB
TypeScript

import { DynamicStructuredTool } from '@langchain/core/tools';
import { z } from 'zod';
import type { FastifyBaseLogger } from 'fastify';
import type { WebExploreSubagent } from '../../harness/subagents/web-explore/index.js';
import type { SubagentContext } from '../../harness/subagents/base-subagent.js';
import type { HarnessEvent } from '../../harness/harness-events.js';
export interface WebExploreAgentToolConfig {
webExploreSubagent: WebExploreSubagent;
context: SubagentContext;
logger: FastifyBaseLogger;
}
/**
* Creates a LangChain tool that delegates to the web-explore subagent.
* The subagent decides whether to use web search or arXiv based on the instruction.
*/
export function createWebExploreAgentTool(config: WebExploreAgentToolConfig): DynamicStructuredTool & { streamFunc: (args: { instruction: string }, signal?: AbortSignal) => AsyncGenerator<HarnessEvent, string> } {
const { webExploreSubagent, context, logger } = config;
async function* streamFunc({ instruction }: { instruction: string }, signal?: AbortSignal): AsyncGenerator<HarnessEvent, string> {
logger.info({ instruction: instruction.substring(0, 100) }, 'Streaming web-explore subagent');
const gen = webExploreSubagent.streamEvents(context, instruction, signal);
let step: IteratorResult<HarnessEvent, string>;
while (!(step = await gen.next()).done) {
yield step.value;
}
return step.value;
}
const tool = new DynamicStructuredTool({
name: 'web_explore',
description: `Search the EXTERNAL web or academic databases and return a summarized answer.
Use this tool ONLY for external, public information:
- Current events, news, or real-time information
- External documentation, tutorials, or how-to guides for third-party libraries/tools
- Academic papers, research findings, or scientific topics
- Any topic requiring external sources
NEVER use this tool for:
- Questions about the Dexorder platform itself (workspace tools, chartState, indicators, strategies)
- Internal API usage (workspace_patch, workspace_read, etc.) — consult the system prompt instead
- Anything that can be answered from the context already available
The subagent will search the web (or arXiv for academic queries), fetch relevant content, and return a markdown summary with cited sources.`,
schema: z.object({
instruction: z.string().describe(
'What to search for and summarize. Be specific — include the topic, what aspects matter, ' +
'and any context that helps narrow the search (e.g. "recent papers on momentum factor in equities" ' +
'or "how to configure rate limiting in Fastify").'
),
}),
func: async ({ instruction }: { instruction: string }): Promise<string> => {
logger.info({ instruction: instruction.substring(0, 100) }, 'Delegating to web-explore subagent');
try {
return await webExploreSubagent.execute(context, instruction);
} catch (error) {
logger.error({ error, errorMessage: (error as Error)?.message }, 'Web explore subagent failed');
throw error;
}
},
});
return Object.assign(tool, { streamFunc });
}