LUIS-alkalmazás létrehozása programozott módon Node.js

Fontos

A LUIS 2025. október 1-jén megszűnik, és 2023. április 1-től nem hozhat létre új LUIS-erőforrásokat. Javasoljuk, hogy a LUIS-alkalmazásokat a beszélgetési nyelv megértésére migrálja, hogy kihasználhassa a terméktámogatás és a többnyelvű képességek előnyeit.

A LUIS egy programozott API-t biztosít, amely mindent megtesz, amit a LUIS webhelye csinál. Ez időt takaríthat meg, ha már meglévő adatokkal rendelkezik, és gyorsabban hozhat létre programozott módon egy LUIS-alkalmazást, mint ha kézzel adja meg az adatokat.

Figyelemfelhívás

Ez a dokumentum nem frissült szöveggel és képernyőképekkel a legújabb LUIS-portálon.

Előfeltételek

  • Jelentkezzen be a LUIS webhelyére, és keresse meg a szerzői kulcsot a Fiók Gépház. Ezzel a kulccsal hívhatja meg a Szerzői API-kat.
  • Ha még nincs Azure-előfizetése, kezdés előtt hozzon létre egy ingyenes fiókot.
  • Ez a cikk egy csv-vel kezdődik egy feltételezett vállalat felhasználói kérések naplófájljaihoz. Töltse le ide.
  • Telepítse a legújabb Node.js verziót. Töltse le innen.
  • [Ajánlott] Visual Studio Code for IntelliSense és hibakeresés, töltse le innen ingyen.

A cikkben szereplő összes kód elérhető az Azure-Samples Language Understanding GitHub-adattárban.

Meglévő adatok leképezése szándékokhoz és entitásokhoz

Még ha van is olyan rendszere, amelyet nem a LUIS-val hoztak létre, ha szöveges adatokat tartalmaz, amelyek a felhasználók által kívánt műveletekre vannak leképezve, előfordulhat, hogy a luis-ban meglévő felhasználói beviteli kategóriákból a szándékok leképezésére is képes lesz. Ha azonosítani tudja a felhasználók által elmondott fontos szavakat vagy kifejezéseket, ezek a szavak entitásokra képezhetők le.

Nyissa meg a IoT.csv fájlt. Egy hipotetikus otthoni automatizálási szolgáltatás felhasználói lekérdezéseinek naplóját tartalmazza, beleértve a kategorizálás módját, a felhasználó által elmondottakat, valamint néhány hasznos információt tartalmazó oszlopot.

MEGLÉVŐ ADATOK CSV-fájlja

Láthatja, hogy a RequestType oszlop szándékok lehetnek, a Kérés oszlop pedig egy példa kimondott szövegre. A többi mező entitás lehet, ha a kimondott szövegben fordulnak elő. Mivel vannak szándékok, entitások és példa kimondott szövegek, egy egyszerű, mintaalkalmazásra vonatkozó követelményekkel rendelkezik.

Luis-alkalmazás nem LUIS-adatokból történő létrehozásának lépései

Új LUIS-alkalmazás létrehozása a CSV-fájlból:

  • Elemezni kell a CSV-fájlból származó adatokat:
    • Alakítsa át a LUIS-ba a Authoring API használatával feltölthető formátumot.
    • Az elemzési adatokból gyűjtsön információkat a szándékokról és entitásokról.
  • Api-hívások készítése a következőre:
    • Hozza létre az alkalmazást.
    • Az elemzési adatokból összegyűjtött szándékok és entitások hozzáadása.
    • A LUIS-alkalmazás létrehozása után hozzáadhatja a példa kimondott szövegeket az elemzett adatokból.

Ez a programfolyamat a fájl utolsó részében index.js látható. Másolja vagy töltse le a kódot, és mentse a fájlba index.js.

Fontos

Ne felejtse el eltávolítani a kulcsot a kódból, amikor elkészült, és soha ne tegye közzé nyilvánosan. Éles környezetben biztonságos módon tárolhatja és érheti el a hitelesítő adatait, például az Azure Key Vaultot. További információkért tekintse meg az Azure AI-szolgáltatások biztonsági cikkét.

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

