Använda Ollama Web Search API i Go
Bygg AI-söklägenheter med Go och Ollama
Ollamas webbsöknings-API låter dig komplettera lokala LLMs med realtidsinformation från webben. Den här guiden visar hur du implementerar webbsökningsfunktioner i Go, från enkla API-anrop till fullständiga sökningsagenter.

Kom igång
Har Ollama en officiell Go-bibliotek för webbsökning? Ollama erbjuder ett REST-API för webbsökning som fungerar med vilken Go HTTP-klient som helst. Även om det inte finns någon officiell Go-SDK för webbsökning ännu, kan du enkelt implementera API-anropen med standardbibliotekspaket.
Skapa först ett API-nyckel från ditt Ollama-konto. För en omfattande referens om Ollama-kommandon och användning, kolla in Ollama cheatsheet.
Ange ditt API-nyckel som en miljövariabel:
export OLLAMA_API_KEY="din_api_nyckel"
På Windows PowerShell:
$env:OLLAMA_API_KEY = "din_api_nyckel"
Projektinställning
Skapa ett nytt Go-modul:
mkdir ollama-search
cd ollama-search
go mod init ollama-search
Grundläggande webbsökning
Hur autentiserar jag mig med Ollamas webbsöknings-API i Go? Ange Authorization-rubriken med ditt API-nyckel som en Bearer-token. Skapa ett API-nyckel från ditt Ollama-konto och skicka det i begäransrubriken.
Här är en fullständig implementering av webbsöknings-API:
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
// Request/Response typer för web_search
type WebSearchRequest struct {
Query string `json:"query"`
MaxResults int `json:"max_results,omitempty"`
}
type WebSearchResult struct {
Title string `json:"title"`
URL string `json:"url"`
Content string `json:"content"`
}
type WebSearchResponse struct {
Results []WebSearchResult `json:"results"`
}
func webSearch(query string, maxResults int) (*WebSearchResponse, error) {
apiKey := os.Getenv("OLLAMA_API_KEY")
if apiKey == "" {
return nil, fmt.Errorf("OLLAMA_API_KEY miljövariabel inte inställd")
}
reqBody := WebSearchRequest{
Query: query,
MaxResults: maxResults,
}
jsonData, err := json.Marshal(reqBody)
if err != nil {
return nil, fmt.Errorf("misslyckades med att marshala begäran: %w", err)
}
req, err := http.NewRequest("POST", "https://ollama.com/api/web_search", bytes.NewBuffer(jsonData))
if err != nil {
return nil, fmt.Errorf("misslyckades med att skapa begäran: %w", err)
}
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("begäran misslyckades: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("API-fel (status %d): %s", resp.StatusCode, string(body))
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("misslyckades med att läsa svar: %w", err)
}
var searchResp WebSearchResponse
if err := json.Unmarshal(body, &searchResp); err != nil {
return nil, fmt.Errorf("misslyckades med att unmarshala svar: %w", err)
}
return &searchResp, nil
}
func main() {
results, err := webSearch("Vad är Ollama?", 5)
if err != nil {
fmt.Printf("Fel: %v\n", err)
return
}
fmt.Println("Sökresultat:")
fmt.Println("===============")
for i, result := range results.Results {
fmt.Printf("\n%d. %s\n", i+1, result.Title)
fmt.Printf(" URL: %s\n", result.URL)
fmt.Printf(" %s\n", truncate(result.Content, 150))
}
}
func truncate(s string, maxLen int) string {
if len(s) <= maxLen {
return s
}
return s[:maxLen] + "..."
}
Web Fetch-implementering
Vad är skillnaden mellan web_search och web_fetch-endpunkter? web_search-endpunkten frågar internet och returnerar flera sökträffar med titlar, URL:er och utdrag. web_fetch-endpunkten hämtar fullständigt innehåll från en specifik URL, returnerar sidtiteln, innehållet och länkarna.
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
// Request/Response typer för web_fetch
type WebFetchRequest struct {
URL string `json:"url"`
}
type WebFetchResponse struct {
Title string `json:"title"`
Content string `json:"content"`
Links []string `json:"links"`
}
func webFetch(url string) (*WebFetchResponse, error) {
apiKey := os.Getenv("OLLAMA_API_KEY")
if apiKey == "" {
return nil, fmt.Errorf("OLLAMA_API_KEY miljövariabel inte inställd")
}
reqBody := WebFetchRequest{URL: url}
jsonData, err := json.Marshal(reqBody)
if err != nil {
return nil, fmt.Errorf("misslyckades med att marshala begäran: %w", err)
}
req, err := http.NewRequest("POST", "https://ollama.com/api/web_fetch", bytes.NewBuffer(jsonData))
if err != nil {
return nil, fmt.Errorf("misslyckades med att skapa begäran: %w", err)
}
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("begäran misslyckades: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("API-fel (status %d): %s", resp.StatusCode, string(body))
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("misslyckades med att läsa svar: %w", err)
}
var fetchResp WebFetchResponse
if err := json.Unmarshal(body, &fetchResp); err != nil {
return nil, fmt.Errorf("misslyckades med att unmarshala svar: %w", err)
}
return &fetchResp, nil
}
func main() {
result, err := webFetch("https://ollama.com")
if err != nil {
fmt.Printf("Fel: %v\n", err)
return
}
fmt.Printf("Titel: %s\n\n", result.Title)
fmt.Printf("Innehåll:\n%s\n\n", result.Content)
fmt.Printf("Hittade länkar: %d\n", len(result.Links))
for i, link := range result.Links {
if i >= 5 {
fmt.Printf(" ... och %d till\n", len(result.Links)-5)
break
}
fmt.Printf(" - %s\n", link)
}
}
Återanvändbar klientpaket
Skapa ett återanvändbart Ollama-klientpaket för renare kod:
// ollama/client.go
package ollama
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"time"
)
type Client struct {
apiKey string
httpClient *http.Client
baseURL string
}
func NewClient() (*Client, error) {
apiKey := os.Getenv("OLLAMA_API_KEY")
if apiKey == "" {
return nil, fmt.Errorf("OLLAMA_API_KEY miljövariabel inte inställd")
}
return &Client{
apiKey: apiKey,
httpClient: &http.Client{
Timeout: 30 * time.Second,
},
baseURL: "https://ollama.com/api",
}, nil
}
type SearchResult struct {
Title string `json:"title"`
URL string `json:"url"`
Content string `json:"content"`
}
type SearchResponse struct {
Results []SearchResult `json:"results"`
}
type FetchResponse struct {
Title string `json:"title"`
Content string `json:"content"`
Links []string `json:"links"`
}
func (c *Client) WebSearch(query string, maxResults int) (*SearchResponse, error) {
payload := map[string]interface{}{
"query": query,
"max_results": maxResults,
}
return doRequest[SearchResponse](c, "/web_search", payload)
}
func (c *Client) WebFetch(url string) (*FetchResponse, error) {
payload := map[string]string{"url": url}
return doRequest[FetchResponse](c, "/web_fetch", payload)
}
func doRequest[T any](c *Client, endpoint string, payload interface{}) (*T, error) {
jsonData, err := json.Marshal(payload)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", c.baseURL+endpoint, bytes.NewBuffer(jsonData))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+c.apiKey)
req.Header.Set("Content-Type", "application/json")
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("API-fel (status %d): %s", resp.StatusCode, string(body))
}
var result T
if err := json.Unmarshal(body, &result); err != nil {
return nil, err
}
return &result, nil
}
Användning:
package main
import (
"fmt"
"log"
"ollama-search/ollama"
)
func main() {
client, err := ollama.NewClient()
if err != nil {
log.Fatal(err)
}
// Sök
results, err := client.WebSearch("Ollama nya funktioner", 5)
if err != nil {
log.Fatal(err)
}
for _, r := range results.Results {
fmt.Printf("- %s\n %s\n\n", r.Title, r.URL)
}
// Hämta
page, err := client.WebFetch("https://ollama.com")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Sida: %s\n", page.Title)
}
Bygga en söksagent
Vilka modeller fungerar bäst för Go-baserade Ollama-söksagenter? Modeller med starka verktygsanvändningsförmågor fungerar bäst, inklusive qwen3, gpt-oss, och molnmodeller som qwen3:480b-cloud och deepseek-v3.1-cloud. Om du arbetar med Qwen3-modeller och behöver bearbeta eller rangordna sökträffar, se vår guide om att rangordna textdokument med Ollama och Qwen3 Embedding-modell i Go.
Här är en komplett implementering av en söksagent:
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
// Chat typer
type Message struct {
Role string `json:"role"`
Content string `json:"content,omitempty"`
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
ToolName string `json:"tool_name,omitempty"`
}
type ToolCall struct {
Function FunctionCall `json:"function"`
}
type FunctionCall struct {
Name string `json:"name"`
Arguments map[string]interface{} `json:"arguments"`
}
type Tool struct {
Type string `json:"type"`
Function ToolFunction `json:"function"`
}
type ToolFunction struct {
Name string `json:"name"`
Description string `json:"description"`
Parameters map[string]interface{} `json:"parameters"`
}
type ChatRequest struct {
Model string `json:"model"`
Messages []Message `json:"messages"`
Tools []Tool `json:"tools,omitempty"`
Stream bool `json:"stream"`
}
type ChatResponse struct {
Message Message `json:"message"`
}
// SearchAgent organiserar webbsökning med LLM
type SearchAgent struct {
model string
ollamaURL string
apiKey string
maxIter int
httpClient *http.Client
}
func NewSearchAgent(model string) *SearchAgent {
return &SearchAgent{
model: model,
ollamaURL: "http://localhost:11434/api/chat",
apiKey: os.Getenv("OLLAMA_API_KEY"),
maxIter: 10,
httpClient: &http.Client{},
}
}
func (a *SearchAgent) Query(question string) (string, error) {
tools := []Tool{
{
Type: "function",
Function: ToolFunction{
Name: "web_search",
Description: "Sök på webben för aktuell information",
Parameters: map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"query": map[string]string{
"type": "string",
"description": "Sökfrågan",
},
},
"required": []string{"query"},
},
},
},
{
Type: "function",
Function: ToolFunction{
Name: "web_fetch",
Description: "Hämta fullständigt innehåll från en webbsida",
Parameters: map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"url": map[string]string{
"type": "string",
"description": "URL:n att hämta",
},
},
"required": []string{"url"},
},
},
},
}
messages := []Message{
{Role: "user", Content: question},
}
for i := 0; i < a.maxIter; i++ {
response, err := a.chat(messages, tools)
if err != nil {
return "", fmt.Errorf("chat error: %w", err)
}
messages = append(messages, response.Message)
// Inga verktygsanrop betyder att vi har ett slutgiltigt svar
if len(response.Message.ToolCalls) == 0 {
return response.Message.Content, nil
}
// Utför verktygsanrop
for _, toolCall := range response.Message.ToolCalls {
fmt.Printf("🔧 Anropar: %s\n", toolCall.Function.Name)
result, err := a.executeTool(toolCall)
if err != nil {
result = fmt.Sprintf("Fel: %v", err)
}
// Avkorta för kontextbegränsningar
if len(result) > 8000 {
result = result[:8000] + "... [avkortat]"
}
messages = append(messages, Message{
Role: "tool",
Content: result,
ToolName: toolCall.Function.Name,
})
}
}
return "", fmt.Errorf("max antal iterationer nådd")
}
func (a *SearchAgent) chat(messages []Message, tools []Tool) (*ChatResponse, error) {
reqBody := ChatRequest{
Model: a.model,
Messages: messages,
Tools: tools,
Stream: false,
}
jsonData, _ := json.Marshal(reqBody)
resp, err := a.httpClient.Post(a.ollamaURL, "application/json", bytes.NewBuffer(jsonData))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var chatResp ChatResponse
if err := json.Unmarshal(body, &chatResp); err != nil {
return nil, err
}
return &chatResp, nil
}
func (a *SearchAgent) executeTool(toolCall ToolCall) (string, error) {
switch toolCall.Function.Name {
case "web_search":
query, ok := toolCall.Function.Arguments["query"].(string)
if !ok {
return "", fmt.Errorf("ogiltigt frågeargument")
}
return a.webSearch(query)
case "web_fetch":
url, ok := toolCall.Function.Arguments["url"].(string)
if !ok {
return "", fmt.Errorf("ogiltigt URL-argument")
}
return a.webFetch(url)
default:
return "", fmt.Errorf("okänt verktyg: %s", toolCall.Function.Name)
}
}
func (a *SearchAgent) webSearch(query string) (string, error) {
payload := map[string]interface{}{"query": query, "max_results": 5}
jsonData, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", "https://ollama.com/api/web_search", bytes.NewBuffer(jsonData))
req.Header.Set("Authorization", "Bearer "+a.apiKey)
req.Header.Set("Content-Type", "application/json")
resp, err := a.httpClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
return string(body), nil
}
func (a *SearchAgent) webFetch(url string) (string, error) {
payload := map[string]string{"url": url}
jsonData, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", "https://ollama.com/api/web_fetch", bytes.NewBuffer(jsonData))
req.Header.Set("Authorization", "Bearer "+a.apiKey)
req.Header.Set("Content-Type", "application/json")
resp, err := a.httpClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
return string(body), nil
}
func main() {
agent := NewSearchAgent("qwen3:4b")
answer, err := agent.Query("Vad är de senaste funktionerna i Ollama?")
if err != nil {
fmt.Printf("Fel: %v\n", err)
return
}
fmt.Println("\n📝 Svar:")
fmt.Println(answer)
}
Hur hanterar jag stora webbsöksrespons i Go? Avkorta innehållet innan du skickar det till modellens kontext. Använd strängskärning för att begränsa innehållet till cirka 8000 tecken för att passa inom kontextbegränsningarna.
Samtidig sökning
Go är utmärkt för samtidiga operationer. Här är hur du utför flera sökningar parallellt. Att förstå hur Ollama hanterar parallella begäranden kan hjälpa dig att optimera dina samtidiga sökimplementeringar.
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"sync"
)
type SearchResult struct {
Query string
Results []struct {
Title string `json:"title"`
URL string `json:"url"`
Content string `json:"content"`
} `json:"results"`
Error error
}
func concurrentSearch(queries []string) []SearchResult {
results := make([]SearchResult, len(queries))
var wg sync.WaitGroup
for i, query := range queries {
wg.Add(1)
go func(idx int, q string) {
defer wg.Done()
result := SearchResult{Query: q}
payload := map[string]string{"query": q}
jsonData, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", "https://ollama.com/api/web_search", bytes.NewBuffer(jsonData))
req.Header.Set("Authorization", "Bearer "+os.Getenv("OLLAMA_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
result.Error = err
results[idx] = result
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
json.Unmarshal(body, &result)
results[idx] = result
}(i, query)
}
wg.Wait()
return results
}
func main() {
queries := []string{
"Ollama senaste funktioner",
"lokal LLM-distribution",
"AI-söksagenter Go",
}
results := concurrentSearch(queries)
for _, r := range results {
fmt.Printf("\n🔍 Fråga: %s\n", r.Query)
if r.Error != nil {
fmt.Printf(" Fel: %v\n", r.Error)
continue
}
for _, item := range r.Results[:min(3, len(r.Results))] {
fmt.Printf(" • %s\n", item.Title)
}
}
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
Kontext och avbrytning
Lägg till kontextstöd för tidsgränser och avbrytning:
package main
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"time"
)
func webSearchWithContext(ctx context.Context, query string) (string, error) {
payload := map[string]string{"query": query}
jsonData, _ := json.Marshal(payload)
req, err := http.NewRequestWithContext(ctx, "POST", "https://ollama.com/api/web_search", bytes.NewBuffer(jsonData))
if err != nil {
return "", err
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("OLLAMA_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
return string(body), nil
}
func main() {
// Skapa kontext med 10 sekunders tidsgräns
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
result, err := webSearchWithContext(ctx, "Ollama webbsöks-API")
if err != nil {
if ctx.Err() == context.DeadlineExceeded {
fmt.Println("Begäran tog för lång tid")
} else {
fmt.Printf("Fel: %v\n", err)
}
return
}
fmt.Println(result)
}
Rekommenderade modeller
Vilken kontextlängd bör jag använda för Go-söksagenter? Använd en kontextlängd på cirka 32000 tokens för rimlig prestanda. Söksagenter fungerar bäst med full kontextlängd eftersom webbsökträffar kan vara omfattande. Om du behöver hantera dina Ollama-modeller eller flytta dem till olika platser, se vår guide om hur man flyttar Ollama-modeller till olika disk eller mapp.
| Modell | Parametrar | Bäst för |
|---|---|---|
qwen3:4b |
4B | Snabba lokala sökningar |
qwen3 |
8B | Allmänt ändamål |
gpt-oss |
Olika | Forskningsuppgifter |
qwen3:480b-cloud |
480B | Komplex resonemang (moln) |
gpt-oss:120b-cloud |
120B | Långformad forskning (moln) |
deepseek-v3.1-cloud |
- | Avancerad analys (moln) |
För avancerade AI-applikationer som kombinerar text och visuellt innehåll, överväg att utforska korsmodalitetskodningar för att utöka dina sökmöjligheter bortom textbaserade frågor.
Bäst praxis
- Felhantering: Kontrollera alltid efter fel och hantera API-fel på ett smidigt sätt
- Tidbegränsningar: Använd kontext med tidbegränsningar för nätverksförfrågningar
- Hastighetsbegränsningar: Respektera Ollamas API-hastighetsbegränsningar. Var medveten om potentiella förändringar i Ollamas API, som diskuteras i första tecknen på Ollama enshittification
- Resultattrunkering: Trunkera resultat till ~8000 tecken för kontextbegränsningar
- Samverkande förfrågningar: Använd goroutines för parallella sökningar
- Anslutningsåteranvändning: Återanvänd HTTP-klient för bättre prestanda
- Testning: Skriv omfattande enhetstester för dina sökimplementeringar. Följ Go enhetstestning bäst praxis
Användbara länkar
- Ollama snabbguide
- Rangordning av textdokument med Ollama och Qwen3 Embedding-modell - i Go
- Korsmodala embeddingar: Broar mellan AI-modaliteter
- Go Enhetstestning: Struktur & Bäst Praxis
- Hur man flyttar Ollama-modeller till olika enhet eller mapp
- Hur Ollama hanterar parallella förfrågningar
- Första tecknen på Ollama enshittification
- Ollama Web Search Blog Post
- Ollama officiell dokumentation
- Ollama Go-exempel
- Ollama GitHub-repository