إنشاء تطبيق LUIS برمجيا باستخدام Node.js
يوفر LUIS واجهة برمجة تطبيقات برمجية تقوم بكل ما يفعله موقع LUIS على الويب. يمكن أن يوفر ذلك الوقت عندما يكون لديك بيانات موجودة مسبقا وسيكون من الأسرع إنشاء تطبيق LUIS برمجيا بدلا من إدخال المعلومات يدويا.
تنبيه
لم يتم تحديث هذا المستند بنص ولقطات شاشة لأحدث مدخل LUIS.
المتطلبات الأساسية
- سجل الدخول إلى موقع LUIS على الويب وابحث عن مفتاح التأليف في الإعدادات الحساب. يمكنك استخدام هذا المفتاح لاستدعاء واجهات برمجة تطبيقات التأليف.
- إذا لم يكن لديك اشتراك Azure، فأنشئ حساباً مجانياً قبل أن تبدأ.
- تبدأ هذه المقالة بملف CSV لملفات سجل شركة افتراضية لطلبات المستخدمين. قم بتنزيله هنا.
- قم بتثبيت أحدث Node.js باستخدام NPM. قم بتنزيله من هنا.
- [مستحسن] رمز Visual Studio ل IntelliSense وتصحيح الأخطاء ، قم بتنزيله من هنا مجانا.
تتوفر كافة التعليمات البرمجية في هذه المقالة على مستودع GitHub Azure-Samples لفهم اللغة.
تعيين البيانات الموجودة مسبقا إلى المقاصد والكيانات
حتى إذا كان لديك نظام لم يتم إنشاؤه مع وضع LUIS في الاعتبار ، إذا كان يحتوي على بيانات نصية تحدد لأشياء مختلفة يريد المستخدمون القيام بها ، فقد تتمكن من التوصل إلى تعيين من الفئات الحالية لإدخال المستخدم إلى النوايا في LUIS. إذا تمكنت من تحديد الكلمات أو العبارات المهمة في ما قاله المستخدمون، فقد يتم تعيين هذه الكلمات إلى الكيانات.
افتح الملف IoT.csv يحتوي على سجل لاستعلامات المستخدم لخدمة أتمتة المنزل الافتراضية ، بما في ذلك كيفية تصنيفها ، وما قاله المستخدم ، وبعض الأعمدة التي تحتوي على معلومات مفيدة تم سحبها منها.

