mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 21:01:08 +02:00
feat(news-research): add depth=shallow|deep option to research_news tool
The existing RSS-based path stays the default (shallow, free). `depth=deep` fans out to two research agents in parallel (Perplexity Sonar first, then Gemini Grounding if available) via the new mana-research /v1/research/compare endpoint, merges their answers + citations into a single markdown context block that the AI can cite from, and attaches the runId so the user can revisit the comparison in Research Lab later. AI missions keep calling the tool with no depth arg — they still get free RSS results. Only explicit depth=deep consumes credits. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
8f0a74b2e7
commit
011946bb4b
1 changed files with 73 additions and 2 deletions
|
|
@ -12,9 +12,18 @@
|
|||
|
||||
import type { ModuleTool } from '$lib/data/tools/types';
|
||||
import { discoverByQuery, searchFeeds } from './api';
|
||||
import { compareResearch } from '$lib/modules/research-lab/api';
|
||||
import type { AgentAnswer } from '$lib/modules/research-lab/types';
|
||||
|
||||
const MAX_RESULTS = 15;
|
||||
|
||||
const DEEP_AGENT_PREFERENCE = [
|
||||
'perplexity-sonar',
|
||||
'gemini-grounding',
|
||||
'openai-responses',
|
||||
'claude-web-search',
|
||||
] as const;
|
||||
|
||||
function formatContext(
|
||||
query: string,
|
||||
feedCount: number,
|
||||
|
|
@ -45,6 +54,28 @@ function formatContext(
|
|||
return lines.join('\n');
|
||||
}
|
||||
|
||||
function formatDeepContext(query: string, providers: string[], answers: AgentAnswer[]): string {
|
||||
const lines = [
|
||||
`# Deep Research — Query: ${query}`,
|
||||
`Agents consulted: ${providers.join(', ')}`,
|
||||
'',
|
||||
];
|
||||
for (let i = 0; i < answers.length; i++) {
|
||||
const provider = providers[i];
|
||||
const answer = answers[i];
|
||||
if (!answer) continue;
|
||||
lines.push(`## ${provider}`, '', answer.answer, '');
|
||||
if (answer.citations.length > 0) {
|
||||
lines.push('### Sources');
|
||||
for (const cit of answer.citations) {
|
||||
lines.push(`- [${cit.title || cit.url}](${cit.url})`);
|
||||
}
|
||||
lines.push('');
|
||||
}
|
||||
}
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
export const newsResearchTools: ModuleTool[] = [
|
||||
{
|
||||
name: 'research_news',
|
||||
|
|
@ -58,16 +89,23 @@ export const newsResearchTools: ModuleTool[] = [
|
|||
description: 'Thema oder Stichworte (z.B. "KI-Regulierung EU")',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'depth',
|
||||
type: 'string',
|
||||
description:
|
||||
'"shallow" (Standard, kostenlos — RSS-Feeds durchsuchen) oder "deep" (kostet Credits — fragt 1–2 Research-Agents wie Perplexity/Gemini mit web_search).',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: 'language',
|
||||
type: 'string',
|
||||
description: 'Sprache der Feeds (de/en); optional',
|
||||
description: 'Sprache der Feeds (de/en); optional, nur für shallow',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
description: `Maximale Anzahl Treffer (Standard ${MAX_RESULTS})`,
|
||||
description: `Maximale Anzahl Treffer im shallow-Modus (Standard ${MAX_RESULTS})`,
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
|
|
@ -76,9 +114,42 @@ export const newsResearchTools: ModuleTool[] = [
|
|||
if (!query) {
|
||||
return { success: false, message: 'query is required' };
|
||||
}
|
||||
const depth = String(params.depth ?? 'shallow').toLowerCase();
|
||||
const language = typeof params.language === 'string' ? params.language : undefined;
|
||||
const limit = Math.min(Math.max(Number(params.limit) || MAX_RESULTS, 1), 50);
|
||||
|
||||
if (depth === 'deep') {
|
||||
// Ask 2 research agents in parallel; prefer cheapest-first order.
|
||||
const providers = [...DEEP_AGENT_PREFERENCE].slice(0, 2);
|
||||
try {
|
||||
const res = await compareResearch(query, providers);
|
||||
const answers = res.results.map((r) => r.data?.answer).filter(Boolean) as AgentAnswer[];
|
||||
const successful = res.results.filter((r) => r.success);
|
||||
const context = formatDeepContext(
|
||||
query,
|
||||
successful.map((r) => r.provider),
|
||||
answers
|
||||
);
|
||||
return {
|
||||
success: true,
|
||||
message: `Deep Research: ${successful.length} / ${res.results.length} Agents geantwortet.`,
|
||||
data: {
|
||||
context,
|
||||
runId: res.runId,
|
||||
answers: answers.map((a, i) => ({
|
||||
provider: successful[i]?.provider,
|
||||
answer: a.answer,
|
||||
citations: a.citations,
|
||||
})),
|
||||
},
|
||||
};
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : 'Deep research fehlgeschlagen';
|
||||
return { success: false, message };
|
||||
}
|
||||
}
|
||||
|
||||
// shallow (default): RSS discovery + keyword search
|
||||
const discovered = await discoverByQuery(query, language);
|
||||
const feedUrls = discovered.feeds.slice(0, 10).map((f) => f.url);
|
||||
if (feedUrls.length === 0) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue