Anda dapat menggunakan keterampilan untuk memperluas bot lain.
Keterampilan adalah bot yang dapat melakukan serangkaian tugas untuk bot lain.
Manifes menjelaskan antarmuka keterampilan. Pengembang yang tidak memiliki akses ke kode sumber keterampilan dapat menggunakan informasi dalam manifes untuk merancang konsumen keterampilan mereka.
Keterampilan dapat menggunakan validasi klaim untuk mengelola bot atau pengguna mana yang dapat mengaksesnya.
Artikel ini menunjukkan cara menerapkan keterampilan yang menggemakan input pengguna.
Beberapa jenis konsumen keterampilan tidak dapat menggunakan beberapa jenis bot keterampilan.
Tabel berikut ini menjelaskan kombinasi mana yang didukung.
Keterampilan multi-penyewa
Keterampilan penyewa tunggal
Keterampilan identitas terkelola yang ditetapkan pengguna
Konsumen multi-penyewa
Didukung
Tidak didukung
Tidak didukung
Konsumen penyewa tunggal
Tidak didukung
Didukung jika kedua aplikasi milik penyewa yang sama
Didukung jika kedua aplikasi milik penyewa yang sama
Konsumen identitas terkelola yang ditetapkan pengguna
Tidak didukung
Didukung jika kedua aplikasi milik penyewa yang sama
Didukung jika kedua aplikasi milik penyewa yang sama
Catatan
Bot Framework JavaScript, C#, dan Python SDK akan terus didukung, namun, Java SDK dihentikan dengan dukungan jangka panjang akhir yang berakhir pada November 2023.
Bot yang ada yang dibangun dengan Java SDK akan terus berfungsi.
Dimulai dengan versi 4.11, Anda tidak memerlukan ID dan kata sandi aplikasi untuk menguji keterampilan secara lokal di Bot Framework Emulator. Langganan Azure masih diperlukan untuk menyebarkan keterampilan Anda ke Azure.
Tentang sampel ini
Sampel bot-ke-bot sederhana keterampilan mencakup proyek untuk dua bot:
Bot keterampilan gema, yang mengimplementasikan keterampilan.
Bot akar sederhana, yang mengimplementasikan bot akar yang mengonsumsi keterampilan.
Artikel ini berfokus pada keterampilan, yang mencakup logika dukungan dalam bot dan adaptornya.
Untuk informasi tentang bot root sederhana, lihat cara Menerapkan konsumen keterampilan.
Sumber
Untuk bot yang disebarkan, autentikasi bot-ke-bot mengharuskan setiap bot yang berpartisipasi memiliki informasi identitas yang valid.
Namun, Anda dapat menguji keterampilan multipenyewa dan konsumen keterampilan secara lokal dengan Emulator tanpa ID aplikasi dan kata sandi.
Untuk membuat keterampilan tersedia untuk bot yang menghadap pengguna, daftarkan keterampilan dengan Azure. Untuk informasi selengkapnya, lihat cara mendaftarkan bot dengan Azure AI Bot Service.
Konfigurasi aplikasi
Secara opsional, tambahkan informasi identitas keterampilan ke file konfigurasinya. Jika konsumen keterampilan atau keterampilan memberikan informasi identitas, keduanya harus.
Array penelepon yang diizinkan dapat membatasi keterampilan mana yang dapat diakses konsumen keterampilan.
Untuk menerima panggilan dari konsumen keterampilan apa pun, tambahkan elemen "*".
Catatan
Jika Anda menguji keterampilan Anda secara lokal tanpa informasi identitas bot, baik keterampilan maupun konsumen keterampilan menjalankan kode untuk melakukan validasi klaim.
Secara opsional, tambahkan informasi identitas keterampilan ke file appsettings.json.
{
"MicrosoftAppType": "",
"MicrosoftAppId": "",
"MicrosoftAppPassword": "",
"MicrosoftAppTenantId": "",
// This is a comma separate list with the App IDs that will have access to the skill.
// This setting is used in AllowedCallersClaimsValidator.
// Examples:
// [ "*" ] allows all callers.
// [ "AppId1", "AppId2" ] only allows access to parent bots with "AppId1" and "AppId2".
"AllowedCallers": [ "*" ]
}
echo-skill-bot/.env
Secara opsional, tambahkan informasi identitas keterampilan ke file .env.
Secara opsional, tambahkan ID aplikasi dan kata sandi keterampilan ke file application.properties.
MicrosoftAppId=
MicrosoftAppPassword=
server.port=39783
# This is a comma separate list with the App IDs that will have access to the skill.
# This setting is used in AllowedCallersClaimsValidator.
# Examples:
# * allows all callers.
# AppId1,AppId2 only allows access to parent bots with "AppId1" and "AppId2".
AllowedCallers=*
Secara opsional, tambahkan ID aplikasi dan kata sandi keterampilan ke file config.py.
config.py
APP_ID = os.environ.get("MicrosoftAppId", "")
APP_PASSWORD = os.environ.get("MicrosoftAppPassword", "")
APP_TYPE = os.environ.get("MicrosoftAppType", "MultiTenant")
APP_TENANTID = os.environ.get("MicrosoftAppTenantId", "")
# Callers to only those specified, '*' allows any caller.
Logika penangan aktivitas
Untuk menerima parameter input
Konsumen keterampilan dapat mengirim informasi ke keterampilan. Salah satu cara untuk menerima informasi tersebut adalah dengan menerimanya melalui properti nilai pada pesan masuk. Cara lain adalah dengan menangani aktivitas dan memanggil aktivitas.
Keterampilan dalam contoh ini tidak menerima parameter input.
Untuk melanjutkan atau menyelesaikan percakapan
Ketika keterampilan mengirim aktivitas, konsumen keterampilan harus meneruskan aktivitas ke pengguna.
Namun, Anda perlu mengirim endOfConversation aktivitas ketika keterampilan selesai; jika tidak, konsumen keterampilan terus meneruskan aktivitas pengguna ke keterampilan.
Secara opsional, gunakan properti nilai aktivitas untuk menyertakan nilai pengembalian, dan gunakan properti kode aktivitas untuk menunjukkan mengapa keterampilan berakhir.
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
if (turnContext.Activity.Text.Contains("end") || turnContext.Activity.Text.Contains("stop"))
{
// Send End of conversation at the end.
var messageText = $"ending conversation from the skill...";
await turnContext.SendActivityAsync(MessageFactory.Text(messageText, messageText, InputHints.IgnoringInput), cancellationToken);
var endOfConversation = Activity.CreateEndOfConversationActivity();
endOfConversation.Code = EndOfConversationCodes.CompletedSuccessfully;
await turnContext.SendActivityAsync(endOfConversation, cancellationToken);
}
else
{
var messageText = $"Echo: {turnContext.Activity.Text}";
await turnContext.SendActivityAsync(MessageFactory.Text(messageText, messageText, InputHints.IgnoringInput), cancellationToken);
messageText = "Say \"end\" or \"stop\" and I'll end the conversation and back to the parent.";
await turnContext.SendActivityAsync(MessageFactory.Text(messageText, messageText, InputHints.ExpectingInput), cancellationToken);
}
}
echo-skill-bot/bot.js
this.onMessage(async (context, next) => {
switch (context.activity.text.toLowerCase()) {
case 'end':
case 'stop':
await context.sendActivity({
type: ActivityTypes.EndOfConversation,
code: EndOfConversationCodes.CompletedSuccessfully
});
break;
default:
await context.sendActivity(`Echo (JS) : '${ context.activity.text }'`);
await context.sendActivity('Say "end" or "stop" and I\'ll end the conversation and back to the parent.');
}
// By calling next() you ensure that the next BotHandler is run.
await next();
});
echoSkillBot\EchoBot.java
protected CompletableFuture<Void> onMessageActivity(TurnContext turnContext) {
if (
turnContext.getActivity().getText().contains("end") || turnContext.getActivity().getText().contains("stop")
) {
String messageText = "ending conversation from the skill...";
return turnContext.sendActivity(MessageFactory.text(messageText, messageText, InputHints.IGNORING_INPUT))
.thenApply(result -> {
Activity endOfConversation = Activity.createEndOfConversationActivity();
endOfConversation.setCode(EndOfConversationCodes.COMPLETED_SUCCESSFULLY);
return turnContext.sendActivity(endOfConversation);
})
.thenApply(finalResult -> null);
} else {
String messageText = String.format("Echo: %s", turnContext.getActivity().getText());
return turnContext.sendActivity(MessageFactory.text(messageText, messageText, InputHints.IGNORING_INPUT))
.thenApply(result -> {
String nextMessageText =
"Say \"end\" or \"stop\" and I'll end the conversation and back to the parent.";
return turnContext.sendActivity(
MessageFactory.text(nextMessageText, nextMessageText, InputHints.EXPECTING_INPUT)
);
})
.thenApply(result -> null);
}
}
echo-skill-bot/bot/echo_bot.py
async def on_message_activity(self, turn_context: TurnContext):
if "end" in turn_context.activity.text or "stop" in turn_context.activity.text:
# Send End of conversation at the end.
await turn_context.send_activity(
MessageFactory.text("Ending conversation from the skill...")
)
end_of_conversation = Activity(type=ActivityTypes.end_of_conversation)
end_of_conversation.code = EndOfConversationCodes.completed_successfully
await turn_context.send_activity(end_of_conversation)
else:
await turn_context.send_activity(
MessageFactory.text(f"Echo (python): {turn_context.activity.text}")
)
await turn_context.send_activity(
MessageFactory.text(
f'Say "end" or "stop" and I\'ll end the conversation and back to the parent.'
)
)
Untuk membatalkan keterampilan
Untuk keterampilan multi-giliran, Anda juga akan menerima endOfConversation aktivitas dari konsumen keterampilan, untuk memungkinkan konsumen membatalkan percakapan saat ini.
Logika untuk keterampilan ini tidak berubah dari giliran ke giliran. Jika Anda menerapkan keterampilan yang mengalokasikan sumber daya percakapan, tambahkan kode pembersihan sumber daya ke handler akhir percakapan.
protected override Task OnEndOfConversationActivityAsync(ITurnContext<IEndOfConversationActivity> turnContext, CancellationToken cancellationToken)
{
// This will be called if the root bot is ending the conversation. Sending additional messages should be
// avoided as the conversation may have been deleted.
// Perform cleanup of resources if needed.
return Task.CompletedTask;
}
echo-skill-bot/bot.js
onUnrecognizedActivityType Gunakan metode untuk menambahkan logika akhir percakapan. Di handler, periksa apakah aktivitas type yang tidak dikenal sama dengan endOfConversation.
this.onEndOfConversation(async (context, next) => {
// This will be called if the root bot is ending the conversation. Sending additional messages should be
// avoided as the conversation may have been deleted.
// Perform cleanup of resources if needed.
// By calling next() you ensure that the next BotHandler is run.
await next();
});
echoSkillBot\EchoBot.java
protected CompletableFuture<Void> onEndOfConversationActivity(TurnContext turnContext) {
// This will be called if the root bot is ending the conversation. Sending
// additional messages should be
// avoided as the conversation may have been deleted.
// Perform cleanup of resources if needed.
return CompletableFuture.completedFuture(null);
}
echo-skill-bot/bot/echo_bot.py
async def on_end_of_conversation_activity(self, turn_context: TurnContext):
# This will be called if the root bot is ending the conversation. Sending additional messages should be
# avoided as the conversation may have been deleted.
# Perform cleanup of resources if needed.
pass
Validator klaim
Sampel ini menggunakan daftar penelepon yang diizinkan untuk validasi klaim. File konfigurasi keterampilan menentukan daftar. Objek validator kemudian membaca daftar.
Anda harus menambahkan validator klaim ke konfigurasi autentikasi. Klaim dievaluasi setelah header autentikasi. Kode validasi Anda harus melemparkan kesalahan atau pengecualian untuk menolak permintaan. Ada banyak alasan Anda mungkin ingin menolak permintaan yang diautentikasi sebaliknya. Contohnya:
Keterampilan ini adalah bagian dari layanan berbayar. Pengguna tidak berada dalam database seharusnya tidak memiliki akses.
Keterampilannya adalah kepemilikan. Hanya konsumen keterampilan tertentu yang dapat memanggil keterampilan.
Penting
Jika Anda tidak memberikan validator klaim, bot Anda akan menghasilkan kesalahan atau pengecualian saat menerima aktivitas dari konsumen keterampilan.
SDK menyediakan AllowedCallersClaimsValidator kelas yang menambahkan otorisasi tingkat aplikasi berdasarkan daftar ID sederhana aplikasi yang diizinkan untuk memanggil keterampilan. Jika daftar berisi tanda bintang (*), maka semua penelepon diizinkan. Validator klaim dikonfigurasi dalam Startup.cs.
SDK menyediakan allowedCallersClaimsValidator kelas yang menambahkan otorisasi tingkat aplikasi berdasarkan daftar ID sederhana aplikasi yang diizinkan untuk memanggil keterampilan. Jika daftar berisi tanda bintang (*), maka semua penelepon diizinkan. Validator klaim dikonfigurasi dalam index.js.
SDK menyediakan AllowedCallersClaimsValidator kelas yang menambahkan otorisasi tingkat aplikasi berdasarkan daftar ID sederhana aplikasi yang diizinkan untuk memanggil keterampilan. Jika daftar berisi tanda bintang (*), maka semua penelepon diizinkan. Validator klaim dikonfigurasi dalam Application.java.
Tentukan metode validasi klaim yang melemparkan kesalahan untuk menolak permintaan masuk.
class AllowedCallersClaimsValidator:
config_key = "ALLOWED_CALLERS"
def __init__(self, config: DefaultConfig):
if not config:
raise TypeError(
"AllowedCallersClaimsValidator: config object cannot be None."
)
# ALLOWED_CALLERS is the setting in config.py file
# that consists of the list of parent bot ids that are allowed to access the skill
# to add a new parent bot simply go to the AllowedCallers and add
# the parent bot's microsoft app id to the list
caller_list = getattr(config, self.config_key)
if caller_list is None:
raise TypeError(f'"{self.config_key}" not found in configuration.')
self._allowed_callers = frozenset(caller_list)
@property
def claims_validator(self) -> Callable[[List[Dict]], Awaitable]:
async def allow_callers_claims_validator(claims: Dict[str, object]):
# if allowed_callers is None we allow all calls
if "*" not in self._allowed_callers and SkillValidation.is_skill_claim(
claims
):
# Check that the appId claim in the skill request is in the list of skills configured for this bot.
app_id = JwtTokenValidation.get_app_id_from_claims(claims)
if app_id not in self._allowed_callers:
raise PermissionError(
f'Received a request from a bot with an app ID of "{app_id}".'
f" To enable requests from this caller, add the app ID to your configuration file."
)
return
return allow_callers_claims_validator
Adaptor keterampilan
Ketika kesalahan terjadi, adaptor keterampilan harus menghapus status percakapan untuk keterampilan, dan itu juga harus mengirim endOfConversation aktivitas ke konsumen keterampilan. Untuk memberi sinyal bahwa keterampilan berakhir karena kesalahan, gunakan properti kode aktivitas.
private async Task HandleTurnError(ITurnContext turnContext, Exception exception)
{
// Log any leaked exception from the application.
_logger.LogError(exception, $"[OnTurnError] unhandled error : {exception.Message}");
await SendErrorMessageAsync(turnContext, exception);
await SendEoCToParentAsync(turnContext, exception);
}
private async Task SendErrorMessageAsync(ITurnContext turnContext, Exception exception)
{
try
{
// Send a message to the user.
var errorMessageText = "The skill encountered an error or bug.";
var errorMessage = MessageFactory.Text(errorMessageText, errorMessageText, InputHints.IgnoringInput);
await turnContext.SendActivityAsync(errorMessage);
errorMessageText = "To continue to run this bot, please fix the bot source code.";
errorMessage = MessageFactory.Text(errorMessageText, errorMessageText, InputHints.ExpectingInput);
await turnContext.SendActivityAsync(errorMessage);
// Send a trace activity, which will be displayed in the Bot Framework Emulator.
// Note: we return the entire exception in the value property to help the developer;
// this should not be done in production.
await turnContext.TraceActivityAsync("OnTurnError Trace", exception.ToString(), "https://www.botframework.com/schemas/error", "TurnError");
}
catch (Exception ex)
{
_logger.LogError(ex, $"Exception caught in SendErrorMessageAsync : {ex}");
}
}
private async Task SendEoCToParentAsync(ITurnContext turnContext, Exception exception)
{
try
{
// Send an EndOfConversation activity to the skill caller with the error to end the conversation,
// and let the caller decide what to do.
var endOfConversation = Activity.CreateEndOfConversationActivity();
endOfConversation.Code = "SkillError";
endOfConversation.Text = exception.Message;
await turnContext.SendActivityAsync(endOfConversation);
}
catch (Exception ex)
{
_logger.LogError(ex, $"Exception caught in SendEoCToParentAsync : {ex}");
}
}
echo-skill-bot/index.js
// Catch-all for errors.
adapter.onTurnError = async (context, error) => {
// This check writes out errors to the console log, instead of to app insights.
// NOTE: In a production environment, you should consider logging this to Azure
// application insights.
console.error(`\n [onTurnError] unhandled error: ${ error }`);
await sendErrorMessage(context, error);
await sendEoCToParent(context, error);
};
async function sendErrorMessage(context, error) {
try {
// Send a message to the user.
let onTurnErrorMessage = 'The skill encountered an error or bug.';
await context.sendActivity(onTurnErrorMessage, onTurnErrorMessage, InputHints.ExpectingInput);
onTurnErrorMessage = 'To continue to run this bot, please fix the bot source code.';
await context.sendActivity(onTurnErrorMessage, onTurnErrorMessage, InputHints.ExpectingInput);
// Send a trace activity, which will be displayed in the Bot Framework Emulator.
// Note: we return the entire exception in the value property to help the developer;
// this should not be done in production.
await context.sendTraceActivity('OnTurnError Trace', error.toString(), 'https://www.botframework.com/schemas/error', 'TurnError');
} catch (err) {
console.error(`\n [onTurnError] Exception caught in sendErrorMessage: ${ err }`);
}
}
async function sendEoCToParent(context, error) {
try {
// Send an EndOfConversation activity to the skill caller with the error to end the conversation,
// and let the caller decide what to do.
const endOfConversation = {
type: ActivityTypes.EndOfConversation,
code: 'SkillError',
text: error.toString()
};
await context.sendActivity(endOfConversation);
} catch (err) {
console.error(`\n [onTurnError] Exception caught in sendEoCToParent: ${ err }`);
}
}
echoSkillBot\SkillAdapterWithErrorHandler.java
public SkillAdapterWithErrorHandler(
Configuration configuration,
AuthenticationConfiguration authenticationConfiguration
) {
super(configuration, authenticationConfiguration);
setOnTurnError(new SkillAdapterErrorHandler());
}
private class SkillAdapterErrorHandler implements OnTurnErrorHandler {
@Override
public CompletableFuture<Void> invoke(TurnContext turnContext, Throwable exception) {
return sendErrorMessage(turnContext, exception).thenAccept(result -> {
sendEoCToParent(turnContext, exception);
});
}
private CompletableFuture<Void> sendErrorMessage(TurnContext turnContext, Throwable exception) {
try {
// Send a message to the user.
String errorMessageText = "The skill encountered an error or bug.";
Activity errorMessage =
MessageFactory.text(errorMessageText, errorMessageText, InputHints.IGNORING_INPUT);
return turnContext.sendActivity(errorMessage).thenAccept(result -> {
String secondLineMessageText = "To continue to run this bot, please fix the bot source code.";
Activity secondErrorMessage =
MessageFactory.text(secondLineMessageText, secondLineMessageText, InputHints.EXPECTING_INPUT);
turnContext.sendActivity(secondErrorMessage)
.thenApply(
sendResult -> {
// Send a trace activity, which will be displayed in the Bot Framework Emulator.
// Note: we return the entire exception in the value property to help the
// developer;
// this should not be done in production.
return TurnContext.traceActivity(
turnContext,
String.format("OnTurnError Trace %s", exception.toString())
);
}
);
});
} catch (Exception ex) {
return Async.completeExceptionally(ex);
}
}
private CompletableFuture<Void> sendEoCToParent(TurnContext turnContext, Throwable exception) {
try {
// Send an EndOfConversation activity to the skill caller with the error to end
// the conversation,
// and let the caller decide what to do.
Activity endOfConversation = Activity.createEndOfConversationActivity();
endOfConversation.setCode(EndOfConversationCodes.SKILL_ERROR);
endOfConversation.setText(exception.getMessage());
return turnContext.sendActivity(endOfConversation).thenApply(result -> null);
} catch (Exception ex) {
return Async.completeExceptionally(ex);
}
}
}
echo-skill-bot/adapter_with_error_handler.py
# This check writes out errors to console log
# NOTE: In production environment, you should consider logging this to Azure
# application insights.
print(f"\n [on_turn_error] unhandled error: {error}", file=sys.stderr)
traceback.print_exc()
await self._send_error_message(turn_context, error)
await self._send_eoc_to_parent(turn_context, error)
async def _send_error_message(self, turn_context: TurnContext, error: Exception):
try:
# Send a message to the user.
error_message_text = "The skill encountered an error or bug."
error_message = MessageFactory.text(
error_message_text, error_message_text, InputHints.ignoring_input
)
await turn_context.send_activity(error_message)
error_message_text = (
"To continue to run this bot, please fix the bot source code."
)
error_message = MessageFactory.text(
error_message_text, error_message_text, InputHints.ignoring_input
)
await turn_context.send_activity(error_message)
# Send a trace activity, which will be displayed in Bot Framework Emulator.
await turn_context.send_trace_activity(
label="TurnError",
name="on_turn_error Trace",
value=f"{error}",
value_type="https://www.botframework.com/schemas/error",
)
except Exception as exception:
print(
f"\n Exception caught on _send_error_message : {exception}",
file=sys.stderr,
)
traceback.print_exc()
async def _send_eoc_to_parent(self, turn_context: TurnContext, error: Exception):
try:
# Send an EndOfConversation activity to the skill caller with the error to end the conversation,
# and let the caller decide what to do.
end_of_conversation = Activity(type=ActivityTypes.end_of_conversation)
end_of_conversation.code = "SkillError"
end_of_conversation.text = str(error)
await turn_context.send_activity(end_of_conversation)
except Exception as exception:
print(
f"\n Exception caught on _send_eoc_to_parent : {exception}",
file=sys.stderr,
)
traceback.print_exc()
Registrasi layanan
Adaptor Bot Framework menggunakan objek konfigurasi autentikasi (diatur saat adaptor dibuat) untuk memvalidasi header autentikasi pada permintaan masuk.
Sampel ini menambahkan validasi klaim ke konfigurasi autentikasi dan menggunakan adaptor keterampilan dengan penanganan kesalahan yang dijelaskan di bagian sebelumnya.
options.SerializerSettings.MaxDepth = HttpHelper.BotMessageSerializerSettings.MaxDepth;
});
// Register AuthConfiguration to enable custom claim validation.
services.AddSingleton(sp =>
{
var allowedCallers = new List<string>(sp.GetService<IConfiguration>().GetSection("AllowedCallers").Get<string[]>());
var claimsValidator = new AllowedCallersClaimsValidator(allowedCallers);
// If TenantId is specified in config, add the tenant as a valid JWT token issuer for Bot to Skill conversation.
// The token issuer for MSI and single tenant scenarios will be the tenant where the bot is registered.
var validTokenIssuers = new List<string>();
var tenantId = sp.GetService<IConfiguration>().GetSection(MicrosoftAppCredentials.MicrosoftAppTenantIdKey)?.Value;
if (!string.IsNullOrWhiteSpace(tenantId))
{
// For SingleTenant/MSI auth, the JWT tokens will be issued from the bot's home tenant.
// Therefore, these issuers need to be added to the list of valid token issuers for authenticating activity requests.
validTokenIssuers.Add(string.Format(CultureInfo.InvariantCulture, AuthenticationConstants.ValidTokenIssuerUrlTemplateV1, tenantId));
validTokenIssuers.Add(string.Format(CultureInfo.InvariantCulture, AuthenticationConstants.ValidTokenIssuerUrlTemplateV2, tenantId));
validTokenIssuers.Add(string.Format(CultureInfo.InvariantCulture, AuthenticationConstants.ValidGovernmentTokenIssuerUrlTemplateV1, tenantId));
validTokenIssuers.Add(string.Format(CultureInfo.InvariantCulture, AuthenticationConstants.ValidGovernmentTokenIssuerUrlTemplateV2, tenantId));
}
return new AuthenticationConfiguration
{
ClaimsValidator = claimsValidator,
ValidTokenIssuers = validTokenIssuers
};
});
// Create the Bot Framework Authentication to be used with the Bot Adapter.
services.AddSingleton<BotFrameworkAuthentication, ConfigurationBotFrameworkAuthentication>();
echo-skill-bot/index.js
const allowedCallers = (process.env.AllowedCallers || '').split(',').filter((val) => val) || [];
const claimsValidators = allowedCallersClaimsValidator(allowedCallers);
// If the MicrosoftAppTenantId is specified in the environment config, add the tenant as a valid JWT token issuer for Bot to Skill conversation.
// The token issuer for MSI and single tenant scenarios will be the tenant where the bot is registered.
let validTokenIssuers = [];
const { MicrosoftAppTenantId } = process.env;
if (MicrosoftAppTenantId) {
// For SingleTenant/MSI auth, the JWT tokens will be issued from the bot's home tenant.
// Therefore, these issuers need to be added to the list of valid token issuers for authenticating activity requests.
validTokenIssuers = [
`${ AuthenticationConstants.ValidTokenIssuerUrlTemplateV1 }${ MicrosoftAppTenantId }/`,
`${ AuthenticationConstants.ValidTokenIssuerUrlTemplateV2 }${ MicrosoftAppTenantId }/v2.0/`,
`${ AuthenticationConstants.ValidGovernmentTokenIssuerUrlTemplateV1 }${ MicrosoftAppTenantId }/`,
`${ AuthenticationConstants.ValidGovernmentTokenIssuerUrlTemplateV2 }${ MicrosoftAppTenantId }/v2.0/`
];
}
// Define our authentication configuration.
const authConfig = new AuthenticationConfiguration([], claimsValidators, validTokenIssuers);
const credentialsFactory = new ConfigurationServiceClientCredentialFactory({
MicrosoftAppId: process.env.MicrosoftAppId,
MicrosoftAppPassword: process.env.MicrosoftAppPassword,
MicrosoftAppType: process.env.MicrosoftAppType,
MicrosoftAppTenantId: process.env.MicrosoftAppTenantId
});
const botFrameworkAuthentication = new ConfigurationBotFrameworkAuthentication(process.env, credentialsFactory, authConfig);
// Create adapter.
// See https://aka.ms/about-bot-adapter to learn more about how bots work.
const adapter = new CloudAdapter(botFrameworkAuthentication);
echoSkillBot\Application.java
@Override
public AuthenticationConfiguration getAuthenticationConfiguration(Configuration configuration) {
AuthenticationConfiguration authenticationConfiguration = new AuthenticationConfiguration();
authenticationConfiguration.setClaimsValidator(
new AllowedCallersClaimsValidator(Arrays.asList(configuration.getProperties(configKey)))
);
return authenticationConfiguration;
}
echo-skill-bot/app.py
CLAIMS_VALIDATOR = AllowedCallersClaimsValidator(CONFIG)
AUTH_CONFIG = AuthenticationConfiguration(
claims_validator=CLAIMS_VALIDATOR.claims_validator
)
# Create adapter.
# See https://aka.ms/about-bot-adapter to learn more about how bots work.
SETTINGS = ConfigurationBotFrameworkAuthentication(
CONFIG,
auth_configuration=AUTH_CONFIG,
)
ADAPTER = AdapterWithErrorHandler(SETTINGS)
Manifes keterampilan
Manifes keterampilan adalah file JSON yang menjelaskan aktivitas yang dapat dilakukan keterampilan, parameter input dan outputnya, dan titik akhir keterampilan.
Manifes berisi informasi yang Anda butuhkan untuk mengakses keterampilan dari bot lain.
Versi skema terbaru adalah v2.1.
{
"$schema": "https://schemas.botframework.com/schemas/skills/skill-manifest-2.0.0.json",
"$id": "EchoSkillBot",
"name": "Echo Skill bot",
"version": "1.0",
"description": "This is a sample echo skill",
"publisherName": "Microsoft",
"privacyUrl": "https://echoskillbot.contoso.com/privacy.html",
"copyright": "Copyright (c) Microsoft Corporation. All rights reserved.",
"license": "",
"iconUrl": "https://echoskillbot.contoso.com/icon.png",
"tags": [
"sample",
"echo"
],
"endpoints": [
{
"name": "default",
"protocol": "BotFrameworkV3",
"description": "Default endpoint for the skill",
"endpointUrl": "http://echoskillbot.contoso.com/api/messages",
"msAppId": "00000000-0000-0000-0000-000000000000"
}
]
}
Skema manifes keterampilan adalah file JSON yang menjelaskan skema manifes keterampilan. Versi skema saat ini adalah 2.1.0.
Menguji keterampilan
Pada titik ini, Anda dapat menguji keterampilan di Emulator seolah-olah itu adalah bot normal. Namun, untuk mengujinya sebagai keterampilan, Anda harus menerapkan konsumen keterampilan.
Unduh dan instal Emulator Kerangka Kerja Bot terbaru
Jalankan bot keterampilan gema secara lokal di komputer Anda. Jika Anda memerlukan instruksi, lihat README file untuk sampel C#, JavaScript, Java, atau Python .
Gunakan Emulator untuk menguji bot. Saat Anda mengirim pesan "akhir" atau "hentikan" ke keterampilan, pesan tersebut mengirimkan endOfConversation aktivitas selain pesan balasan. Keterampilan mengirimkan endOfConversation aktivitas untuk menunjukkan keterampilan selesai.
Selengkapnya tentang penelusuran kesalahan
Karena lalu lintas antara keterampilan dan konsumen keterampilan diautentikasi, ada langkah tambahan saat men-debug bot tersebut.
Konsumen keterampilan dan semua keterampilan yang dikonsumsinya, secara langsung atau tidak langsung, harus berjalan.
Jika bot berjalan secara lokal dan jika salah satu bot memiliki ID aplikasi dan kata sandi, semua bot harus memiliki ID dan kata sandi yang valid.
Jika tidak, Anda dapat men-debug konsumen keterampilan atau keterampilan seperti Anda men-debug bot lain. Untuk informasi selengkapnya, lihat Men-debug bot dan Debug dengan Emulator Kerangka Kerja Bot.