Programmgesteuertes Erstellen einer LUIS-App mit Node.js
Wichtig
LUIS wird am 1. Oktober 2025 eingestellt, und ab dem 1. April 2023 können Sie keine neuen LUIS-Ressourcen erstellen. Es wird die Migration Ihrer LUIS-Anwendungen zu Conversational Language Understanding empfohlen, um fortgesetzte Produktunterstützung und mehrsprachige Funktionen zu erhalten.
LUIS bietet eine programmgesteuerte API, die alle Funktionen der LUIS-Website aufweist. Dies kann Zeit sparen, wenn Sie über bereits vorhandene Daten verfügen und es schneller geht, eine LUIS-App programmgesteuert zu erstellen, als die Informationen manuell einzugeben.
Achtung
Dieses Dokument wurde nicht mit Text und Screenshots für das neueste LUIS-Portal aktualisiert.
Voraussetzungen
- Melden Sie sich auf der Website von LUIS an, und suchen Sie Ihren Erstellungsschlüssel in den Kontoeinstellungen. Sie können mit diesem Schlüssel die Erstellungs-APIs aufrufen.
- Wenn Sie kein Azure-Abonnement besitzen, können Sie ein kostenloses Konto erstellen, bevor Sie beginnen.
- Dieser Artikel beginnt mit Daten im CSV-Format für Protokolldateien von Benutzeranforderungen eines hypothetischen Unternehmens. Sie können sie hierherunterladen.
- Installieren Sie die neueste Version von Node.js. Laden Sie es hier herunter.
- [Empfohlen:] Visual Studio Code für IntelliSense und Debuggen, laden Sie es hier kostenlos herunter.
Der gesamte Code in diesem Artikel ist im GitHub-Repository mit Azure-Beispielen zu Language Understanding verfügbar.
Zuordnen bereits vorhandener Daten zu Absichten und Entitäten
Selbst wenn Sie ein System haben, bei dessen Erstellung LUIS nicht berücksichtigt wurde, enthält dieses möglicherweise Textdaten, die verschiedenen möglichen Aktionen von Benutzern zugeordnet sind. In diesem Fall können Sie u.U. eine Zuordnung zwischen den vorhandenen Kategorien von Benutzereingaben und den Absichten in LUIS erstellen. Wenn es möglich ist, wichtige Wörter oder Ausdrücke in den Aussagen der Benutzern zu identifizieren, können Sie diese Wörter möglicherweise Entitäten zuordnen.
Öffnen Sie die Datei IoT.csv
. Sie enthält ein Protokoll von Benutzerabfragen eines hypothetischen Heimautomatisierungsdiensts, einschließlich der Art ihrer Kategorisierung, und einiger Spalten mit nützlichen Informationen, die diesen entnommen wurden.
Sie sehen, dass es sich bei der Spalte RequestType um Absichten handeln könnte und dass die Spalte Request eine Beispieläußerung enthält. Bei den anderen Feldern könnte es sich um Entitäten handeln, wenn sie in der Äußerung auftreten. Da es Absichten, Entitäten und Beispieläußerungen gibt, sind alle Anforderungen an eine einfache Beispiel-App erfüllt.
Schritte zum Generieren einer LUIS-App aus Nicht-LUIS-Daten
So generieren Sie eine neue LUIS-App auf der Grundlage der CSV-Datei:
- Analysieren Sie die Daten aus der CSV-Datei:
- Konvertieren Sie sie in ein Format, das Sie über die Erstellungs-API in LUIS hochladen können.
- Sammeln Sie auf der Grundlage der analysierten Daten Informationen zu Absichten und Entitäten.
- Rufen Sie die Erstellungs-API auf, um Folgendes auszuführen:
- Erstellen der App
- Hinzufügen von Absichten und Entitäten, die auf der Grundlage der analysierten Daten gesammelt wurden
- Nachdem Sie die LUIS-App erstellt haben, können Sie die Beispieläußerungen aus den analysierten Daten hinzufügen.
Diesen Programmablauf finden Sie im letzten Teil der Datei index.js
. Kopieren Sie diesen Code, oder laden Sie ihn herunter, und speichern Sie ihn in index.js
.
Wichtig
Denken Sie daran, den Schlüssel aus Ihrem Code zu entfernen, wenn Sie fertig sind, und ihn niemals zu veröffentlichen. Verwenden Sie für die Produktion eine sichere Art der Speicherung und des Zugriffs auf Ihre Anmeldeinformationen wie Azure Key Vault. Weitere Informationen finden Sie im Artikel zur Azure KI Services-Sicherheit.
var path = require('path');
const parse = require('./_parse');
const createApp = require('./_create');
const addEntities = require('./_entities');
const addIntents = require('./_intents');
const upload = require('./_upload');
// Change these values
const LUIS_authoringKey = "YOUR_AUTHORING_KEY";
const LUIS_appName = "Sample App - build from IoT csv file";
const LUIS_appCulture = "en-us";
const LUIS_versionId = "0.1";
// NOTE: final output of add-utterances api named utterances.upload.json
const downloadFile = "./IoT.csv";
const uploadFile = "./utterances.json"
// The app ID is returned from LUIS when your app is created
var LUIS_appId = ""; // default app ID
var intents = [];
var entities = [];
/* add utterances parameters */
var configAddUtterances = {
LUIS_subscriptionKey: LUIS_authoringKey,
LUIS_appId: LUIS_appId,
LUIS_versionId: LUIS_versionId,
inFile: path.join(__dirname, uploadFile),
batchSize: 100,
uri: "https://westus.api.cognitive.microsoft.com/luis/api/v2.0/apps/{appId}/versions/{versionId}/examples"
};
/* create app parameters */
var configCreateApp = {
LUIS_subscriptionKey: LUIS_authoringKey,
LUIS_versionId: LUIS_versionId,
appName: LUIS_appName,
culture: LUIS_appCulture,
uri: "https://westus.api.cognitive.microsoft.com/luis/api/v2.0/apps/"
};
/* add intents parameters */
var configAddIntents = {
LUIS_subscriptionKey: LUIS_authoringKey,
LUIS_appId: LUIS_appId,
LUIS_versionId: LUIS_versionId,
intentList: intents,
uri: "https://westus.api.cognitive.microsoft.com/luis/api/v2.0/apps/{appId}/versions/{versionId}/intents"
};
/* add entities parameters */
var configAddEntities = {
LUIS_subscriptionKey: LUIS_authoringKey,
LUIS_appId: LUIS_appId,
LUIS_versionId: LUIS_versionId,
entityList: entities,
uri: "https://westus.api.cognitive.microsoft.com/luis/api/v2.0/apps/{appId}/versions/{versionId}/entities"
};
/* input and output files for parsing CSV */
var configParse = {
inFile: path.join(__dirname, downloadFile),
outFile: path.join(__dirname, uploadFile)
};
// Parse CSV
parse(configParse)
.then((model) => {
// Save intent and entity names from parse
intents = model.intents;
entities = model.entities;
// Create the LUIS app
return createApp(configCreateApp);
}).then((appId) => {
// Add intents
LUIS_appId = appId;
configAddIntents.LUIS_appId = appId;
configAddIntents.intentList = intents;
return addIntents(configAddIntents);
}).then(() => {
// Add entities
configAddEntities.LUIS_appId = LUIS_appId;
configAddEntities.entityList = entities;
return addEntities(configAddEntities);
}).then(() => {
// Add example utterances to the intents in the app
configAddUtterances.LUIS_appId = LUIS_appId;
return upload(configAddUtterances);
}).catch(err => {
console.log(err.message);
});
Analysieren der CSV-Daten
Die Spalteneinträge, die die Äußerungen in den CSV-Daten enthalten, müssen in ein JSON-Format umgewandelt werden, das LUIS verstehen kann. Dieses JSON-Format muss ein Feld intentName
enthalten, das die Absicht der Äußerung identifiziert. Es muss auch ein Feld entityLabels
enthalten sein, das leer sein kann, wenn die Äußerung keine Entitäten enthält.
Der Eintrag für „Turn on the lights“ wird z.B. folgendem JSON-Code zugeordnet:
{
"text": "Turn on the lights",
"intentName": "TurnOn",
"entityLabels": [
{
"entityName": "Operation",
"startCharIndex": 5,
"endCharIndex": 6
},
{
"entityName": "Device",
"startCharIndex": 12,
"endCharIndex": 17
}
]
}
In diesem Beispiel stammt der intentName
von der Anforderung des Benutzers unter der Spaltenüberschrift Request in der CSV-Datei, und der entityName
stammt aus den anderen Spalten mit Schlüsselinformationen. Wenn beispielsweise ein Eintrag für Operation oder Device vorliegt und diese Zeichenfolge auch in der eigentlichen Anforderung auftritt, kann sie als Entität bezeichnet werden. Der folgende Code veranschaulicht den Analyseprozesses. Sie können ihn kopieren oder herunterladen und als _parse.js
speichern.
// node 7.x
// built with streams for larger files
const fse = require('fs-extra');
const path = require('path');
const lineReader = require('line-reader');
const babyparse = require('babyparse');
const Promise = require('bluebird');
const intent_column = 0;
const utterance_column = 1;
var entityNames = [];
var eachLine = Promise.promisify(lineReader.eachLine);
function listOfIntents(intents) {
return intents.reduce(function (a, d) {
if (a.indexOf(d.intentName) === -1) {
a.push(d.intentName);
}
return a;
}, []);
}
function listOfEntities(utterances) {
return utterances.reduce(function (a, d) {
d.entityLabels.forEach(function(entityLabel) {
if (a.indexOf(entityLabel.entityName) === -1) {
a.push(entityLabel.entityName);
}
}, this);
return a;
}, []);
}
var utterance = function (rowAsString) {
let json = {
"text": "",
"intentName": "",
"entityLabels": [],
};
if (!rowAsString) return json;
let dataRow = babyparse.parse(rowAsString);
// Get intent name and utterance text
json.intentName = dataRow.data[0][intent_column];
json.text = dataRow.data[0][utterance_column];
// For each column heading that may be an entity, search for the element in this column in the utterance.
entityNames.forEach(function (entityName) {
entityToFind = dataRow.data[0][entityName.column];
if (entityToFind != "") {
strInd = json.text.indexOf(entityToFind);
if (strInd > -1) {
let entityLabel = {
"entityName": entityName.name,
"startCharIndex": strInd,
"endCharIndex": strInd + entityToFind.length - 1
}
json.entityLabels.push(entityLabel);
}
}
}, this);
return json;
};
const convert = async (config) => {
try {
var i = 0;
// get inFile stream
inFileStream = await fse.createReadStream(config.inFile, 'utf-8')
// create out file
var myOutFile = await fse.createWriteStream(config.outFile, 'utf-8');
var utterances = [];
// read 1 line at a time
return eachLine(inFileStream, (line) => {
// skip first line with headers
if (i++ == 0) {
// csv to baby parser object
let dataRow = babyparse.parse(line);
// populate entityType list
var index = 0;
dataRow.data[0].forEach(function (element) {
if ((index != intent_column) && (index != utterance_column)) {
entityNames.push({ name: element, column: index });
}
index++;
}, this);
return;
}
// transform utterance from csv to json
utterances.push(utterance(line));
}).then(() => {
console.log("intents: " + JSON.stringify(listOfIntents(utterances)));
console.log("entities: " + JSON.stringify(listOfEntities(utterances)));
myOutFile.write(JSON.stringify({ "converted_date": new Date().toLocaleString(), "utterances": utterances }));
myOutFile.end();
console.log("parse done");
console.log("JSON file should contain utterances. Next step is to create an app with the intents and entities it found.");
var model =
{
intents: listOfIntents(utterances),
entities: listOfEntities(utterances)
}
return model;
});
} catch (err) {
throw err;
}
}
module.exports = convert;
Erstellen der LUIS-App
Nachdem die Daten in JSON analysiert wurden, fügen Sie sie einer LUIS-App hinzu. Der folgende Code erstellt die LUIS-App. Kopieren Sie ihn, oder laden Sie ihn herunter, und speichern Sie ihn in _create.js
.
// node 7.x
// uses async/await - promises
var rp = require('request-promise');
var fse = require('fs-extra');
var path = require('path');
// main function to call
// Call Apps_Create
var createApp = async (config) => {
try {
// JSON for the request body
// { "name": MyAppName, "culture": "en-us"}
var jsonBody = {
"name": config.appName,
"culture": config.culture
};
// Create a LUIS app
var createAppPromise = callCreateApp({
uri: config.uri,
method: 'POST',
headers: {
'Ocp-Apim-Subscription-Key': config.LUIS_subscriptionKey
},
json: true,
body: jsonBody
});
let results = await createAppPromise;
// Create app returns an app ID
let appId = results.response;
console.log(`Called createApp, created app with ID ${appId}`);
return appId;
} catch (err) {
console.log(`Error creating app: ${err.message} `);
throw err;
}
}
// Send JSON as the body of the POST request to the API
var callCreateApp = async (options) => {
try {
var response;
if (options.method === 'POST') {
response = await rp.post(options);
} else if (options.method === 'GET') { // TODO: There's no GET for create app
response = await rp.get(options);
}
// response from successful create should be the new app ID
return { response };
} catch (err) {
throw err;
}
}
module.exports = createApp;
Hinzufügen von Absichten
Nachdem Sie eine App haben, müssen Sie ihr Absichten hinzufügen. Der folgende Code erstellt die LUIS-App. Kopieren Sie ihn, oder laden Sie ihn herunter, und speichern Sie ihn in _intents.js
.
var rp = require('request-promise');
var fse = require('fs-extra');
var path = require('path');
var request = require('requestretry');
// time delay between requests
const delayMS = 1000;
// retry recount
const maxRetry = 5;
// retry request if error or 429 received
var retryStrategy = function (err, response, body) {
let shouldRetry = err || (response.statusCode === 429);
if (shouldRetry) console.log("retrying add intent...");
return shouldRetry;
}
// Call add-intents
var addIntents = async (config) => {
var intentPromises = [];
config.uri = config.uri.replace("{appId}", config.LUIS_appId).replace("{versionId}", config.LUIS_versionId);
config.intentList.forEach(function (intent) {
config.intentName = intent;
try {
// JSON for the request body
var jsonBody = {
"name": config.intentName,
};
// Create an intent
var addIntentPromise = callAddIntent({
// uri: config.uri,
url: config.uri,
fullResponse: false,
method: 'POST',
headers: {
'Ocp-Apim-Subscription-Key': config.LUIS_subscriptionKey
},
json: true,
body: jsonBody,
maxAttempts: maxRetry,
retryDelay: delayMS,
retryStrategy: retryStrategy
});
intentPromises.push(addIntentPromise);
console.log(`Called addIntents for intent named ${intent}.`);
} catch (err) {
console.log(`Error in addIntents: ${err.message} `);
}
}, this);
let results = await Promise.all(intentPromises);
console.log(`Results of all promises = ${JSON.stringify(results)}`);
let response = results;
}
// Send JSON as the body of the POST request to the API
var callAddIntent = async (options) => {
try {
var response;
response = await request(options);
return { response: response };
} catch (err) {
console.log(`Error in callAddIntent: ${err.message} `);
}
}
module.exports = addIntents;
Hinzufügen von Entitäten
Der folgende Code fügt die Entitäten der LUIS-App hinzu. Kopieren Sie ihn, oder laden Sie ihn herunter, und speichern Sie ihn in _entities.js
.
// node 7.x
// uses async/await - promises
const request = require("requestretry");
var rp = require('request-promise');
var fse = require('fs-extra');
var path = require('path');
// time delay between requests
const delayMS = 1000;
// retry recount
const maxRetry = 5;
// retry request if error or 429 received
var retryStrategy = function (err, response, body) {
let shouldRetry = err || (response.statusCode === 429);
if (shouldRetry) console.log("retrying add entity...");
return shouldRetry;
}
// main function to call
// Call add-entities
var addEntities = async (config) => {
var entityPromises = [];
config.uri = config.uri.replace("{appId}", config.LUIS_appId).replace("{versionId}", config.LUIS_versionId);
config.entityList.forEach(function (entity) {
try {
config.entityName = entity;
// JSON for the request body
// { "name": MyEntityName}
var jsonBody = {
"name": config.entityName,
};
// Create an app
var addEntityPromise = callAddEntity({
url: config.uri,
fullResponse: false,
method: 'POST',
headers: {
'Ocp-Apim-Subscription-Key': config.LUIS_subscriptionKey
},
json: true,
body: jsonBody,
maxAttempts: maxRetry,
retryDelay: delayMS,
retryStrategy: retryStrategy
});
entityPromises.push(addEntityPromise);
console.log(`called addEntity for entity named ${entity}.`);
} catch (err) {
console.log(`Error in addEntities: ${err.message} `);
//throw err;
}
}, this);
let results = await Promise.all(entityPromises);
console.log(`Results of all promises = ${JSON.stringify(results)}`);
let response = results;// await fse.writeJson(createResults.json, results);
}
// Send JSON as the body of the POST request to the API
var callAddEntity = async (options) => {
try {
var response;
response = await request(options);
return { response: response };
} catch (err) {
console.log(`error in callAddEntity: ${err.message}`);
}
}
module.exports = addEntities;
Hinzufügen von Äußerungen
Nachdem die Entitäten und Absichten in der LUIS-App definiert wurden, können Sie die Äußerungen hinzufügen. Der folgende Code verwendet die Utterances_AddBatch-API, mit der Sie bis zu 100 Äußerungen gleichzeitig hinzufügen können. Kopieren Sie ihn, oder laden Sie ihn herunter, und speichern Sie ihn in _upload.js
.
// node 7.x
// uses async/await - promises
var rp = require('request-promise');
var fse = require('fs-extra');
var path = require('path');
var request = require('requestretry');
// time delay between requests
const delayMS = 500;
// retry recount
const maxRetry = 5;
// retry request if error or 429 received
var retryStrategy = function (err, response, body) {
let shouldRetry = err || (response.statusCode === 429);
if (shouldRetry) console.log("retrying add examples...");
return shouldRetry;
}
// main function to call
var upload = async (config) => {
try{
// read in utterances
var entireBatch = await fse.readJson(config.inFile);
// break items into pages to fit max batch size
var pages = getPagesForBatch(entireBatch.utterances, config.batchSize);
var uploadPromises = [];
// load up promise array
pages.forEach(page => {
config.uri = "https://westus.api.cognitive.microsoft.com/luis/api/v2.0/apps/{appId}/versions/{versionId}/examples".replace("{appId}", config.LUIS_appId).replace("{versionId}", config.LUIS_versionId)
var pagePromise = sendBatchToApi({
url: config.uri,
fullResponse: false,
method: 'POST',
headers: {
'Ocp-Apim-Subscription-Key': config.LUIS_subscriptionKey
},
json: true,
body: page,
maxAttempts: maxRetry,
retryDelay: delayMS,
retryStrategy: retryStrategy
});
uploadPromises.push(pagePromise);
})
//execute promise array
let results = await Promise.all(uploadPromises)
console.log(`\n\nResults of all promises = ${JSON.stringify(results)}`);
let response = await fse.writeJson(config.inFile.replace('.json','.upload.json'),results);
console.log("upload done");
} catch(err){
throw err;
}
}
// turn whole batch into pages batch
// because API can only deal with N items in batch
var getPagesForBatch = (batch, maxItems) => {
try{
var pages = [];
var currentPage = 0;
var pageCount = (batch.length % maxItems == 0) ? Math.round(batch.length / maxItems) : Math.round((batch.length / maxItems) + 1);
for (let i = 0;i<pageCount;i++){
var currentStart = currentPage * maxItems;
var currentEnd = currentStart + maxItems;
var pagedBatch = batch.slice(currentStart,currentEnd);
var j = 0;
pagedBatch.forEach(item=>{
item.ExampleId = j++;
});
pages.push(pagedBatch);
currentPage++;
}
return pages;
}catch(err){
throw(err);
}
}
// send json batch as post.body to API
var sendBatchToApi = async (options) => {
try {
var response = await request(options);
//return {page: options.body, response:response};
return {response:response};
}catch(err){
throw err;
}
}
module.exports = upload;
Ausführen des Codes
Installieren von Node.js-Abhängigkeiten
Installieren Sie die Node.js-Abhängigkeiten im Terminal bzw. an der Befehlszeile.
> npm install
Ändern der Konfigurationseinstellungen
Um diese Anwendung verwenden zu können, müssen Sie die Werte in der Datei „index.js“ in Ihren eigenen Endpunktschlüssel ändern und den gewünschten Namen für die App angeben. Sie können auch die Kultur der App festlegen oder die Versionsnummer ändern.
Öffnen Sie die Datei „Index.js“, und ändern Sie diese Werte am Anfang der Datei.
// Change these values
const LUIS_programmaticKey = "YOUR_AUTHORING_KEY";
const LUIS_appName = "Sample App";
const LUIS_appCulture = "en-us";
const LUIS_versionId = "0.1";
Führen Sie das Skript aus.
Führen Sie das Skript über ein Terminal oder die Befehlszeile mit Node.js aus.
> node index.js
Oder
> npm start
Anwendungsfortschritt
Während die Anwendung ausgeführt wird, wird an der Befehlszeile der Fortschritt angezeigt. Die Befehlszeilenausgabe schließt das Format der Antworten von LUIS ein.
> node index.js
intents: ["TurnOn","TurnOff","Dim","Other"]
entities: ["Operation","Device","Room"]
parse done
JSON file should contain utterances. Next step is to create an app with the intents and entities it found.
Called createApp, created app with ID 314b306c-0033-4e09-92ab-94fe5ed158a2
Called addIntents for intent named TurnOn.
Called addIntents for intent named TurnOff.
Called addIntents for intent named Dim.
Called addIntents for intent named Other.
Results of all calls to addIntent = [{"response":"e7eaf224-8c61-44ed-a6b0-2ab4dc56f1d0"},{"response":"a8a17efd-f01c-488d-ad44-a31a818cf7d7"},{"response":"bc7c32fc-14a0-4b72-bad4-d345d807f965"},{"response":"727a8d73-cd3b-4096-bc8d-d7cfba12eb44"}]
called addEntity for entity named Operation.
called addEntity for entity named Device.
called addEntity for entity named Room.
Results of all calls to addEntity= [{"response":"6a7e914f-911d-4c6c-a5bc-377afdce4390"},{"response":"56c35237-593d-47f6-9d01-2912fa488760"},{"response":"f1dd440c-2ce3-4a20-a817-a57273f169f3"}]
retrying add examples...
Results of add utterances = [{"response":[{"value":{"UtteranceText":"turn on the lights","ExampleId":-67649},"hasError":false},{"value":{"UtteranceText":"turn the heat on","ExampleId":-69067},"hasError":false},{"value":{"UtteranceText":"switch on the kitchen fan","ExampleId":-3395901},"hasError":false},{"value":{"UtteranceText":"turn off bedroom lights","ExampleId":-85402},"hasError":false},{"value":{"UtteranceText":"turn off air conditioning","ExampleId":-8991572},"hasError":false},{"value":{"UtteranceText":"kill the lights","ExampleId":-70124},"hasError":false},{"value":{"UtteranceText":"dim the lights","ExampleId":-174358},"hasError":false},{"value":{"UtteranceText":"hi how are you","ExampleId":-143722},"hasError":false},{"value":{"UtteranceText":"answer the phone","ExampleId":-69939},"hasError":false},{"value":{"UtteranceText":"are you there","ExampleId":-149588},"hasError":false},{"value":{"UtteranceText":"help","ExampleId":-81949},"hasError":false},{"value":{"UtteranceText":"testing the circuit","ExampleId":-11548708},"hasError":false}]}]
upload done
Öffnen der LUIS-App
Nach Abschluss des Skripts können Sie sich bei LUIS anmelden und sich die erstellte LUIS-App unter Meine Apps ansehen. Sie sollten die hinzugefügten Äußerungen unter den Absichten TurnOn, TurnOff und None sehen.
Nächste Schritte
Testen und Trainieren Ihrer App auf der LUIS-Website
Zusätzliche Ressourcen
In dieser Beispielanwendung werden die folgenden LUIS-APIs verwendet: