Bagikan melalui


Tutorial: Membangun agen LangChain.js dengan Azure AI Search

Dalam tutorial ini, Anda menggunakan LangChain.js untuk membangun agen LangChain.js yang memungkinkan karyawan perusahaan NorthWind mengajukan pertanyaan terkait sumber daya manusia. Dengan menggunakan kerangka kerja, Anda menghindari kode boilerplate biasanya diperlukan untuk agen LangChain.js dan integrasi layanan Azure, memungkinkan Anda untuk fokus pada kebutuhan bisnis Anda.

Di tutorial ini, Anda akan:

  • Menyiapkan agen LangChain.js
  • Mengintegrasikan sumber daya Azure ke agen LangChain.js Anda
  • Anda dapat menguji agen LangChain.js Anda di LangSmith Studio

NorthWind bergantung pada dua sumber data: dokumentasi SDM publik yang dapat diakses oleh semua karyawan dan 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.

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

Peringatan

Artikel ini menggunakan kunci untuk mengakses sumber daya. Di lingkungan produksi, praktik terbaik yang direkomendasikan adalah menggunakan Azure RBAC dan identitas terkelola. Pendekatan ini menghilangkan kebutuhan untuk mengelola atau memutar kunci, meningkatkan keamanan dan menyederhanakan kontrol akses.

Prasyarat

  • Akun Azure aktif. Buat akun secara gratis jika Anda tidak memilikinya.
  • Node.js LTS terinstal pada sistem Anda.
  • TypeScript untuk menulis dan mengkompilasi kode TypeScript.
  • LangChain.js perpustakaan untuk membangun agen.
  • Opsional: LangSmith untuk memantau penggunaan AI. Anda memerlukan nama proyek, kunci, dan titik akhir.
  • Opsional: LangGraph Studio untuk debugging rantai LangGraph dan agen LangChain.js.
  • Sumber daya Pencarian Azure AI: Pastikan Anda memiliki titik akhir sumber daya, kunci admin (untuk penyisipan dokumen), kunci kueri (untuk membaca dokumen), dan nama indeks.
  • Sumber daya Azure OpenAI: Anda memerlukan nama instans sumber daya, kunci, dan dua model dengan versi API mereka:
    • Model penyematan seperti text-embedding-ada-002.
    • Model bahasa besar seperti gpt-4o.

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.
  • Ambil dokumen yang relevan dari Azure AI Search.
  • 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:

    • Menyisipkan dokumen SDM ke penyimpanan vektor sebagai penyematan.
    • Menggunakan model penyematan (text-embedding-ada-002) untuk membuat penyematan ini.
    • Mengambil dokumen yang relevan berdasarkan permintaan pengguna.
  • Integrasi Azure OpenAI:

    • Menggunakan model bahasa besar (gpt-4o) untuk:
      • Menentukan apakah pertanyaan dapat dijawab dari dokumen SDM umum.
      • 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 Relevansi dengan Dokumen SDM
Does the NorthWind Health Plus plan cover eye exams? Relevan. Dokumen SDM, seperti buku pegangan karyawan, harus memberikan jawaban.
How much of my perks + benefits have I spent? Tidak relevan. Pertanyaan ini memerlukan akses ke data karyawan rahasia, yang berada di luar cakupan agen ini.

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

Menginisialisasi proyek Node.js Anda

Di direktori baru, inisialisasi proyek Node.js Anda untuk agen TypeScript Anda. Jalankan perintah berikut:

npm init -y
npm pkg set type=module
npx tsc --init

Membuat file lingkungan

Buat .env file untuk pengembangan lokal untuk menyimpan variabel lingkungan untuk sumber daya Azure dan LangGraph. Pastikan nama instans sumber daya untuk embedding dan LLM hanya berupa nama sumber daya, bukan endpoint.

Opsional: Jika menggunakan LangSmith, atur LANGSMITH_TRACING ke true untuk pengembangan lokal. Nonaktifkan (false) atau hapus di lingkungan produksi.

Pasang dependensi

  1. Instal dependensi Azure untuk Azure AI Search:

    npm install @azure/search-documents
    
  2. Instal dependensi LangChain.js untuk membuat dan menggunakan agen:

    npm install @langchain/community @langchain/core @langchain/langgraph @langchain/openai langchain
    
  3. Pasang dependensi pengembangan untuk pengembangan lokal:

    npm install --save-dev dotenv
    

Membuat file konfigurasi sumber daya pencarian Azure AI

