Dovednosti můžete využít k rozšíření dalšího robota.
Dovednost je robot, který může provádět sadu úkolů pro jiného robota.
Manifest popisuje rozhraní dovednosti. Vývojáři, kteří nemají přístup ke zdrojovému kódu dovednosti, můžou informace v manifestu použít k návrhu příjemce dovedností.
Dovednost může pomocí ověřování deklarací identity spravovat roboty nebo uživatele, kteří k němu mají přístup.
Tento článek ukazuje, jak implementovat dovednost, která odpovídá vstupu uživatele.
Některé typy uživatelů dovedností nemůžou používat některé typy robotů dovedností.
Následující tabulka popisuje podporované kombinace.
Podporováno, pokud obě aplikace patří do stejného tenanta
Podporováno, pokud obě aplikace patří do stejného tenanta
Příjemce spravované identity přiřazené uživatelem
Nepodporováno
Podporováno, pokud obě aplikace patří do stejného tenanta
Podporováno, pokud obě aplikace patří do stejného tenanta
Poznámka:
Sady SDK služby Bot Framework JavaScript, C# a Python budou nadále podporovány, ale sada Java SDK se vyřazuje s konečnou dlouhodobou podporou končící v listopadu 2023.
Stávající roboti sestavení pomocí sady Java SDK budou i nadále fungovat.
Předplatné Azure (pro nasazení dovednosti) Pokud ho nemáte, vytvořte si bezplatný účet před tím, než začnete.
Kopie ukázky jednoduchých dovedností robota k robotovi v jazyce C#, JavaScript, Java nebo Python
Poznámka:
Počínaje verzí 4.11 nepotřebujete ID a heslo aplikace k místnímu testování dovednosti v bot Framework Emulatoru. K nasazení dovedností do Azure se stále vyžaduje předplatné Azure.
O této ukázce
Ukázka jednoduchých dovedností robota-robota zahrnuje projekty pro dva roboty:
Robot dovedností echo, který implementuje dovednosti.
Jednoduchý kořenový robot, který implementuje kořenového robota, který tuto dovednost využívá.
Tento článek se zaměřuje na dovednost, která zahrnuje logiku podpory v robotovi a adaptéru.
U nasazených robotů vyžaduje ověřování robota k robotovi, aby měl každý účastník platné informace o identitě.
Můžete ale otestovat víceklientských dovedností a uživatele dovedností místně pomocí emulátoru bez ID a hesla aplikace.
Volitelně můžete do konfiguračního souboru přidat informace o identitě dovednosti. Pokud dovednost nebo spotřebitel dovedností poskytuje informace o identitě, musí být obojí.
Pole povolených volajících může omezit, ke kterým dovednostem mají uživatelé přístup.
Pokud chcete přijímat volání od libovolného příjemce dovedností, přidejte prvek "*".
Poznámka:
Pokud testujete svoji dovednost místně bez informací o identitě robota, dovednost ani uživatel dovednosti nespustí kód k ověření deklarací identity.
Volitelně můžete do souboru appsettings.json přidat informace o identitě dovednosti.
{
"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
Volitelně můžete do souboru .env přidat informace o identitě dovednosti.
Volitelně můžete do souboru application.properties přidat ID aplikace a heslo dovednosti.
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=*
Volitelně můžete do souboru config.py přidat ID aplikace a heslo dovednosti.
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 obslužné rutiny aktivity
Přijetí vstupních parametrů
Spotřebitel dovedností může posílat informace do dovednosti. Jedním ze způsobů, jak tyto informace přijmout, je přijmout je prostřednictvím vlastnosti hodnoty příchozích zpráv. Dalším způsobem je zpracování událostí a vyvolání aktivit.
Dovednost v tomto příkladu nepřijímá vstupní parametry.
Pokračování nebo dokončení konverzace
Když dovednost odešle aktivitu, uživatel dovednosti by měl aktivitu předat uživateli.
Když se ale dovednost dokončí, musíte odeslat endOfConversation aktivitu. Jinak uživatel dovednosti bude dál předávat aktivity uživatelů do dovednosti.
Volitelně můžete použít vlastnost hodnoty aktivity k zahrnutí návratové hodnoty a pomocí vlastnosti kódu aktivity určit, proč dovednost končí.
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/bots/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.'
)
)
Zrušení dovednosti
V případě vícenásobných dovedností byste také přijali endOfConversation aktivity od příjemce dovedností, aby spotřebitel mohl aktuální konverzaci zrušit.
Logika pro tuto dovednost se nemění z střídmého otáčení. Pokud implementujete dovednost, která přiděluje prostředky konverzace, přidejte do obslužné rutiny konverzace kód čištění prostředků.
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 Metoda slouží k přidání logiky ukončení konverzace. V obslužné rutině zkontrolujte, zda se nerozpoznaná aktivita type rovná 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/bots/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
Validátor deklarací identity
Tato ukázka používá seznam povolených volajících k ověření deklarací identity. Konfigurační soubor dovednosti definuje seznam. Objekt validátoru pak přečte seznam.
Do konfigurace ověřování musíte přidat validátor deklarací identity. Deklarace identity se vyhodnocují za hlavičkou ověřování. Ověřovací kód by měl vyvolat chybu nebo výjimku, aby požadavek odmítl. Existuje mnoho důvodů, proč byste mohli chtít zamítnout jinak ověřený požadavek. Příklad:
Dovednost je součástí placené služby. Uživatel není v databázi by neměl mít přístup.
Dovednost je proprietární. Dovednost může zavolat pouze někteří spotřebitelé dovedností.
Důležité
Pokud nezadáte validátor deklarací identity, robot při přijetí aktivity od příjemce dovednosti vygeneruje chybu nebo výjimku.
Sada SDK poskytuje AllowedCallersClaimsValidator třídu, která přidává autorizaci na úrovni aplikace na základě jednoduchého seznamu ID aplikací, které mohou volat dovednosti. Pokud seznam obsahuje hvězdičku (*), jsou všichni volající povoleni. Validátor deklarací identity je nakonfigurovaný v Startup.cs.
Sada SDK poskytuje allowedCallersClaimsValidator třídu, která přidává autorizaci na úrovni aplikace na základě jednoduchého seznamu ID aplikací, které mohou volat dovednosti. Pokud seznam obsahuje hvězdičku (*), jsou všichni volající povoleni. Validátor deklarací identity je nakonfigurovaný v index.js.
Sada SDK poskytuje AllowedCallersClaimsValidator třídu, která přidává autorizaci na úrovni aplikace na základě jednoduchého seznamu ID aplikací, které mohou volat dovednosti. Pokud seznam obsahuje hvězdičku (*), jsou všichni volající povoleni. Validátor deklarací identity je nakonfigurovaný v Application.java.
Definujte metodu ověřování deklarací identity, která vyvolá chybu, která odmítne příchozí požadavek.
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
Adaptér dovedností
Když dojde k chybě, adaptér dovednosti by měl vymazat stav konverzace dovednosti a měl by také odeslat endOfConversation aktivitu příjemci dovednosti. Pokud chcete signalizovat, že dovednost skončila kvůli chybě, použijte vlastnost kódu aktivity.
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()
Registrace služby
Adaptér Bot Framework používá objekt konfigurace ověřování (nastavený při vytvoření adaptéru) k ověření hlavičky ověřování u příchozích požadavků.
Tato ukázka přidává ověřování deklarací identity do konfigurace ověřování a používá adaptér dovedností s obslužnou rutinou chyb popsanou v předchozí části.
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)
Manifest dovednosti
Manifest dovednosti je soubor JSON, který popisuje aktivity, které může dovednost provádět, její vstupní a výstupní parametry a koncové body dovednosti.
Manifest obsahuje informace, které potřebujete pro přístup ke dovednosti z jiného robota.
Nejnovější verze schématu je verze 2.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"
}
]
}
Schéma manifestu dovedností je soubor JSON, který popisuje schéma manifestu dovednosti. Aktuální verze schématu je 2.1.0.
Otestování dovednosti
V tomto okamžiku můžete otestovat dovednosti v emulátoru, jako by šlo o normálního robota. Pokud ho ale chcete otestovat jako dovednost, musíte implementovat dovednost spotřebitele.
Spusťte robota dovedností echo místně na svém počítači. Pokud potřebujete pokyny, projděte si README soubor ukázky C#, JavaScriptu, Javy nebo Pythonu .
K otestování robota použijte emulátor. Když dovednost pošlete zprávu "end" nebo "stop", odešle endOfConversation aktivitu kromě zprávy odpovědi. Dovednost odešle endOfConversation aktivitu, která označuje, že dovednost je dokončena.
Další informace o ladění
Vzhledem k tomu, že se ověřuje provoz mezi dovednostmi a uživateli dovedností, při ladění takových robotů existují další kroky.
Dovednost konzument a všechny dovednosti, které využívá, přímo nebo nepřímo, musí být spuštěné.
Pokud jsou roboti spuštěni místně a pokud některý z robotů má ID a heslo aplikace, musí mít všichni roboti platné ID a hesla.