Compartilhar via


Início Rápido: Agente de IA com pesquisa de vetor no Azure DocumentDB usando o Go

Crie um agente de IA inteligente usando o Go e o Azure DocumentDB. Este início rápido demonstra uma arquitetura de dois agentes que executa uma pesquisa semântica de hotéis e gera recomendações personalizadas.

Importante

Este exemplo é uma implementação de referência que demonstra padrões agênticos no Go. Ele usa uma arquitetura de agente personalizada em vez de uma estrutura de agente, que é a abordagem recomendada para aplicativos agentes de produção.

Pré-requisitos

Você pode usar a CLI do Desenvolvedor do Azure para criar os recursos necessários do Azure executando os azd comandos no repositório de exemplo. Para obter mais informações, consulte Implantar Infraestrutura com a CLI do Desenvolvedor do Azure.

Recursos do Azure

  • Recurso do Azure OpenAI com as seguintes implantações de modelo no Microsoft Foundry:

    • Implantação degpt-4o (Agente Sintetizador) – Recomendado: capacidade de 50.000 tokens por minuto (TPM)
    • Implantação degpt-4o-mini (Agente Planejador) – Recomendado: capacidade de 30.000 tokens por minuto (TPM)
    • Implantação de text-embedding-3-small (Inserções) – Recomendado: capacidade de 10.000 tokens por minuto (TPM)
    • Cotas de token: configurar TPM suficiente para cada implantação a fim de evitar a limitação de taxa
      • Consulte Gerenciar cotas do Azure OpenAI para gerenciamento de cotas
      • Se você encontrar 429 erros, aumente a cota do TPM ou reduza a frequência da solicitação
  • Cluster do Azure DocumentDB (com compatibilidade do MongoDB) com suporte à pesquisa de vetor:

    • Requisitos de camada de cluster com base no algoritmo de índice de vetor:
      • FIV (Índice de Arquivo Invertido): M10 ou superior (algoritmo padrão)
      • HNSW (Mundo Pequeno Navegável Hierárquico): M30 ou superior (baseado em grafo)
      • DiskANN: M40 ou superior (otimizado para grande escala)
    • Configuração do firewall: OBRIGATÓRIO Sem configuração de firewall adequada, as tentativas de conexão falham
    • Para a autenticação sem senha, com o RBAC (Controle de Acesso Baseado em Função) habilitado,

Ferramentas de desenvolvimento

Architecture

O exemplo usa uma arquitetura de dois agentes em que cada agente tem uma função específica.

Diagrama de arquitetura mostrando o fluxo de trabalho de dois agentes com o agente do planner, a ferramenta de pesquisa de vetor e o agente de sintetizador.

Este exemplo usa uma implementação personalizada diretamente com o SDK do OpenAI, sem depender de uma estrutura de agente. Ele aproveita a função OpenAI que solicita a integração de ferramentas e segue um fluxo de trabalho linear entre os agentes e a ferramenta de pesquisa. A execução é sem estado e sem histórico de conversa, o que a torna adequada para os cenários de consulta e resposta de turno único.

Obter o código de exemplo

  1. Baixe ou clone o repositório Azure DocumentDB Samples para o seu computador local para seguir o tutorial de início rápido.

  2. Navegue até o diretório do projeto:

    cd ai/vector-search-agent-go
    

Configurar variáveis de ambiente

Crie um .env arquivo na raiz do projeto para configurar variáveis de ambiente. Você pode criar uma cópia do .env.sample arquivo do repositório.

Edite o arquivo .env e substitua estes valores marcadores temporários:

Esse início rápido usa uma arquitetura de dois agentes (planejador + sintetizador) com três implantações de modelo (dois modelos de chat + inserções). As variáveis de ambiente são configuradas para cada implantação de modelo.

  • AZURE_OPENAI_PLANNER_DEPLOYMENT: seu nome de implantação do gpt-4o-mini
  • AZURE_OPENAI_SYNTH_DEPLOYMENT: seu nome de implantação gpt-4o
  • AZURE_OPENAI_EMBEDDING_DEPLOYMENT: seu nome de implantação do text-embedding-3-small

Você pode escolher entre dois métodos de autenticação: autenticação sem senha usando a Identidade do Azure (recomendado) ou a cadeia de conexão tradicional e a chave de API.

Opção 1: autenticação sem senha

Use a autenticação sem senha com o Azure OpenAI e o Azure DocumentDB. Definir USE_PASSWORDLESS=true, AZURE_OPENAI_ENDPOINTe AZURE_DOCUMENTDB_CLUSTER.

# Enable passwordless authentication
USE_PASSWORDLESS=true

# Azure OpenAI Configuration (passwordless)
AZURE_OPENAI_ENDPOINT=your-openai-endpoint

# Azure DocumentDB (passwordless)
AZURE_DOCUMENTDB_CLUSTER=your-mongo-cluster-name
AZURE_DOCUMENTDB_DATABASENAME=Hotels
AZURE_DOCUMENTDB_COLLECTION=hotel_data
AZURE_DOCUMENTDB_INDEX_NAME=vectorIndex

Pré-requisitos para autenticação sem senha:

  • Verifique se você está conectado ao Azure: az login

  • Conceda à sua identidade as seguintes funções:

    • Cognitive Services OpenAI User no recurso Azure OpenAI
    • DocumentDB Account Contributor e Cosmos DB Account Reader Role no recurso do Azure DocumentDB

    Para obter mais informações sobre como atribuir funções, consulte Atribuir funções do Azure usando o portal do Azure.

Opção 2: cadeia de conexão e autenticação de chave de API

Use autenticação baseada em chave, definindo USE_PASSWORDLESS=false (ou omitindo-o) e fornecendo os valores AZURE_OPENAI_API_KEY e AZURE_DOCUMENTDB_CONNECTION_STRING em seu arquivo .env.

# Disable passwordless authentication
USE_PASSWORDLESS=false

# Azure OpenAI Configuration (API key)
AZURE_OPENAI_ENDPOINT=your-openai-endpoint
AZURE_OPENAI_API_KEY=your-azure-openai-api-key

# Azure DocumentDB (connection string)
AZURE_DOCUMENTDB_CONNECTION_STRING=mongodb+srv://username:password@cluster.mongocluster.cosmos.azure.com/
AZURE_DOCUMENTDB_DATABASENAME=Hotels
AZURE_DOCUMENTDB_COLLECTION=hotel_data
AZURE_DOCUMENTDB_INDEX_NAME=vectorIndex

Estrutura do projeto

O projeto segue o layout padrão do projeto Go. Sua estrutura de diretório deve se parecer com a seguinte estrutura:

mongo-vcore-agent-go/
├── cmd/
│   ├── agent/          # Main agent application
│   │   └── main.go
│   ├── upload/         # Data upload utility
│   │   └── main.go
│   └── cleanup/        # Database cleanup utility
│       └── main.go
├── internal/
│   ├── agents/         # Agent and tool implementations
│   │   ├── agents.go   # Planner and synthesizer agents
│   │   └── tools.go    # Vector search tool
│   ├── clients/        # Azure OpenAI client
│   │   └── openai.go
│   ├── models/         # Hotel data models
│   │   └── hotel.go
│   ├── prompts/        # System prompts and tool definitions
│   │   └── prompts.go
│   └── vectorstore/    # Azure DocumentDB vector store operations
│       └── store.go
├── .env                # Environment variable configuration
├── go.mod              # Go module file
└── go.sum              # Go module checksum file

Explorar o código

Esta seção percorre os principais componentes do fluxo de trabalho do agente de IA. Ele destaca como os agentes processam solicitações, como as ferramentas conectam a IA ao banco de dados e como os prompts orientam o comportamento da IA.

Aplicativo do agente

O cmd/agent/main.go arquivo orquestra um sistema de recomendação de hotel alimentado por IA.

