Możesz użyć umiejętności, aby rozszerzyć innego bota.
Umiejętność to bot, który może wykonywać zestaw zadań dla innego bota.
Manifest opisuje interfejs umiejętności. Deweloperzy, którzy nie mają dostępu do kodu źródłowego umiejętności, mogą używać informacji w manifeście, aby zaprojektować swojego konsumenta umiejętności.
Umiejętność może używać weryfikacji oświadczeń do zarządzania, które boty lub użytkownicy mogą uzyskać do niego dostęp.
W tym artykule pokazano, jak zaimplementować umiejętność, która odzwierciedla dane wejściowe użytkownika.
Niektóre typy konsumentów umiejętności nie mogą używać niektórych typów botów umiejętności.
W poniższej tabeli opisano, które kombinacje są obsługiwane.
Umiejętność obsługi wielu dzierżaw
Umiejętność z jedną dzierżawą
Umiejętność tożsamości zarządzanej przypisanej przez użytkownika
Użytkownik z wieloma dzierżawami
Obsługiwane
Nieobsługiwane
Nieobsługiwane
Użytkownik z jedną dzierżawą
Nieobsługiwane
Obsługiwane, jeśli obie aplikacje należą do tej samej dzierżawy
Obsługiwane, jeśli obie aplikacje należą do tej samej dzierżawy
Odbiorca tożsamości zarządzanej przypisanej przez użytkownika
Nieobsługiwane
Obsługiwane, jeśli obie aplikacje należą do tej samej dzierżawy
Obsługiwane, jeśli obie aplikacje należą do tej samej dzierżawy
Uwaga
Zestawy SDK języka JavaScript, C# i Python platformy Bot Framework będą nadal obsługiwane, jednak zestaw SDK języka Java jest wycofywany z ostatecznym długoterminowym wsparciem kończącym się w listopadzie 2023 r.
Istniejące boty utworzone za pomocą zestawu JAVA SDK będą nadal działać.
Począwszy od wersji 4.11, nie potrzebujesz identyfikatora aplikacji i hasła, aby przetestować umiejętności lokalnie w emulatorze bot framework. Subskrypcja platformy Azure jest nadal wymagana do wdrożenia umiejętności na platformie Azure.
Informacje o tym przykładzie
Przykładowe umiejętności prostego bota do bota obejmują projekty dla dwóch botów:
Bot umiejętności echo, który implementuje umiejętności.
Prosty bot główny, który implementuje bota głównego, który korzysta z umiejętności.
Ten artykuł koncentruje się na umiejętnościach, które obejmują logikę obsługi w bota i adapterze.
Aby uzyskać informacje na temat prostego bota głównego, zobacz implementowanie konsumenta umiejętności.
Zasoby
W przypadku wdrożonych botów uwierzytelnianie bot-to-bot wymaga, aby każdy uczestniczący bot miał prawidłowe informacje o tożsamości.
Możesz jednak przetestować wielodostępne umiejętności i umiejętności konsumentów lokalnie za pomocą emulatora bez identyfikatora aplikacji i hasła.
Aby udostępnić umiejętności botom dostępnym dla użytkowników, zarejestruj umiejętności na platformie Azure. Aby uzyskać więcej informacji, zobacz jak zarejestrować bota w usłudze Azure AI Bot Service.
Konfiguracja aplikacji
Opcjonalnie dodaj informacje o tożsamości umiejętności do pliku konfiguracji. Jeśli użytkownik umiejętności lub umiejętności dostarcza informacje o tożsamości, oba muszą.
Dozwolona tablica rozmówców może ograniczyć, którzy użytkownicy umiejętności mogą uzyskiwać dostęp do umiejętności.
Aby zaakceptować wywołania od dowolnego konsumenta umiejętności, dodaj element "*".
Uwaga
Jeśli testujesz umiejętności lokalnie bez informacji o tożsamości bota, ani umiejętności, ani użytkownik umiejętności nie uruchamia kodu w celu przeprowadzenia weryfikacji oświadczeń.
Opcjonalnie dodaj informacje o tożsamości umiejętności do pliku 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
Opcjonalnie dodaj informacje o tożsamości umiejętności do pliku env.
Opcjonalnie dodaj identyfikator aplikacji i hasło umiejętności do pliku 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=*
Opcjonalnie dodaj identyfikator aplikacji i hasło umiejętności do pliku 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 obsługi działań
Aby zaakceptować parametry wejściowe
Użytkownik umiejętności może wysyłać informacje do umiejętności. Jednym ze sposobów akceptowania takich informacji jest akceptowanie ich za pośrednictwem właściwości value w komunikatach przychodzących. Innym sposobem jest obsługa zdarzeń i wywoływanie działań.
Umiejętność w tym przykładzie nie akceptuje parametrów wejściowych.
Aby kontynuować lub ukończyć konwersację
Gdy umiejętności wysyłają działanie, użytkownik umiejętności powinien przekazać działanie użytkownikowi.
Jednak musisz wysłać endOfConversation działanie po zakończeniu umiejętności. W przeciwnym razie użytkownik umiejętności nadal przekazuje działania użytkownika do umiejętności.
Opcjonalnie użyj właściwości value działania, aby uwzględnić wartość zwracaną, i użyj właściwości kodu działania, aby wskazać, dlaczego umiejętności kończą się.
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.'
)
)
Aby anulować umiejętności
W przypadku umiejętności obejmujących wiele kolei możesz również zaakceptować endOfConversation działania od konsumenta umiejętności, aby umożliwić konsumentowi anulowanie bieżącej konwersacji.
Logika tej umiejętności nie zmienia się z kolei na kolei. Jeśli zaimplementujesz umiejętności przydzielające zasoby konwersacji, dodaj kod oczyszczania zasobów do procedury obsługi końcowej konwersacji.
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 Użyj metody , aby dodać logikę końca konwersacji. W procedurze obsługi sprawdź, czy nierozpoznane działanie jest type równe 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
Moduł sprawdzania poprawności oświadczeń
W tym przykładzie użyto listy dozwolonych wywołań na potrzeby weryfikacji oświadczeń. Plik konfiguracji umiejętności definiuje listę. Następnie obiekt modułu sprawdzania poprawności odczytuje listę.
Do konfiguracji uwierzytelniania należy dodać moduł sprawdzania poprawności oświadczeń. Oświadczenia są oceniane po nagłówku uwierzytelniania. Kod weryfikacji powinien zgłosić błąd lub wyjątek, aby odrzucić żądanie. Istnieje wiele powodów, dla których można odrzucić w inny sposób uwierzytelnione żądanie. Na przykład:
Umiejętność jest częścią płatnej usługi. Użytkownik, który nie znajduje się w bazie danych, nie powinien mieć dostępu.
Umiejętność jest własnością. Tylko niektórzy konsumenci umiejętności mogą nazwać umiejętności.
Ważne
Jeśli nie podasz modułu sprawdzania poprawności oświadczeń, bot wygeneruje błąd lub wyjątek po otrzymaniu działania od konsumenta umiejętności.
Zestaw SDK udostępnia klasę AllowedCallersClaimsValidator , która dodaje autoryzację na poziomie aplikacji na podstawie prostej listy identyfikatorów aplikacji, które mogą wywoływać umiejętności. Jeśli lista zawiera gwiazdkę (*), wszystkie osoby wywołujące są dozwolone. Moduł sprawdzania poprawności oświadczeń jest skonfigurowany w Startup.cs.
Zestaw SDK udostępnia klasę allowedCallersClaimsValidator , która dodaje autoryzację na poziomie aplikacji na podstawie prostej listy identyfikatorów aplikacji, które mogą wywoływać umiejętności. Jeśli lista zawiera gwiazdkę (*), wszystkie osoby wywołujące są dozwolone. Moduł sprawdzania poprawności oświadczeń jest skonfigurowany w index.js.
Zestaw SDK udostępnia klasę AllowedCallersClaimsValidator , która dodaje autoryzację na poziomie aplikacji na podstawie prostej listy identyfikatorów aplikacji, które mogą wywoływać umiejętności. Jeśli lista zawiera gwiazdkę (*), wszystkie osoby wywołujące są dozwolone. Moduł sprawdzania poprawności oświadczeń jest skonfigurowany w Application.java.
Zdefiniuj metodę weryfikacji oświadczeń, która zgłasza błąd w celu odrzucenia żądania przychodzącego.
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
Karta umiejętności
W przypadku wystąpienia błędu adapter umiejętności powinien wyczyścić stan konwersacji dla umiejętności i powinien również wysłać endOfConversation działanie do konsumenta umiejętności. Aby zasygnalizować, że umiejętności zakończyły się z powodu błędu, użyj właściwości kodu działania.
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()
Rejestracja usługi
Karta Bot Framework używa obiektu konfiguracji uwierzytelniania (ustawionego podczas tworzenia karty) w celu zweryfikowania nagłówka uwierzytelniania na żądaniach przychodzących.
Ten przykład dodaje weryfikację oświadczeń do konfiguracji uwierzytelniania i używa karty umiejętności z procedurą obsługi błędów opisaną w poprzedniej sekcji.
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 umiejętności
Manifest umiejętności to plik JSON opisujący działania, które mogą wykonywać umiejętności, jego parametry wejściowe i wyjściowe oraz punkty końcowe umiejętności.
Manifest zawiera informacje potrzebne do uzyskania dostępu do umiejętności innego bota.
Najnowsza wersja schematu to wersja 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"
}
]
}
Schemat manifestu umiejętności to plik JSON opisujący schemat manifestu umiejętności. Bieżąca wersja schematu to 2.1.0.
Testowanie umiejętności
W tym momencie możesz przetestować umiejętności w emulatorze tak, jakby był to normalny bot. Jednak aby przetestować go jako umiejętności, musisz zaimplementować konsumenta umiejętności.
Uruchom bota umiejętności echa lokalnie na maszynie. Jeśli potrzebujesz instrukcji, zapoznaj się z plikiem README przykładowym języka C#, JavaScript, Java lub Python .
Użyj emulatora, aby przetestować bota. Po wysłaniu komunikatu "end" lub "stop" do umiejętności wysyła endOfConversation działanie oprócz wiadomości odpowiedzi. Umiejętność wysyła działanie, endOfConversation aby wskazać, że umiejętności zostały zakończone.
Więcej informacji o debugowaniu
Ponieważ ruch między umiejętnościami i użytkownikami umiejętności jest uwierzytelniany, podczas debugowania takich botów są wykonywane dodatkowe kroki.
Użytkownik umiejętności i wszystkie umiejętności, których używa, bezpośrednio lub pośrednio, muszą być uruchomione.
Jeśli boty działają lokalnie i jeśli którykolwiek z botów ma identyfikator aplikacji i hasło, wszystkie boty muszą mieć prawidłowe identyfikatory i hasła.
W przeciwnym razie możesz debugować użytkownika umiejętności lub umiejętności podobne do debugowania innych botów. Aby uzyskać więcej informacji, zobacz Debugowanie bota i Debugowanie za pomocą emulatora platformy Bot Framework.