Compartilhar via


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 TurnStateFactoryo , 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 no skprompt.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, LightsOffe 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? como false na AIOptions definição.
  • Pode definir max_repair_attempts como 0 no index.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.

Próxima etapa