O aplicativo usa dois serviços do Azure:

  • Azure OpenAI que usa modelos de IA que entendem consultas e geram recomendações
  • Azure DocumentDB que armazena dados de hotéis e executa pesquisas de similaridade de vetor

Componentes do agente e da ferramenta

Os três componentes trabalham juntos para processar a solicitação de pesquisa do hotel:

  • Agente do Planner – interpreta a solicitação e decide como pesquisar
  • Ferramenta de pesquisa de vetor – localiza hotéis semelhantes ao que o agente do planejador descreve
  • Agente de sintetizador – grava uma recomendação útil com base nos resultados da pesquisa

Fluxo de trabalho do aplicativo

O aplicativo processa uma solicitação de pesquisa de hotel em duas etapas:

  • Planejamento: O fluxo de trabalho chama o agente do planejador, que analisa a consulta do usuário (como "hotéis próximos a trilhas para corrida") e pesquisa o banco de dados para encontrar hotéis correspondentes.
  • Sintetizar: O fluxo de trabalho chama o agente sintetizador, que analisa os resultados da pesquisa e grava uma recomendação personalizada explicando quais hotéis correspondem melhor à solicitação.
// Run planner agent
hotelContext, err := plannerAgent.Run(ctx, query, nearestNeighbors)
if err != nil {
    log.Fatalf("Planner agent failed: %v", err)
}

if debug {
    fmt.Printf("\n--- HOTEL CONTEXT ---\n%s\n", hotelContext)
}

// Run synthesizer agent
finalAnswer, err := synthesizerAgent.Run(ctx, query, hotelContext)
if err != nil {
    log.Fatalf("Synthesizer agent failed: %v", err)
}

Agents

O arquivo de origem internal/agents/agents.go implementa os agentes planejadores e sintetizadores que trabalham juntos a fim de processar as solicitações de pesquisa de hotéis.

Agente do Planner

O agente do planner é o tomador de decisão que determina como procurar hotéis.

O agente do planner recebe a consulta de linguagem natural do usuário e a envia para um modelo de IA, juntamente com as ferramentas disponíveis que ele pode usar. A IA decide chamar a ferramenta de pesquisa de vetor e fornece parâmetros de pesquisa. Em seguida, o agente extrai o nome da ferramenta e os argumentos da resposta da IA, executa a ferramenta de pesquisa e retorna os hotéis correspondentes. Em vez de codificar logicamente a pesquisa, a IA interpreta o que o usuário deseja e escolhe de que forma pesquisar, tornando o sistema flexível a diferentes tipos de consultas.

// PlannerAgent orchestrates the tool calling
type PlannerAgent struct {
    openAIClients *clients.OpenAIClients
    searchTool    *VectorSearchTool
    debug         bool
}

// NewPlannerAgent creates a new planner agent
func NewPlannerAgent(openaiClients *clients.OpenAIClients, searchTool *VectorSearchTool, debug bool) *PlannerAgent {
    return &PlannerAgent{
        openAIClients: openaiClients,
        searchTool:    searchTool,
        debug:         debug,
    }
}