Untuk mengelola berbagai sumber daya dan model Azure yang digunakan dalam tutorial ini, buat file konfigurasi tertentu untuk setiap sumber daya. Pendekatan ini memastikan kejelasan dan pemisahan kekhawatiran, sehingga lebih mudah untuk mengelola dan memelihara konfigurasi.

Konfigurasi untuk mengunggah dokumen ke penyimpanan vektor

File konfigurasi Azure AI Search menggunakan kunci admin untuk menyisipkan dokumen ke penyimpanan vektor. Kunci ini penting untuk mengelola penyerapan data ke Dalam Azure AI Search.

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

export const VECTOR_STORE_ADMIN = {
  endpoint,
  key: adminKey,
  indexName,
};

LangChain.js mengabstraksi kebutuhan untuk menentukan skema untuk penyerapan data ke Azure AI Search, menyediakan skema default yang cocok untuk sebagian besar skenario. Abstraksi ini menyederhanakan proses dan mengurangi kebutuhan akan definisi skema kustom.

Konfigurasi untuk mengkueri penyimpanan vektor

Untuk mengkueri penyimpanan vektor, buat file konfigurasi terpisah:

import {
  AzureAISearchConfig,
  AzureAISearchQueryType,
} from "@langchain/community/vectorstores/azure_aisearch";
const endpoint = process.env.AZURE_AISEARCH_ENDPOINT;
const queryKey = process.env.AZURE_AISEARCH_QUERY_KEY;
const indexName = process.env.AZURE_AISEARCH_INDEX_NAME;

export const DOC_COUNT = 3;

export const VECTOR_STORE_QUERY: AzureAISearchConfig = {
  endpoint,
  key: queryKey,
  indexName,
  search: {
    type: AzureAISearchQueryType.Similarity,
  },
};

Saat mengkueri penyimpanan vektor, gunakan kunci kueri sebagai gantinya. Pemisahan kunci ini memastikan akses yang aman dan efisien ke sumber daya.

Membuat file konfigurasi sumber daya Azure OpenAI

Untuk mengelola dua model yang berbeda, penyematan dan LLM, buat file konfigurasi terpisah. Pendekatan ini memastikan kejelasan dan pemisahan kekhawatiran, sehingga lebih mudah untuk mengelola dan memelihara konfigurasi.

Konfigurasi penyematan pada penyimpanan vektor

Untuk membuat penyematan untuk menyisipkan dokumen ke penyimpanan vektor Azure AI Search, buat file konfigurasi:

const key = process.env.AZURE_OPENAI_EMBEDDING_KEY;
const instance = process.env.AZURE_OPENAI_EMBEDDING_INSTANCE;
const apiVersion =
  process.env.AZURE_OPENAI_EMBEDDING_API_VERSION || "2023-05-15";
const model =
  process.env.AZURE_OPENAI_EMBEDDING_MODEL || "text-embedding-ada-002";

export const EMBEDDINGS_CONFIG = {
  azureOpenAIApiKey: key,
  azureOpenAIApiInstanceName: instance,
  azureOpenAIApiEmbeddingsDeploymentName: model,
  azureOpenAIApiVersion: apiVersion,
  maxRetries: 1,
};

Konfigurasi untuk LLM untuk menghasilkan jawaban

Untuk membuat jawaban dari model bahasa besar, buat file konfigurasi:

const key = process.env.AZURE_OPENAI_COMPLETE_KEY;
const instance = process.env.AZURE_OPENAI_COMPLETE_INSTANCE;
const apiVersion =
  process.env.AZURE_OPENAI_COMPLETE_API_VERSION || "2024-10-21";
const model = process.env.AZURE_OPENAI_COMPLETE_MODEL || "gpt-4o";
const maxTokens = process.env.AZURE_OPENAI_COMPLETE_MAX_TOKENS;

export const LLM_CONFIG = {
  model,
  azureOpenAIApiKey: key,
  azureOpenAIApiInstanceName: instance,
  azureOpenAIApiDeploymentName: model,
  azureOpenAIApiVersion: apiVersion,
  maxTokens: maxTokens ? parseInt(maxTokens, 10) : 100,
  maxRetries: 1,
  timeout: 60000,
};

Konstanta dan perintah

Aplikasi AI sering mengandalkan string dan perintah konstanta. Kelola konstanta ini dengan file terpisah.

Buat prompt sistem:

export const SYSTEM_PROMPT = `Answer the query with a complete paragraph based on the following context:`;

Buat konstanta simpul:

