Skip to content

Planet computer prompting #566

@ngoiyaeric

Description

@ngoiyaeric

Improving Planet Computer Prompting in QCX

Based on the codebase analysis, the "planet computer" prompting system in QCX can be improved through several key areas:

Current Prompting Architecture

The system uses a multi-layered prompting approach with:

  • Main system prompt in researcher.tsx with "Exploration Efficiency Directives" 1
  • Task manager prompt for query classification 2
  • Writer prompt for response synthesis 3
  • Hardcoded responses for specific queries 4

Recommended Improvements

1. Centralize Prompt Management

Currently, prompts are scattered across multiple agent files. Consider creating a centralized prompt configuration system:

// lib/prompts/index.ts - New file suggestion
export const PLANET_COMPUTER_PROMPTS = {
  researcher: {
    base: "...", // Current researcher prompt
    enhanced: "..." // Improved version
  },
  taskManager: "...",
  writer: "..."
}

2. Enhance the Researcher Prompt

The current researcher prompt 5 could be improved by:

  • Adding more specific geospatial reasoning guidelines
  • Including better error handling instructions
  • Expanding the "Exploration Efficiency" principles with planet-specific context

3. Dynamic Prompt Injection

Instead of hardcoded responses in app/actions.tsx 4 , implement dynamic prompt injection:

// In getDefaultSystemPrompt()
const planetComputerContext = userHasAskedAboutPlanetComputer 
  ? `\n\nAdditional Context: You are a planet computer system...`
  : '';

4. Improve Settings UI Integration

The current system prompt form 6 only allows editing one global prompt. Consider:

  • Adding separate tabs for each agent's prompt
  • Including prompt templates for different use cases
  • Adding prompt versioning and rollback capabilities

5. Context-Aware Prompt Selection

Implement logic to select different prompts based on:

  • Query type (geospatial vs general knowledge)
  • User's selected model 7
  • Available tools and integrations

6. Enhanced Pre-configured Responses

Update the hardcoded responses to be more informative and include actionable next steps:

// Improved response in app/actions.tsx
const definition = userInput.toLowerCase().trim() === 'what is a planet computer?'
  ? `A planet computer is a proprietary environment aware system that interoperates weather forecasting, mapping and scheduling using cutting edge multi-agents to streamline automation and exploration on a planet. 

Try asking me to:
• Analyze satellite imagery of any location
• Find routes between points with real-time traffic
• Search for places of interest near you
• Generate maps with custom overlays

Available for Pro and Enterprise customers. [QCX Pricing](https://www.queue.cx/#pricing)`

Implementation Priority

  1. High Priority: Centralize prompt management and enhance the researcher prompt
  2. Medium Priority: Improve settings UI and implement dynamic prompt injection
  3. Low Priority: Add context-aware selection and enhanced pre-configured responses

These improvements would make the "planet computer" prompting more coherent, maintainable, and user-friendly while preserving the existing multi-agent architecture.

Notes

  • The current system already has a solid foundation with multi-agent orchestration 8
  • The settings UI 9 provides a good starting point for prompt management
  • Consider maintaining backward compatibility when implementing changes to the prompt system

Wiki pages you might want to explore:

Citations

File: lib/agents/researcher.tsx (L20-80)

const getDefaultSystemPrompt = (date: string, drawnFeatures?: DrawnFeature[]) => raw`
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.

Current date and time: ${date}.

${drawnFeatures && drawnFeatures.length > 0 ? `The user has drawn the following features on the map for your reference:
${drawnFeatures.map(f => `- ${f.type} with measurement ${f.measurement}`).join('\n')}
Use these user-drawn areas/lines as primary areas of interest for your analysis if applicable to the query.` : ''}

**Exploration Efficiency Directives:**
1. **Tool First:** Always check if a tool can directly or partially answer the user's query. Use the most specific tool available.
2. **Geospatial Priority:** For any query involving locations, places, addresses, geographical features, finding businesses, distances, or directions → you **MUST** use the 'geospatialQueryTool'.
3. **Search Specificity:** When using the 'search' tool, formulate queries that are as specific as possible.
4. **Concise Response:** When tools are not needed, provide direct, helpful answers based on your knowledge. Match the user's language.
5. **Citations:** Always cite source URLs when using information from tools.

### **Tool Usage Guidelines (Mandatory)**

#### **1. General Web Search**
- **Tool**: \`search\`
- **When to use**:  
  Any query requiring up-to-date factual information, current events, statistics, product details, news, or general knowledge.
- **Do NOT use** \`retrieve\` for URLs discovered via search results.

#### **2. Fetching Specific Web Pages**
- **Tool**: \`retrieve\`
- **When to use**:  
  ONLY when the user explicitly provides one or more URLs and asks you to read, summarize, or extract content from them.
- **Never use** this tool proactively.

#### **3. Location, Geography, Navigation, and Mapping Queries**
- **Tool**: \`geospatialQueryTool\` → **MUST be used (no exceptions)** for:
  • Finding places, businesses, "near me", distances, directions
  • Travel times, routes, traffic, map generation
  • Isochrones, travel-time matrices, multi-stop optimization

**Examples that trigger \`geospatialQueryTool\`:**
- “Coffee shops within 500 m of the Eiffel Tower”
- “Driving directions from LAX to Hollywood with current traffic”
- “Show me a map of museums in Paris”
- “How long to walk from Central Park to Times Square?”
- “Areas reachable in 30 minutes from downtown Portland”

**Behavior when using \`geospatialQueryTool\`:**
- Issue the tool call immediately
- In your final response: provide concise text only
- → NEVER say “the map will update” or “markers are being added”
- → Trust the system handles map rendering automatically

#### **Summary of Decision Flow**
1. User gave explicit URLs? → \`retrieve\`
2. Location/distance/direction/maps? → \`geospatialQueryTool\` (mandatory)
3. Everything else needing external data? → \`search\`
4. Otherwise → answer from knowledge

These rules override all previous instructions.

**Pre-configured Responses:**
- "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"
- "What is QCX-Terra" → "QCX-Terra is a model garden of pixel level precision geospatial foundational models for efficient land prediction from satellite images"
`

