Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Cree un asistente inteligente de RR. HH. mediante LangChain.js y servicios de Azure. Este agente ayuda a los empleados de la empresa ficticia NorthWind a encontrar respuestas a preguntas de recursos humanos mediante la búsqueda en la documentación de la empresa.
Usará Azure AI Search para buscar documentos relevantes y Azure OpenAI para generar respuestas precisas. El marco de LangChain.js controla la complejidad de la orquestación del agente, lo que le permite centrarse en sus requisitos empresariales específicos.
Temas que se abordarán:
- Implementación de recursos de Azure mediante la CLI para desarrolladores de Azure
- Creación de un agente de LangChain.js que se integra con los servicios de Azure
- Implementación de la generación aumentada de recuperación (RAG) para la búsqueda de documentos
- Probar y depurar el agente localmente y en Azure
Al final de este tutorial, tiene una API REST en funcionamiento que responde a las preguntas de RR. HH. mediante la documentación de su empresa.
Introducción a la arquitectura
NorthWind se basa en dos orígenes de datos:
- Documentación de RR. HH. accesible para todos los empleados
- Base de datos confidencial de RR. HH. que contiene datos confidenciales de los empleados.
Este tutorial se centra en la creación de un agente de LangChain.js que determina si se puede responder a la pregunta de un empleado mediante los documentos públicos de RR. HH. Si es así, el agente de LangChain.js proporciona la respuesta directamente.
Prerrequisitos
Para usar este ejemplo en Codespace o en el contenedor de desarrollo local, incluida la compilación y ejecución del agente de LangChain.js, necesita lo siguiente:
- Una cuenta de Azure activa. Cree una cuenta gratuita si no tiene una.
Si ejecuta el código de ejemplo localmente sin un contenedor de desarrollo, también necesita lo siguiente:
- Node.js LTS instalado en el sistema.
- TypeScript para escribir y compilar código TypeScript.
- LA CLI para desarrolladores de Azure (azd) instalada y configurada.
- LangChain.js biblioteca para construir el agente.
- Opcional: LangSmith para supervisar el uso de la inteligencia artificial. Necesita el nombre del proyecto, la clave y el punto de conexión.
- Opcional: LangGraph Studio para depurar las cadenas de LangGraph y los agentes LangChain.js.
Recursos de Azure
Se requieren los siguientes recursos de Azure. Se crean automáticamente en este artículo mediante la CLI para desarrolladores de Azure y las plantillas de Bicep mediante módulos comprobados de Azure (AVM). Los recursos se crean con acceso sin contraseña y clave con fines de aprendizaje. En este tutorial se usa la cuenta de desarrollador local para la autenticación sin contraseña:
- Identidad administrada para la autenticación sin contraseña en los servicios de Azure.
- Azure Container Registry para almacenar la imagen de Docker para el servidor de API de Fastify de Node.js.
- Azure Container App para hospedar el servidor de API de Node.js Fastify.
- Recurso de Azure AI Search para la búsqueda de vectores.
-
Recurso de Azure OpenAI con los siguientes modelos:
- Un modelo de incrustaciones como
text-embedding-3-small. - Un modelo de lenguaje grande (LLM) como
'gpt-4.1-mini.
- Un modelo de incrustaciones como
Arquitectura del agente
El marco de LangChain.js proporciona un flujo de decisión para crear agentes inteligentes como langGraph. En este tutorial, creará un agente de LangChain.js que se integra con Azure AI Search y Azure OpenAI para responder a preguntas relacionadas con los recursos humanos. La arquitectura del agente está diseñada para:
- Determine si una pregunta es relevante para la documentación general de RR. HH. disponible para todos los empleados.
- Recupere documentos pertinentes de Azure AI Search en función de la consulta de usuario.
- Use Azure OpenAI para generar una respuesta basada en los documentos recuperados y el modelo LLM.
Componentes clave:
Estructura del grafo: el agente de LangChain.js se representa como un grafo, donde:
- Los nodos realizan tareas específicas, como la toma de decisiones o la recuperación de datos.
- Los bordes definen el flujo entre nodos y determinan la secuencia de operaciones.
Integración de Azure AI Search:
- Usa un modelo de incrustaciones para crear vectores.
- Inserta documentos de RR. HH. (*.md, *.pdf) en el almacén vectorial. Los documentos incluyen:
- Información de la empresa
- Manual de empleados
- Manual de beneficios
- Biblioteca de roles de empleado
- Recupera los documentos pertinentes en función del mensaje del usuario.
-
Integración de Azure OpenAI:
- Usa un modelo de lenguaje grande para:
- Determina si una pregunta es respondible a partir de documentos de RR. HH. impersonales.
- Genera una respuesta con una instrucción utilizando el contexto de documentos y la pregunta del usuario.
- Usa un modelo de lenguaje grande para:
En la tabla siguiente se muestran ejemplos de preguntas de usuario que son y no son relevantes y respondibles de documentos generales de recursos humanos:
| Pregunta | Pertinente | Explanation |
|---|---|---|
Does the NorthWind Health Plus plan cover eye exams? |
Sí | Los documentos de recursos humanos, como el manual del empleado, deben proporcionar una respuesta. |
How much of my perks + benefits have I spent? |
No | Esta pregunta requiere acceso a los datos confidenciales de los empleados, que están fuera del ámbito de este agente. |
Mediante el uso del marco LangChain.js, se evita gran parte del código repetitivo de agentes y de integración de servicios de Azure típicamente requerido, lo que le permite centrarse en sus necesidades empresariales.
Clonación del repositorio de código de ejemplo
En un nuevo directorio, clone el repositorio de código de ejemplo y cambie al nuevo directorio:
git clone https://github.com/Azure-Samples/azure-typescript-langchainjs.git
cd azure-typescript-langchainjs
En este ejemplo se proporciona el código que necesita para crear recursos de Azure seguros, compilar el agente de LangChain.js con Azure AI Search y Azure OpenAI, y usar el agente desde un servidor de API de Node.js Fastify.
Autenticación en la CLI de Azure y en la CLI para desarrolladores de Azure
Inicie sesión en Azure con la CLI para desarrolladores de Azure, cree los recursos de Azure e implemente el código fuente. Dado que el proceso de implementación usa la CLI de Azure y la CLI para desarrolladores de Azure, inicie sesión en la CLI de Azure y configure la CLI para desarrolladores de Azure para que use la autenticación desde la CLI de Azure:
az login
azd config set auth.useAzCliAuth true
Creación de recursos e implementación de código con la CLI para desarrolladores de Azure
Para comenzar el proceso de implementación, ejecute el azd up comando :
azd up
Durante el azd up comando, responda a las preguntas:
-
Nuevo nombre de entorno: escriba un nombre de entorno único, como
langchain-agent. Este nombre de entorno se usa como parte del grupo de recursos de Azure. - Seleccione una suscripción de Azure: seleccione la suscripción donde se crean los recursos.
-
Seleccione una región: como
eastus2.
La implementación tarda aproximadamente entre 10 y 15 minutos. La CLI para desarrolladores de Azure organiza el proceso mediante fases y enlaces definidos en el azure.yaml archivo:
Fase de aprovisionamiento (equivalente a azd provision):
- Crea recursos de Azure definidos en
infra/main.bicep:- Aplicación contenedora de Azure
- OpenAI
- Búsqueda IA
- Container Registry
- Identidad administrada
-
Enlace posterior al aprovisionamiento: comprueba si el índice
northwindde Azure AI Search ya existe- Si el índice no existe: se ejecutan
npm installynpm run load_datapara cargar documentos de RR. HH. usando el cargador de PDF de LangChain.js y el cliente de incrustación. - Si el índice existe: omite la carga de datos para evitar duplicados (puede volver a cargar manualmente eliminando el índice o ejecutando
npm run load_data) Fase de implementación (equivalente aazd deploy):
- Si el índice no existe: se ejecutan
- Enlace de implementación previa: compila la imagen de Docker para el servidor fastify API y la inserta en Azure Container Registry.
- Implementa el servidor de API en contenedor en Azure Container Apps
Cuando se completa la implementación, las variables de entorno y la información de recursos se guardan en el .env archivo en la raíz del repositorio. Puede ver los recursos en Azure Portal.
Los recursos se crean con acceso sin contraseña y clave con fines de aprendizaje. En este tutorial introductorio se usa la cuenta de desarrollador local para la autenticación sin contraseña. En el caso de las aplicaciones de producción, use solo la autenticación sin contraseña con identidades administradas. Obtenga más información sobre la autenticación sin contraseña.
Usa el código de ejemplo localmente
Ahora que se crean los recursos de Azure, puede ejecutar el agente de LangChain.js localmente.
Instalación de dependencias
Instale los paquetes de Node.js para este proyecto.
npm installEste comando instala las dependencias definidas en los dos
package.jsonarchivos delpackages-v1directorio, entre los que se incluyen:-
./packages-v1/server-api:- Fastify para el servidor web
-
./packages-v1/langgraph-agent:- LangChain.js para construir el agente
- Biblioteca
@azure/search-documentscliente de Azure SDK para la integración con el recurso de Azure AI Search. La documentación de referencia está aquí.
-
Compile los dos paquetes: el servidor de API y el agente de IA.
npm run buildEste comando crea un vínculo entre los dos paquetes para que el servidor de API pueda llamar al agente de IA.
Ejecución local del servidor de API
La CLI para desarrolladores de Azure creó los recursos de Azure necesarios y configuró las variables de entorno en el archivo raíz .env . Esta configuración incluía un gancho posterior al aprovisionamiento para cargar los datos en el almacén vectorial. Ahora, puede ejecutar el servidor de API de Fastify que hospeda el agente de LangChain.js. Inicie el servidor de API fastify.
npm run dev
El servidor se inicia y escucha en el puerto 3000. Para probar el servidor, vaya a [http://localhost:3000] en el explorador web. Debería ver un mensaje de bienvenida que indica que el servidor se está ejecutando.
Uso de la API para formular preguntas
Puede usar una herramienta como el cliente REST o curl enviar una solicitud POST al /ask punto de conexión con un cuerpo JSON que contenga la pregunta.
Las consultas de cliente REST están disponibles en el directorio packages-v1/server-api/http.
Ejemplo 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?\"}"
Deberías recibir una respuesta JSON del 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."
}
Hay varias preguntas de ejemplo disponibles en el packages-v1/server-api/http directorio . Abra los archivos en Visual Studio Code con el cliente REST para probarlos rápidamente.
Descripción del código de la aplicación
En esta sección se explica cómo se integra el agente de LangChain.js con los servicios de Azure. La aplicación del repositorio se organiza como un área de trabajo de npm con dos paquetes principales:
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
Decisiones clave de arquitectura:
- Estructura de Monorepo: las áreas de trabajo de npm permiten dependencias compartidas y paquetes vinculados
-
Separación de problemas: la lógica del agente (
langgraph-agent) es independiente del servidor de API (server-api) -
Autenticación centralizada: archivos en
./langgraph-agent/src/azuremanejan la autenticación basada en claves y sin contraseña, e integración con servicios de Azure
Autenticación en servicios de Azure
La aplicación admite métodos de autenticación sin contraseña y basados en claves, controlados por la variable de SET_PASSWORDLESS entorno. La API DefaultAzureCredential de la biblioteca de identidades de Azure se usa para la autenticación sin contraseña, lo que permite que la aplicación se ejecute sin problemas en entornos de desarrollo y Azure locales. Puede ver esta autenticación en el siguiente fragmento de código:
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;
}
Al usar bibliotecas de terceros como LangChain.js o la biblioteca openAI para acceder a Azure OpenAI, necesita una función de proveedor de tokens en lugar de pasar un objeto de credencial directamente. La getBearerTokenProvider función de la biblioteca de identidades de Azure resuelve este problema mediante la creación de un proveedor de tokens que captura y actualiza automáticamente los tokens de portador de OAuth 2.0 para un ámbito de recurso de Azure específico (por ejemplo, "https://cognitiveservices.azure.com/.default"). El ámbito se configura una vez durante la instalación y el proveedor de tokens controla automáticamente toda la administración de tokens. Este enfoque funciona con cualquier credencial de la biblioteca de identidades de Azure, incluidas las credenciales de identidad administrada y la CLI de Azure. Aunque las bibliotecas del SDK de Azure aceptan DefaultAzureCredential directamente, las bibliotecas de terceros, como LangChain.js, requieren este patrón de proveedor de tokens para puentear la brecha de autenticación.
Integración de Azure AI Search
El recurso de Azure AI Search almacena incrustaciones de documentos y habilita la búsqueda semántica de contenido relevante. La aplicación usa LangChain AzureAISearchVectorStore para administrar el almacén de vectores sin tener que definir el esquema de índice.
El almacén de vectores se crea con configuración para las operaciones de administración (escritura) y consulta (lectura) para que la carga y consulta de documentos puedan usar configuraciones diferentes. Esto es importante si usa claves o autenticación sin contraseña con identidades administradas.
La implementación de la CLI para desarrolladores de Azure incluye un gancho posterior a la implementación que carga los documentos en el almacén de vectores con LangChain.js cargador de PDF y cliente de inserción. Este enlace posterior a la implementación es el último paso del azd up comando después de crear el recurso de Azure AI Search. El script de carga de documentos usa la lógica de procesamiento por lotes y reintento para controlar los límites de velocidad de servicio.
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
Utilice el archivo raíz .env creado por la CLI para desarrolladores de Azure, puede autenticarse en el recurso Azure AI Search y crear el cliente 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;
Cuando se consulta, el almacén de vectores convierte la consulta del usuario en una inserción, busca documentos con representaciones vectoriales similares y devuelve los fragmentos más relevantes.
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);
}
Dado que el almacén de vectores se basa en LangChain.js, abstrae la complejidad de interactuar directamente con el almacén de vectores. Una vez que aprenda la interfaz de almacén de vectores de LangChain.js, puede cambiar fácilmente a otras implementaciones de almacén de vectores en el futuro.
Integración de Azure OpenAI
La aplicación usa Azure OpenAI para las funcionalidades de inserción y modelo de lenguaje grande (LLM). La AzureOpenAIEmbeddings clase de LangChain.js se usa para generar incrustaciones para documentos y consultas. Una vez creado el cliente de incrustaciones, LangChain.js lo usa para crear las incrustaciones.
Integración de Azure OpenAI para incrustaciones
Use el archivo raíz .env creado por la CLI para desarrolladores de Azure para autenticarse en el recurso de Azure OpenAI y crear el cliente 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 });
}
Integración de Azure OpenAI para LLM
Use el archivo raíz .env creado por la CLI para desarrolladores de Azure para autenticarse en el recurso de Azure OpenAI y crear el cliente 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;
La aplicación usa la AzureChatOpenAI clase de LangChain.js @langchain/openai para interactuar con los modelos de 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,
},
],
};
};
Flujo de trabajo del agente de LangGraph
El agente usa LangGraph para definir un flujo de trabajo de decisión que determina si se puede responder una pregunta mediante documentos de RR. HH.
Estructura 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";
El flujo de trabajo consta de los siguientes pasos:
- Inicio: el usuario envía una pregunta.
- requires_hr_documents nodo: LLM determina si la pregunta puede ser respondida con documentos generales de RR. HH.
-
Enrutamiento condicional:
- Si es así, continúa con el
get_answernodo. - Si no, entonces devuelve un mensaje que indica que la pregunta requiere los datos personales de RR. HH.
- Si es así, continúa con el
- get_answer nodo: recupera documentos y genera respuesta.
- End: devuelve la respuesta al usuario.
Esta comprobación de relevancia es importante porque no todas las preguntas de rr. HH. se pueden responder desde documentos generales. Preguntas personales como "¿Cuánto PTO tengo?" requieren acceso a las bases de datos de empleados que contienen datos de empleados individuales. Al comprobar primero la relevancia, el agente evita respuestas alucinantes para preguntas que necesitan información personal a la que no tiene acceso.
Decidir si la pregunta requiere documentos de RR. HH.
El requires_hr_documents nodo usa un LLM para determinar si la pregunta del usuario se puede responder mediante documentos generales de RR. HH. Usa una plantilla de aviso que indica al modelo que responda con YES o NO en función de la relevancia de la pregunta. Devuelve la respuesta en un mensaje estructurado, que se puede pasar a lo largo del flujo de trabajo. El siguiente nodo usa esta respuesta para enrutar el flujo de trabajo a ya sea END o 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;
};
Obtención de los documentos de RR. HH. necesarios
Una vez que se determina que la pregunta requiere documentos de RR. HH., el flujo de trabajo usa getAnswer para recuperar los documentos pertinentes del almacén de vectores, agréguelos al contexto del símbolo del sistema y pase todo el mensaje al 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],
};
}
Si no se encuentra ningún documento relevante, el agente devuelve un mensaje que indica que no pudo encontrar una respuesta en los documentos de RR. HH.
Solución de problemas
Para cualquier problema con el procedimiento, cree un problema en el repositorio de código de ejemplo.
Limpieza de recursos
Puede eliminar el grupo de recursos, que contiene el recurso de Azure AI Search y el recurso de Azure OpenAI o usar la CLI para desarrolladores de Azure para eliminar inmediatamente todos los recursos creados por este tutorial.
azd down --purge