export const ANSWER_NODE = "vector_store_retrieval";
export const DECISION_NODE = "requires_hr_documents";
export const START = "__start__";
export const END = "__end__";

Buat contoh kueri pengguna:

export const USER_QUERIES = [
  "Does the NorthWind Health plus plan cover eye exams?",
  "What is included in the NorthWind Health plus plan that is not included in the standard?",
  "What happens in a performance review?",
];

Untuk memuat dokumen ke Azure AI Search, gunakan LangChain.js untuk menyederhanakan proses. Dokumen, yang disimpan sebagai PDF, dikonversi menjadi embedding dan dimasukkan ke dalam penyimpanan vektor. Proses ini memastikan bahwa dokumen siap untuk pengambilan dan kueri yang efisien.

Pertimbangan Utama:

  • abstraksiLangChain.js: LangChain.js menangani banyak kompleksitas, seperti definisi skema dan pembuatan klien, membuat prosesnya mudah.
  • Logika pembatasan dan coba lagi: Meskipun kode sampel menyertakan fungsi tunggu minimal, aplikasi produksi harus menerapkan penanganan kesalahan komprehensif dan logika coba lagi untuk mengelola pembatasan dan kesalahan sementara.

Langkah-langkah untuk memuat dokumen

  1. Temukan Dokumen PDF: Dokumen disimpan di direktori data.

  2. Muat PDF ke LangChain.js: Gunakan loadPdfsFromDirectory fungsi untuk memuat dokumen. Fungsi ini menggunakan metode komunitas LangChain.js PDFLoader.load untuk membaca setiap file dan mengembalikan larik Document[]. Array ini adalah format dokumen LangChain.js standar.

    import { PDFLoader } from "@langchain/community/document_loaders/fs/pdf";
    import { waiter } from "../utils/waiter.js";
    import { loadDocsIntoAiSearchVector } from "./load_vector_store.js";
    import fs from "fs/promises";
    import path from "path";
    
    export async function loadPdfsFromDirectory(
      embeddings: any,
      dirPath: string,
    ): Promise<void> {
      try {
        const files = await fs.readdir(dirPath);
        console.log(
          `PDF: Loading directory ${dirPath}, ${files.length} files found`,
        );
        for (const file of files) {
          if (file.toLowerCase().endsWith(".pdf")) {
            const fullPath = path.join(dirPath, file);
            console.log(`PDF: Found ${fullPath}`);
    
            const pdfLoader = new PDFLoader(fullPath);
            console.log(`PDF: Loading ${fullPath}`);
            const docs = await pdfLoader.load();
    
            console.log(`PDF: Sending ${fullPath} to index`);
            const storeResult = await loadDocsIntoAiSearchVector(embeddings, docs);
            console.log(`PDF: Indexing result: ${JSON.stringify(storeResult)}`);
    
            await waiter(1000 * 60); // waits for 1 minute between files
          }
        }
      } catch (err) {
        console.error("Error loading PDFs:", err);
      }
    }
    
  3. Menyisipkan dokumen ke dalam Azure AI Search: Gunakan loadDocsIntoAiSearchVector fungsi untuk mengirim array dokumen ke penyimpanan vektor Azure AI Search. Fungsi ini menggunakan klien embedding untuk memproses dokumen dan menyertakan fungsi tunggu sederhana untuk mengatasi pembatasan. Untuk produksi, terapkan mekanisme coba lagi/backoff yang kuat.

    import { AzureAISearchVectorStore } from "@langchain/community/vectorstores/azure_aisearch";
    
    import type { Document } from "@langchain/core/documents";
    import type { EmbeddingsInterface } from "@langchain/core/embeddings";
    import { VECTOR_STORE_ADMIN } from "../config/vector_store_admin.js";
    
    export async function loadDocsIntoAiSearchVector(
      embeddings: EmbeddingsInterface,
      documents: Document[],
    ): Promise<AzureAISearchVectorStore> {
      const vectorStore = await AzureAISearchVectorStore.fromDocuments(
        documents,
        embeddings,
        VECTOR_STORE_ADMIN,
      );
      return vectorStore;
    }
    

Membuat alur kerja agen

Di LangChain.js, buat agen LangChain.js dengan LangGraph. LangGraph memungkinkan Anda menentukan simpul dan tepi:

  • Nodo: tempat pekerjaan berlangsung.
  • Edge: menentukan koneksi antara node.

Komponen alur kerja

Dalam aplikasi ini, dua simpul kerja adalah:

  • requiresHrResources: menentukan apakah pertanyaan tersebut relevan dengan dokumentasi SDM menggunakan Azure OpenAI LLM.
  • getAnswer: mengambil jawabannya. Jawabannya berasal dari rantai LangChain.js retriever, yang menggunakan embedding dokumen dari Azure AI Search dan mengirimkannya ke Azure OpenAI LLM. Ini adalah inti dari generasi berbasis pengambilan.

Batas menentukan awal, akhir, dan kondisi yang diperlukan untuk memanggil simpul getAnswer.

Mengekspor grafik

Untuk menggunakan LangGraph Studio untuk menjalankan dan men-debug graf, ekspor sebagai objek tersendiri.

import { StateGraph } from "@langchain/langgraph";
import { StateAnnotation } from "./langchain/state.js";
import { route as endRoute } from "./langchain/check_route_end.js";
import { getAnswer } from "./azure/get_answer.js";
import { START, ANSWER_NODE, DECISION_NODE } from "./config/nodes.js";
import {
  requiresHrResources,
  routeRequiresHrResources,
} from "./azure/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";

Dalam metode addNode, addEdge, dan addConditionalEdges , parameter pertama adalah nama, sebagai string, untuk mengidentifikasi objek dalam grafik. Parameter kedua adalah fungsi yang harus dipanggil pada langkah tersebut atau nama node yang akan dipanggil.

Untuk metode addEdge , namanya adalah START ("start" yang ditentukan dalam file ./src/config/nodes.ts) dan selalu memanggil DECISION_NODE. Node tersebut didefinisikan dengan dua parameternya: yang pertama adalah namanya, DECISION_NODE, dan yang kedua adalah fungsi yang disebut requiresHrResources.

Fungsionalitas umum

Aplikasi ini menyediakan fungsionalitas LangChain umum:

  • Manajemen status:

    import { BaseMessage, BaseMessageLike } from "@langchain/core/messages";
    import { Annotation, messagesStateReducer } from "@langchain/langgraph";
    
    export const StateAnnotation = Annotation.Root({
      messages: Annotation<BaseMessage[], BaseMessageLike[]>({
        reducer: messagesStateReducer,
        default: () => [],
      }),
    });
    
  • Penghentian rute:

    import { StateAnnotation } from "./state.js";
    import { END, ANSWER_NODE } from "../config/nodes.js";
    
    export const route = (
      state: typeof StateAnnotation.State,
    ): typeof END | typeof ANSWER_NODE => {
      if (state.messages.length > 0) {
        return END;
      }
      return ANSWER_NODE;
    };
    

Satu-satunya rute kustom untuk aplikasi ini adalah routeRequiresHrResources. Rute ini digunakan untuk menentukan apakah jawaban dari node requiresHrResources menunjukkan bahwa pertanyaan pengguna harus berlanjut ke simpul ANSWER_NODE . Karena rute ini menerima output dari requiresHrResources, rute ini berada dalam file yang sama.

Mengintegrasikan sumber daya Azure OpenAI

Integrasi Azure OpenAI menggunakan dua model berbeda:

  • Penyematan: Digunakan untuk menyisipkan dokumen ke penyimpanan vektor.
  • LLM: Digunakan untuk menjawab pertanyaan dengan mengkueri penyimpanan vektor dan menghasilkan respons.

Klien embedding dan klien LLM memiliki tujuan yang berbeda. Jangan menyederhanakan mereka menjadi satu model atau klien.

Model penyematan

Klien penanaman diperlukan setiap kali dokumen diambil dari penyimpanan vektor. Ini termasuk konfigurasi untuk maxRetries untuk menangani kesalahan sementara.

import { AzureOpenAIEmbeddings } from "@langchain/openai";
import { EMBEDDINGS_CONFIG } from "../config/embeddings.js";

export function getEmbeddingClient(): AzureOpenAIEmbeddings {
  return new AzureOpenAIEmbeddings({ ...EMBEDDINGS_CONFIG, maxRetries: 1 });
}

Model LLM

Model LLM digunakan untuk menjawab dua jenis pertanyaan:

  • Relevansi dengan SDM: Menentukan apakah pertanyaan pengguna relevan dengan dokumentasi SDM.
  • Pembuatan jawaban: Memberikan jawaban atas pertanyaan pengguna, yang dilengkapi dengan dokumen dari Azure AI Search.

Klien LLM dibuat dan dipanggil ketika jawaban diperlukan.

import { RunnableConfig } from "@langchain/core/runnables";
import { StateAnnotation } from "../langchain/state.js";
import { AzureChatOpenAI } from "@langchain/openai";
import { LLM_CONFIG } from "../config/llm.js";

export const getLlmChatClient = (): AzureChatOpenAI => {
  return new AzureChatOpenAI({
    ...LLM_CONFIG,
    temperature: 0,
  });
};

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

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

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

Agen LangChain.js menggunakan LLM untuk memutuskan apakah pertanyaan relevan dengan dokumentasi SDM atau apakah alur kerja harus merutekan ke akhir grafik.

// @ts-nocheck
import { getLlmChatClient } from "./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 "../config/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;
};

Fungsi requiresHrResources menetapkan pesan dengan konten HR resources required detected dalam status yang diperbarui. Router, routeRequiresHrResources, mencari konten tersebut untuk menentukan tempat mengirim pesan.

Mengintegrasikan sumber daya Azure AI Search untuk penyimpanan vektor

Integrasi Azure AI Search menyediakan dokumen penyimpanan vektor sehingga LLM dapat menambah jawaban untuk simpul getAnswer . LangChain.js lagi menyediakan banyak abstraksi sehingga kode yang diperlukan minimal. Fungsinya adalah:

  • getReadOnlyVectorStore: Mengambil klien dengan kata kunci kueri.
  • getDocsFromVectorStore: Menemukan dokumen yang relevan dengan pertanyaan pengguna.
import { AzureAISearchVectorStore } from "@langchain/community/vectorstores/azure_aisearch";
import { VECTOR_STORE_QUERY, DOC_COUNT } from "../config/vector_store_query.js";
import { getEmbeddingClient } from "./embeddings.js";

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

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

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

Kode integrasi LangChain.js membuat pengambilan dokumen yang relevan dari toko vektor sangat mudah.

Menulis kode untuk mendapatkan jawaban dari LLM

Sekarang setelah komponen integrasi dibuat, buat fungsi getAnswer untuk mengambil dokumen penyimpanan vektor yang relevan dan menghasilkan jawaban menggunakan LLM.

import { ChatPromptTemplate } from "@langchain/core/prompts";
import { createStuffDocumentsChain } from "langchain/chains/combine_documents";
import { createRetrievalChain } from "langchain/chains/retrieval";
import { getLlmChatClient } from "./llm.js";
import { StateAnnotation } from "../langchain/state.js";
import { AIMessage } from "@langchain/core/messages";
import { getReadOnlyVectorStore } from "./vector_store.js";

const EMPTY_STATE = { messages: [] };

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

  // 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 questionAnsweringPrompt = ChatPromptTemplate.fromMessages([
    [
      "system",
      "Answer the user's questions based on the below context:\n\n{context}",
    ],
    ["human", "{input}"],
  ]);

  const combineDocsChain = await createStuffDocumentsChain({
    llm: getLlmChatClient(),
    prompt: questionAnsweringPrompt,
  });

  const retrievalChain = await createRetrievalChain({
    retriever: vectorStore.asRetriever(2),
    combineDocsChain,
  });
  const result = await retrievalChain.invoke({ input: userInput });
  const assistantMessage = new AIMessage(result.answer);

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

Fungsi ini menyediakan prompt dengan dua placeholder: satu untuk pertanyaan pengguna dan satu untuk konteks. Konteksnya adalah semua dokumen yang relevan dari penyimpanan vektor Pencarian AI. Teruskan perintah dan klien LLM ke createStuffDocumentsChain untuk membuat rantai LLM. Teruskan rantai LLM ke createRetrievalChain untuk membuat rantai yang mencakup petunjuk, dokumen relevan, dan LLM.

Jalankan rantai dengan retrievalChain.invoke dan pertanyaan pengguna sebagai input untuk mendapatkan jawabannya. Kembalikan jawaban dalam status pesan.

Membangun paket agen

  1. Tambahkan skrip ke package.json untuk membangun aplikasi TypeScript:

    "build": "tsc",
    
  2. Bangun agen LangChain.js.

    npm run build
    

Opsional - jalankan agen LangChain.js dalam pengembangan lokal dengan LangChain Studio

Secara opsional, untuk pengembangan lokal, gunakan LangChain Studio untuk bekerja dengan agen LangChain.js Anda.

  1. Buat langgraph.json file untuk menentukan grafik.

    {
        "dependencies": [],
        "graphs": {
          "agent": "./src/graph.ts:hr_documents_answer_graph"
        },
        "env": "../.env"
      }
    
  2. Instal CLI LangGraph.

    npm install @langchain/langgraph-cli --save-dev
    
  3. Buat skrip di package.json untuk meneruskan .env file ke LangGraph CLI.

    "studio": "npx @langchain/langgraph-cli dev",
    
  4. CLI berjalan di terminal Anda dan membuka browser ke LangGraph Studio.

              Welcome to
    
    ╦  ┌─┐┌┐┌┌─┐╔═╗┬─┐┌─┐┌─┐┬ ┬
    ║  ├─┤││││ ┬║ ╦├┬┘├─┤├─┘├─┤
    ╩═╝┴ ┴┘└┘└─┘╚═╝┴└─┴ ┴┴  ┴ ┴.js
    
    - 🚀 API: http://localhost:2024
    - 🎨 Studio UI: https://smith.langchain.com/studio?baseUrl=http://localhost:2024
    
    This in-memory server is designed for development and testing.
    For production use, please use LangGraph Cloud.
    
    info:    ▪ Starting server...
    info:    ▪ Initializing storage...
    info:    ▪ Registering graphs from C:\Users\myusername\azure-typescript-langchainjs\packages\langgraph-agent
    info:    ┏ Registering graph with id 'agent'
    info:    ┗ [1] { graph_id: 'agent' }
    info:    ▪ Starting 10 workers
    info:    ▪ Server running at ::1:2024
    
  5. Lihat agen LangChain.js di LangGraph Studio.

    Cuplikan layar LangSmith Studio dengan grafik yang dimuat.

  6. Pilih + Pesan untuk menambahkan pertanyaan pengguna lalu pilih Kirim.

    Pertanyaan Relevansi dengan dokumen SDM
    Does the NorthWind Health plus plan cover eye exams? Pertanyaan ini relevan dengan SDM dan cukup umum sehingga dokumen SDM seperti buku pegangan karyawan, buku pegangan manfaat, dan pustaka peran karyawan harus dapat menjawabnya.
    What is included in the NorthWind Health plus plan that is not included in the standard? Pertanyaan ini relevan dengan SDM dan cukup umum sehingga dokumen SDM seperti buku pegangan karyawan, buku pegangan manfaat, dan pustaka peran karyawan harus dapat menjawabnya.
    How much of my perks + benefit have I spent Pertanyaan ini tidak relevan dengan dokumen SDM umum dan impersonal. Pertanyaan ini harus dikirim ke agen yang memiliki akses ke data karyawan.
  7. Jika pertanyaan relevan dengan dokumen SDM, itu harus melewati DECISION_NODE dan seterusnya ke ANSWER_NODE.

    Amati keluaran terminal untuk melihat pertanyaan kepada LLM dan jawabannya dari LLM.

  8. Jika pertanyaan tidak relevan dengan dokumen SDM, alur akan langsung berakhir.

Ketika agen LangChain.js membuat keputusan yang salah, masalahnya mungkin:

  • Model LLM yang digunakan
  • Jumlah dokumen dari penyimpanan vektor
  • Perintah yang digunakan dalam simpul keputusan.

Menjalankan agen LangChain.js dari aplikasi

Untuk memanggil agen LangChain.js dari aplikasi induk, seperti API web, Anda perlu memberikan pemanggilan agen LangChain.js.

import { HumanMessage } from "@langchain/core/messages";
import { hr_documents_answer_graph as app } from "./graph.js";

const AIMESSAGE = "aimessage";

export async function ask_agent(question: string) {
  const initialState = { messages: [new HumanMessage(question)], iteration: 0 };
  const finalState = await app.invoke(initialState);

  return finalState;
}
export async function get_answer(question: string) {
  try {
    const answerResponse = await ask_agent(question);

    const answer = answerResponse.messages
      .filter(
        (m: any) =>
          m &&
          m.constructor?.name?.toLowerCase() === AIMESSAGE.toLocaleLowerCase(),
      )
      .map((m: any) => m.content)
      .join("\n");

    return answer;
  } catch (e) {
    console.error("Error in get_answer:", e);
    throw e;
  }
}

Dua fungsi tersebut adalah:

  • ask_agent: Fungsi ini mengembalikan status sehingga memungkinkan Anda menambahkan agen LangChain.js ke alur kerja multi-agen LangChain.
  • get_answer: Fungsi ini hanya mengembalikan teks jawaban. Fungsi ini dapat dipanggil dari API.

Penyelesaian Masalah

Membersihkan sumber daya

Hapus grup sumber daya yang menyimpan sumber daya Azure AI Search dan sumber daya Azure OpenAI.