A CSV elemzése

A CSV-ben a kimondott szövegeket tartalmazó oszlopbejegyzéseket JSON formátumban kell elemezni, amelyet a LUIS megért. Ennek a JSON-formátumnak tartalmaznia kell egy intentName mezőt, amely azonosítja a kimondott szöveg szándékát. Tartalmaznia kell egy entityLabels mezőt is, amely üres lehet, ha nincsenek entitások a kimondott szövegben.

A "Világítás bekapcsolása" bejegyzés például erre a JSON-ra lesz leképezve:

{
    "text": "Turn on the lights",
    "intentName": "TurnOn",
    "entityLabels": [
        {
            "entityName": "Operation",
            "startCharIndex": 5,
            "endCharIndex": 6
        },
        {
            "entityName": "Device",
            "startCharIndex": 12,
            "endCharIndex": 17
        }
    ]
}

Ebben a példában a intentName CSV-fájl Kérés oszlopfejléce alatti felhasználói kérelemből származik, a entityName többi oszlopból pedig kulcsinformációkat tartalmazó oszlopból származik. Ha például egy művelet vagy eszköz bejegyzése van, és ez a sztring a tényleges kérelemben is előfordul, akkor entitásként címkézhető. Az alábbi kód bemutatja ezt az elemzési folyamatot. Másolhatja vagy letöltheti , és mentheti a fájlba _parse.js.

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

A LUIS-alkalmazás létrehozása

Miután elemezte az adatokat a JSON-ban, adja hozzá egy LUIS-alkalmazáshoz. Az alábbi kód létrehozza a LUIS-alkalmazást. Másolja vagy töltse le , majd mentse a fájlba _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;

Leképezések hozzáadása

Ha már rendelkezik egy alkalmazással, szándékot kell adnia rá. Az alábbi kód létrehozza a LUIS-alkalmazást. Másolja vagy töltse le , majd mentse a fájlba _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;

Entitások hozzáadása

Az alábbi kód hozzáadja az entitásokat a LUIS-alkalmazáshoz. Másolja vagy töltse le , majd mentse a fájlba _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;

Beszédmódok hozzáadása

Miután definiálta az entitásokat és szándékokat a LUIS-alkalmazásban, hozzáadhatja a kimondott szövegeket. Az alábbi kód a Utterances_AddBatch API-t használja, amely lehetővé teszi, hogy egyszerre legfeljebb 100 kimondott szöveget adjon hozzá. Másolja vagy töltse le , majd mentse a fájlba _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;

A kód futtatása

Node.js függőségek telepítése

Telepítse a Node.js függőségeket a terminálban/parancssorban.

> npm install

Konfigurációs Gépház módosítása

Az alkalmazás használatához módosítania kell a index.js fájl értékeit a saját végpontkulcsára, és meg kell adnia az alkalmazás nevét. Beállíthatja az alkalmazás kulturális környezetét, vagy módosíthatja a verziószámot.

Nyissa meg a index.js fájlt, és módosítsa ezeket az értékeket a fájl tetején.

// Change these values
const LUIS_programmaticKey = "YOUR_AUTHORING_KEY";
const LUIS_appName = "Sample App";
const LUIS_appCulture = "en-us";
const LUIS_versionId = "0.1";

A szkript futtatása

Futtassa a szkriptet egy terminálból vagy parancssorból Node.js.

> node index.js

Vagy

> npm start

Alkalmazás állapota

Amíg az alkalmazás fut, a parancssor megjeleníti az előrehaladást. A parancssor kimenete tartalmazza a LUIS-ból érkező válaszok formátumát.

> 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

A LUIS-alkalmazás megnyitása

A szkript befejeződése után bejelentkezhet a LUIS-ba, és megtekintheti a Saját alkalmazások alatt létrehozott LUIS-alkalmazást. Látnia kell a TurnOn, a TurnOff és a None szándék alatt hozzáadott kimondott szövegeket.

TurnOn-szándék

Következő lépések

Tesztelje és tanítsa be az alkalmazást a LUIS webhelyén.

További erőforrások

Ez a mintaalkalmazás a következő LUIS API-kat használja: