Introdução à biblioteca de IA do Teams
A biblioteca de IA do Teams simplifica o processo de criação de aplicações inteligentes do Microsoft Teams com os componentes de IA. Fornece APIs para aceder e manipular dados, bem como uma variedade de controlos e componentes para criar interfaces de utilizador personalizadas.
Pode integrar facilmente a biblioteca de IA do Teams, a gestão de pedidos e a moderação de segurança nas suas aplicações e melhorar a experiência do utilizador. Também facilita a criação de bots que utilizam uma chave de API OpenAI ou o Azure OpenAI para proporcionar uma experiência de conversação orientada por IA.
Configuração inicial
A biblioteca de IA do Teams baseia-se no SDK do Bot Framework e utiliza as suas noções básicas para oferecer uma extensão às capacidades do SDK do Bot Framework. Como parte da configuração inicial, é importante importar as funcionalidades do SDK do Bot Framework.
Observação
A classe de adaptador que processa a conectividade com os canais é importada do SDK do Bot Framework.
Referência de código de exemplo
using Microsoft.Teams.AI;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.TeamsFx.Conversation;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddHttpClient("WebClient", client => client.Timeout = TimeSpan.FromSeconds(600));
builder.Services.AddHttpContextAccessor();
// Prepare Configuration for ConfigurationBotFrameworkAuthentication
var config = builder.Configuration.Get<ConfigOptions>();
builder.Configuration["MicrosoftAppType"] = "MultiTenant";
builder.Configuration["MicrosoftAppId"] = config.BOT_ID;
builder.Configuration["MicrosoftAppPassword"] = config.BOT_PASSWORD;
// Create the Bot Framework Authentication to be used with the Bot Adapter.
builder.Services.AddSingleton<BotFrameworkAuthentication, ConfigurationBotFrameworkAuthentication>();
// Create the Cloud Adapter with error handling enabled.
// Note: some classes expect a BotAdapter and some expect a BotFrameworkHttpAdapter, so
// register the same adapter instance for all types.
builder.Services.AddSingleton<CloudAdapter, AdapterWithErrorHandler>();
builder.Services.AddSingleton<IBotFrameworkHttpAdapter>(sp => sp.GetService<CloudAdapter>());
builder.Services.AddSingleton<BotAdapter>(sp => sp.GetService<CloudAdapter>());
Importar biblioteca de IA do Teams
Importe todas as classes de @microsoft/teams-ai
para criar o bot e utilize as capacidades da biblioteca de IA do Teams.
Referência de código de exemplo
// import Teams AI library
import {
AI,
Application,
ActionPlanner,
OpenAIModerator,
OpenAIModel,
PromptManager,
TurnState
} from '@microsoft/teams-ai';
import { addResponseFormatter } from './responseFormatter';
import { VectraDataSource } from './VectraDataSource';
Criar componentes de IA
Adicione capacidades de IA à sua aplicação existente ou a uma nova aplicação do Bot Framework.
OpenAIModel: a classe OpenAIModel fornece uma forma de aceder à API OpenAI ou a qualquer outro serviço, que cumpre o formato REST OpenAI. É compatível com os modelos de linguagem OpenAI e Azure OpenAI.
Gestor de pedidos: o gestor de pedidos gere a criação de pedidos. Chama funções e injeta do seu código na linha de comandos. Copia o estado da conversação e o estado do utilizador para o pedido automaticamente.
ActionPlanner: O ActionPlanner é o componente main que chama o seu Modelo de Linguagem Grande (LLM) e inclui várias funcionalidades para melhorar e personalizar o seu modelo. É responsável por gerar e executar planos com base na entrada do utilizador e nas ações disponíveis.
Referência de código de exemplo.
// Create model
OpenAIModel? model = null;
if (!string.IsNullOrEmpty(config.OpenAI?.ApiKey))
{
model = new(new OpenAIModelOptions(config.OpenAI.ApiKey, "gpt-3.5-turbo"));
}
else if (!string.IsNullOrEmpty(config.Azure?.OpenAIApiKey) && !string.IsNullOrEmpty(config.Azure.OpenAIEndpoint))
{
model = new(new AzureOpenAIModelOptions(
config.Azure.OpenAIApiKey,
"gpt-35-turbo",
config.Azure.OpenAIEndpoint
));
}
if (model == null)
{
throw new Exception("please configure settings for either OpenAI or Azure");
}
// Create prompt manager
PromptManager prompts = new(new()
{
PromptFolder = "./Prompts",
});
// Add function to be referenced in the prompt template
prompts.AddFunction("getLightStatus", async (context, memory, functions, tokenizer, args) =>
{
bool lightsOn = (bool)(memory.GetValue("conversation.lightsOn") ?? false);
return await Task.FromResult(lightsOn ? "on" : "off");
});
// Create ActionPlanner
ActionPlanner<AppState> planner = new(
options: new(
model: model,
prompts: prompts,
defaultPrompt: async (context, state, planner) =>
{
PromptTemplate template = prompts.GetPrompt("sequence");
return await Task.FromResult(template);
}
)
{ LogRepairs = true },
loggerFactory: loggerFactory
);
Definir armazenamento e aplicação
O objeto de aplicação gere automaticamente a conversação e o estado do utilizador do bot.
Armazenamento: crie um fornecedor de armazenamento para armazenar a conversação e o estado do utilizador do bot.
Aplicação: a classe de aplicação tem todas as informações e lógica de bot necessárias para uma aplicação. Pode registar ações ou processadores de atividades para a aplicação nesta classe.
Referência de código de exemplo
return new TeamsLightBot(new()
{
Storage = sp.GetService<IStorage>(),
AI = new(planner),
LoggerFactory = loggerFactory,
TurnStateFactory = () =>
{
return new AppState();
}
});
TurnStateFactory
permite-lhe criar uma classe de estado personalizada para a sua aplicação. Pode utilizá-la para armazenar informações ou lógicas adicionais necessárias para o bot. Também pode substituir algumas das propriedades predefinidas do estado de viragem, como a entrada do utilizador, a saída do bot ou o histórico de conversações. Para utilizar TurnStateFactory
o , tem de criar uma classe que expanda o estado de viragem predefinido e transmita uma função que cria uma instância da sua classe para o construtor da aplicação.
Registar origens de dados
Uma origem de dados de vetor facilita a adição de RAG a qualquer pedido. Pode registar uma origem de dados com nome no planner e, em seguida, especificar o nome[s] das origens de dados para aumentar o pedido no ficheiro da linha de config.json
comandos. As origens de dados permitem que a IA injete informações relevantes de origens externas no pedido, como bases de dados de vetores ou pesquisa cognitiva. Pode registar origens de dados nomeadas com o planeador e, em seguida, especificar o[s] nome[s] das origens de dados que pretendem aumentar a linha de comandos no ficheiro da linha de config.json
comandos.
Referência de código de exemplo
// Register your data source with planner
planner.prompts.addDataSource(new VectraDataSource({
name: 'teams-ai',
apiKey: process.env.OPENAI_API_KEY!,
indexFolder: path.join(__dirname, '../index'),
}));
Incorporações
Uma Incorporação é uma espécie de Vetor gerado por um LLM que representa uma parte do texto. O texto pode ser uma palavra, frase ou um documento inteiro. Uma vez que o modelo compreende a sintaxe e a semântica da linguagem, a Incorporação pode capturar o significado semântico do texto numa forma compacta. As incorporações são frequentemente utilizadas em tarefas de processamento de linguagem natural, como a classificação de textos ou a análise de sentimentos, mas também são utilizadas para pesquisa.
O modelo para gerar Incorporações é diferente dos LLMs fundamentais. Por exemplo, o OpenAI fornece um modelo de incorporação denominado text-embedding-ada-002, que devolve uma lista de 1536 números que representa o texto de entrada. O sistema cria incorporações para texto nos documentos e armazena-as numa Base de Dados de Vetores. Agora, a partir da nossa aplicação de Chat, podemos implementar o padrão RAG ao obter primeiro dados relevantes sobre os documentos a partir da Base de Dados de Vetores e, em seguida, ao aumentar o Prompt com estas informações obtidas.
Segue-se um exemplo de vectraDataSource e OpenAIEmbeddings:
import { DataSource, Memory, RenderedPromptSection, Tokenizer } from '@microsoft/teams-ai';
import { OpenAIEmbeddings, LocalDocumentIndex } from 'vectra';
import * as path from 'path';
import { TurnContext } from 'botbuilder';
/**
* Options for creating a `VectraDataSource`.
*/
export interface VectraDataSourceOptions {
/**
* Name of the data source and local index.
*/
name: string;
/**
* OpenAI API key to use for generating embeddings.
*/
apiKey: string;
/**
* Path to the folder containing the local index.
* @remarks
* This should be the root folder for all local indexes and the index itself
* needs to be in a subfolder under this folder.
*/
indexFolder: string;
/**
* Optional. Maximum number of documents to return.
* @remarks
* Defaults to `5`.
*/
maxDocuments?: number;
/**
* Optional. Maximum number of chunks to return per document.
* @remarks
* Defaults to `50`.
*/
maxChunks?: number;
/**
* Optional. Maximum number of tokens to return per document.
* @remarks
* Defaults to `600`.
*/
maxTokensPerDocument?: number;
}
/**
* A data source that uses a local Vectra index to inject text snippets into a prompt.
*/
export class VectraDataSource implements DataSource {
private readonly _options: VectraDataSourceOptions;
private readonly _index: LocalDocumentIndex;
/**
* Name of the data source.
* @remarks
* This is also the name of the local Vectra index.
*/
public readonly name: string;
/**
* Creates a new `VectraDataSource` instance.
* @param options Options for creating the data source.
*/
public constructor(options: VectraDataSourceOptions) {
this._options = options;
this.name = options.name;
// Create embeddings model
const embeddings = new OpenAIEmbeddings({
model: 'text-embedding-ada-002',
apiKey: options.apiKey,
});
// Create local index
this._index = new LocalDocumentIndex({
embeddings,
folderPath: path.join(options.indexFolder, options.name),
});
}
/**
* Renders the data source as a string of text.
* @param context Turn context for the current turn of conversation with the user.
* @param memory An interface for accessing state values.
* @param tokenizer Tokenizer to use when rendering the data source.
* @param maxTokens Maximum number of tokens allowed to be rendered.
*/
public async renderData(context: TurnContext, memory: Memory, tokenizer: Tokenizer, maxTokens: number): Promise<RenderedPromptSection<string>> {
// Query index
const query = memory.getValue('temp.input') as string;
const results = await this._index.queryDocuments(query, {
maxDocuments: this._options.maxDocuments ?? 5,
maxChunks: this._options.maxChunks ?? 50,
});
// Add documents until you run out of tokens
let length = 0;
let output = '';
let connector = '';
for (const result of results) {
// Start a new doc
let doc = `${connector}url: ${result.uri}\n`;
let docLength = tokenizer.encode(doc).length;
const remainingTokens = maxTokens - (length + docLength);
if (remainingTokens <= 0) {
break;
}
// Render document section
const sections = await result.renderSections(Math.min(remainingTokens, this._options.maxTokensPerDocument ?? 600), 1);
docLength += sections[0].tokenCount;
doc += sections[0].text;
// Append do to output
output += doc;
length += docLength;
connector = '\n\n';
}
return { output, length, tooLong: length > maxTokens };
}
}
Prompt
Os pedidos são partes de texto que podem ser utilizadas para criar experiências de conversação. Os pedidos são utilizados para iniciar conversações, fazer perguntas e gerar respostas. A utilização de pedidos ajuda a reduzir a complexidade da criação de experiências de conversação e a torná-las mais envolventes para o utilizador.
Um novo sistema de pedidos baseado em objetos divide um pedido em secções e cada secção pode receber um orçamento de token que é um conjunto fixo de tokens ou proporcional aos tokens restantes gerais. Pode gerar pedidos para as APIs de estilo Conclusão de Texto e Conclusão de Chat.
Seguem-se algumas diretrizes para criar pedidos:
- Forneça instruções, exemplos ou ambos.
- Forneça dados de qualidade. Certifique-se de que existem exemplos suficientes e verifique os seus exemplos. O modelo é inteligente o suficiente para ver os erros ortográficos básicos e dar-lhe uma resposta, mas também pode assumir que a entrada é intencional e pode afetar a resposta.
- Verifique as definições de pedido. As definições de temperatura e top_p controlam o quão determinista o modelo está na geração de uma resposta. Um valor mais elevado, como 0,8, torna a saída aleatória, enquanto o valor mais baixo, como 0,2, torna a saída focada e determinista.
Crie uma pasta denominada prompts e defina os pedidos na pasta . Quando o utilizador interage com o bot ao introduzir um pedido de texto, o bot responde com uma conclusão de texto.
skprompt.txt
: contém o texto dos pedidos e suporta variáveis e funções de modelo. Defina todos os pedidos de texto noskprompt.txt
ficheiro.config.json
: contém as definições do modelo de pedido. Forneça a configuração correta para garantir que as respostas do bot estão alinhadas com o seu requisito.Referência de código de exemplo
{ "schema": 1.1, "description": "A bot that can turn the lights on and off", "type": "completion", "completion": { "model": "gpt-3.5-turbo", "completion_type": "chat", "include_history": true, "include_input": true, "max_input_tokens": 2800, "max_tokens": 1000, "temperature": 0.2, "top_p": 0.0, "presence_penalty": 0.6, "frequency_penalty": 0.0, "stop_sequences": [] }, "augmentation": { "augmentation_type": "sequence" "data_sources": { "teams-ai": 1200 } } }
Parâmetros de consulta
A tabela a seguir inclui os parâmetros de consulta:
Valor | Descrição |
---|---|
model |
ID do modelo a utilizar. |
completion_type |
O tipo de conclusão que pretende utilizar para o seu modelo. Tendo em conta um pedido, o modelo devolverá uma ou mais conclusões previstas, juntamente com as probabilidades de tokens alternativos em cada posição. As opções suportadas são chat e text . O padrão é chat . |
include_history |
Valor booleano. Se quiser incluir o histórico. Cada pedido obtém o seu próprio histórico de conversações separado para garantir que o modelo não fica confuso. |
include_input |
Valor booleano. Se quiser incluir a entrada do utilizador na linha de comandos. Quantos tokens para o pedido. |
max_input_tokens |
O número máximo de tokens para entrada. O máximo de tokens suportados é 4000. |
max_tokens |
O número máximo de tokens a gerar na conclusão. A contagem de tokens do pedido mais max_tokens não pode exceder o comprimento de contexto do modelo. |
temperature |
Que temperatura de amostragem deve utilizar, entre 0 e 2. Valores mais elevados, como 0,8, torna a saída mais aleatória, enquanto valores mais baixos como 0,2 o torna mais focado e determinista. |
top_p |
Uma alternativa à amostragem com temperatura, denominada amostragem de núcleos, em que o modelo considera os resultados dos tokens com top_p massa de probabilidade. Portanto, 0,1 significa que apenas os tokens que incluem a massa de probabilidade superior de 10% são considerados. |
presence_penalty |
Número entre -2.0 e 2.0. Os valores positivos penalizam os novos tokens com base no facto de estes aparecerem no texto até agora, aumentando a probabilidade de o modelo falar sobre novos tópicos. |
frequency_penalty |
Número entre -2.0 e 2.0. Os valores positivos penalizam os novos tokens com base na respetiva frequência existente no texto até agora, diminuindo a probabilidade de o modelo repetir o mesmo texto literal de linha. |
stop_sequences |
Até quatro sequências em que a API deixa de gerar mais tokens. O texto devolvido não conterá a sequência de paragem. |
augmentation_type |
O tipo de aumento. Os valores suportados são sequence , monologue e tools . |
Gestão de pedidos
A gestão de pedidos ajuda a ajustar o tamanho e o conteúdo do pedido enviado para o modelo de linguagem, tendo em conta o orçamento do token disponível e as origens de dados ou aumentos.
Se um bot tiver um máximo de 4000 tokens em que 2800 tokens são para entrada e 1000 tokens são para saída, o modelo pode gerir a janela de contexto geral e garantir que nunca processa mais de 3800 tokens. O modelo começa com um texto com cerca de 100 tokens, adiciona a origem de dados de outros 1200 tokens e, em seguida, analisa o orçamento restante de 1500 tokens. O sistema atribui os restantes 1500 tokens ao histórico de conversações e à entrada. Em seguida, o histórico de conversações é condensado para se ajustar ao espaço restante, garantindo que o modelo nunca ultrapassa os 2800 tokens.
Ações de pedido
Os planos permitem que o modelo execute ações ou responda ao utilizador. Pode criar um esquema do plano e adicionar uma lista de ações que suporta para executar uma ação e transmitir argumentos. O ponto final openAI descreve as ações necessárias para serem utilizadas, extrai todas as entidades e transmite-as como argumentos para a chamada de ação.
The following is a conversation with an AI assistant.
The assistant can turn a light on or off.
context:
The lights are currently {{getLightStatus}}.
Modelo de pedido de aviso
O modelo prompt é uma forma simples e poderosa de definir e compor funções de IA com texto simples. Pode utilizar o modelo de pedido para criar pedidos de linguagem natural, gerar respostas, extrair informações, invocar outros pedidos ou efetuar qualquer outra tarefa que possa ser expressa com texto.
A linguagem suporta funcionalidades que lhe permitem incluir variáveis, chamar funções externas e transmitir parâmetros para funções. Não precisa de escrever código nem importar bibliotecas externas, basta utilizar as chavetas {{...}} para incorporar expressões nos seus pedidos. O Teams analisa o seu modelo e executa a lógica por trás do mesmo. Desta forma, pode integrar facilmente a IA nas suas aplicações com um esforço mínimo e flexibilidade máxima.
{{function}}
: chama uma função registada e insere a respetiva cadeia de valor devolvido.{{$input}}
: insere o texto da mensagem. Obtém o valor de state.temp.input.{{$state.[property]}}
: insere as propriedades de estado.
Ações
As ações processam eventos acionados por componentes de IA.
FlaggedInputAction
e FlaggedOutputAction
são os processadores de ações incorporados para processar os sinalizadores moderadores. Se o moderador sinalizar uma entrada de mensagem recebida, o moderador redireciona para o FlaggedInputAction
processador e envia context.sendActivity
uma mensagem ao utilizador sobre o sinalizador. Se quiser parar a ação, tem de adicionar AI.StopCommandName
.
Referência de código de exemplo
// Register other AI actions
app.ai.action(
AI.FlaggedInputActionName,
async (context: TurnContext, state: ApplicationTurnState, data: Record<string, any>) => {
await context.sendActivity(`I'm sorry your message was flagged: ${JSON.stringify(data)}`);
return AI.StopCommandName;
}
);
app.ai.action(AI.FlaggedOutputActionName, async (context: TurnContext, state: ApplicationTurnState, data: any) => {
await context.sendActivity(`I'm not allowed to talk about such things.`);
return AI.StopCommandName;
});
Registar Processadores de Ações
Os processadores de ações ajudam os utilizadores a atingir os objetivos, que são partilhados nas intenções do utilizador.
Um dos principais aspetos nos processadores de ações é que primeiro tem de registar as ações nos pedidos e, em seguida, ajudar o utilizador a alcançar o objetivo.
Tem de registar um processador para cada ação listada na linha de comandos e também adicionar um processador para lidar com ações desconhecidas.
No exemplo seguinte de um bot claro, temos a ação LightsOn
, LightsOff
e Pause
. Sempre que uma ação é chamada, devolve um string
. Se precisar que o bot devolva tempo, não precisa de analisar a hora e convertê-la num número. A PauseParameters
propriedade garante que devolve tempo no formato de número sem colocar o pedido em pausa.
Referência de código de exemplo
public class LightBotActions
{
[Action("LightsOn")]
public async Task<string> LightsOn([ActionTurnContext] ITurnContext turnContext, [ActionTurnState] AppState turnState)
{
turnState.Conversation!.LightsOn = true;
await turnContext.SendActivityAsync(MessageFactory.Text("[lights on]"));
return "the lights are now on";
}
[Action("LightsOff")]
public async Task<string> LightsOff([ActionTurnContext] ITurnContext turnContext, [ActionTurnState] AppState turnState)
{
turnState.Conversation!.LightsOn = false;
await turnContext.SendActivityAsync(MessageFactory.Text("[lights off]"));
return "the lights are now off";
}
[Action("Pause")]
public async Task<string> LightsOff([ActionTurnContext] ITurnContext turnContext, [ActionParameters] Dictionary<string, object> args)
{
// Try to parse entities returned by the model.
// Expecting "time" to be a number of milliseconds to pause.
if (args.TryGetValue("time", out object? time))
{
if (time != null && time is string timeString)
{
if (int.TryParse(timeString, out int timeInt))
{
await turnContext.SendActivityAsync(MessageFactory.Text($"[pausing for {timeInt / 1000} seconds]"));
await Task.Delay(timeInt);
}
}
}
return "done pausing";
}
[Action("LightStatus")]
public async Task<string> LightStatus([ActionTurnContext] ITurnContext turnContext, [ActionTurnState] AppState turnState)
{
await turnContext.SendActivityAsync(ResponseGenerator.LightStatus(turnState.Conversation!.LightsOn));
return turnState.Conversation!.LightsOn ? "the lights are on" : "the lights are off";
}
[Action(AIConstants.UnknownActionName)]
public async Task<string> UnknownAction([ActionTurnContext] TurnContext turnContext, [ActionName] string action)
{
await turnContext.SendActivityAsync(ResponseGenerator.UnknownAction(action ?? "Unknown"));
return "unknown action";
}
}
}
Se utilizar sequence
, monologue
ou tools
aumento, é impossível para o modelo alucinar um nome de função inválido, um nome de ação ou os parâmetros corretos. Tem de criar um novo ficheiro de ações e definir todas as ações que pretende que o pedido suporte para aumento. Tem de definir as ações para indicar ao modelo quando deve efetuar a ação. O aumento de sequência é adequado para tarefas que requerem vários passos ou lógica complexa.
O aumento do monólogo é adequado para tarefas que exigem compreensão e geração de linguagem natural e mais flexibilidade e criatividade.
No exemplo seguinte de um bot claro, o actions.json
ficheiro tem uma lista de todas as ações que o bot pode realizar:
[
{
"name": "LightsOn",
"description": "Turns on the lights"
},
{
"name": "LightsOff",
"description": "Turns off the lights"
},
{
"name": "Pause",
"description": "Delays for a period of time",
"parameters": {
"type": "object",
"properties": {
"time": {
"type": "number",
"description": "The amount of time to delay in milliseconds"
}
},
"required": [
"time"
]
}
}
]
-
name
: nome da ação. Obrigatório. -
description
: descrição da ação. Opcional. -
parameters
: adicione um objeto de esquema JSON dos parâmetros necessários.
O ciclo de comentários é a resposta de um modelo para validar, corrigir ou refinar a resposta à sua pergunta. Se estiver a utilizar um sequence
aumento, pode desativar o ciclo para proteger contra qualquer ciclo acidental das seguintes formas:
- Pode definir
allow_looping?
comofalse
naAIOptions
definição. - Pode definir
max_repair_attempts
como0
noindex.ts
ficheiro.
Gerir histórico
Pode utilizar os MaxHistoryMessages
argumentos e MaxConversationHistoryTokens
para permitir que a biblioteca de IA faça a gestão automática do seu histórico.
Ciclo de comentários
Um ciclo de comentários permite-lhe monitorizar e melhorar as interações do bot ao longo do tempo, o que leva a aplicações mais eficazes e fáceis de utilizar. Os comentários recebidos podem ser utilizados para fazer ajustes e melhorias, garantindo que o bot cumpre consistentemente as necessidades e expectativas dos utilizadores.
Um ciclo de comentários consiste no seguinte:
Reparar Loop: se a resposta do modelo ficar aquém das expectativas, aciona um ciclo de reparação. Os forks do histórico de conversações permitem que o sistema experimente várias soluções sem afetar a conversação main.
Validação: a validação verifica a resposta corrigida. Se passar a validação com êxito, o sistema anula a resolução da conversação e reinsere a estrutura reparada na main conversação.
Aprender com os Erros: quando o modelo vê um exemplo de comportamento correto, aprende a evitar erros semelhantes no futuro.
Processar Comandos Complexos: assim que o modelo tiver aprendido com os seus erros, torna-se capaz de processar comandos mais complexos e devolver o plano pretendido.