Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Creare un assistente hr intelligente usando LangChain.js e i servizi di Azure. Questo agente aiuta i dipendenti dell'azienda fittizia NorthWind a trovare risposte alle domande sulle risorse umane eseguendo una ricerca nella documentazione aziendale.
Si userà Ricerca intelligenza artificiale di Azure per trovare documenti pertinenti e Azure OpenAI per generare risposte accurate. Il framework LangChain.js gestisce la complessità dell'orchestrazione dell'agente, consentendo di concentrarsi sui requisiti aziendali specifici.
Contenuto dell'esercitazione:
- Distribuire le risorse di Azure usando l'interfaccia della riga di comando per sviluppatori di Azure
- Creare un agente LangChain.js che si integra con i servizi di Azure
- Implementare la generazione aumentata di recupero (RAG) per la ricerca di documenti
- Testare ed eseguire il debug dell'agente in locale e in Azure
Al termine di questa esercitazione è disponibile un'API REST funzionante che risponde alle domande sulle risorse umane usando la documentazione dell'azienda.
Panoramica dell'architettura
Screenshot del diagramma che illustra il flusso di lavoro dell'agente LangChain.js e il relativo ramo decisionale per usare la documentazione HR per rispondere alle domande.
NorthWind si basa su due origini dati:
- Documentazione delle risorse umane accessibile a tutti i dipendenti
- Database hr riservato contenente i dati sensibili dei dipendenti.
Questa esercitazione è incentrata sulla creazione di un agente LangChain.js che determina se è possibile rispondere alla domanda di un dipendente usando i documenti delle risorse umane pubbliche. In tal caso, l'agente LangChain.js fornisce direttamente la risposta.
Prerequisiti
Per usare questo esempio in Codespace o nel contenitore di sviluppo locale, inclusa la compilazione e l'esecuzione dell'agente LangChain.js, è necessario quanto segue:
- Un account Azure attivo. Crea gratuitamente un account se non ne hai uno.
Se si esegue il codice di esempio in locale senza un contenitore di sviluppo, è necessario anche:
- Node.js LTS installato nel sistema.
- TypeScript per la scrittura e la compilazione di codice TypeScript.
- Interfaccia della riga di comando per sviluppatori di Azure (azd) installata e configurata.
- LangChain.js libreria per la creazione dell'agente.
- Facoltativo: LangSmith per il monitoraggio dell'utilizzo dell'intelligenza artificiale. Sono necessari il nome, la chiave e l'endpoint del progetto.
- Facoltativo: LangGraph Studio per il debug delle catene LangGraph e degli agenti LangChain.js.
Risorse di Azure
Sono necessarie le risorse di Azure seguenti. Vengono creati automaticamente in questo articolo usando l'interfaccia della riga di comando per sviluppatori di Azure e i modelli Bicep usando i moduli verificati di Azure (AVM). Le risorse vengono create con accesso senza password e chiave a scopo di apprendimento. Questo tutorial usa il tuo account per sviluppatore locale per l'autenticazione senza password.
- Risorsa ricerca di intelligenza artificiale di Azure per la ricerca vettoriale.
-
Risorsa OpenAI di Azure con i modelli seguenti:
- Modello di incorporamento come
text-embedding-3-small. - Un modello di linguaggio di grandi dimensioni (LLM) come
'gpt-4.1-mini.
- Modello di incorporamento come
Architettura dell'agente
Il framework LangChain.js fornisce un flusso decisionale per la creazione di agenti intelligenti come LangGraph. In questa esercitazione viene creato un agente LangChain.js che si integra con Ricerca di intelligenza artificiale di Azure e Azure OpenAI per rispondere alle domande correlate alle risorse umane. L'architettura dell'agente è progettata per:
- Determinare se una domanda è rilevante per la documentazione generale delle risorse umane disponibile per tutti i dipendenti.
- Recuperare i documenti pertinenti da Ricerca di intelligenza artificiale di Azure in base alla query dell'utente.
- Usare Azure OpenAI per generare una risposta in base ai documenti recuperati e al modello LLM.
Componenti chiave:
Struttura del grafo: l'agente LangChain.js è rappresentato come grafico, dove:
- I nodi eseguono attività specifiche, ad esempio il processo decisionale o il recupero dei dati.
- Gli archi definiscono il flusso tra i nodi, determinando la sequenza di operazioni.
Integrazione di Ricerca AI di Azure:
- Usa un modello di incorporamento per creare vettori.
- Inserisce i documenti HR (*.md, *.pdf) nell'archivio vettoriale. I documenti includono:
- Informazioni società
- Manuale dei dipendenti
- Manuale dei vantaggi
- Libreria dei ruoli dei dipendenti
- Recupera i documenti pertinenti in base alla richiesta dell'utente.
-
Integrazione di Azure OpenAI:
- Usa un modello linguistico di grandi dimensioni per:
- Determina se una domanda è rispondebile da documenti HR impersonali.
- Genera una risposta con prompt usando il contesto dei documenti e delle domande dell'utente.
- Usa un modello linguistico di grandi dimensioni per:
La tabella seguente contiene esempi di domande utente che sono e non sono pertinenti e rispondebili dai documenti generali risorse umane:
| Domanda | Rilevante | Explanation |
|---|---|---|
Does the NorthWind Health Plus plan cover eye exams? |
Yes | I documenti delle risorse umane, ad esempio il manuale dei dipendenti, devono fornire una risposta. |
How much of my perks + benefits have I spent? |
NO | Questa domanda richiede l'accesso ai dati dei dipendenti riservati, che non rientrano nell'ambito di questo agente. |
Usando il framework di LangChain.js, si evita gran parte del codice boilerplate agentico in genere necessario per gli agenti e l'integrazione dei servizi di Azure, consentendo di concentrarsi sulle esigenze aziendali.
Clonare il repository di codice di esempio
In una nuova directory clonare il repository di codice di esempio e passare alla nuova directory:
git clone https://github.com/Azure-Samples/azure-typescript-langchainjs.git
cd azure-typescript-langchainjs
Questo esempio fornisce il codice necessario per creare risorse di Azure sicure, compilare l'agente LangChain.js con Ricerca di intelligenza artificiale di Azure e Azure OpenAI e usare l'agente da un server API Node.js Fastify.
Eseguire l'autenticazione nell'interfaccia della riga di comando di Azure e nell'interfaccia della riga di comando per sviluppatori di Azure
Accedere ad Azure con l'interfaccia della riga di comando per sviluppatori di Azure, creare le risorse di Azure e distribuire il codice sorgente. Poiché il processo di distribuzione usa sia l'interfaccia della riga di comando di Azure che l'interfaccia della riga di comando per sviluppatori di Azure, accedere all'interfaccia della riga di comando di Azure e quindi configurare l'interfaccia della riga di comando per sviluppatori di Azure per usare l'autenticazione dall'interfaccia della riga di comando di Azure:
az login
azd config set auth.useAzCliAuth true
Creare risorse e distribuire codice con l'interfaccia della riga di comando per sviluppatori di Azure
Avviare il processo di distribuzione eseguendo il azd up comando :
azd up
Durante il azd up comando rispondere alle domande:
-
Nome nuovo ambiente: immettere un nome di ambiente univoco,
langchain-agentad esempio . Questo nome di ambiente viene usato come parte del gruppo di risorse di Azure. - Selezionare una sottoscrizione di Azure: selezionare la sottoscrizione in cui vengono create le risorse.
-
Selezionare un'area: ad esempio
eastus2.
La distribuzione richiede circa 10-15 minuti. L'interfaccia della riga di comando per sviluppatori di Azure orchestra il processo usando fasi e hook definiti nel azure.yaml file:
Fase di provisioning (equivalente a azd provision):
- Crea risorse di Azure definite in
infra/main.bicep:- Azure Container App
- OpenAI
- Ricerca IA
- Registro dei Contenitori
- Identità gestita
- Hook di post-provisioning: verifica se l'indice di Azure AI Search esiste già
- Se l'indice non esiste: vengono eseguiti
npm installenpm run load_dataper caricare documenti HR usando LangChain.js, caricatore PDF e client di incorporazione. - Se l'indice esiste: ignora il caricamento dei dati per evitare duplicati (è possibile ricaricare manualmente eliminando l'indice o eseguendo
npm run load_data) Fase di distribuzione (equivalente aazd deploy):
- Se l'indice non esiste: vengono eseguiti
- Hook di pre-distribuzione: compila l'immagine Docker per il server API Fastify ed esegue il push nel Registro Azure Container
- Distribuisce il server API containerizzato su Azure Container Apps.
Al termine della distribuzione, le variabili di ambiente e le informazioni sulle risorse vengono salvate nel .env file nella radice del repository. È possibile visualizzare le risorse nel portale di Azure.
Le risorse vengono create con accesso senza password e chiave a scopo di apprendimento. Questa esercitazione introduttiva usa l'account sviluppatore locale per l'autenticazione priva di password. Per le applicazioni di produzione, usare solo l'autenticazione senza password con identità gestite. Altre informazioni sull'autenticazione senza password.
Usare il codice di esempio in locale
Dopo aver creato le risorse di Azure, è possibile eseguire l'agente LangChain.js in locale.
Installa le dipendenze
Installare i pacchetti Node.js per questo progetto.
npm installQuesto comando installa le dipendenze definite nei due
package.jsonfile nellapackages-v1directory, tra cui:-
./packages-v1/server-api:- Fastify per il server Web
-
./packages-v1/langgraph-agent:- LangChain.js per la compilazione dell'agente
- Libreria
@azure/search-documentsclient di Azure SDK per l'integrazione con la risorsa di Ricerca di intelligenza artificiale di Azure. La documentazione di riferimento è disponibile qui.
-
Compilare i due pacchetti: il server API e l'agente di intelligenza artificiale.
npm run buildQuesto comando crea un collegamento tra i due pacchetti in modo che il server API possa chiamare l'agente di intelligenza artificiale.
Eseguire il server API in locale
L'interfaccia della riga di comando per sviluppatori di Azure ha creato le risorse di Azure necessarie e configurato le variabili di ambiente nel file radice .env . Questa configurazione includeva un hook di post provisioning per trasferire i dati nell'archivio vettoriale. È ora possibile eseguire il server API Fastify che ospita l'agente LangChain.js. Avviare il server API Fastify.
npm run dev
Il server viene avviato e si mette in ascolto sulla porta 3000. È possibile testare il server passando a [http://localhost:3000] nel Web browser. Verrà visualizzato un messaggio di benvenuto che indica che il server è in esecuzione.
Usare l'API per porre domande
È possibile usare uno strumento come client REST o curl per inviare una richiesta POST all'endpoint /ask con un corpo JSON contenente la domanda.
Le query del client REST sono disponibili nella directory packages-v1/server-api/http.
Esempio con curl:
curl -X POST http://localhost:3000/answer -H "Content-Type: application/json" -d "{\"question\": \"Does the NorthWind Health Plus plan cover eye exams?\"}"
Si dovrebbe ricevere una risposta JSON con la risposta dell'agente 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."
}
Nella directory packages-v1/server-api/http ci sono diversi esempi di domande disponibili. Aprire i file in Visual Studio Code con il client REST per testarli rapidamente.
Informazioni sul codice dell'applicazione
Questa sezione illustra come l'agente LangChain.js si integra con i servizi di Azure. L'applicazione del repository è organizzata come area di lavoro npm con due pacchetti principali:
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
Decisioni chiave relative all'architettura:
- Struttura Monorepo: le aree di lavoro npm consentono dipendenze condivise e pacchetti collegati
-
Separazione dei problemi: la logica dell'agente (
langgraph-agent) è indipendente dal server API (server-api) -
Autenticazione centralizzata: i file in
./langgraph-agent/src/azuregestiscono l'autenticazione basata su chiave e senza password e l'integrazione del servizio di Azure
Autenticazione in Servizi di Azure
L'applicazione supporta metodi di autenticazione basati su chiave e senza password, controllati dalla SET_PASSWORDLESS variabile di ambiente. L'API DefaultAzureCredential della libreria di identità di Azure viene usata per l'autenticazione senza password, consentendo l'esecuzione dell'applicazione senza problemi negli ambienti di sviluppo locale e di Azure. È possibile visualizzare questa autenticazione nel frammento di codice seguente:
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;
}
Quando si usano librerie di terze parti come LangChain.js o la libreria OpenAI per accedere ad Azure OpenAI, è necessaria una funzione del provider di token anziché passare direttamente un oggetto credenziale. La getBearerTokenProvider funzione della libreria di identità di Azure risolve questo problema creando un provider di token che recupera e aggiorna automaticamente i token di connessione OAuth 2.0 per un ambito specifico delle risorse di Azure , ad esempio "https://cognitiveservices.azure.com/.default". L'ambito viene configurato una sola volta durante l'installazione e il provider di token gestisce automaticamente tutta la gestione dei token. Questo approccio funziona con qualsiasi credenziale della libreria di identità di Azure, incluse le credenziali dell'identità gestita e l'interfaccia della riga di comando di Azure. Anche se le librerie di Azure SDK accettano DefaultAzureCredential direttamente, librerie di terze parti come LangChain.js richiedono questo modello di provider di token per colmare il divario di autenticazione.
Integrazione della Ricerca di Intelligenza Artificiale di Azure
La risorsa di Ricerca intelligenza artificiale di Azure archivia incorporamenti di documenti e abilita la ricerca semantica di contenuto pertinente. L'applicazione usa LangChain per gestire l'archivio AzureAISearchVectorStore vettoriale senza dover definire lo schema dell'indice.
L'archivio vettoriale viene creato con la configurazione per le operazioni di amministrazione (scrittura) e query (lettura) in modo che il caricamento e l'esecuzione di query dei documenti possano usare configurazioni diverse. Questo è importante se si usano chiavi o l'autenticazione senza password con identità gestite.
La distribuzione della Azure Developer CLI include un hook post-distribuzione che carica i documenti nell'archivio vettoriale utilizzando il caricatore di PDF LangChain.js e il client di incorporamento. Questo hook post-distribuzione è l'ultimo passaggio del azd up comando dopo la creazione della risorsa Azure AI Search. Lo script di caricamento del documento usa la logica di invio in batch e ripetizione dei tentativi per gestire i limiti di frequenza del servizio.
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
Utilizza il file radice .env creato dalla CLI di Azure Developer per autenticarti alla risorsa Azure AI Search e creare il client 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;
Quando si esegue una query, l'archivio vettoriale converte la query dell'utente in un incorporamento, cerca documenti con rappresentazioni vettoriali simili e restituisce i blocchi più rilevanti.
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);
}
Poiché l'archivio vettoriale è basato su LangChain.js, consente di astrarre la complessità dell'interazione diretta con l'archivio vettoriale. Dopo aver appreso l'interfaccia dell'archivio vettoriale LangChain.js, è possibile passare facilmente ad altre implementazioni dell'archivio vettoriale in futuro.
Integrazione di Azure OpenAI
L'applicazione usa Azure OpenAI per incorporamenti e funzionalità LLM (Large Language Model). La AzureOpenAIEmbeddings classe di LangChain.js viene usata per generare incorporamenti per documenti e query. Dopo aver creato il client di incorporamenti, LangChain.js lo usa per creare gli incorporamenti.
Integrazione di Azure OpenAI per incorporamenti
Usare il file radice .env creato dall'interfaccia della riga di comando per sviluppatori di Azure per eseguire l'autenticazione alla risorsa OpenAI di Azure e creare il client 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 });
}
Integrazione di Azure OpenAI per LLM
Usare il file radice .env creato dall'interfaccia della riga di comando per sviluppatori di Azure per eseguire l'autenticazione alla risorsa OpenAI di Azure e creare il client AzureChatOpenAI :
const shared = {
azureOpenAIApiInstanceName: instance,
azureOpenAIApiDeploymentName: model,
azureOpenAIApiVersion: apiVersion,
azureOpenAIBasePath,
maxTokens: maxTokens ? parseInt(maxTokens, 10) : 100,
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;
L'applicazione usa la AzureChatOpenAI classe di LangChain.js @langchain/openai per interagire con i modelli OpenAI di Azure.
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,
},
],
};
};
Flusso di lavoro dell'agente LangGraph
L'agente usa LangGraph per definire un flusso di lavoro decisionale che determina se è possibile rispondere a una domanda usando documenti HR.
Struttura del grafo:
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";
Il flusso di lavoro è costituito dai seguenti passaggi:
- Start: l'utente invia una domanda.
- nodo requires_hr_documents: LLM determina se la domanda è rispondibile nei documenti generali delle Risorse Umane.
-
Routing condizionale:
- In caso affermativo, procede al
get_answernodo. - In caso contrario, restituisce un messaggio che richiede dati personali sulle risorse umane.
- In caso affermativo, procede al
- get_answer nodo: recupera i documenti e genera una risposta.
- Fine: restituisce la risposta all'utente.
Questo controllo di pertinenza è importante perché non tutte le domande sulle risorse umane possono essere risposte da documenti generali. Le domande personali, ad esempio "Quanto PTO ho?" richiedono l'accesso ai database dei dipendenti che contengono i dati dei singoli dipendenti. Controllando prima la pertinenza, l'agente evita risposte allucinanti per le domande che necessitano di informazioni personali a cui non ha accesso.
Decidere se la domanda richiede documenti HR
Il requires_hr_documents nodo usa un LLM per determinare se la domanda dell'utente può essere risolta usando documenti hr generali. Usa un modello di richiesta che indica al modello di rispondere YES o NO in base alla pertinenza della domanda. Restituisce la risposta in un messaggio strutturato, che può essere passato lungo il flusso di lavoro. Il nodo successivo usa questa risposta per instradare il flusso di lavoro a END o a 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;
};
Ottenere i documenti HR necessari
Dopo aver determinato che la domanda richiede documenti HR, il flusso di lavoro usa getAnswer per recuperare i documenti pertinenti dall'archivio vettoriale, aggiungerli al contesto della richiesta e passare l'intera richiesta all'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],
};
}
Se non vengono trovati documenti pertinenti, l'agente restituisce un messaggio che indica che non è stato possibile trovare una risposta nei documenti HR.
Risoluzione dei problemi
Per eventuali problemi con la procedura, segnalare un problema nel repository di codice di esempio
Pulire le risorse
È possibile eliminare il gruppo di risorse che contiene la risorsa ricerca di intelligenza artificiale di Azure e la risorsa OpenAI di Azure oppure usare l'interfaccia della riga di comando per sviluppatori di Azure per eliminare immediatamente tutte le risorse create da questa esercitazione.
azd down --purge