Tutorial: Membangun agen LangChain.js dengan Azure AI Search

Bangun asisten SDM cerdas menggunakan layanan LangChain.js dan Azure. Agen ini membantu karyawan di perusahaan NorthWind fiktif menemukan jawaban atas pertanyaan sumber daya manusia dengan mencari melalui dokumentasi perusahaan.

Anda akan menggunakan Azure AI Search untuk menemukan dokumen yang relevan dan Azure OpenAI untuk menghasilkan jawaban yang akurat. Kerangka kerja LangChain.js menangani kompleksitas orkestrasi agen, memungkinkan Anda fokus pada persyaratan bisnis spesifik Anda.

Yang akan Anda pelajari:

  • Menyebarkan sumber daya Azure menggunakan Azure Developer CLI
  • Membangun agen LangChain.js yang terintegrasi dengan layanan Azure
  • Menerapkan retrieval-augmented generation (RAG) untuk pencarian dokumen
  • Menguji dan men-debug agen Anda secara lokal dan di Azure

Pada akhir tutorial ini, Anda memiliki REST API yang berfungsi yang menjawab pertanyaan SDM menggunakan dokumentasi perusahaan Anda.

Gambaran umum arsitektur

Cuplikan layar diagram yang mengilustrasikan alur kerja agen LangChain.js dan cabang keputusannya untuk menggunakan dokumentasi SDM untuk menjawab pertanyaan.

NorthWind bergantung pada dua sumber data:

  • Dokumentasi SDM dapat diakses oleh semua karyawan
  • Database SDM rahasia yang berisi data karyawan sensitif.

Tutorial ini berfokus pada pembangunan agen LangChain.js yang menentukan apakah pertanyaan karyawan dapat dijawab menggunakan dokumen SDM publik. Jika demikian, agen LangChain.js memberikan jawabannya secara langsung.

Prasyarat

Untuk menggunakan sampel ini di Codespace atau kontainer pengembangan lokal, termasuk membangun dan menjalankan agen LangChain.js, Anda memerlukan hal berikut:

Jika Anda menjalankan kode sampel secara lokal tanpa kontainer pengembangan, Anda juga memerlukan:

Sumber daya Azure

Sumber daya Azure berikut diperlukan. Mereka dibuat untuk Anda dalam artikel ini menggunakan templat Azure Developer CLI dan Bicep menggunakan Azure Verified Modules (AVM). Sumber daya dibuat dengan akses tanpa kata sandi dan kunci untuk tujuan pembelajaran. Tutorial ini menggunakan akun pengembang lokal Anda untuk autentikasi tanpa kata sandi:

Diagram arsitektur Azure memperlihatkan implementasi RAG dengan komponen Container Apps, OpenAI, AI Search, Container Registry, dan Managed Identity dengan koneksi mereka.

Arsitektur agen

Kerangka kerja LangChain.js menyediakan alur keputusan untuk membangun agen cerdas sebagai LangGraph. Dalam tutorial ini, Anda membuat agen LangChain.js yang terintegrasi dengan Azure AI Search dan Azure OpenAI untuk menjawab pertanyaan terkait SDM. Arsitektur dari agen ini dirancang untuk:

  • Tentukan apakah pertanyaan relevan dengan dokumentasi SDM umum yang tersedia untuk semua karyawan.
  • Ambil dokumen yang relevan dari Azure AI Search berdasarkan kueri pengguna.
  • Gunakan Azure OpenAI untuk menghasilkan jawaban berdasarkan dokumen dan model LLM yang diambil.

Komponen Utama:

  • Struktur grafik: Agen LangChain.js diwakili sebagai grafik, di mana:

    • Simpul melakukan tugas tertentu, seperti membuat keputusan atau mengambil data.
    • Tepi menentukan aliran antar simpul, menentukan urutan operasi.
  • Integrasi Azure AI Search:

    • Menggunakan model penyematan untuk membuat vektor.
    • Menyisipkan dokumen SDM (*.md, *.pdf) ke penyimpanan vektor. Dokumen tersebut meliputi:
      • Informasi perusahaan
      • Buku pegangan karyawan
      • Buku pegangan tunjangan
      • Pustaka peran karyawan
    • Mengambil dokumen yang relevan berdasarkan perintah pengguna.
  • Integrasi Azure OpenAI:
    • Menggunakan model bahasa besar untuk:
      • Menentukan apakah pertanyaan dapat dijawab dari dokumen SDM impersonal.
      • Menghasilkan jawaban dengan perintah menggunakan konteks dari dokumen dan pertanyaan pengguna.