ترى أن العمود RequestType يمكن أن يكون مقاصد، ويعرض العمود طلب مثالا على الكلام. يمكن أن تكون الحقول الأخرى كيانات إذا حدثت في الكلام. نظرا لوجود نوايا وكيانات وأقوال أمثلة ، فلديك متطلبات تطبيق بسيط ونموذجي.
خطوات إنشاء تطبيق LUIS من بيانات غير LUIS
لإنشاء تطبيق LUIS جديد من ملف CSV:
- تحليل البيانات من ملف CSV:
- قم بالتحويل إلى تنسيق يمكنك تحميله إلى LUIS باستخدام واجهة برمجة تطبيقات التأليف.
- من البيانات التي تم تحليلها ، اجمع معلومات حول النوايا والكيانات.
- إجراء استدعاءات لواجهة برمجة التطبيقات للتأليف إلى:
- إنشاء التطبيق.
- إضافة المقاصد والكيانات التي تم جمعها من البيانات التي تم تحليلها.
- بمجرد إنشاء تطبيق LUIS ، يمكنك إضافة أمثلة على الألفاظ من البيانات التي تم تحليلها.
يمكنك رؤية تدفق هذا البرنامج في الجزء الأخير من index.js الملف. انسخ أو قم بتنزيل هذا الرمز واحفظه في index.js.
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);
});
تحليل ملف CSV
يجب تحليل إدخالات الأعمدة التي تحتوي على الألفاظ في CSV إلى تنسيق JSON يمكن ل LUIS فهمه. يجب أن يحتوي تنسيق JSON هذا على intentName حقل يحدد الغرض من الكلام. يجب أن يحتوي أيضا على حقل ، والذي يمكن أن يكون فارغا entityLabels إذا لم تكن هناك كيانات في الكلام.
على سبيل المثال، يشير إدخال "تشغيل الأضواء" إلى JSON هذا:
{
"text": "Turn on the lights",
"intentName": "TurnOn",
"entityLabels": [
{
"entityName": "Operation",
"startCharIndex": 5,
"endCharIndex": 6
},
{
"entityName": "Device",
"startCharIndex": 12,
"endCharIndex": 17
}
]
}
في هذا المثال، يأتي طلب المستخدم ضمن عنوان عمود الطلب في ملف CSV، intentName ويأتي من الأعمدة entityName الأخرى التي تحتوي على معلومات أساسية. على سبيل المثال، إذا كان هناك إدخال للتشغيل أو الجهاز، وتحدث هذه السلسلة أيضا في الطلب الفعلي، فيمكن تصنيفها ككيان. توضح التعليمة البرمجية التالية عملية التحليل هذه. يمكنك نسخه أو تنزيله وحفظه في _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;
إنشاء تطبيق LUIS
بمجرد تحليل البيانات في JSON ، أضفها إلى تطبيق LUIS. التعليمة البرمجية التالية بإنشاء تطبيق LUIS. انسخه أو قم بتنزيله واحفظه في _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;
إضافة نوايا
بمجرد أن يكون لديك تطبيق ، تحتاج إلى نوايا لذلك. التعليمة البرمجية التالية بإنشاء تطبيق LUIS. انسخه أو قم بتنزيله واحفظه في _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;
إضافة كيانات
تضيف التعليمة البرمجية التالية الكيانات إلى تطبيق LUIS. انسخه أو قم بتنزيله واحفظه في _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;
إضافة التصريحات
بمجرد تحديد الكيانات والنوايا في تطبيق LUIS ، يمكنك إضافة الكلمات. تستخدم التعليمة البرمجية التالية واجهة برمجة تطبيقات Utterances_AddBatch ، والتي تسمح لك بإضافة ما يصل إلى 100 كلمة في المرة الواحدة. انسخه أو قم بتنزيله واحفظه في _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;
تشغيل التعليمات البرمجية
تثبيت تبعيات Node.js
قم بتثبيت تبعيات Node.js من NPM في سطر المحطة الطرفية/الأوامر.
> npm install
تغيير الإعدادات التكوين
لاستخدام هذا التطبيق ، تحتاج إلى تغيير القيم الموجودة في ملف index.js إلى مفتاح نقطة النهاية الخاص بك ، وتوفير الاسم الذي تريد أن يكون لدى التطبيق. يمكنك أيضا تعيين ثقافة التطبيق أو تغيير رقم الإصدار.
افتح ملف index.js، وقم بتغيير هذه القيم في أعلى الملف.
// Change these values
const LUIS_programmaticKey = "YOUR_AUTHORING_KEY";
const LUIS_appName = "Sample App";
const LUIS_appCulture = "en-us";
const LUIS_versionId = "0.1";
تشغيل البرنامج النصي
قم بتشغيل البرنامج النصي من سطر طرفي/أوامر باستخدام Node.js.
> node index.js
أو
> npm start
تقدم الطلب
أثناء تشغيل التطبيق، يعرض سطر الأوامر التقدم. يتضمن إخراج سطر الأوامر تنسيق الردود من LUIS.
> 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
افتح تطبيق LUIS
بمجرد اكتمال البرنامج النصي، يمكنك تسجيل الدخول إلى LUIS ومشاهدة تطبيق LUIS الذي أنشأته ضمن تطبيقاتي. يجب أن تكون قادرا على رؤية الألفاظ التي أضفتها ضمن مقاصد TurnOn و TurnOff و None .

الخطوات التالية
اختبار تطبيقك وتدريبه في موقع LUIS على الويب
الموارد الإضافية
يستخدم هذا التطبيق النموذجي واجهات برمجة تطبيقات LUIS التالية: