Gyors kezdés: AI-ügynök vektorkereséssel az Azure DocumentDB-ben, TypeScript használatával.

Intelligens AI-ügynök létrehozása TypeScript és Azure DocumentDB használatával. Ez a rövid útmutató egy kétügynök-architektúrát mutat be, amely szemantikai szállodakeresést végez, és személyre szabott javaslatokat hoz létre.

Important

Ez a minta a LangChaint használja, amely egy népszerű keretrendszer az AI-alkalmazások létrehozásához. A LangChain absztrakciókat biztosít az ügynökök, eszközök és kérések számára, amelyek leegyszerűsítik az ügynökök fejlesztését.

Prerequisites

Az Azure Developer CLI használatával létrehozhatja a szükséges Azure-erőforrásokat a azd mintaadattár parancsainak futtatásával. További információ: Infrastruktúra üzembe helyezése az Azure Developer CLI-vel.

Azure-erőforrások

  • Azure OpenAI a Microsoft Foundry Models erőforrásban (klasszikus) a következő modelltelepítésekkel a Microsoft Azure AI Foundryben:

    • gpt-4o üzembe helyezés (szintetizátorügynök) – Ajánlott: 50 000 token/perc (TPM) kapacitás
    • gpt-4o-mini üzembe helyezés (Planner-ügynök) – Ajánlott: 30 000 token/perc (TPM) kapacitás
    • text-embedding-3-small üzembe helyezés (beágyazások) – Ajánlott: 10 000 token/perc (TPM) kapacitás
    • Jogkivonatkvóták: Konfiguráljon elegendő TPM-et minden üzembe helyezéshez a sebességkorlátozás elkerülése érdekében
  • Azure DocumentDB (MongoDB-kompatibilitással) fürt vektorkeresési támogatással:

    • A fürtréteg követelményei az előnyben részesített vektorindex-algoritmus alapján:
      • IVF (invertált fájlindex): M10 vagy újabb (alapértelmezett algoritmus)
      • HNSW (Hierarchikus navigálható kisvilág): M30 vagy újabb (gráfalapú)
      • DiskANN: M40 vagy újabb (nagy léptékűre optimalizálva)
    • Tűzfalkonfiguráció: KÖTELEZŐ. Megfelelő tűzfalkonfiguráció nélkül a csatlakozási kísérletek meghiúsulnak.
    • A jelszó nélküli hitelesítéshez győződjön meg arról, hogy a szerepköralapú hozzáférés-vezérlés (RBAC) engedélyezve van.

Fejlesztői eszközök

Agentic RAG alkalmazás-architektúra Node.js-hez

A minta egy kétügynök-architektúrát használ, amelyben minden ügynöknek van egy adott szerepköre.

Architektúradiagram a planner-ügynökkel, vektorkereső eszközzel és szintetizátorügynökkel rendelkező kétügynök-munkafolyamatról.

Ez a minta LangChain ügynök-keretrendszerét használja az OpenAI SDK-val. A LangChain függvényhívási absztrakciókat alkalmaz az eszközintegrációhoz, és lineáris munkafolyamatot követ az ügynökök és a keresőeszköz között. A végrehajtás állapot nélküli, beszélgetési előzmények nélkül, így alkalmas egyfordulós lekérdezési és válaszhelyzetekhez.

A Node.js mintakód lekérése

  1. A gyors kezdés követéséhez klónozza vagy töltse le az Azure DocumentDB mintákat a helyi gépére.

  2. Lépjen be a projektkönyvtárba:

    cd ai/vector-search-agent-typescript
    

Azure-erőforrások üzembe helyezése az Azure Developer CLI-vel

Az Azure Developer CLI (azd) használatával kiépíteni a szükséges Azure OpenAI- és DocumentDB-erőforrásokat.

  1. Jelentkezzen be az Azure-ba:

    azd auth login
    
  2. Az infrastruktúra kiépítése és üzembe helyezése:

    azd up
    
  3. Amikor a rendszer kéri, válassza ki az előfizetést és egy helyet (például swedencentral vagy eastus2).

  4. Az üzembe helyezés befejezése azd után a szükséges környezeti változókat adja ki. Másolja őket a .env fájlba (lásd : Környezeti változók konfigurálása).

Jótanács

Futtassa a azd env get-values bármikor az aktuális környezeti értékek megtekintéséhez.

Környezeti változók konfigurálása

Ha manuálisan hozta létre az Azure-erőforrásokat, vagy saját meglévő erőforrásait szeretné használni, konfigurálnia kell az alkalmazás környezeti változóit az Azure OpenAI-hoz és az Azure DocumentDB-hez való csatlakozáshoz. Ha azd up használta, akkor kihagyhatja ezt a lépést, mivel a szükséges környezeti változók automatikusan be vannak állítva a azd környezetben, és elérhetők a azd env get-values segítségével.

Hozzon létre egy .env fájlt a projekt gyökérkönyvtárában a környezeti változók konfigurálásához. Létrehozhat egy másolatot a .env.sample fájlról az adattárból.

Szerkessze a .env fájlt, és cserélje le a helyőrző értékeket.

Ez a gyorsindítás egy kétügynökös architektúrát (tervező + szintetizátor) használ három modelltelepítéssel (két csevegőmodell + beágyazások). A környezeti változók minden modelltelepítéshez konfigurálva vannak.

  • AZURE_OPENAI_PLANNER_MODEL: A gpt-4o-mini modell neve
  • AZURE_OPENAI_SYNTH_MODEL: A(z) gpt-4o modell neve
  • AZURE_OPENAI_EMBEDDING_MODEL: text-embedding-3-small modell neve

Két hitelesítési módszer közül választhat: jelszó nélküli hitelesítés az Azure Identity használatával (ajánlott), vagy hagyományos kapcsolati sztring és API-kulcs.

1. lehetőség: Jelszó nélküli hitelesítés

Használjon jelszó nélküli hitelesítést az Azure OpenAI-val és az Azure DocumentDB-vel is. Beállítás USE_PASSWORDLESS=true, AZURE_OPENAI_ENDPOINT, és AZURE_DOCUMENTDB_CLUSTER.

# Enable passwordless authentication
USE_PASSWORDLESS=true

# Azure OpenAI Configuration (passwordless)
AZURE_OPENAI_ENDPOINT=your-openai-endpoint
AZURE_OPENAI_PLANNER_MODEL=gpt-4o-mini
AZURE_OPENAI_PLANNER_API_VERSION=2024-08-01-preview
AZURE_OPENAI_SYNTH_MODEL=gpt-4o
AZURE_OPENAI_SYNTH_API_VERSION=2024-08-01-preview
AZURE_OPENAI_EMBEDDING_MODEL=text-embedding-3-small
AZURE_OPENAI_EMBEDDING_API_VERSION=2023-05-15

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

# Data Configuration
DATA_FILE_WITHOUT_VECTORS=../data/Hotels.json

# Vector Index Configuration
VECTOR_INDEX_ALGORITHM=vector-ivf
EMBEDDING_DIMENSIONS=1536

A jelszó nélküli hitelesítés előfeltételei:

  • Győződjön meg arról, hogy bejelentkezett az Azure-ba: az login

  • Adja meg identitásának a következő szerepköröket:

    • Cognitive Services OpenAI User az Azure OpenAI-erőforráson
    • DocumentDB Account Contributor és Cosmos DB Account Reader Role az Azure DocumentDB-erőforráson

    A szerepkörök hozzárendelésével kapcsolatos további információkért lásd: Azure-szerepkörök hozzárendelése az Azure Portal használatával.

A jelszó nélküli hitelesítés működése

Amikor USE_PASSWORDLESS=true az alkalmazás az Azure Identity SDK-ból szerez be DefaultAzureCredential egy OAuth-jogkivonatot. Az Azure DocumentDB-kapcsolatok esetében OIDC-jogkivonat-visszahívást használ, amely közvetlenül a MongoDB-illesztőnek továbbítja a hozzáférési jogkivonatot. Ez azt jelenti, hogy a konfigurációs fájlok nem tárolnak jelszavakat vagy kapcsolati sztringeket.

A hitelesítési folyamat:

  1. DefaultAzureCredential a rendelkezésre álló hitelesítő adatokat (Azure CLI, felügyelt identitás, környezeti változók) sorrendben ellenőrzi.
  2. Az Azure OpenAI esetében a token automatikusan átadásra kerül a LangChain AzureChatOpenAI és AzureOpenAIEmbeddings klienseknek.
  3. Az Azure DocumentDB esetében a token visszahívási függvény lekéri a hozzáférési tokent, és azt a MONGODB-OIDC hitelesítési mechanizmuson keresztül biztosítja a MongoDB-ügyfélnek.