File: lib/agents/researcher.tsx (L82-166)

export async function researcher(
  dynamicSystemPrompt: string,
  uiStream: ReturnType<typeof createStreamableUI>,
  streamText: ReturnType<typeof createStreamableValue<string>>,
  messages: CoreMessage[],
  mapProvider: MapProvider,
  useSpecificModel?: boolean,
  drawnFeatures?: DrawnFeature[]
) {
  let fullResponse = ''
  let hasError = false

  const answerSection = (
    <Section title="response">
      <BotMessage content={streamText.value} />
    </Section>
  )

  const currentDate = new Date().toLocaleString()

  const systemPromptToUse =
    dynamicSystemPrompt?.trim()
      ? dynamicSystemPrompt
      : getDefaultSystemPrompt(currentDate, drawnFeatures)

  // Check if any message contains an image
  const hasImage = messages.some(message =>
    Array.isArray(message.content) &&
    message.content.some(part => part.type === 'image')
  )

  const result = await nonexperimental_streamText({
    model: (await getModel(hasImage)) as LanguageModel,
    maxTokens: 4096,
    system: systemPromptToUse,
    messages,
    tools: getTools({ uiStream, fullResponse, mapProvider }),
  })

  uiStream.update(null) // remove spinner

  const toolCalls: ToolCallPart[] = []
  const toolResponses: ToolResultPart[] = []

  for await (const delta of result.fullStream) {
    switch (delta.type) {
      case 'text-delta':
        if (delta.textDelta) {
          if (fullResponse.length === 0 && delta.textDelta.length > 0) {
            uiStream.update(answerSection)
          }
          fullResponse += delta.textDelta
          streamText.update(fullResponse)
        }
        break

      case 'tool-call':
        toolCalls.push(delta)
        break

      case 'tool-result':
        if (!useSpecificModel && toolResponses.length === 0 && delta.result) {
          uiStream.append(answerSection)
        }
        if (!delta.result) hasError = true
        toolResponses.push(delta)
        break

      case 'error':
        hasError = true
        fullResponse += `\n\nError: Tool execution failed.`
        break
    }
  }

  messages.push({
    role: 'assistant',
    content: [{ type: 'text', text: fullResponse }, ...toolCalls],
  })

  if (toolResponses.length > 0) {
    messages.push({ role: 'tool', content: toolResponses })
  }

  return { result, fullResponse, hasError, toolResponses }

File: lib/agents/task-manager.tsx (L20-45)

      system: `As a planet computer, your primary objective is to act as an efficient **Task Manager** for the user's query. Your goal is to minimize unnecessary steps and maximize the efficiency of the subsequent exploration phase (researcher agent).

	    You must first analyze the user's input and determine the optimal course of action. You have two options at your disposal:

	    **Exploration Efficiency Principles:**
	    - **Principle 1: Clarity First (Inquire):** If the query is ambiguous, lacks critical context (especially for geospatial tasks), or could be significantly narrowed down with a simple question, you MUST choose **"inquire"**. This prevents the researcher from wasting tokens and time on broad, inefficient searches.
	    - **Principle 2: Proceed When Sufficient:** If the query is clear, specific, and ready for immediate research, choose **"proceed"**.

	    **Options:**
	    1. **"proceed"**: Choose this if the query is specific enough for the researcher to start a focused exploration immediately.
	    2. **"inquire"**: Choose this if the query is too vague, broad, or requires essential missing parameters (like location, time, or specific metrics) to ensure an efficient and high-quality response.

	    **Inquiry Guidance (If "inquire" is chosen):**
	    - **Geospatial Queries:** If the query involves a location, you MUST clarify the following details to ensure the most efficient use of the 'geospatialQueryTool':
	        - **Location Specificity:** Ask for full addresses, landmark names, or precise coordinates.
	        - **Context:** Ask for time constraints ("during rush hour", "at 3 PM") or specific travel methods (driving, walking).
	        - **Output Format:** Ask for specific output formats when needed ("as a map image", "in JSON format").

	    **Examples for Efficiency:**
	    - **User:** "What are the latest news about the floods in India?" -> **Action:** "proceed" (Clear, ready for web search).
	    - **User:** "What's the warmest temperature in my area?" -> **Action:** "inquire" (Missing location and preferred metric).
	    - **User:** "Show me the nearest park." -> **Action:** "inquire" (Missing current location).
	    - **User:** "Tell me about the new AI model." -> **Action:** "inquire" (Too broad; ask for the model name or specific aspect).

	    Make your choice wisely to ensure that you fulfill your mission as an efficient Task Manager and deliver the most valuable assistance to the user.

File: lib/agents/writer.tsx (L22-30)

  const default_system_prompt = `As a professional writer, your job is to generate a comprehensive and informative, yet concise answer of 400 words or less for the given question based solely on the provided search results (URL and content). You must only use information from the provided search results. Use an unbiased and journalistic tone. Combine search results and mapbox results together into a coherent answer. Do not repeat text. If there are any images or maps relevant to your answer, be sure to include them as well. Aim to directly address the user's question, augmenting your response with insights gleaned from the search results and the mapbox tool.
    Whenever quoting or referencing information from a specific URL, always cite the source URL explicitly. Please match the language of the response to the user's language.
    Always answer in Markdown format. Links and images must follow the correct format.
    Link format: [link text](url)
    Image format: ![alt text](url)

    There are also some proconfigured example queires. 
    When asked about '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'
    `;

File: app/actions.tsx (L230-233)

  if (userInput && (userInput.toLowerCase().trim() === 'what is a planet computer?' || userInput.toLowerCase().trim() === 'what is qcx-terra?')) {
    const definition = userInput.toLowerCase().trim() === 'what is a planet computer?'
      ? `A planet computer is a proprietary environment aware system that interoperates weather forecasting, mapping and scheduling using cutting edge multi-agents to streamline automation and exploration on a planet. Available for our Pro and Enterprise customers. [QCX Pricing](https://www.queue.cx/#pricing)`
      : `QCX-Terra is a model garden of pixel level precision geospatial foundational models for efficient land feature predictions from satellite imagery. Available for our Pro and Enterprise customers. [QCX Pricing] (https://www.queue.cx/#pricing)`;

File: components/settings/components/system-prompt-form.tsx (L9-35)

export function SystemPromptForm({ form }: SystemPromptFormProps) {
  const systemPrompt = form.watch("systemPrompt")
  const characterCount = systemPrompt?.length || 0

  return (
    <FormField
      control={form.control}
      name="systemPrompt"
      render={({ field, fieldState, formState }: { field: import("react-hook-form").ControllerRenderProps<any, "systemPrompt">; fieldState: import("react-hook-form").ControllerFieldState; formState: import("react-hook-form").UseFormStateReturn<any>; }) => (
        <FormItem>
          <FormLabel>System Prompt</FormLabel>
          <FormControl>
            <Textarea
              placeholder="Enter the system prompt for your planetary copilot..."
              className="min-h-[200px] resize-y"
              {...field}
            />
          </FormControl>
          <FormDescription className="flex justify-between">
            <span>Define how your copilot should behave and respond to user queries.</span>
            <span className={characterCount > 1800 ? "text-amber-500" : ""}>{characterCount}/2000</span>
          </FormDescription>
          <FormMessage />
        </FormItem>
      )}
    />
  )

File: components/settings/components/settings.tsx (L84-150)

  useEffect(() => {
    async function fetchData() {
      if (!userId || authLoading) return;

      const [existingPrompt, selectedModel] = await Promise.all([
        getSystemPrompt(userId),
        getSelectedModel(),
      ]);

      if (existingPrompt) {
        form.setValue("systemPrompt", existingPrompt, { shouldValidate: true, shouldDirty: false });
      }
      if (selectedModel) {
        form.setValue("selectedModel", selectedModel, { shouldValidate: true, shouldDirty: false });
      }
    }
    fetchData();
  }, [form, userId, authLoading]);

  if (authLoading) {
    return <SettingsSkeleton />;
  }

  async function onSubmit(data: SettingsFormValues) {
    if (!userId) {
      toast({
        title: "Error",
        description: "You must be logged in to save settings.",
        variant: "destructive",
      });
      return;
    }

    setIsSaving(true)

    try {
      // Save the system prompt and selected model
      const [promptSaveResult, modelSaveResult] = await Promise.all([
        saveSystemPrompt(userId, data.systemPrompt),
        saveSelectedModel(data.selectedModel),
      ]);

      if (promptSaveResult?.error) {
        throw new Error(promptSaveResult.error);
      }
      if (modelSaveResult?.error) {
        throw new Error(modelSaveResult.error);
      }

      console.log("Submitted data:", data)

      // Success notification
      toast({
        title: "Settings updated",
        description: "Your settings have been saved successfully.",
      })
    } catch (error: any) {
      // Error notification
      toast({
        title: "Something went wrong",
        description: error.message || "Your settings could not be saved. Please try again.",
        variant: "destructive",
      })
    } finally {
      setIsSaving(false)
    }
  }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions