Oktatóanyag: LangChain.js-ügynök létrehozása az Azure AI Search használatával

Intelligens HR-asszisztens létrehozása LangChain.js és Azure-szolgáltatások használatával. Ez az ügynök segít az alkalmazottaknak a fiktív NorthWind-vállalatnál, hogy választ találjanak az emberi erőforrásokra vonatkozó kérdésekre a vállalati dokumentációban való kereséssel.

Az Azure AI Search használatával megkeresheti a releváns dokumentumokat, az Azure OpenAI pedig pontos válaszokat hozhat létre. A LangChain.js keretrendszer kezeli az ügynökök vezénylésének összetettségét, így a konkrét üzleti követelményekre összpontosíthat.

Ismertetett témák:

  • Azure-erőforrások üzembe helyezése az Azure Developer CLI használatával
  • Azure-szolgáltatásokkal integrálható LangChain.js ügynök létrehozása
  • Lekérés-kiterjesztett generáció (RAG) megvalósítása dokumentumkereséshez
  • Az ügynök tesztelése és hibakeresése helyileg és az Azure-ban

Az oktatóanyag végére egy működő REST API-val rendelkezik, amely a vállalat dokumentációjával válaszol a HR-kérdésekre.

Az architektúra áttekintése

Képernyőkép az LangChain.js ügynök munkafolyamatát és döntési ágát bemutató diagramról, amely a HR-dokumentációt használja a kérdések megválaszolásához.

A NorthWind két adatforrásra támaszkodik:

  • A HR dokumentációja minden alkalmazott számára elérhető
  • Bizalmas alkalmazotti adatokat tartalmazó HR-adatbázis.

Ez az oktatóanyag egy LangChain.js ügynök létrehozására összpontosít, amely meghatározza, hogy egy alkalmazott kérdése megválaszolható-e a nyilvános HR-dokumentumok használatával. Ha igen, a LangChain.js ügynök közvetlenül adja meg a választ.

Előfeltételek

Ha ezt a mintát a Codespace-ben vagy a helyi fejlesztési tárolóban szeretné használni, beleértve a LangChain.js-ügynök felépítését és futtatását, a következőkre van szüksége:

Ha a mintakódot helyileg futtatja fejlesztési tároló nélkül, a következőkre is szüksége van:

Azure-erőforrások

A következő Azure-erőforrásokra van szükség. Ezeket ebben a cikkben az Azure Developer CLI - és Bicep-sablonok használatával hoztuk létre az Azure Verified Modules (AVM) használatával. Az erőforrások jelszó nélküli és kulcs nélküli hozzáféréssel is jönnek létre tanulási célokra. Ez az oktatóanyag a helyi fejlesztői fiókot használja jelszó nélküli hitelesítéshez:

Azure-architektúradiagram a RAG-implementációról a Container Apps, az OpenAI, az AI Search, a Container Registry és a Managed Identity összetevőivel a kapcsolatukkal.

Ügynökarchitektúra

A LangChain.js keretrendszer döntési folyamatot biztosít az intelligens ügynökök LangGraphként való létrehozásához. Ebben az oktatóanyagban egy LangChain.js ügynököt hoz létre, amely integrálható az Azure AI Search és az Azure OpenAI szolgáltatással a HR-sel kapcsolatos kérdések megválaszolásához. Az ügynök architektúrája a következőkre lett tervezve:

  • Állapítsa meg, hogy egy kérdés az összes alkalmazott számára elérhető általános HR-dokumentáció szempontjából releváns-e.
  • A felhasználói lekérdezés alapján lekérheti a releváns dokumentumokat az Azure AI Searchből.
  • Az Azure OpenAI használatával választ hozhat létre a lekért dokumentumok és az LLM-modell alapján.

Fő összetevők:

  • Gráfstruktúra: A LangChain.js ügynök gráfként jelenik meg, ahol:

    • A csomópontok konkrét feladatokat hajtanak végre, például döntéshozatalt vagy adatok beolvasását.
    • Az élek határozzák meg a csomópontok közötti folyamatot, meghatározva a műveletek sorrendjét.
  • Azure AI Search-integráció:

    • Beágyazási modellt használ vektorok létrehozásához.
    • HR-dokumentumokat (*.md, *.pdf) szúr be a vektortárolóba. A dokumentumok a következők:
      • Céges információk
      • Alkalmazotti kézikönyv
      • Előnyök kézikönyv
      • Alkalmazotti szerepkör-kódtár
    • A felhasználói kérés alapján lekéri a releváns dokumentumokat.
  • Azure OpenAI-integráció:
    • Nagy nyelvi modellt használ a következő célokra:
      • Meghatározza, hogy egy kérdés megválaszolható-e személytelen HR-dokumentumokból.
      • A dokumentumokból és a felhasználói kérdésekből származó környezet használatával válasz generál.

Az alábbi táblázat olyan felhasználói kérdésekre mutat be példákat, amelyek általános emberierőforrás-dokumentumokból nem relevánsak és megválaszolhatók:

Kérdés Releváns Explanation
Does the NorthWind Health Plus plan cover eye exams? Igen A HR-dokumentumoknak, például az alkalmazotti kézikönyvnek választ kell adniuk.
How much of my perks + benefits have I spent? Nem Ehhez a kérdéshez hozzá kell férni a bizalmas alkalmazottak adataihoz, amelyek nem tartoznak az ügynök hatókörébe.

A LangChain.js keretrendszer használatával elkerülheti az ügynökökhöz és az Azure-szolgáltatások integrációjához általában szükséges ügynöki sablonkód nagy részét, így az üzleti igényekre összpontosíthat.

A mintakódtár klónozása

Egy új könyvtárban klónozza a mintakódtárat, és váltson az új könyvtárra:

git clone https://github.com/Azure-Samples/azure-typescript-langchainjs.git
cd azure-typescript-langchainjs

Ez a minta tartalmazza a biztonságos Azure-erőforrások létrehozásához, a LangChain.js-ügynök Azure AI Search és Az Azure OpenAI használatával való létrehozásához szükséges kódot, valamint az ügynök használatát egy Node.js Fastify API-kiszolgálóról.

Hitelesítés az Azure CLI-ben és az Azure Developer CLI-ben

Jelentkezzen be az Azure-ba az Azure Developer CLI-vel, hozza létre az Azure-erőforrásokat, és telepítse a forráskódot. Mivel az üzembe helyezési folyamat az Azure CLI-t és az Azure Developer CLI-t is használja, jelentkezzen be az Azure CLI-be, majd konfigurálja az Azure Developer CLI-t az Azure CLI-ből származó hitelesítés használatára:

az login
azd config set auth.useAzCliAuth true

Erőforrások létrehozása és kód üzembe helyezése az Azure Developer CLI-vel

A parancs futtatásával kezdje el az üzembe helyezési azd up folyamatot:

azd up

azd up A parancs során válaszoljon a következő kérdésekre:

  • Új környezetnév: adjon meg egy egyedi környezetnevet, például langchain-agent. Ez a környezetnév az Azure-erőforráscsoport részeként használatos.
  • Válasszon ki egy Azure-előfizetést: válassza ki azt az előfizetést, amelyben az erőforrások létre lettek hozva.
  • Válasszon ki egy régiót: például eastus2.

Az üzembe helyezés körülbelül 10–15 percet vesz igénybe. Az Azure Developer CLI a fájlban meghatározott fázisok és horgok használatával vezényli a azure.yaml folyamatot:

Kiépítési fázis (a következőnek felel meg azd provision):

  • A következőben definiált infra/main.bicepAzure-erőforrásokat hozza létre:
  • Kiépítés utáni horog: Ellenőrzi, hogy az Azure AI Search-index northwind már létezik-e
    • Ha az index nem létezik: futtatja npm install és npm run load_data feltölti a HR-dokumentumokat LangChain.js PDF-betöltő és beágyazó ügyfél használatával
    • Ha az index létezik: kihagyja az adatbetöltést az ismétlődések elkerülése érdekében (manuálisan újra betölthető az index törlésével vagy futtatásával npm run load_data) Üzembe helyezési fázis (egyenértékű a következővel azd deploy):
  • A hook előzetes üzembe helyezése: Létrehozza a Docker-rendszerképet a Fastify API-kiszolgálóhoz, és leküldi az Azure Container Registrybe
  • A tárolóalapú API-kiszolgáló üzembe helyezése az Azure Container Appsben

Az üzembe helyezés befejezésekor a környezeti változók és az erőforrás-információk az .env adattár gyökérkönyvtárában lévő fájlba lesznek mentve. Az erőforrásokat az Azure Portalon tekintheti meg.

Az erőforrások jelszó nélküli és kulcs nélküli hozzáféréssel is jönnek létre tanulási célokra. Ez a bevezető oktatóanyag a helyi fejlesztői fiókot használja jelszó nélküli hitelesítéshez. Éles alkalmazások esetén csak jelszó nélküli hitelesítést használjon felügyelt identitásokkal. További információ a jelszó nélküli hitelesítésről.

A mintakód helyi használata

Az Azure-erőforrások létrehozása után helyileg futtathatja a LangChain.js ügynököt.

Függőségek telepítése

  1. Telepítse a projekthez tartozó Node.js csomagokat.

    npm install 
    

    Ez a parancs telepíti a könyvtár két package.json fájljában definiált függőségeket, többek között a packages-v1 következőket:

  2. Hozza létre a két csomagot: az API-kiszolgálót és az AI-ügynököt.

    npm run build
    

    Ez a parancs kapcsolatot hoz létre a két csomag között, hogy az API-kiszolgáló meghívhassa az AI-ügynököt.

Az API-kiszolgáló helyi futtatása

Az Azure Developer CLI létrehozta a szükséges Azure-erőforrásokat, és konfigurálta a környezeti változókat a gyökérfájlban .env . Ez a konfiguráció tartalmazott egy telepítés utáni kiváltó pontot, amely feltölti az adatokat a vektortárolóba. Most futtathatja az LangChain.js-ügynököt üzemeltető Fastify API-kiszolgálót. Indítsa el a Fastify API-kiszolgálót.

npm run dev

A kiszolgáló a 3000-s porton indul el és figyel. A kiszolgálót a böngészőben a [http://localhost:3000] elemre lépve tesztelheti. Egy üdvözlő üzenetnek kell megjelennie, amely jelzi, hogy a kiszolgáló fut.

Kérdések feltevése az API-val

Használhat olyan eszközt, mint a REST-ügyfél , vagy curl post kérést küldhet a /ask végpontnak a kérdést tartalmazó JSON-törzstel.

REST ügyfél-lekérdezések a packages-v1/server-api/http címtárban érhetők el.

Példa a curl használatára:

curl -X POST http://localhost:3000/answer -H "Content-Type: application/json" -d "{\"question\": \"Does the NorthWind Health Plus plan cover eye exams?\"}"

JSON-választ kell kapnia a LangChain.js ügynök válaszával.

{
  "answer": "Yes, the NorthWind Health Plus plan covers eye exams. According to the Employee Handbook, employees enrolled in the Health Plus plan are eligible for annual eye exams as part of their vision benefits."
}

A címtárban packages-v1/server-api/http számos példakérdés érhető el. Nyissa meg a fájlokat a Visual Studio CodeREST Client segítségével, hogy gyorsan tesztelje őket.

Az alkalmazáskód ismertetése

Ez a szakasz bemutatja, hogyan integrálható az LangChain.js-ügynök az Azure-szolgáltatásokkal. Az adattár alkalmazása npm-munkaterületként van rendszerezve, két fő csomaggal:

Project Root
│
├── packages-v1/
│   │
│   ├── langgraph-agent/                    # Core LangGraph agent implementation
│   │   ├── src/
│   │   │   ├── azure/                      # Azure service integrations
│   │   │   │   ├── azure-credential.ts     # Centralized auth with DefaultAzureCredential
│   │   │   │   ├── embeddings.ts           # Azure OpenAI embeddings + PDF loading + rate limiting
│   │   │   │   ├── llm.ts                  # Azure OpenAI chat completion (key-based & passwordless)
│   │   │   │   └── vector_store.ts         # Azure AI Search vector store + indexing + similarity search
│   │   │   │
│   │   │   ├── langchain/                  # LangChain agent logic
│   │   │   │   ├── node_get_answer.ts      # RAG: retrieves docs + generates answers
│   │   │   │   ├── node_requires_hr_documents.ts  # Determines if HR docs needed
│   │   │   │   ├── nodes.ts                # LangGraph node definitions + state management
│   │   │   │   └── prompt.ts               # System prompts + conversation templates
│   │   │   │
│   │   │   └── scripts/                    # Utility scripts
│   │   │       └── load_vector_store.ts    # Uploads PDFs to Azure AI Search
│   │   │
│   │   └── data/                           # Source documents (PDFs) for vector store
│   │
│   └── server-api/                         # Fastify REST API server
│       └── src/
│           └── server.ts                   # HTTP server with /answer endpoint
│
├── infra/                                  # Infrastructure as Code
│   └── main.bicep                          # Azure resources: Container Apps, OpenAI, AI Search, ACR, managed identity
│
├── azure.yaml                              # Azure Developer CLI config + deployment hooks
├── Dockerfile                              # Multi-stage Docker build for containerized deployment
└── package.json                            # Workspace configuration + build scripts

Főbb architekturális döntések:

  • Monorepo struktúra: az npm-munkaterületek lehetővé teszik a megosztott függőségeket és a csatolt csomagokat
  • Az aggodalmak elkülönítése: Az ügynöklogika (langgraph-agent) független az API-kiszolgálótól (server-api)
  • Központosított hitelesítés: A kulcsalapú és a jelszó nélküli hitelesítést és az Azure-szolgáltatásintegrációt egyaránt kezelő fájlok ./langgraph-agent/src/azure

Hitelesítés az Azure Servicesben

Az alkalmazás a környezeti változó által SET_PASSWORDLESS vezérelt kulcsalapú és jelszó nélküli hitelesítési módszereket is támogatja. Az Azure Identity-kódtárból származó DefaultAzureCredential API jelszó nélküli hitelesítésre szolgál, így az alkalmazás zökkenőmentesen futhat a helyi fejlesztési és Azure-környezetekben. Ezt a hitelesítést a következő kódrészletben tekintheti meg:

import { DefaultAzureCredential } from "@azure/identity";

export const CREDENTIAL = new DefaultAzureCredential();

export const SCOPE_OPENAI = "https://cognitiveservices.azure.com/.default";

export async function azureADTokenProvider_OpenAI() {
  const tokenResponse = await CREDENTIAL.getToken(SCOPE_OPENAI);
  return tokenResponse.token;
}

Ha olyan külső kódtárakat használ, mint a LangChain.js vagy az OpenAI-kódtár az Azure OpenAI eléréséhez, a hitelesítő adatok objektumának közvetlen átadása helyett jogkivonat-szolgáltatói függvényre van szüksége. Az getBearerTokenProvider Azure Identity-kódtárból származó függvény úgy oldja meg ezt a problémát, "https://cognitiveservices.azure.com/.default"hogy létrehoz egy jogkivonat-szolgáltatót, amely automatikusan lekéri és frissíti az OAuth 2.0 tulajdonosi jogkivonatokat egy adott Azure-erőforrás hatóköréhez (például). A beállítás során egyszer konfigurálja a hatókört, és a jogkivonat-szolgáltató automatikusan kezeli az összes jogkivonat-kezelést. Ez a módszer bármilyen Azure Identity-kódtár hitelesítő adataival működik, beleértve a felügyelt identitást és az Azure CLI hitelesítő adatait. Bár az Azure SDK-könyvtárak közvetlenül elfogadják a DefaultAzureCredential -t, a külső könyvtárak, például a LangChain.js, megkövetelik ezt a token szolgáltatói mintát a hitelesítési hiányosságok áthidalásához.

Azure AI Search-integráció

Az Azure AI Search-erőforrás tárolja a dokumentumbeágyazást, és lehetővé teszi a szemantikai keresést a releváns tartalmakban. Az alkalmazás a LangChain's AzureAISearchVectorStore használatával kezeli a vektortárolót anélkül, hogy definiálnia kellene az indexsémát.

A vektortároló a rendszergazdai (írási) és a lekérdezési (olvasási) műveletek konfigurációjával jön létre, így a dokumentumok betöltése és lekérdezése különböző konfigurációkat használhat. Ez fontos, függetlenül attól, hogy kulcsokat vagy jelszó nélküli hitelesítést használ-e felügyelt identitásokkal.

Az Azure Developer CLI üzembe helyezése tartalmaz egy üzembe helyezés utáni horogot, amely feltölti a dokumentumokat a vektortárolóba LangChain.js PDF-betöltővel és beágyazási ügyféllel. Ez az üzembe helyezés utáni horog az Azure AI Search-erőforrás létrehozása után a azd up parancs utolsó lépése. A dokumentumbetöltési szkript kötegelési és újrapróbálkozási logikával kezeli a szolgáltatási sebesség korlátait.

postdeploy:
  posix:
    sh: bash
    run: |
      echo "Checking if vector store data needs to be loaded..."
      
      # Check if already loaded
      INDEX_CREATED=$(azd env get-values | grep INDEX_CREATED | cut -d'=' -f2 || echo "false")
      
      if [ "$INDEX_CREATED" = "true" ]; then
        echo "Index already created. Skipping data load."
        echo "Current document count: $(azd env get-values | grep INDEX_DOCUMENT_COUNT | cut -d'=' -f2)"
      else
        echo "Loading vector store data..."
        npm install
        npm run build
        npm run load_data
        
        # Get document count from the index
        SEARCH_SERVICE=$(azd env get-values | grep AZURE_AISEARCH_ENDPOINT | cut -d'/' -f3 | cut -d'.' -f1)
        DOC_COUNT=$(az search index show --service-name $SEARCH_SERVICE --name northwind --query "documentCount" -o tsv 2>/dev/null || echo "0")
        
        # Mark as loaded
        azd env set INDEX_CREATED true
        azd env set INDEX_DOCUMENT_COUNT $DOC_COUNT
        
        echo "Data loading complete! Indexed $DOC_COUNT documents."
      fi

Az Azure Developer CLI által létrehozott .env gyökérfájlt felhasználva hitelesítheti az Azure AI Search erőforrást, és létrehozhatja az AzureAISearchVectorStore klienst.

const endpoint = process.env.AZURE_AISEARCH_ENDPOINT;
const indexName = process.env.AZURE_AISEARCH_INDEX_NAME;

const adminKey = process.env.AZURE_AISEARCH_ADMIN_KEY;
const queryKey = process.env.AZURE_AISEARCH_QUERY_KEY;

export const QUERY_DOC_COUNT = 3;
const MAX_INSERT_RETRIES = 3;

const shared_admin = {
  endpoint,
  indexName,
};

export const VECTOR_STORE_ADMIN_KEY: AzureAISearchConfig = {
  ...shared_admin,
  key: adminKey,
};

export const VECTOR_STORE_ADMIN_PASSWORDLESS: AzureAISearchConfig = {
  ...shared_admin,
  credentials: CREDENTIAL,
};

export const VECTOR_STORE_ADMIN_CONFIG: AzureAISearchConfig =
  process.env.SET_PASSWORDLESS == "true"
    ? VECTOR_STORE_ADMIN_PASSWORDLESS
    : VECTOR_STORE_ADMIN_KEY;

const shared_query = {
  endpoint,
  indexName,
  search: {
    type: AzureAISearchQueryType.Similarity,
  },
};

// Key-based config
export const VECTOR_STORE_QUERY_KEY: AzureAISearchConfig = {
  key: queryKey,
  ...shared_query,
};

export const VECTOR_STORE_QUERY_PASSWORDLESS: AzureAISearchConfig = {
  credentials: CREDENTIAL,
  ...shared_query,
};

export const VECTOR_STORE_QUERY_CONFIG =
  process.env.SET_PASSWORDLESS == "true"
    ? VECTOR_STORE_QUERY_PASSWORDLESS
    : VECTOR_STORE_QUERY_KEY;

Lekérdezéskor a vektortároló beágyazássá alakítja a felhasználó lekérdezését, hasonló vektoros ábrázolású dokumentumokat keres, és visszaadja a legrelevánsabb adattömböket.

export function getReadOnlyVectorStore(): AzureAISearchVectorStore {
  const embeddings = getEmbeddingClient();
  return new AzureAISearchVectorStore(embeddings, VECTOR_STORE_QUERY_CONFIG);
}

export async function getDocsFromVectorStore(
  query: string,
): Promise<Document[]> {
  const store = getReadOnlyVectorStore();

  // @ts-ignore
  //return store.similaritySearchWithScore(query, QUERY_DOC_COUNT);
  return store.similaritySearch(query, QUERY_DOC_COUNT);
}

Mivel a vektortároló a LangChain.js-re épül, elrejti a vektortárolóval való közvetlen interakció összetettségét. A LangChain.js vektortároló felületének elsajátítása után a jövőben egyszerűen válthat más vektortároló-implementációkra.

Azure OpenAI-integráció

Az alkalmazás az Azure OpenAI-t használja a beágyazásokhoz és a nagy nyelvi modellek (LLM) képességeihez is. A AzureOpenAIEmbeddings LangChain.js osztályával beágyazásokat hozhat létre dokumentumokhoz és lekérdezésekhez. Miután létrehozta a beágyazási ügyfelet, LangChain.js használja a beágyazások létrehozásához.

Azure OpenAI-integráció beágyazásokhoz

Az Azure Developer CLI által létrehozott gyökérfájl .env használatával hitelesítheti az Azure OpenAI-erőforrást, és létrehozhatja az AzureOpenAIEmbeddings-ügyfelet :

const shared = {
  azureOpenAIApiInstanceName: instance,
  azureOpenAIApiEmbeddingsDeploymentName: model,
  azureOpenAIApiVersion: apiVersion,
  azureOpenAIBasePath,
  dimensions: 1536, // for text-embedding-3-small
  batchSize: EMBEDDING_BATCH_SIZE,
  maxRetries: 7,
  timeout: 60000,
};

export const EMBEDDINGS_KEY_CONFIG = {
  azureOpenAIApiKey: key,
  ...shared,
};

export const EMBEDDINGS_CONFIG_PASSWORDLESS = {
  azureADTokenProvider: azureADTokenProvider_OpenAI,
  ...shared,
};

export const EMBEDDINGS_CONFIG =
  process.env.SET_PASSWORDLESS == "true"
    ? EMBEDDINGS_CONFIG_PASSWORDLESS
    : EMBEDDINGS_KEY_CONFIG;
export function getEmbeddingClient(): AzureOpenAIEmbeddings {
  return new AzureOpenAIEmbeddings({ ...EMBEDDINGS_CONFIG });
}

Azure OpenAI-integráció LLM-hez

Az Azure Developer CLI által létrehozott gyökérfájl .env használatával hitelesítheti az Azure OpenAI-erőforrást, és létrehozhatja az AzureChatOpenAI-ügyfelet :

const shared = {
  azureOpenAIApiInstanceName: instance,
  azureOpenAIApiDeploymentName: model,
  azureOpenAIApiVersion: apiVersion,
  azureOpenAIBasePath,
  maxTokens: maxTokens ? parseInt(maxTokens, 10) : 1000,
  maxRetries: 7,
  timeout: 60000,
  temperature: 0,
};

export const LLM_KEY_CONFIG = {
  azureOpenAIApiKey: key,
  ...shared,
};

export const LLM_CONFIG_PASSWORDLESS = {
  azureADTokenProvider: azureADTokenProvider_OpenAI,
  ...shared,
};

export const LLM_CONFIG =
  process.env.SET_PASSWORDLESS == "true"
    ? LLM_CONFIG_PASSWORDLESS
    : LLM_KEY_CONFIG;

Az alkalmazás a AzureChatOpenAI LangChain.js @langchain/openai osztályát használja az Azure OpenAI-modellek használatához.

export const callChatCompletionModel = async (
  state: typeof StateAnnotation.State,
  _config: RunnableConfig,
): Promise<typeof StateAnnotation.Update> => {
  const llm = new AzureChatOpenAI({
    ...LLM_CONFIG,
  });

  const completion = await llm.invoke(state.messages);
  completion;

  return {
    messages: [
      ...state.messages,
      {
        role: "assistant",
        content: completion.content,
      },
    ],
  };
};

LangGraph-ügynök munkafolyamata

Az ügynök a LangGraph használatával határoz meg egy döntési munkafolyamatot, amely meghatározza, hogy egy kérdés megválaszolható-e HR-dokumentumokkal.

Gráfstruktúra:

import { StateGraph } from "@langchain/langgraph";
import {
  START,
  ANSWER_NODE,
  DECISION_NODE,
  route as endRoute,
  StateAnnotation,
} from "./langchain/nodes.js";
import { getAnswer } from "./langchain/node_get_answer.js";
import {
  requiresHrResources,
  routeRequiresHrResources,
} from "./langchain/node_requires_hr_documents.js";

const builder = new StateGraph(StateAnnotation)
  .addNode(DECISION_NODE, requiresHrResources)
  .addNode(ANSWER_NODE, getAnswer)
  .addEdge(START, DECISION_NODE)
  .addConditionalEdges(DECISION_NODE, routeRequiresHrResources)
  .addConditionalEdges(ANSWER_NODE, endRoute);

export const hr_documents_answer_graph = builder.compile();
hr_documents_answer_graph.name = "Azure AI Search + Azure OpenAI";

A munkafolyamat a következő lépésekből áll:

  • Kezdés: A felhasználó elküld egy kérdést.
  • requires_hr_documents csomópont: Az LLM meghatározza, hogy a kérdés megválaszolható-e az általános HR-dokumentumokból.
  • Feltételes útválasztás:
    • Ha igen, akkor továbblép a csomópontra get_answer .
    • Ha nem, akkor azt az üzenetet adja vissza, amely a kérdéshez személyes HR-adatokat igényel.
  • get_answer csomópont: Lekéri a dokumentumokat, és választ hoz létre.
  • Vége: Válasz a felhasználónak.

Ez a relevanciaellenőrzés azért fontos, mert nem minden HR-kérdésre lehet általános dokumentumokból válaszolni. Az olyan személyes kérdések, mint például a "Mennyi személyi erőforrással rendelkezem?", hozzáférést igényelnek az egyes alkalmazottak adatait tartalmazó alkalmazotti adatbázisokhoz. A relevancia első ellenőrzésével az ügynök elkerüli a hallucinált válaszokat azokra a kérdésekre, amelyekhez olyan személyes adatokra van szüksége, amelyekhez nem fér hozzá.

Döntse el, hogy a kérdéshez HR-dokumentumokra van-e szükség

A requires_hr_documents csomópont egy LLM használatával állapítja meg, hogy a felhasználó kérdése megválaszolható-e általános HR-dokumentumok használatával. Egy prompt sablont használ, amely arra utasítja a modellt, hogy a kérdés relevanciája alapján válaszoljon YES vagy NO. Egy strukturált üzenetben adja vissza a választ, amely a munkafolyamat mentén továbbítható. A következő csomópont ezt a választ használja a munkafolyamat útvonalának meghatározására, hogy vagy a END-hoz, vagy a ANSWER_NODE-hoz vezessen.

// @ts-nocheck
import { getLlmChatClient } from "../azure/llm.js";
import { StateAnnotation } from "../langchain/state.js";
import { RunnableConfig } from "@langchain/core/runnables";
import { BaseMessage } from "@langchain/core/messages";
import { ANSWER_NODE, END } from "./nodes.js";

const PDF_DOCS_REQUIRED = "Answer requires HR PDF docs.";

export async function requiresHrResources(
  state: typeof StateAnnotation.State,
  _config: RunnableConfig,
): Promise<typeof StateAnnotation.Update> {
  const lastUserMessage: BaseMessage = [...state.messages].reverse()[0];

  let pdfDocsRequired = false;

  if (lastUserMessage && typeof lastUserMessage.content === "string") {
    const question = `Does the following question require general company policy information that could be found in HR documents like employee handbooks, benefits overviews, or company-wide policies, then answer yes. Answer no if this requires personal employee-specific information that would require access to an individual's private data, employment records, or personalized benefits details: '${lastUserMessage.content}'. Answer with only "yes" or "no".`;

    const llm = getLlmChatClient();
    const response = await llm.invoke(question);
    const answer = response.content.toLocaleLowerCase().trim();
    console.log(`LLM question (is HR PDF documents required): ${question}`);
    console.log(`LLM answer (is HR PDF documents required): ${answer}`);
    pdfDocsRequired = answer === "yes";
  }

  // If HR documents (aka vector store) are required, append an assistant message to signal this.
  if (!pdfDocsRequired) {
    const updatedState = {
      messages: [
        ...state.messages,
        {
          role: "assistant",
          content:
            "Not a question for our HR PDF resources. This requires data specific to the asker.",
        },
      ],
    };

    return updatedState;
  } else {
    const updatedState = {
      messages: [
        ...state.messages,
        {
          role: "assistant",
          content: `${PDF_DOCS_REQUIRED} You asked: ${lastUserMessage.content}. Let me check.`,
        },
      ],
    };

    return updatedState;
  }
}

export const routeRequiresHrResources = (
  state: typeof StateAnnotation.State,
): typeof END | typeof ANSWER_NODE => {
  const lastMessage: BaseMessage = [...state.messages].reverse()[0];

  if (lastMessage && !lastMessage.content.includes(PDF_DOCS_REQUIRED)) {
    console.log("go to end");
    return END;
  }
  console.log("go to llm");
  return ANSWER_NODE;
};

A szükséges HR-dokumentumok lekérése

Miután kiderült, hogy a kérdéshez HR-dokumentumok szükségesek, a munkafolyamat a megfelelő dokumentumok vektortárolóból való lekérésére, a parancssor getAnswer való hozzáadására és a teljes kérésnek az LLM-nek való átadására használja.

import { ChatPromptTemplate } from "@langchain/core/prompts";
import { getLlmChatClient } from "../azure/llm.js";
import { StateAnnotation } from "./nodes.js";
import { AIMessage } from "@langchain/core/messages";
import { getReadOnlyVectorStore } from "../azure/vector_store.js";

const EMPTY_STATE = { messages: [] };

export async function getAnswer(
  state: typeof StateAnnotation.State = EMPTY_STATE,
): Promise<typeof StateAnnotation.Update> {
  const vectorStore = getReadOnlyVectorStore();
  const llm = getLlmChatClient();

  // Extract the last user message's content from the state as input
  const lastMessage = state.messages[state.messages.length - 1];

  const userInput =
    lastMessage && typeof lastMessage.content === "string"
      ? lastMessage.content
      : "";

  const docs = await vectorStore.similaritySearch(userInput, 3);

  if (docs.length === 0) {
    const noDocMessage = new AIMessage(
      "I'm sorry, I couldn't find any relevant information to answer your question.",
    );
    return {
      messages: [...state.messages, noDocMessage],
    };
  }

  const formattedDocs = docs.map((doc) => doc.pageContent).join("\n\n");

  const prompt = ChatPromptTemplate.fromTemplate(`
    Use the following context to answer the question:

    {context}

    Question: {question}
    `);

  const ragChain = prompt.pipe(llm);

  const result = await ragChain.invoke({
    context: formattedDocs,
    question: userInput,
  });

  const assistantMessage = new AIMessage(result.text);

  return {
    messages: [...state.messages, assistantMessage],
  };
}

Ha nem találhatók releváns dokumentumok, az ügynök egy üzenetet ad vissza, amely jelzi, hogy nem talált választ a HR-dokumentumokban.

Hibaelhárítás

Az eljárással kapcsolatos problémák esetén hozzon létre egy hibát a mintakódtárban

Erőforrások tisztítása

Törölheti az Azure AI Search-erőforrást és az Azure OpenAI-erőforrást tartalmazó erőforráscsoportot, vagy az Azure Developer CLI használatával azonnal törölheti az oktatóanyag által létrehozott összes erőforrást.

azd down --purge