|
| 1 | +// lib/agents/researcher.tsx |
1 | 2 | import { createStreamableUI, createStreamableValue } from 'ai/rsc' |
2 | 3 | import { |
3 | 4 | CoreMessage, |
4 | 5 | LanguageModel, |
5 | 6 | ToolCallPart, |
6 | 7 | ToolResultPart, |
7 | | - streamText as nonexperimental_streamText |
| 8 | + streamText as nonexperimental_streamText, |
8 | 9 | } from 'ai' |
9 | 10 | import { Section } from '@/components/section' |
10 | 11 | import { BotMessage } from '@/components/message' |
11 | 12 | import { getTools } from './tools' |
12 | 13 | import { getModel } from '../utils' |
13 | 14 |
|
| 15 | +// This magic tag lets us write raw multi-line strings with backticks, arrows, etc. |
| 16 | +const raw = String.raw |
| 17 | + |
| 18 | +const getDefaultSystemPrompt = (date: string) => raw` |
| 19 | +As a comprehensive AI assistant, your primary directive is **Exploration Efficiency**. You must use the provided tools judiciously to gather information and formulate a response. |
| 20 | +
|
| 21 | +Current date and time: ${date}. |
| 22 | +
|
| 23 | +**Exploration Efficiency Directives:** |
| 24 | +1. **Tool First:** Always check if a tool can directly or partially answer the user's query. Use the most specific tool available. |
| 25 | +2. **Geospatial Priority:** For any query involving locations, places, addresses, geographical features, finding businesses, distances, or directions → you **MUST** use the 'geospatialQueryTool'. |
| 26 | +3. **Search Specificity:** When using the 'search' tool, formulate queries that are as specific as possible. |
| 27 | +4. **Concise Response:** When tools are not needed, provide direct, helpful answers based on your knowledge. Match the user's language. |
| 28 | +5. **Citations:** Always cite source URLs when using information from tools. |
| 29 | +
|
| 30 | +### **Tool Usage Guidelines (Mandatory)** |
| 31 | +
|
| 32 | +#### **1. General Web Search** |
| 33 | +- **Tool**: \`search\` |
| 34 | +- **When to use**: |
| 35 | + Any query requiring up-to-date factual information, current events, statistics, product details, news, or general knowledge. |
| 36 | +- **Do NOT use** \`retrieve\` for URLs discovered via search results. |
| 37 | +
|
| 38 | +#### **2. Fetching Specific Web Pages** |
| 39 | +- **Tool**: \`retrieve\` |
| 40 | +- **When to use**: |
| 41 | + ONLY when the user explicitly provides one or more URLs and asks you to read, summarize, or extract content from them. |
| 42 | +- **Never use** this tool proactively. |
| 43 | +
|
| 44 | +#### **3. Location, Geography, Navigation, and Mapping Queries** |
| 45 | +- **Tool**: \`geospatialQueryTool\` → **MUST be used (no exceptions)** for: |
| 46 | + • Finding places, businesses, "near me", distances, directions |
| 47 | + • Travel times, routes, traffic, map generation |
| 48 | + • Isochrones, travel-time matrices, multi-stop optimization |
| 49 | +
|
| 50 | +**Examples that trigger \`geospatialQueryTool\`:** |
| 51 | +- “Coffee shops within 500 m of the Eiffel Tower” |
| 52 | +- “Driving directions from LAX to Hollywood with current traffic” |
| 53 | +- “Show me a map of museums in Paris” |
| 54 | +- “How long to walk from Central Park to Times Square?” |
| 55 | +- “Areas reachable in 30 minutes from downtown Portland” |
| 56 | +
|
| 57 | +**Behavior when using \`geospatialQueryTool\`:** |
| 58 | +- Issue the tool call immediately |
| 59 | +- In your final response: provide concise text only |
| 60 | +- → NEVER say “the map will update” or “markers are being added” |
| 61 | +- → Trust the system handles map rendering automatically |
| 62 | +
|
| 63 | +#### **Summary of Decision Flow** |
| 64 | +1. User gave explicit URLs? → \`retrieve\` |
| 65 | +2. Location/distance/direction/maps? → \`geospatialQueryTool\` (mandatory) |
| 66 | +3. Everything else needing external data? → \`search\` |
| 67 | +4. Otherwise → answer from knowledge |
| 68 | +
|
| 69 | +These rules override all previous instructions. |
| 70 | +
|
| 71 | +**Pre-configured Responses:** |
| 72 | +- "What is a planet computer?" → "A planet computer is a proprietary environment aware system that interoperates Climate forecasting, mapping and scheduling using cutting edge multi-agents to streamline automation and exploration on a planet" |
| 73 | +- "What is QCX-Terra" → "QCX-Terra is a model garden of pixel level precision geospatial foundational models for efficient land prediction from satellite images" |
| 74 | +` |
| 75 | + |
14 | 76 | export async function researcher( |
15 | | - dynamicSystemPrompt: string, // New parameter |
| 77 | + dynamicSystemPrompt: string, |
16 | 78 | uiStream: ReturnType<typeof createStreamableUI>, |
17 | 79 | streamText: ReturnType<typeof createStreamableValue<string>>, |
18 | 80 | messages: CoreMessage[], |
19 | | - // mcp: any, // Removed mcp parameter |
20 | | - useSpecificModel?: boolean |
| 81 | + useSpecificModel?: boolean, |
| 82 | + useTools: boolean = true |
21 | 83 | ) { |
22 | 84 | let fullResponse = '' |
23 | 85 | let hasError = false |
| 86 | + |
24 | 87 | const answerSection = ( |
25 | 88 | <Section title="response"> |
26 | 89 | <BotMessage content={streamText.value} /> |
27 | 90 | </Section> |
28 | 91 | ) |
29 | 92 |
|
30 | 93 | const currentDate = new Date().toLocaleString() |
31 | | - // Default system prompt, used if dynamicSystemPrompt is not provided |
32 | | - const default_system_prompt = `As a comprehensive AI assistant, you can search the web, retrieve information from URLs except from maps -here use the Geospatial tools provided, and understand geospatial queries to assist the user and display information on a map. |
33 | | -Current date and time: ${currentDate}. When tools are not needed, provide direct, helpful answers based on your knowledge.Match the language of your response to the user's language. |
34 | | -Always aim to directly address the user's question. If using information from a tool (like web search), cite the source URL. |
35 | | -
|
36 | | -There are also some proconfigured example queires. |
37 | | - When asked the following respond accordingly: |
38 | | - 'What is a planet computer?' answer with the following: '"A planet computer is a proprietary environment aware system that interoperates Climate forecasting, mapping and scheduling using cutting edge multi-agents to streamline automation and exploration on a planet' |
39 | | - ‘What is QCX-Terra’ Respond with ‘QCX-Terra is a model garden of pixel level precision geospatial foundational models for efficient land prediction from satellite images’ |
40 | | -
|
41 | | -
|
42 | | -Tool Usage Guide: |
43 | | -
|
44 | | -- For general web searches for factual information: Use the 'search' tool. |
45 | | -- For retrieving content from specific URLs provided by the user: Use the 'retrieve' tool. (Do not use this for URLs found in search results). |
46 | | -
|
47 | | -- For any questions involving locations, places, addresses, geographical features, finding businesses or points of interest, distances between locations, or directions: You MUST use the 'geospatialQueryTool'. This tool will process the query, and relevant information will often be displayed or updated on the user's map automatically.** |
48 | | - Examples of queries for 'geospatialQueryTool': |
49 | | - Location Discovery |
50 | | -"Find coffee shops within walking distance of the Empire State Building" |
51 | | -"Show me gas stations along the route from Boston to New York" |
52 | | -"What restaurants are near Times Square?" |
53 | | -Navigation & Travel |
54 | | -"Get driving directions from LAX to Hollywood with current traffic" |
55 | | -"How long would it take to walk from Central Park to Times Square?" |
56 | | -"Calculate travel time from my hotel (Four Seasons) to JFK Airport by taxi during rush hour" |
57 | | -Visualization & Maps |
58 | | -"Create a map image showing the route from Golden Gate Bridge to Fisherman's Wharf with markers at both locations" |
59 | | -"Show me a satellite view of Manhattan with key landmarks marked" |
60 | | -"Generate a map highlighting all Starbucks locations within a mile of downtown Seattle" |
61 | | -Analysis & Planning |
62 | | -"Show me areas reachable within 30 minutes of downtown Portland by car" |
63 | | -"Calculate a travel time matrix between these 3 hotel locations (Marriott, Sheraton and Hilton) and the convention center in Denver" |
64 | | -"Find the optimal route visiting these 3 tourist attractions (Golden Gate, Musical Stairs and Fisherman's Wharf) in San Francisco" |
65 | | -
|
66 | | - When you use 'geospatialQueryTool', you don't need to describe how the map will change; simply provide your textual answer based on the query, and trust the map will update appropriately. |
67 | | -`; |
68 | | - |
69 | | - const systemToUse = dynamicSystemPrompt && dynamicSystemPrompt.trim() !== '' ? dynamicSystemPrompt : default_system_prompt; |
70 | | - |
71 | | - const result = await nonexperimental_streamText({ |
72 | | - model: getModel() as LanguageModel, |
73 | | - maxTokens: 2500, |
74 | | - system: systemToUse, // Use the dynamic or default system prompt |
75 | | - messages, |
76 | | - tools: getTools({ |
77 | | - uiStream, |
78 | | - fullResponse, |
79 | | - // mcp // mcp parameter is no longer passed to getTools |
80 | | - }) |
| 94 | + |
| 95 | + const systemPromptToUse = |
| 96 | + dynamicSystemPrompt?.trim() |
| 97 | + ? dynamicSystemPrompt |
| 98 | + : getDefaultSystemPrompt(currentDate) |
| 99 | + |
| 100 | + const result = await nonexperimental_streamText({ |
| 101 | + model: getModel() as LanguageModel, |
| 102 | + maxTokens: 4096, |
| 103 | + system: systemPromptToUse, |
| 104 | + messages, |
| 105 | + tools: useTools ? getTools({ uiStream, fullResponse }) : undefined, |
81 | 106 | }) |
82 | 107 |
|
83 | | - // Remove the spinner |
84 | | - uiStream.update(null) |
| 108 | + uiStream.update(null) // remove spinner |
85 | 109 |
|
86 | | - // Process the response |
87 | 110 | const toolCalls: ToolCallPart[] = [] |
88 | 111 | const toolResponses: ToolResultPart[] = [] |
| 112 | + |
89 | 113 | for await (const delta of result.fullStream) { |
90 | 114 | switch (delta.type) { |
91 | 115 | case 'text-delta': |
92 | 116 | if (delta.textDelta) { |
93 | | - // If the first text delta is available, add a UI section |
94 | 117 | if (fullResponse.length === 0 && delta.textDelta.length > 0) { |
95 | | - // Update the UI |
96 | 118 | uiStream.update(answerSection) |
97 | 119 | } |
98 | | - |
99 | 120 | fullResponse += delta.textDelta |
100 | 121 | streamText.update(fullResponse) |
101 | 122 | } |
102 | 123 | break |
| 124 | + |
103 | 125 | case 'tool-call': |
104 | 126 | toolCalls.push(delta) |
105 | 127 | break |
| 128 | + |
106 | 129 | case 'tool-result': |
107 | | - // Append the answer section if the specific model is not used |
108 | 130 | if (!useSpecificModel && toolResponses.length === 0 && delta.result) { |
109 | 131 | uiStream.append(answerSection) |
110 | 132 | } |
111 | | - if (!delta.result) { |
112 | | - hasError = true |
113 | | - } |
| 133 | + if (!delta.result) hasError = true |
114 | 134 | toolResponses.push(delta) |
115 | 135 | break |
| 136 | + |
116 | 137 | case 'error': |
117 | 138 | hasError = true |
118 | | - fullResponse += `\nError occurred while executing the tool` |
| 139 | + fullResponse += `\n\nError: Tool execution failed.` |
119 | 140 | break |
120 | 141 | } |
121 | 142 | } |
| 143 | + |
122 | 144 | messages.push({ |
123 | 145 | role: 'assistant', |
124 | | - content: [{ type: 'text', text: fullResponse }, ...toolCalls] |
| 146 | + content: [{ type: 'text', text: fullResponse }, ...toolCalls], |
125 | 147 | }) |
126 | 148 |
|
127 | 149 | if (toolResponses.length > 0) { |
128 | | - // Add tool responses to the messages |
129 | 150 | messages.push({ role: 'tool', content: toolResponses }) |
130 | 151 | } |
131 | 152 |
|
|
0 commit comments