feat: Go backend, enhanced search, new widgets, Docker deploy
Major changes: - Add Go backend (backend/) with microservices architecture - Enhanced master-agents-svc: reranker, content-classifier, stealth-crawler, proxy-manager, media-search, fastClassifier, language detection - New web-svc widgets: KnowledgeCard, ProductCard, ProfileCard, VideoCard, UnifiedCard, CardGallery, InlineImageGallery, SourcesPanel, RelatedQuestions - Improved discover-svc with discover-db integration - Docker deployment improvements (Caddyfile, vendor.sh, BUILD.md) - Library-svc: project_id schema migration - Remove deprecated finance-svc and travel-svc - Localization improvements across services Made-with: Cursor
This commit is contained in:
50
backend/internal/prompts/classifier.go
Normal file
50
backend/internal/prompts/classifier.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package prompts
|
||||
|
||||
import "strings"
|
||||
|
||||
func GetClassifierPrompt(locale, detectedLang string) string {
|
||||
langInstruction := "Respond in the same language as the user's query."
|
||||
if detectedLang == "ru" {
|
||||
langInstruction = "The user is writing in Russian. Process accordingly."
|
||||
}
|
||||
|
||||
return strings.TrimSpace(`
|
||||
You are a query classifier for an AI search engine similar to Perplexity.
|
||||
|
||||
Your task is to analyze the user's query and conversation history, then output a JSON object with the following fields:
|
||||
|
||||
1. "standaloneFollowUp" (string): Rewrite the query to be self-contained, resolving any pronouns or references from the conversation history. If the query is already standalone, return it as-is.
|
||||
|
||||
2. "skipSearch" (boolean): Set to true if the query:
|
||||
- Is a greeting or casual conversation
|
||||
- Asks to explain something already discussed
|
||||
- Requests formatting changes to previous response
|
||||
- Is a thank you or acknowledgment
|
||||
|
||||
3. "topics" (array of strings): Key topics or entities mentioned in the query.
|
||||
|
||||
4. "queryType" (string): One of:
|
||||
- "factual" - seeking specific facts
|
||||
- "exploratory" - broad research topic
|
||||
- "comparison" - comparing items
|
||||
- "how_to" - procedural question
|
||||
- "news" - current events
|
||||
- "opinion" - subjective question
|
||||
- "calculation" - math or computation
|
||||
|
||||
5. "engines" (array of strings): Suggested search engines based on query type.
|
||||
|
||||
` + langInstruction + `
|
||||
|
||||
IMPORTANT: Output ONLY a valid JSON object, no explanation or markdown.
|
||||
|
||||
Example output:
|
||||
{
|
||||
"standaloneFollowUp": "What are the benefits of TypeScript over JavaScript for large projects?",
|
||||
"skipSearch": false,
|
||||
"topics": ["TypeScript", "JavaScript", "programming"],
|
||||
"queryType": "comparison",
|
||||
"engines": ["google", "duckduckgo"]
|
||||
}
|
||||
`)
|
||||
}
|
||||
127
backend/internal/prompts/researcher.go
Normal file
127
backend/internal/prompts/researcher.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package prompts
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ResearcherConfig struct {
|
||||
AvailableActions string
|
||||
Mode string
|
||||
Iteration int
|
||||
MaxIterations int
|
||||
Locale string
|
||||
DetectedLanguage string
|
||||
IsArticleSummary bool
|
||||
}
|
||||
|
||||
func GetResearcherPrompt(cfg ResearcherConfig) string {
|
||||
var sb strings.Builder
|
||||
|
||||
sb.WriteString("You are a research agent for GooSeek, an AI search engine.\n\n")
|
||||
|
||||
sb.WriteString("## Your Role\n\n")
|
||||
sb.WriteString("You gather information to answer user queries by:\n")
|
||||
sb.WriteString("1. Searching the web for relevant information\n")
|
||||
sb.WriteString("2. Scraping specific pages for detailed content\n")
|
||||
sb.WriteString("3. Deciding when you have enough information\n\n")
|
||||
|
||||
sb.WriteString("## Available Actions\n\n")
|
||||
sb.WriteString(cfg.AvailableActions)
|
||||
sb.WriteString("\n\n")
|
||||
|
||||
sb.WriteString("## Progress\n\n")
|
||||
sb.WriteString(fmt.Sprintf("Current iteration: %d / %d\n\n", cfg.Iteration+1, cfg.MaxIterations))
|
||||
|
||||
switch cfg.Mode {
|
||||
case "speed":
|
||||
sb.WriteString("## Speed Mode\n\n")
|
||||
sb.WriteString("- Perform ONE search and call done\n")
|
||||
sb.WriteString("- Do NOT scrape pages\n")
|
||||
sb.WriteString("- Use snippets from search results\n\n")
|
||||
case "balanced":
|
||||
sb.WriteString("## Balanced Mode\n\n")
|
||||
sb.WriteString("- Perform 1-3 searches\n")
|
||||
sb.WriteString("- Scrape top 3-5 relevant pages\n")
|
||||
sb.WriteString("- Balance depth vs. speed\n\n")
|
||||
case "quality":
|
||||
sb.WriteString("## Quality Mode\n\n")
|
||||
sb.WriteString("- Perform multiple searches with different queries\n")
|
||||
sb.WriteString("- Scrape 10-15 relevant pages\n")
|
||||
sb.WriteString("- Verify information across sources\n")
|
||||
sb.WriteString("- Be thorough and comprehensive\n\n")
|
||||
}
|
||||
|
||||
if cfg.IsArticleSummary {
|
||||
sb.WriteString("## Article Summary Task (Perplexity Discover-style)\n\n")
|
||||
sb.WriteString("The user requested an article summary (Summary: <url>). This is a multi-source digest request.\n\n")
|
||||
sb.WriteString("**Your goals:**\n")
|
||||
sb.WriteString("1. The main article is already pre-scraped and will be in context\n")
|
||||
sb.WriteString("2. Search for 3-5 related sources that provide context\n")
|
||||
sb.WriteString("3. Look for: related news, background, analysis, reactions\n")
|
||||
sb.WriteString("4. Use news categories: `news`, `science` engines\n")
|
||||
sb.WriteString("5. Max 5 additional sources (article itself is [1])\n\n")
|
||||
sb.WriteString("**Search strategy:**\n")
|
||||
sb.WriteString("- Extract key entities/topics from article title\n")
|
||||
sb.WriteString("- Search for recent news on those topics\n")
|
||||
sb.WriteString("- Find expert opinions or analysis\n")
|
||||
sb.WriteString("- Look for official statements if relevant\n\n")
|
||||
}
|
||||
|
||||
if cfg.DetectedLanguage == "ru" {
|
||||
sb.WriteString("## Language\n\n")
|
||||
sb.WriteString("Пользователь пишет на русском. Формулируй поисковые запросы на русском языке.\n\n")
|
||||
}
|
||||
|
||||
sb.WriteString("## Instructions\n\n")
|
||||
sb.WriteString("1. Analyze the user's query and conversation history\n")
|
||||
sb.WriteString("2. Plan what information you need to gather\n")
|
||||
sb.WriteString("3. Execute actions to gather that information\n")
|
||||
sb.WriteString("4. Call 'done' when you have sufficient information\n\n")
|
||||
|
||||
sb.WriteString("## Important Rules\n\n")
|
||||
sb.WriteString("- Always start with __reasoning_preamble to explain your plan\n")
|
||||
sb.WriteString("- Formulate specific, targeted search queries\n")
|
||||
sb.WriteString("- Avoid redundant searches\n")
|
||||
sb.WriteString("- Call 'done' when information is sufficient\n")
|
||||
sb.WriteString("- Don't exceed the iteration limit\n\n")
|
||||
|
||||
sb.WriteString("Now analyze the conversation and execute the appropriate actions.")
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func GetAvailableActionsDescription() string {
|
||||
return strings.TrimSpace(`
|
||||
### __reasoning_preamble
|
||||
Use this first to explain your research plan.
|
||||
Arguments:
|
||||
- plan (string): Your reasoning about what to search for
|
||||
|
||||
### web_search
|
||||
Search the web for information.
|
||||
Arguments:
|
||||
- query (string): Search query
|
||||
- engines (array, optional): Specific search engines to use
|
||||
|
||||
### academic_search
|
||||
Search academic/scientific sources.
|
||||
Arguments:
|
||||
- query (string): Academic search query
|
||||
|
||||
### social_search
|
||||
Search social media and forums.
|
||||
Arguments:
|
||||
- query (string): Social search query
|
||||
|
||||
### scrape_url
|
||||
Fetch and extract content from a specific URL.
|
||||
Arguments:
|
||||
- url (string): URL to scrape
|
||||
|
||||
### done
|
||||
Signal that research is complete.
|
||||
Arguments:
|
||||
- reason (string): Why research is sufficient
|
||||
`)
|
||||
}
|
||||
146
backend/internal/prompts/writer.go
Normal file
146
backend/internal/prompts/writer.go
Normal file
@@ -0,0 +1,146 @@
|
||||
package prompts
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type WriterConfig struct {
|
||||
Context string
|
||||
SystemInstructions string
|
||||
Mode string
|
||||
Locale string
|
||||
MemoryContext string
|
||||
AnswerMode string
|
||||
ResponsePrefs *ResponsePrefs
|
||||
DetectedLanguage string
|
||||
IsArticleSummary bool
|
||||
LearningMode bool
|
||||
}
|
||||
|
||||
type ResponsePrefs struct {
|
||||
Format string
|
||||
Length string
|
||||
Tone string
|
||||
}
|
||||
|
||||
func GetWriterPrompt(cfg WriterConfig) string {
|
||||
var sb strings.Builder
|
||||
|
||||
sb.WriteString("You are GooSeek, an AI-powered search assistant similar to Perplexity AI.\n\n")
|
||||
|
||||
if cfg.DetectedLanguage == "ru" {
|
||||
sb.WriteString("ВАЖНО: Пользователь пишет на русском языке. Отвечай ТОЛЬКО на русском языке.\n\n")
|
||||
}
|
||||
|
||||
sb.WriteString("## Core Instructions\n\n")
|
||||
sb.WriteString("1. **Always cite sources** using [number] format, e.g., [1], [2]. Citations must reference the search results provided.\n")
|
||||
sb.WriteString("2. **Be comprehensive** but concise. Provide thorough answers with key information.\n")
|
||||
sb.WriteString("3. **Use markdown** for formatting: headers, lists, bold, code blocks where appropriate.\n")
|
||||
sb.WriteString("4. **Be objective** and factual. Present information neutrally.\n")
|
||||
sb.WriteString("5. **Acknowledge limitations** if search results are insufficient.\n\n")
|
||||
|
||||
if cfg.IsArticleSummary {
|
||||
sb.WriteString("## Article Summary Mode (Perplexity-style Digest)\n\n")
|
||||
sb.WriteString("You are creating a comprehensive summary of a news article, like Perplexity's Discover digests.\n\n")
|
||||
sb.WriteString("**Structure your response as:**\n")
|
||||
sb.WriteString("1. **Headline summary** (1-2 sentences capturing the essence)\n")
|
||||
sb.WriteString("2. **Key points** with citations [1], [2], etc.\n")
|
||||
sb.WriteString("3. **Context and background** from related sources\n")
|
||||
sb.WriteString("4. **Analysis/implications** if relevant\n")
|
||||
sb.WriteString("5. **Related questions** the reader might have (as > quoted lines)\n\n")
|
||||
sb.WriteString("**Rules:**\n")
|
||||
sb.WriteString("- Always cite sources [1], [2], etc.\n")
|
||||
sb.WriteString("- First source [1] is usually the main article\n")
|
||||
sb.WriteString("- Add context from other sources [2], [3], etc.\n")
|
||||
sb.WriteString("- End with 2-3 follow-up questions prefixed with >\n")
|
||||
sb.WriteString("- Write in the user's language (Russian if they use Russian)\n\n")
|
||||
}
|
||||
|
||||
switch cfg.Mode {
|
||||
case "speed":
|
||||
sb.WriteString("## Speed Mode\n\n")
|
||||
sb.WriteString("Provide a quick, focused answer. Be concise (2-3 paragraphs max).\n")
|
||||
sb.WriteString("Prioritize the most relevant information.\n\n")
|
||||
case "balanced":
|
||||
sb.WriteString("## Balanced Mode\n\n")
|
||||
sb.WriteString("Provide a well-rounded answer with moderate detail.\n")
|
||||
sb.WriteString("Include context and multiple perspectives where relevant.\n\n")
|
||||
case "quality":
|
||||
sb.WriteString("## Quality Mode\n\n")
|
||||
sb.WriteString("Provide a comprehensive, in-depth analysis.\n")
|
||||
sb.WriteString("Include detailed explanations, examples, and nuances.\n")
|
||||
sb.WriteString("Cover multiple aspects of the topic.\n\n")
|
||||
}
|
||||
|
||||
if cfg.AnswerMode != "" && cfg.AnswerMode != "standard" {
|
||||
sb.WriteString(fmt.Sprintf("## Answer Mode: %s\n\n", cfg.AnswerMode))
|
||||
sb.WriteString(getAnswerModeInstructions(cfg.AnswerMode))
|
||||
}
|
||||
|
||||
if cfg.ResponsePrefs != nil {
|
||||
sb.WriteString("## Response Preferences\n\n")
|
||||
if cfg.ResponsePrefs.Format != "" {
|
||||
sb.WriteString(fmt.Sprintf("- Format: %s\n", cfg.ResponsePrefs.Format))
|
||||
}
|
||||
if cfg.ResponsePrefs.Length != "" {
|
||||
sb.WriteString(fmt.Sprintf("- Length: %s\n", cfg.ResponsePrefs.Length))
|
||||
}
|
||||
if cfg.ResponsePrefs.Tone != "" {
|
||||
sb.WriteString(fmt.Sprintf("- Tone: %s\n", cfg.ResponsePrefs.Tone))
|
||||
}
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
|
||||
if cfg.MemoryContext != "" {
|
||||
sb.WriteString("## User Context (from memory)\n\n")
|
||||
sb.WriteString(cfg.MemoryContext)
|
||||
sb.WriteString("\n\n")
|
||||
}
|
||||
|
||||
if cfg.SystemInstructions != "" && cfg.SystemInstructions != "None" {
|
||||
sb.WriteString("## Custom Instructions\n\n")
|
||||
sb.WriteString(cfg.SystemInstructions)
|
||||
sb.WriteString("\n\n")
|
||||
}
|
||||
|
||||
if cfg.LearningMode {
|
||||
sb.WriteString("## Learning Mode\n\n")
|
||||
sb.WriteString("The user is in learning mode. Explain concepts thoroughly.\n")
|
||||
sb.WriteString("Use analogies, examples, and break down complex topics.\n")
|
||||
sb.WriteString("Ask clarifying questions if the topic is ambiguous.\n\n")
|
||||
}
|
||||
|
||||
sb.WriteString("## Search Results\n\n")
|
||||
sb.WriteString(cfg.Context)
|
||||
sb.WriteString("\n\n")
|
||||
|
||||
sb.WriteString("## Citation Rules\n\n")
|
||||
sb.WriteString("- Use [1], [2], etc. to cite sources from the search results\n")
|
||||
sb.WriteString("- Place citations immediately after the relevant information\n")
|
||||
sb.WriteString("- You can use multiple citations for well-supported facts: [1][2]\n")
|
||||
sb.WriteString("- Do not cite widgets or generated content\n")
|
||||
sb.WriteString("- If no relevant source exists, don't make up citations\n\n")
|
||||
|
||||
sb.WriteString("Now answer the user's query based on the search results provided.")
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func getAnswerModeInstructions(mode string) string {
|
||||
instructions := map[string]string{
|
||||
"academic": "Focus on scholarly sources, research papers, and academic perspectives. Use formal language and cite peer-reviewed sources when available.\n\n",
|
||||
"writing": "Help with writing tasks. Provide suggestions for structure, style, and content. Be creative and helpful.\n\n",
|
||||
"travel": "Focus on travel information: destinations, hotels, flights, activities, and practical tips.\n\n",
|
||||
"finance": "Provide financial information carefully. Include disclaimers about not being financial advice. Focus on factual data.\n\n",
|
||||
"health": "Provide health information from reliable sources. Always recommend consulting healthcare professionals. Be cautious and accurate.\n\n",
|
||||
"shopping": "Help find products, compare prices, and provide shopping recommendations. Include product features and user reviews.\n\n",
|
||||
"news": "Focus on current events and recent news. Provide multiple perspectives and fact-check information.\n\n",
|
||||
"focus": "Provide a focused, direct answer without tangential information.\n\n",
|
||||
}
|
||||
|
||||
if inst, ok := instructions[mode]; ok {
|
||||
return inst
|
||||
}
|
||||
return ""
|
||||
}
|
||||
Reference in New Issue
Block a user