// Run executes the planner agent workflow
func (a *PlannerAgent) Run(ctx context.Context, userQuery string, nearestNeighbors int) (string, error) {
    fmt.Println("\n--- PLANNER ---")

    userMessage := fmt.Sprintf(
        `Search for hotels matching this request: "%s". Use nearestNeighbors=%d.`,
        userQuery,
        nearestNeighbors,
    )

    // Get tool definition
    toolDef := a.searchTool.GetToolDefinition()

    // Call planner with tool definitions
    resp, err := a.openAIClients.ChatCompletionWithTools(ctx, prompts.PlannerSystemPrompt, userMessage, []openai.ChatCompletionToolUnionParam{toolDef})
    if err != nil {
        return "", fmt.Errorf("planner failed: %w", err)
    }

    // Extract tool call
    toolName, argsMap, err := clients.ExtractToolCall(resp)
    if err != nil {
        return "", fmt.Errorf("failed to extract tool call: %w", err)
    }

    if toolName != prompts.ToolName {
        return "", fmt.Errorf("unexpected tool called: %s", toolName)
    }

    // Parse arguments using typed struct
    args, err := parseToolArgumentsFromMap(argsMap)
    if err != nil {
        return "", fmt.Errorf("failed to parse tool arguments: %w", err)
    }

    // Use default if nearestNeighbors not provided
    if args.NearestNeighbors == 0 {
        args.NearestNeighbors = nearestNeighbors
    }

    fmt.Printf("Tool: %s\n", toolName)
    fmt.Printf("Query: %s\n", args.Query)
    fmt.Printf("K: %d\n", args.NearestNeighbors)

    // Execute the tool
    searchResults, err := a.searchTool.Execute(ctx, args.Query, args.NearestNeighbors)
    if err != nil {
        return "", fmt.Errorf("search tool execution failed: %w", err)
    }

    return searchResults, nil
}

Sintetizador agente

O agente sintetizador é o escritor que cria recomendações úteis.

O agente sintetizador recebe a consulta original do usuário junto com os resultados da pesquisa de hotéis. Ele envia tudo para um modelo de IA com instruções para escrever recomendações. Ele retorna uma resposta de linguagem natural que compara hotéis e explica as melhores opções. Essa abordagem é importante porque os resultados brutos da pesquisa não são amigáveis. O sintetizador transforma registros de banco de dados em uma recomendação de conversa que explica por que determinados hotéis correspondem às necessidades do usuário.

// NewSynthesizerAgent creates a new synthesizer agent
func NewSynthesizerAgent(openaiClients *clients.OpenAIClients, debug bool) *SynthesizerAgent {
    return &SynthesizerAgent{
        openAIClients: openaiClients,
        debug:         debug,
    }
}

// Run executes the synthesizer agent workflow
func (a *SynthesizerAgent) Run(ctx context.Context, userQuery, hotelContext string) (string, error) {
    fmt.Println("\n--- SYNTHESIZER ---")
    fmt.Printf("Context size: %d characters\n", len(hotelContext))

    userMessage := prompts.CreateSynthesizerUserPrompt(userQuery, hotelContext)

    // Call synthesizer (no tools)
    finalAnswer, err := a.openAIClients.ChatCompletion(ctx, prompts.SynthesizerSystemPrompt, userMessage)
    if err != nil {
        return "", fmt.Errorf("synthesizer failed: %w", err)
    }

    return finalAnswer, nil
}

Ferramentas do agente

O internal/agents/tools.go arquivo de origem define a ferramenta de pesquisa de vetor que o agente planejador usa.

O arquivo de ferramentas define uma ferramenta de pesquisa que o agente de IA pode usar para localizar hotéis. Essa ferramenta é como o agente se conecta ao banco de dados. A IA não pesquisa o banco de dados diretamente. Solicita o uso da ferramenta de pesquisa, e a ferramenta executa a pesquisa propriamente dita.

Definição de ferramenta

O GetToolDefinition método descreve a ferramenta para o modelo de IA em um formato que ele entende. Ele especifica o nome da ferramenta, uma descrição do que a ferramenta faz e os parâmetros que definem quais entradas a ferramenta precisa. Essa definição permite que a IA saiba que a ferramenta existe e como usá-la corretamente.

// GetToolDefinition returns the Azure OpenAI tool definition
func (t *VectorSearchTool) GetToolDefinition() openai.ChatCompletionToolUnionParam {
    paramSchema := map[string]any{
        "type": "object",
        "properties": map[string]any{
            "query": map[string]any{
                "type":        "string",
                "description": "Natural language search query describing desired hotel characteristics",
            },
            "nearestNeighbors": map[string]any{
                "type":        "integer",
                "description": "Number of results to return (1-20)",
                "default":     5,
            },
        },
        "required": []string{"query", "nearestNeighbors"},
    }

    return openai.ChatCompletionToolUnionParam{
        OfFunction: &openai.ChatCompletionFunctionToolParam{
            Function: openai.FunctionDefinitionParam{
                Name:        prompts.ToolName,
                Description: openai.String(prompts.ToolDescription),
                Parameters:  paramSchema,
            },
        },
    }
}