Tabel berikut ini memiliki contoh pertanyaan pengguna yang relevan dan dapat dijawab serta pertanyaan yang tidak relevan dari dokumen Sumber Daya Manusia umum.

Pertanyaan Relevan Explanation
Does the NorthWind Health Plus plan cover eye exams? Yes Dokumen SDM, seperti buku pegangan karyawan, harus memberikan jawaban.
How much of my perks + benefits have I spent? Tidak. Pertanyaan ini memerlukan akses ke data karyawan rahasia, yang berada di luar cakupan agen ini.

Dengan menggunakan kerangka kerja LangChain.js, Anda menghindari banyak kode boilerplate agenik yang biasanya diperlukan untuk agen dan integrasi layanan Azure, memungkinkan Anda untuk fokus pada kebutuhan bisnis Anda.

Mengkloning repositori kode sampel

Di direktori baru, kloning repositori kode sampel dan ubah ke direktori baru:

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

Sampel ini menyediakan kode yang Anda butuhkan untuk membuat sumber daya Azure yang aman, membangun agen LangChain.js dengan Azure AI Search dan Azure OpenAI, dan menggunakan agen dari Node.js server Fastify API.

Mengautentikasi ke Azure CLI dan Azure Developer CLI

Masuk ke Azure dengan Azure Developer CLI, buat sumber daya Azure, dan sebarkan kode sumber. Karena proses penyebaran menggunakan Azure CLI dan Azure Developer CLI, masuk ke Azure CLI, lalu konfigurasikan Azure Developer CLI untuk menggunakan autentikasi Anda dari Azure CLI:

az login
azd config set auth.useAzCliAuth true

Membuat sumber daya dan menyebarkan kode dengan Azure Developer CLI

Mulai proses penyebaran dengan menjalankan azd up perintah:

azd up

azd up Selama perintah, jawab pertanyaan:

  • Nama lingkungan baru: masukkan nama lingkungan unik seperti langchain-agent. Nama lingkungan ini digunakan sebagai bagian dari grup sumber daya Azure.
  • Pilih Langganan Azure: pilih langganan tempat sumber daya dibuat.
  • Pilih wilayah: seperti eastus2.

Penyebaran membutuhkan waktu sekitar 10-15 menit. Azure Developer CLI mengatur proses menggunakan fase dan kait yang ditentukan dalam azure.yaml file:

Fase provisi (setara dengan azd provision):

  • Membuat sumber daya Azure yang ditentukan dalam infra/main.bicep:
  • Hook pascaprovisi: Memeriksa apakah indeks northwind Pencarian Azure AI sudah ada
    • Jika indeks tidak ada: menjalankan npm install dan npm run load_data mengunggah dokumen SDM menggunakan LangChain.js pdf loader dan menyematkan klien
    • Jika indeks ada: lewati pemuatan data untuk menghindari duplikat (Anda dapat memuat ulang secara manual dengan menghapus indeks atau menjalankan npm run load_data) Fase penyebaran (setara dengan azd deploy):
  • Pra-penyebaran: Membangun gambar Docker untuk server Fastify API dan mengunggahnya ke Azure Container Registry
  • Menyebarkan server API kontainer ke Azure Container Apps

Ketika penyebaran selesai, variabel lingkungan dan informasi sumber daya disimpan ke .env file di akar repositori. Anda dapat melihat sumber daya di portal Microsoft Azure.

Sumber daya dibuat dengan akses tanpa kata sandi dan kunci untuk tujuan pembelajaran. Tutorial pengantar ini menggunakan akun pengembang lokal Anda untuk autentikasi tanpa kata sandi. Untuk aplikasi produksi, gunakan hanya autentikasi tanpa kata sandi dengan identitas terkelola. Pelajari selengkapnya tentang autentikasi tanpa kata sandi.

Menggunakan kode sampel secara lokal

Sekarang setelah sumber daya Azure dibuat, Anda dapat menjalankan agen LangChain.js secara lokal.

Pasang dependensi

  1. Instal paket Node.js untuk proyek ini.

    npm install 
    

    Perintah ini menginstal dependensi yang ditentukan dalam dua package.json file di packages-v1 direktori, termasuk:

  2. Buat dua paket: server API dan agen AI.

    npm run build
    

    Perintah ini membuat tautan antara dua paket sehingga server API dapat memanggil agen AI.

Jalankan server API secara lokal

Azure Developer CLI membuat sumber daya Azure yang diperlukan dan mengonfigurasi variabel lingkungan dalam file akar .env . Konfigurasi ini mencakup hook pasca-provisi untuk mengunggah data ke penyimpanan vektor. Sekarang, Anda dapat menjalankan server Fastify API yang menghosting agen LangChain.js. Mulai server FASTIFY API.

npm run dev

Server dijalankan dan menunggu pada port 3000. Anda dapat menguji server dengan menavigasi ke [http://localhost:3000] di browser web Anda. Anda akan melihat pesan selamat datang yang menunjukkan bahwa server sedang berjalan.

Gunakan API untuk mengajukan pertanyaan

Anda dapat menggunakan alat seperti Klien REST atau curl untuk mengirim permintaan POST ke /ask titik akhir dengan isi JSON yang berisi pertanyaan Anda.

Kueri klien REST tersedia di direktori packages-v1/server-api/http.

Contoh menggunakan curl:

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

Anda harus menerima respons JSON dengan jawaban dari agen LangChain.js.

{
  "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."
}

Beberapa contoh pertanyaan tersedia di packages-v1/server-api/http direktori. Buka file di Visual Studio Code dengan Rest Client untuk mengujinya dengan cepat.

Memahami kode aplikasi

Bagian ini menjelaskan bagaimana agen LangChain.js terintegrasi dengan layanan Azure. Aplikasi repositori diatur sebagai ruang kerja npm dengan dua paket utama:

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

Keputusan arsitektur utama:

  • Struktur monorepo: ruang kerja npm memungkinkan dependensi bersama dan paket tertaut
  • Pemisahan Tanggung Jawab: Logika agen (langgraph-agent) independen dari server API (server-api)
  • Autentikasi terpusat: File dalam ./langgraph-agent/src/azure menangani autentikasi berbasis kunci dan tanpa kata sandi dan integrasi layanan Azure

Autentikasi ke Azure Services

Aplikasi ini mendukung metode autentikasi berbasis kunci dan tanpa kata sandi, yang dikontrol SET_PASSWORDLESS oleh variabel lingkungan. DEFAULTAzureCredential API dari pustaka Azure Identity digunakan untuk autentikasi tanpa kata sandi, memungkinkan aplikasi berjalan dengan mulus dalam pengembangan lokal dan lingkungan Azure. Anda dapat melihat autentikasi ini dalam cuplikan kode berikut:

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;
}

Saat menggunakan pustaka pihak ketiga seperti LangChain.js atau pustaka OpenAI untuk mengakses Azure OpenAI, Anda memerlukan fungsi penyedia token alih-alih meneruskan objek kredensial secara langsung. Fungsi getBearerTokenProvider dari pustaka Azure Identity memecahkan masalah ini dengan membuat penyedia token yang secara otomatis mengambil dan menyegarkan token pembawa OAuth 2.0 untuk cakupan sumber daya Azure tertentu (misalnya, "https://cognitiveservices.azure.com/.default"). Anda mengonfigurasi cakupan sekali selama penyiapan, dan penyedia token menangani semua manajemen token secara otomatis. Pendekatan ini berfungsi dengan kredensial pustaka Azure Identity apa pun, termasuk identitas terkelola dan kredensial Azure CLI. Meskipun pustaka Azure SDK menerima DefaultAzureCredential secara langsung, pustaka pihak ketiga seperti LangChain.js memerlukan pola penyedia token ini untuk menjembatani kesenjangan autentikasi.

Integrasi Azure AI Search

Sumber daya Azure AI Search menyimpan penyematan dokumen dan memungkinkan pencarian semantik untuk konten yang relevan. Aplikasi ini menggunakan LangChain's AzureAISearchVectorStore untuk mengelola penyimpanan vektor tanpa Anda harus menentukan skema indeks.

Penyimpanan vektor dibuat dengan konfigurasi untuk operasi admin (tulis) dan kueri (baca) sehingga pemuatan dan kueri dokumen dapat menggunakan konfigurasi yang berbeda. Ini penting apakah Anda menggunakan kunci atau autentikasi tanpa kata sandi dengan identitas terkelola.

Penyebaran Azure Developer CLI menyertakan hook pasca-penyebaran yang mengunggah dokumen ke penyimpanan vektor menggunakan LangChain.js PDF loader dan klien embedding. Hook pasca-penerapan ini adalah langkah terakhir dari perintah azd up setelah sumber daya Pencarian Azure AI dibuat. Skrip pemuatan dokumen menggunakan logika batching dan coba lagi untuk menangani batas laju layanan.

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

Gunakan file akar .env dibuat oleh Azure Developer CLI, Anda dapat mengautentikasi ke sumber daya Azure AI Search dan membuat klien AzureAISearchVectorStore :

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;

Saat Anda mengkueri, penyimpanan vektor mengonversi kueri pengguna menjadi penyematan, mencari dokumen dengan representasi vektor serupa, dan mengembalikan gugus yang paling relevan.

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);
}

Karena penyimpanan vektor dibangun di atas LangChain.js, ini mengabstraksi kerumitan dalam berinteraksi langsung dengan penyimpanan vektor. Setelah mempelajari antarmuka penyimpanan vektor LangChain.js, Anda dapat dengan mudah beralih ke implementasi penyimpanan vektor lainnya di masa mendatang.

Integrasi Azure OpenAI

Aplikasi ini menggunakan Azure OpenAI untuk penyematan dan kemampuan model bahasa besar (LLM). Kelas AzureOpenAIEmbeddings dari LangChain.js digunakan untuk menghasilkan embedding untuk dokumen dan kueri. Setelah Anda membuat klien embeddings, LangChain.js menggunakannya untuk membuat embeddings.

Integrasi Azure OpenAI untuk penyematan

Gunakan file akar .env yang dibuat oleh Azure Developer CLI untuk mengautentikasi ke sumber daya Azure OpenAI dan membuat klien AzureOpenAIEmbeddings :

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 });
}

Integrasi Azure OpenAI untuk LLM

Gunakan file akar .env yang dibuat oleh Azure Developer CLI untuk mengautentikasi ke sumber daya Azure OpenAI dan membuat klien AzureChatOpenAI :

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;

Aplikasi ini menggunakan AzureChatOpenAI kelas dari LangChain.js @langchain/openai untuk berinteraksi dengan model Azure OpenAI.

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,
      },
    ],
  };
};

Alur kerja agen LangGraph

Agen menggunakan LangGraph untuk menentukan alur kerja keputusan yang menentukan apakah pertanyaan dapat dijawab menggunakan dokumen SDM.

Struktur grafik:

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";

Alur kerja terdiri dari langkah-langkah berikut:

  • Mulai: Pengguna mengirimkan pertanyaan.
  • requires_hr_documents node: LLM menentukan apakah pertanyaan tersebut dapat dijawab dari dokumen SDM umum.
  • Perutean bersyarkat:
    • Jika ya, maka lanjutkan ke get_answer simpul.
    • Jika tidak, maka mengembalikan pesan bahwa pertanyaan memerlukan data SDM pribadi.
  • simpul get_answer: Mengambil dokumen dan menghasilkan jawaban.
  • Akhir: Mengembalikan jawaban kepada pengguna.

Pemeriksaan relevansi ini penting karena tidak semua pertanyaan SDM dapat dijawab dari dokumen umum. Pertanyaan pribadi seperti "Berapa banyak PTO yang saya miliki?" memerlukan akses ke database karyawan yang berisi data karyawan individu. Dengan memeriksa relevansi terlebih dahulu, agen menghindari jawaban berhalusinasi untuk pertanyaan yang membutuhkan informasi pribadi yang tidak dapat diaksesnya.

Memutuskan apakah pertanyaan memerlukan dokumen SDM

Simpul requires_hr_documents menggunakan LLM untuk menentukan apakah pertanyaan pengguna dapat dijawab menggunakan dokumen SDM umum. Ini menggunakan templat perintah yang menginstruksikan model untuk merespons dengan YES atau NO berdasarkan relevansi pertanyaan. Ini mengembalikan jawaban dalam pesan terstruktur, yang dapat diteruskan di sepanjang alur kerja. Simpul berikutnya menggunakan respons ini untuk merutekan alur kerja ke END atau ANSWER_NODE.

// @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;
};

Mendapatkan dokumen SDM yang diperlukan

Setelah ditentukan bahwa pertanyaan memerlukan dokumen SDM, alur kerja menggunakan getAnswer untuk mengambil dokumen yang relevan dari penyimpanan vektor, menambahkannya ke konteks perintah dan meneruskan seluruh prompt ke LLM.

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],
  };
}

Jika tidak ada dokumen yang relevan yang ditemukan, agen mengembalikan pesan yang menyatakan bahwa agen tersebut tidak dapat menemukan jawaban dalam dokumen Sumber Daya Manusia.

Penyelesaian Masalah

Untuk masalah apa pun dengan prosedur, buat masalah pada repositori kode sampel

Membersihkan sumber daya

Anda dapat menghapus grup sumber daya, yang menyimpan sumber daya Azure AI Search dan sumber daya Azure OpenAI atau menggunakan Azure Developer CLI untuk segera menghapus semua sumber daya yang dibuat oleh tutorial ini.

azd down --purge