import { AzureOpenAIEmbeddings, AzureChatOpenAI } from "@langchain/openai";
import { MongoClient, OIDCCallbackParams } from 'mongodb';
import { AccessToken, DefaultAzureCredential, TokenCredential, getBearerTokenProvider } from '@azure/identity';

/*
This file contains utility functions to create Azure OpenAI clients for embeddings, planning, and synthesis.

It supports two modes of authentication:
1. API Key based authentication using AZURE_OPENAI_API_KEY and AZURE_OPENAI_ENDPOINTenvironment variables.
2. Passwordless authentication using DefaultAzureCredential from Azure Identity library.
*/

// Azure Identity configuration
const OPENAI_SCOPE = 'https://cognitiveservices.azure.com/.default';
const DOCUMENT_DB_SCOPE = 'https://ossrdbms-aad.database.windows.net/.default';

// Azure identity credential (used for passwordless auth)
const CREDENTIAL = new DefaultAzureCredential();

function requireEnvVars(names: string[]) {
  const missing = names.filter((name) => {
    const value = process.env[name];
    return !value || value.trim().length === 0;
  });

  if (missing.length > 0) {
    throw new Error(`Missing required environment variables: ${missing.join(', ')}`);
  }
}

// Token callback for MongoDB OIDC authentication
async function azureIdentityTokenCallback(
  params: OIDCCallbackParams,
  credential: TokenCredential
): Promise<{ accessToken: string; expiresInSeconds: number }> {
  const tokenResponse: AccessToken | null = await credential.getToken([DOCUMENT_DB_SCOPE]);
  return {
    accessToken: tokenResponse?.token || '',
    expiresInSeconds: (tokenResponse?.expiresOnTimestamp || 0) - Math.floor(Date.now() / 1000)
  };

2. lehetőség: Kapcsolati sztring és API-kulcs hitelesítése

Használjon kulcsalapú hitelesítést a USE_PASSWORDLESS=false beállításával (vagy kihagyásával), és adja meg a AZURE_OPENAI_API_KEY és AZURE_DOCUMENTDB_CONNECTION_STRING értékeket a .env fájlban.

# 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_OPENAI_PLANNER_MODEL=gpt-4o-mini
AZURE_OPENAI_PLANNER_API_VERSION=2024-08-01-preview
AZURE_OPENAI_SYNTH_MODEL=gpt-4o
AZURE_OPENAI_SYNTH_API_VERSION=2024-08-01-preview
AZURE_OPENAI_EMBEDDING_MODEL=text-embedding-3-small
AZURE_OPENAI_EMBEDDING_API_VERSION=2023-05-15

# Azure DocumentDB (connection string)
AZURE_DOCUMENTDB_CLUSTER=your-mongo-cluster-name
AZURE_DOCUMENTDB_CONNECTION_STRING=mongodb+srv://username:password@cluster.mongocluster.cosmos.azure.com/
AZURE_DOCUMENTDB_DATABASENAME=Hotels
AZURE_DOCUMENTDB_COLLECTION=hotel_data

# Data Configuration
DATA_FILE_WITHOUT_VECTORS=../data/Hotels.json

# Vector Index Configuration
VECTOR_INDEX_ALGORITHM=vector-ivf
EMBEDDING_DIMENSIONS=1536

A projekt szerkezete

A projekt egy szabványos Node.js/TypeScript-projektelrendezést követ. A címtárszerkezetnek a következő struktúrához hasonlóan kell kinéznie:

vector-search-agent-typescript/
├── src/
│   ├── agent.ts              # Main agent application
│   ├── upload-documents.ts   # Data upload utility
│   ├── cleanup.ts            # Database cleanup utility
│   ├── vector-store.ts       # Vector store and tool implementation
│   ├── utils/
│   │   ├── clients.ts        # Azure OpenAI and DocumentDB client setup
│   │   ├── prompts.ts        # System prompts and tool definitions
│   │   ├── types.ts          # TypeScript type definitions
│   │   └── mongo.ts          # MongoDB utility functions
│   └── scripts/              # Additional utility scripts
├── .env                      # Environment variable configuration
├── package.json              # npm dependencies and scripts
└── tsconfig.json             # TypeScript configuration

Az ügynöki RAG-alkalmazás Node.js kódjának megismerése

Ez a szakasz az AI-ügynök munkafolyamatának alapvető összetevőit ismerteti. Kiemeli, hogy az ügynökök hogyan dolgozzák fel a kéréseket, hogyan csatlakoztatják az eszközök az AI-t az adatbázishoz, és hogy a kérések hogyan irányítja az AI viselkedését.

Node.js Agentic RAG-alkalmazás

A src/agent.ts fájl egy AI-alapú szállodaajánlási rendszert vezényl.

Az alkalmazás két Azure-szolgáltatást használ:

  • A lekérdezéseket megértő és javaslatokat generáló AI-modelleket használó Azure OpenAI
  • Azure DocumentDB, amely szállodai adatokat tárol, és vektoros hasonlósági keresést végez

Node.js Ügynök és eszközösszetevők

A három összetevő együttműködve dolgozza fel a szálloda keresési kérését:

  • Planner-ügynök – Értelmezi a kérést, és eldönti, hogyan kell keresni
  • Vektorkereső eszköz – A Planner-ügynök által leírthoz hasonló szállodák keresése
  • Szintetizátorügynök – Hasznos javaslatot ír a keresési eredmények alapján

Ügynök alapú RAG alkalmazás munkafolyamata

Az alkalmazás két lépésben dolgozza fel a szálloda keresési kérését:

  • Tervezés: A munkafolyamat meghívja a planner-ügynököt, amely elemzi a felhasználó lekérdezését (például "futó nyomvonalak közelében lévő szállodák"), és megkeresi az adatbázisban az egyező szállodákat.
  • Szintetizáló: A munkafolyamat meghívja a szintetizátor-ügynököt, amely áttekinti a keresési eredményeket, és személyre szabott javaslatot ír, amely ismerteti, hogy mely szállodák felelnek meg a legjobban a kérésnek.
// Authentication
const clients = process.env.USE_PASSWORDLESS === 'true' || process.env.USE_PASSWORDLESS === '1' ? createClientsPasswordless() : createClients();
const { embeddingClient, plannerClient, synthClient, dbConfig } = clients;
console.log(`DEBUG mode is ${process.env.DEBUG === 'true' ? 'ON' : 'OFF'}`);
console.log(`DEBUG_CALLBACKS length: ${DEBUG_CALLBACKS.length}`);


// Get vector store (get docs, create embeddings, insert docs)
const store = await getExistingStore(
  embeddingClient,
  dbConfig);

const query = process.env.QUERY || "quintessential lodging near running trails, eateries, retail";
const nearestNeighbors = parseInt(process.env.NEAREST_NEIGHBORS || '5', 10);

//Run planner agent
const hotelContext = await runPlannerAgent(plannerClient, embeddingClient, query, store, nearestNeighbors);
if (process.env.DEBUG === 'true') console.log(hotelContext);

//Run synth agent
const finalAnswer = await runSynthesizerAgent(synthClient, query, hotelContext);
// Get final recommendation (data + AI)
console.log('\n--- FINAL ANSWER ---');
console.log(finalAnswer);

Node.js ügynökök tervezése és szintetizálása

A src/agent.ts forrásfájl implementálja azokat a tervező- és szintetizátorügynököket, amelyek együttműködve dolgozzák fel a szállodai keresési kéréseket.

Planner-ügynök

A planner-ügynök az a döntéshozó , amely meghatározza a szállodák keresésének módját.

A planner-ügynök megkapja a felhasználó természetes nyelvi lekérdezését, és elküldi azt egy AI-modellnek a LangChain ügynök-keretrendszerével, valamint az általa használható elérhető eszközökkel. Az AI úgy dönt, hogy meghívja a vektorkereső eszközt, és keresési paramétereket biztosít. A LangChain automatikusan kezeli az eszköz végrehajtását, és visszaadja a megfelelő szállodákat. A keresési logika keménykódolása helyett az AI értelmezi a felhasználó által kívánt adatokat, és kiválasztja a keresés módját, így a rendszer rugalmassá válik a különböző típusú lekérdezésekhez.

async function runPlannerAgent(
  plannerClient: any,
  embeddingClient: any,
  userQuery: string,
  store: AzureDocumentDBVectorStore,
  nearestNeighbors = 5
): Promise<string> {
  console.log('\n--- PLANNER ---');

  const userMessage = `Use the "${TOOL_NAME}" tool with nearestNeighbors=${nearestNeighbors} and query="${userQuery}". Do not answer directly; call the tool.`;

  const contextSchema = z.object({
    store: z.any(),
    embeddingClient: z.any()
  });

  const agent = createAgent({
    model: plannerClient,
    systemPrompt: PLANNER_SYSTEM_PROMPT,
    tools: [getHotelsToMatchSearchQuery],
    contextSchema,
  });

  const agentResult = await agent.invoke(
    { messages: [{ role: 'user', content: userMessage }] },
    // @ts-ignore
    { context: { store, embeddingClient }, callbacks: DEBUG_CALLBACKS }
  );

  const plannerMessages = agentResult.messages || [];
  const searchResultsAsText = extractPlannerToolOutput(plannerMessages);

  return searchResultsAsText;
}

Szintetizátorügynök

A szintetizátorügynök az az író, aki hasznos javaslatokat hoz létre.

A szintetizátorügynök megkapja az eredeti felhasználói lekérdezést a szálloda keresési eredményeivel együtt. Mindent elküld egy AI-modellnek a javaslatok megírására vonatkozó útmutatással. Természetes nyelvi választ ad vissza, amely összehasonlítja a szállodákat, és elmagyarázza a legjobb lehetőségeket. Ez a megközelítés azért fontos, mert a nyers keresési eredmények nem felhasználóbarátak. A szintetizátor beszélgetési javaslattá alakítja az adatbázisrekordokat, amely elmagyarázza, hogy egyes szállodák miért felelnek meg a felhasználó igényeinek.

async function runSynthesizerAgent(synthClient: any, userQuery: string, hotelContext: string): Promise<string> {
  console.log('\n--- SYNTHESIZER ---');

  let conciseContext = hotelContext;
  console.log(`Context size is ${conciseContext.length} characters`);

  const agent = createAgent({
    model: synthClient,
    systemPrompt: SYNTHESIZER_SYSTEM_PROMPT,
  });

  const agentResult = await agent.invoke({
    messages: [{
      role: 'user',
      content: createSynthesizerUserPrompt(userQuery, conciseContext)
    }]
  });
  const synthMessages = agentResult.messages;
  const finalAnswer = synthMessages[synthMessages.length - 1].content;
  console.log(`Output: ${finalAnswer.length} characters of final recommendation`);
  return finalAnswer as string;
}

A src/vector-store.ts forrásfájl határozza meg a planner-ügynök által használt vektorkeresési eszközt.

Az eszközfájl egy keresőeszközt határoz meg, amelyet az AI-ügynök használhat a szállodák kereséséhez. Az ügynök így csatlakozik az adatbázishoz. Az AI nem keres közvetlenül az adatbázisban. Kéri a keresőeszköz használatát, és az eszköz végrehajtja a tényleges keresést.

Node.js függvény eszközdefinícióként

A LangChain függvénye tool egy szokásos TypeScript-függvényből hoz létre egy eszközt. Az eszközdefiníció tartalmazza a nevet, a leírást és a sémát (a Zod használata az ellenőrzéshez). Ez a definíció tudatja az AI-t az eszköz meglétéről és helyes használatáról.

export const getHotelsToMatchSearchQuery = tool(
  async ({ query, nearestNeighbors }, config): Promise<string> => {
    try {
      const store = config.context.store as AzureDocumentDBVectorStore;
      const embeddingClient = config.context.embeddingClient as AzureOpenAIEmbeddings;

      // Create query embedding and perform search
      const queryVector = await embeddingClient.embedQuery(query);
      const results = await store.similaritySearchVectorWithScore(queryVector, nearestNeighbors);
      console.log(`Found ${results.length} documents from vector store`);

      // Format results for synthesizer
      const formatted = results.map(([doc, score]) => {
        const md = doc.metadata as Partial<HotelForVectorStore>;
        console.log(`Hotel: ${md.HotelName ?? 'N/A'}, Score: ${score}`);
        return formatHotelForSynthesizer(md, score);
      }).join('\n\n');
      
      return formatted;
    } catch (error) {
      console.error('Error in getHotelsToMatchSearchQuery tool:', error);
      return 'Error occurred while searching for hotels.';
    }
  },
  {
    name: TOOL_NAME,
    description: TOOL_DESCRIPTION,
    schema: z.object({
      query: z.string(),
      nearestNeighbors: z.number().optional().default(5),
    }),
  }
);

Amikor az AI meghívja az eszközt, a függvény törzse fut. Beágyazást hoz létre úgy, hogy a szöveges lekérdezést numerikus vektorsá alakítja az Azure OpenAI beágyazási modelljével. Ezután megkeresi az adatbázist úgy, hogy elküldi a vektort az Azure DocumentDB-nek, amely hasonló, hasonló leírást jelentő vektorokkal rendelkező szállodákat talál. Végül úgy formázhatja az eredményeket, hogy az adatbázisrekordokat olvasható szöveggé alakítja, amelyet a szintetizátorügynök megérthet.

Az implementáció a LangChain-eket AzureDocumentDBVectorStore használja az Azure DocumentDB-vel való zökkenőmentes integrációhoz.

Miért érdemes ezt a mintát használni?

Az eszköz az ügynöktől való elválasztása rugalmasságot biztosít. Az AI dönti el, hogy mikor és mire keressen, míg az eszköz kezeli a keresés módját. Az ügynöklogika módosítása nélkül további eszközöket is hozzáadhat.

Az ügynök utasításokat ad az AI viselkedésének irányításához.

A src/utils/prompts.ts forrásfájl rendszerkéréseket és eszközdefiníciókat tartalmaz az ügynökök számára.

A parancssori fájl meghatározza az AI-modelleknek adott utasításokat és kontextust mind a tervező, mind a szintetizáló ügynökök számára. Ezek a kérések útmutatást adnak az AI viselkedéséhez, és biztosítják, hogy megértse a munkafolyamatban betöltött szerepét.

Az AI-válaszok minősége nagymértékben függ az egyértelmű utasításoktól. Ezek a kérések határokat határoznak meg, definiálják a kimeneti formátumot, és a felhasználó döntési céljára összpontosítják az AI-t. Ezeket a kéréseket testreszabhatja úgy, hogy kód módosítása nélkül módosítsa az ügynökök viselkedését.

export const PLANNER_SYSTEM_PROMPT = `You are a hotel search planner. Transform the user's request into a clear, detailed search query for a vector database.

CRITICAL REQUIREMENT: You MUST ALWAYS call the "${TOOL_NAME}" tool. This is MANDATORY for every request.

Use a tool call with:
- query (string)
- nearestNeighbors (number 1-20)

QUERY REFINEMENT RULES:
- If vague (e.g., "nice hotel"), add specific attributes: "hotel with high ratings and good amenities"
- If minimal (e.g., "cheap"), expand: "budget hotel with good value"
- Preserve specific details from user (location, amenities, business/leisure)
- Keep natural language - this is for semantic search
- Don't just echo the input - improve it for better search results
- nearestNeighbors: Use 3-5 for specific requests, 10-15 for broader requests, max 20

EXAMPLES:
User: "cheap hotel" → {"tool": "${TOOL_NAME}", "args": {"query": "budget-friendly hotel with good value and affordable rates", "nearestNeighbors": 10}}
User: "hotel near downtown with parking" → {"tool": "${TOOL_NAME}", "args": {"query": "hotel near downtown with good parking and wifi", "nearestNeighbors": 5}}
User: "nice place to stay" → {"tool": "${TOOL_NAME}", "args": {"query": "hotel with high ratings, good reviews, and quality amenities", "nearestNeighbors": 10}}

Do not answer the user directly. Always call the tool.`;

// ============================================================================
// Synthesizer Prompts
// ============================================================================

export const SYNTHESIZER_SYSTEM_PROMPT = `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.`;

Adatok előkészítése és feltöltése az Azure DocumentDB-be Node.js

A minta egy JSON-fájlból származó szállodai adatokat használ. Az adattár két verziót tartalmaz:

  • Hotels.json - Szállodai adatok vektoros beágyazások nélkül (ezt a mintát használja)
  • Hotels_Vector.json - Szállodai adatok előre kiszámított beágyazásokkal (más minták által használt)

A feltöltés működése

A upload-documents.ts szkript három lépést hajt végre:

  1. Adatok betöltése – Beolvassa a szálloda rekordjait a Hotels.json fájlból.
  2. Beágyazások létrehozása – A szkript minden szállodában elküldi a Description mezőt az Azure OpenAI-modellnek text-embedding-3-small , hogy létrehoz egy 1536 dimenziós vektorbeágyazást. Ez a szövegleírást numerikus ábrázolássá alakítja, amely rögzíti annak szemantikai jelentését.
  3. Beszúrás és indexelés – A szkript beszúrja a dokumentumokat (beágyazásukkal) az Azure DocumentDB-gyűjteménybe, és létrehoz egy vektorindexet a konfigurált algoritmus (IVF, HNSW vagy DiskANN) használatával.
import { createClientsPasswordless, createClients } from './utils/clients.js';
import { getStore } from './vector-store.js';

/**
 * Upload documents to Azure DocumentDB MongoDB Vector Store
 */

async function uploadDocuments() {
  try {
    console.log('Starting document upload...\n');

    // Get clients based on authentication mode
    const usePasswordless = process.env.USE_PASSWORDLESS === 'true' || process.env.USE_PASSWORDLESS === '1';
    console.log(`Authentication mode: ${usePasswordless ? 'Passwordless (Azure AD)' : 'API Key'}`);
    
    console.log('\nEnvironment variables check:');
    console.log(`  DATA_FILE_WITHOUT_VECTORS: ${process.env.DATA_FILE_WITHOUT_VECTORS}`);
    console.log(`  AZURE_DOCUMENTDB_DATABASENAME: ${process.env.AZURE_DOCUMENTDB_DATABASENAME}`);
    console.log(`  AZURE_DOCUMENTDB_COLLECTION: ${process.env.AZURE_DOCUMENTDB_COLLECTION}`);
    console.log(`  AZURE_DOCUMENTDB_CLUSTER: ${process.env.AZURE_DOCUMENTDB_CLUSTER}`);
    console.log(`  AZURE_OPENAI_EMBEDDING_MODEL: ${process.env.AZURE_OPENAI_EMBEDDING_MODEL}`);

    const requiredEnvVars = [
      'DATA_FILE_WITHOUT_VECTORS',
      'AZURE_DOCUMENTDB_DATABASENAME',
      'AZURE_DOCUMENTDB_COLLECTION',
      'AZURE_DOCUMENTDB_CLUSTER',
      'AZURE_OPENAI_EMBEDDING_MODEL',
    ];

    const missingEnvVars = requiredEnvVars.filter((name) => {
      const value = process.env[name];
      return !value || value.trim().length === 0;
    });

    if (missingEnvVars.length > 0) {
      throw new Error(`Missing required environment variables: ${missingEnvVars.join(', ')}`);
    }
    
    const clients = usePasswordless ? createClientsPasswordless() : createClients();
    const { embeddingClient, dbConfig } = clients;

    console.log('\ndbConfig properties:');
    console.log(`  instance: ${dbConfig.instance}`);
    console.log(`  databaseName: ${dbConfig.databaseName}`);
    console.log(`  collectionName: ${dbConfig.collectionName}`);

    // Check for data file path
    const dataFilePath = process.env.DATA_FILE_WITHOUT_VECTORS!;

    console.log(`\nReading data from: ${dataFilePath}`);
    console.log(`Database: ${dbConfig.databaseName}`);
    console.log(`Collection: ${dbConfig.collectionName}`);
    console.log(`Vector algorithm: ${process.env.VECTOR_INDEX_ALGORITHM || 'vector-ivf'}\n`);

    // Upload documents using existing getStore function
    const startTime = Date.now();
    const store = await getStore(dataFilePath, embeddingClient, dbConfig);
    const duration = ((Date.now() - startTime) / 1000).toFixed(2);
    
    console.log(`\n✓ Upload completed in ${duration} seconds`);

    // Close connection
    await store.close();
    console.log('✓ Connection closed');
    
    // Force exit to ensure process terminates (Azure credential timers may still be active)
    process.exit(0);

  } catch (error: any) {
    console.error('\n✗ Upload failed:', error?.message || error);
    console.error('\nFull error:', error);
    process.exit(1);
  }
}

// Run the upload
uploadDocuments();

Vektorindex létrehozása

A vektorindex lehetővé teszi a gyors hasonlóság keresését. Az index létrehozásakor az Azure DocumentDB rendszerezi a beágyazási vektorokat, hogy a "leíráshoz hasonló szállodák keresése" lekérdezések hatékonyan megválaszolhatók legyenek minden dokumentum vizsgálata nélkül.

A választott indextípus befolyásolja a teljesítményt:

Algorithm Klaszterszint A következőkre alkalmas
IVF M10+ Kis- és közepes adathalmazok, alacsonyabb költség
HNSW M30+ Magas visszahívás, gyors lekérdezések
DiskANN M40+ Nagy méretű adathalmazok, milliárdos vektorok
import {
  AzureDocumentDBVectorStore,
  AzureDocumentDBSimilarityType,
  AzureDocumentDBConfig
} from "@langchain/azure-cosmosdb";
import type { AzureOpenAIEmbeddings } from "@langchain/openai";
import { readFileSync } from 'fs';
import { Document } from '@langchain/core/documents';
import { HotelsData, Hotel } from './utils/types.js';
import { TOOL_NAME, TOOL_DESCRIPTION } from './utils/prompts.js';
import { z } from 'zod';
import { tool } from "langchain";
import { MongoClient } from 'mongodb';
import { BaseMessage } from "@langchain/core/messages";

type HotelForVectorStore = Omit<Hotel, 'Description_fr' | 'Location' | 'Rooms'>;

// Helper function for similarity type
function getSimilarityType(similarity: string) {
  switch (similarity.toUpperCase()) {
    case 'COS': return AzureDocumentDBSimilarityType.COS;
    case 'L2': return AzureDocumentDBSimilarityType.L2;
    case 'IP': return AzureDocumentDBSimilarityType.IP;
    default: return AzureDocumentDBSimilarityType.COS;
  }
}

// Consolidated vector index configuration
function getVectorIndexOptions() {
  const algorithm = process.env.VECTOR_INDEX_ALGORITHM || 'vector-ivf';
  const dimensions = parseInt(process.env.EMBEDDING_DIMENSIONS || '1536');
  const similarity = getSimilarityType(process.env.VECTOR_SIMILARITY || 'COS');
  
  const baseOptions = { dimensions, similarity };
  
  switch (algorithm) {
    case 'vector-hnsw':
      return {
        kind: 'vector-hnsw' as const,
        m: parseInt(process.env.HNSW_M || '16'),
        efConstruction: parseInt(process.env.HNSW_EF_CONSTRUCTION || '64'),
        ...baseOptions
      };
    case 'vector-diskann':
      return {
        kind: 'vector-diskann' as const,
        ...baseOptions
      };
    case 'vector-ivf':
    default:

Futtassa az agentikus RAG alkalmazást a Node.js használatával.

  1. Telepítse a függőségeket:

    npm install
    
  2. Az ügynök futtatása előtt töltse fel a szálloda adatait beágyazásokkal. A upload-documents.ts parancs betölti a szállodákat a JSON-fájlból, beágyazásokat hoz létre minden szállodához az Azure DocumentDB használatával text-embedding-3-small, dokumentumokat szúr be az Azure DocumentDB-be, és létrehoz egy vektorindexet.

    npm run upload
    
  3. Futtassa a szállodai ajánlási ügynököt a agent.ts parancs használatával. Az ügynök meghívja a tervezőügynököt, a vektorkeresést és a szintetizátor-ügynököt. A kimenet hasonlósági pontszámokat és a szintetizátorügynök javaslatokkal végzett összehasonlító elemzését tartalmazza.

    npm start
    
    DEBUG mode is OFF
    DEBUG_CALLBACKS length: 0
    Connected to existing vector store: Hotels.hotel_data
    
    --- PLANNER ---
    Found 5 documents from vector store
    Hotel: Nordick's Valley Motel, Score: 0.49866509437561035
    Hotel: White Mountain Lodge & Suites, Score: 0.48731985688209534
    Hotel: Trails End Motel, Score: 0.47985398769378662
    Hotel: Country Comfort Inn, Score: 0.47431993484497070
    Hotel: Lakefront Captain Inn, Score: 0.45787304639816284
    
    --- SYNTHESIZER ---
    Context size is 3233 characters
    Output: 812 characters of final recommendation
    
    --- 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.
    

Adatok megtekintése és kezelése a Visual Studio Code-ban

  1. Válassza ki a DocumentDB bővítményt a Visual Studio Code-ban az Azure DocumentDB-fiókhoz való csatlakozáshoz.

  2. Az adatok és indexek megtekintése a Hotels adatbázisban.

    A Visual Studio Code DocumentDB bővítmény a vektorkeresési indexet és a szállodai dokumentumokat mutatja.

Erőforrások tisztítása

Ha korábban erőforrásokhoz használt azd up, az összes Azure erőforrást eltávolíthatja a következőkkel:

azd down

Ha manuálisan hozta létre az erőforrásokat, és el szeretné távolítani az összes erőforrást, törölje az erőforráscsoportot a többletköltségek elkerülése érdekében.

Ha újra fel szeretné használni az erőforrásokat, a törlési paranccsal törölheti a tesztadatbázist, amikor elkészült. Futtassa a következő parancsot:

npm run cleanup