Execução de ferramenta

Quando a IA chama a ferramenta, o Execute método é executado. Ele gera uma inserção convertendo a consulta de texto em um vetor numérico usando o modelo de inserção do Azure OpenAI. Em seguida, ele pesquisa o banco de dados enviando o vetor para o Azure DocumentDB, que localiza hotéis com vetores semelhantes que significam descrições semelhantes. Por fim, ele formata os resultados convertendo os registros de banco de dados em texto legível que o agente sintetizador pode entender.

// Execute performs the vector search
func (t *VectorSearchTool) Execute(ctx context.Context, query string, nearestNeighbors int) (string, error) {
    // Generate embedding for query
    queryVector, err := t.openAIClients.GenerateEmbedding(ctx, query)
    if err != nil {
        return "", fmt.Errorf("failed to generate embedding: %w", err)
    }

    // Perform vector search
    results, err := t.vectorStore.VectorSearch(ctx, queryVector, nearestNeighbors)
    if err != nil {
        return "", fmt.Errorf("vector search failed: %w", err)
    }

    // Format results for synthesizer
    var formattedResults []string
    for i, result := range results {
        fmt.Printf("Hotel #%d: %s, Score: %.6f\n", i+1, result.Hotel.HotelName, result.Score)
        formattedResults = append(formattedResults, vectorstore.FormatHotelForSynthesizer(result))
    }

    return strings.Join(formattedResults, "\n\n"), nil
}

Por que usar esse padrão?

Separar a ferramenta do agente fornece flexibilidade. A IA decide quando pesquisar e o que pesquisar, enquanto a ferramenta manipula como pesquisar. Você pode adicionar mais ferramentas sem alterar a lógica do agente.

Solicitações

O arquivo de origem internal/prompts/prompts.go contém prompts do sistema e definições de ferramenta para os agentes.

O arquivo de prompts define as instruções e o contexto fornecidos aos modelos de IA para os agentes planejador e sintetizador. Esses prompts orientam o comportamento da IA e garantem que ela entenda sua função no fluxo de trabalho.

A qualidade das respostas de IA depende muito de instruções claras. Esses prompts definem limites, definem o formato de saída e concentram a IA na meta do usuário de tomar uma decisão. Você pode personalizar esses prompts para alterar como os agentes se comportam sem modificar nenhum código.

const PlannerSystemPrompt = `You are a hotel search planner. Your job is to help users find hotels by calling the search tool.

CRITICAL INSTRUCTION: You MUST call the "search_hotels_collection" tool for every request. This is the ONLY way to search the database.

When you call the tool, use these parameters:
- query: A clear, detailed natural language description of what the user is looking for. Expand vague requests (e.g., "nice hotel" → "hotel with high ratings, good reviews, and quality amenities").
- nearestNeighbors: Number of results (1-20). Use 3-5 for specific requests, 10-15 for broader searches.

EXAMPLES of how you should call the tool:
- User: "cheap hotel" → Call tool with query: "budget-friendly hotel with good value and affordable rates", nearestNeighbors: 10
- User: "hotel near downtown with parking" → Call tool with query: "hotel near downtown with good parking and wifi", nearestNeighbors: 5

IMPORTANT: Always call the tool. Do not provide answers without calling the tool first.`

const SynthesizerSystemPrompt = `You are an expert hotel recommendation assistant using vector search results.
Only use the TOP 3 results provided. Do not request additional searches or call other tools.

GOAL: Provide a concise comparative recommendation to help the user choose between the top 3 options.

REQUIREMENTS:
- Compare only the top 3 results across the most important attributes: rating, score, location, price-level (if available), and key tags (parking, wifi, pool).
- Identify the main tradeoffs in one short sentence per tradeoff.
- Give a single clear recommendation with one short justification sentence.
- Provide up to two alternative picks (one sentence each) explaining when they are preferable.

FORMAT CONSTRAINTS:
- Plain text only (no markdown).
- Keep the entire response under 220 words.
- Use simple bullets (•) or numbered lists and short sentences (preferably <25 words per sentence).
- Preserve hotel names exactly as provided in the tool summary.

Do not add extra commentary, marketing language, or follow-up questions. If information is missing and necessary to choose, state it in one sentence and still provide the best recommendation based on available data.`

Executar o exemplo

  1. Antes de executar o agente, carregue os dados sobre os hotéis com as inserções. O cmd/upload/main.go comando carrega hotéis do arquivo JSON, gera inserções para cada hotel usando text-embedding-3-small, insere documentos no Azure DocumentDB e cria um índice de vetor.

    go run cmd/upload/main.go
    
  2. Execute o agente de recomendação do hotel usando o cmd/agent/main.go comando. O agente chama o agente planejador, a busca em vetores e o agente sintetizador. A saída inclui pontuações de similaridade e a análise comparativa do agente sintetizador, juntamente com recomendações.

    go run cmd/agent/main.go
    
    Query: quintessential lodging near running trails, eateries, retail
    Nearest Neighbors: 5
    
    --- PLANNER ---
    Tool: search_hotels_collection
    Query: quintessential lodging near running trails, eateries, and retail shops with good amenities and access to outdoor activities
    K: 5
    Hotel #1: Nordick's Valley Motel, Score: 0.498665
    Hotel #2: White Mountain Lodge & Suites, Score: 0.487320
    Hotel #3: Trails End Motel, Score: 0.479854
    Hotel #4: Country Comfort Inn, Score: 0.474320
    Hotel #5: Lakefront Captain Inn, Score: 0.457873
    
    --- SYNTHESIZER ---
    Context size: 3233 characters
    
    --- FINAL ANSWER ---
    1. COMPARISON SUMMARY:  
    • Nordick's Valley Motel has the highest rating (4.5) and offers free parking, air conditioning, and continental breakfast. It is located in Washington D.C., near historic attractions and trails.
    • White Mountain Lodge & Suites is a resort with unique amenities like a pool, restaurant, and meditation gardens, but has the lowest rating (2.4). It is located in Denver, surrounded by forest trails.
    • Trails End Motel is budget-friendly with a moderate rating (3.2), free parking, free wifi, and a restaurant. It is close to downtown Scottsdale and eateries.
    
    Key tradeoffs:
    - Nordick's Valley Motel excels in rating and proximity to historic attractions but lacks a pool or free wifi.
    - White Mountain Lodge & Suites offers resort-style amenities and forest trails but has the lowest rating.
    - Trails End Motel balances affordability and essential amenities but has fewer unique features compared to the others.
    
    2. BEST OVERALL:
    Nordick's Valley Motel is the best choice for its high rating, proximity to trails and attractions, and free parking.
    
    3. ALTERNATIVE PICKS:
    • Choose White Mountain Lodge & Suites if you prioritize resort amenities and forest trails over rating.
    • Choose Trails End Motel if affordability and proximity to downtown Scottsdale are your main concerns.
    

Exibir e gerenciar dados no Visual Studio Code

  1. Selecione a extensão do DocumentDB no Visual Studio Code para se conectar à sua conta do Azure DocumentDB.

  2. Exiba os dados e índices no banco de dados Hotéis.

    Extensão DocumentDB do Visual Studio Code mostrando o índice de busca vetorial e os documentos do hotel.

Limpar os recursos

Use o comando de limpeza para excluir o banco de dados de teste quando terminar. Execute o comando a seguir:

go run cmd/cleanup/main.go

Exclua o grupo de recursos, a conta do DocumentDB e o recurso do Azure OpenAI quando você não precisar deles para evitar custos extras.