SobreCompetênciasTrabalhoR&DBlogFerramentasComeçarContacto

Um manifesto de agente do Copilot é uma fronteira de segurança. Aqui fica o verificador.

Todas as propriedades de âmbito de um manifesto de agente do Microsoft 365 Copilot são opcionais e, em seis casos, omitir uma dá o âmbito mais largo em vez do mais estreito. Um validador de JSON Schema aprova esses manifestos, porque nenhum deles tem nada de inválido: falta uma coisa que era opcional. Por isso escrevemos a verificação que os apanha. Encontra seis problemas num manifesto que passa em todos os outros testes.

pH7x Systems® · · 6 min de leitura

Um agente declarativo para o Microsoft 365 Copilot não é código. É um manifesto JSON que diz ao modelo para que serve, o que pode ler e o que pode fazer. O modelo, a orquestração e o recorte por permissões são do Copilot. As fronteiras são consigo.

Quase todas as propriedades que definem essas fronteiras são opcionais. Em seis casos, deixar uma de fora não restringe o agente. Dá-lhe o âmbito mais largo que existe.

A linha que dá tudo

Um manifesto precisa de três campos: name, description e instructions. Tudo o resto são capacidades, e esta é a que mais pesa.

json
{
  "capabilities": [
    {
      "name": "OneDriveAndSharePoint",
      "items_by_url": [
        { "url": "https://tenant.sharepoint.com/sites/Tenders/Documents/Live" }
      ]
    }
  ]
}

Agora apague o array items_by_url. A documentação é explícita: sem items_by_url nem items_by_sharepoint_ids, o agente passa a poder aceder a todas as fontes de OneDrive e SharePoint da organização.

Não a algumas. A todas. E a versão errada não dá erro nenhum: produz um agente que funciona, responde a perguntas e fundamenta as respostas em todos os sites a que a pessoa que fala com ele tiver acesso.

O Copilot continua a aplicar o recorte de segurança dessa pessoa, portanto o agente não lê nada que ela própria não pudesse abrir. Essa garantia é real, e escrevemos sobre a mesma fronteira em o assistente de IA que só lê o que o utilizador pode ler. Mas recortar não é delimitar. Quem tiver acesso largo fica com um agente de concursos a responder a partir de ficheiros de recursos humanos e de atas da administração, porque nada lhe disse que não.

A mesma regra aparece seis vezes:

Capacidade Omitir isto E o agente fica com
OneDriveAndSharePoint items_by_url, items_by_sharepoint_ids Todas as fontes de SharePoint e OneDrive da organização
GraphConnectors connections Todos os conectores do Copilot da organização
TeamsMessages urls Todos os canais, reuniões e conversas
Meetings items_by_id Todas as reuniões
WebSearch sites A web aberta
Email folders A caixa de correio inteira

É uma opção defensável: um agente que por omissão não lesse nada seria inútil acabado de instalar. Mas inverte o hábito de quem escreve configuração de segurança, onde uma regra que falta costuma querer dizer negado. Aqui, o silêncio quer dizer sim.

A armadilha que sobrevive a conhecer o padrão

O esquema 1.8 acrescentou duas capacidades de escrita, EmailActions e MeetingActions. A primeira cobre triagem, envio supervisionado, eliminação, regras da caixa de entrada, resposta automática e gestão de pastas.

A documentação diz que EmailActions funciona independentemente de Email e que as restrições de âmbito definidas em Email, incluindo folders, shared_mailbox e group_mailboxes, não se lhe aplicam. Por isso este manifesto não faz o que se queria:

json
{
  "capabilities": [
    { "name": "Email", "folders": [ { "folder_id": "inbox" } ] },
    { "name": "EmailActions" }
  ]
}

A leitura fica limitada à caixa de entrada. Apagar, mover e criar regras não fica limitado a coisa nenhuma. Não há maneira de delimitar EmailActions: o objeto tem exatamente uma propriedade, o nome. A decisão é binária e tem de ser tomada de propósito.

Aquele manifesto é obra de alguém que estava a ter cuidado. Ter cuidado no sítio errado dá um ficheiro que parece prudente e não é.

A verificação que apanha isto

Um validador de JSON Schema aprova todos os manifestos errados que estão aqui atrás, porque nenhum deles tem nada de inválido. Falta uma coisa que era opcional, que é diferente, e é a única que aqui interessa.

Ou seja, a revisão tem de procurar ausências, e isso é trabalho mecânico, o que quer dizer que não devia caber a uma pessoa a ler com atenção às cinco da tarde.

python
# Each capability, the properties that scope it, and what happens without them.
SCOPE = {
    "OneDriveAndSharePoint": (("items_by_url", "items_by_sharepoint_ids"),
                              "every SharePoint and OneDrive source"),
    "GraphConnectors": (("connections",), "every Copilot connector"),
    "TeamsMessages":   (("urls",),        "every channel, meeting and chat"),
    "Meetings":        (("items_by_id",), "every meeting"),
    "WebSearch":       (("sites",),       "the open web"),
    "Email":           (("folders", "shared_mailbox", "group_mailboxes"),
                        "the whole mailbox"),
}
# Write capabilities. There is no way to scope them: the choice is binary.
WRITES = {"EmailActions": "delete, move and create inbox rules",
          "MeetingActions": "book meetings and change the calendar"}

def check(manifest: dict) -> list[tuple[str, str]]:
    found = []
    caps = {c.get("name"): c for c in manifest.get("capabilities", [])
            if isinstance(c, dict)}

    for name, cap in caps.items():
        if name in SCOPE:
            props, everything = SCOPE[name]
            if not any(cap.get(p) for p in props):
                found.append(("OPEN", f"{name} without {' or '.join(props)}:"
                                      f" reads {everything}"))
        if name in WRITES:
            found.append(("WRITES", f"{name} can {WRITES[name]}"))

    # Email is scoped, EmailActions is not, and the file gives no sign of it.
    if "EmailActions" in caps and any(caps.get("Email", {}).get(p)
                                      for p in SCOPE["Email"][0]):
        found.append(("TRAP", "Email is scoped but EmailActions ignores that limit"))

    if not manifest.get("behavior_overrides", {}).get(
            "special_instructions", {}).get("discourage_model_knowledge"):
        found.append(("INVENTS", "answers from model knowledge when sources are silent"))
    if not manifest.get("disclaimer", {}).get("text"):
        found.append(("NO NOTICE", "the reader is not told where answers come from"))
    return found

Junte-lhe vinte linhas que leem um ficheiro e devolvem um código de saída diferente de zero, e corra-o contra um manifesto que todas as outras ferramentas aprovam:

bash
$ python check_agent.py tender-desk.json
Tender Desk  schema v1.8
  OPEN      OneDriveAndSharePoint without items_by_url or items_by_sharepoint_ids:
            reads every SharePoint and OneDrive source
  WRITES    EmailActions can delete, move and create inbox rules
  OPEN      WebSearch without sites: reads the open web
  TRAP      Email is scoped but EmailActions ignores that limit
  INVENTS   answers from model knowledge when sources are silent
  NO NOTICE the reader is not told where answers come from

6 to review
$ echo $?
1

Delimite o mesmo agente como deve ser e ele cala-se e sai com zero. É isso que lhe permite travar uma publicação, em vez de ficar num documento que ninguém abre.

A linha TRAP é a que justifica o script inteiro. Só dispara quando alguém delimitou Email e a seguir pôs EmailActions ao lado, que é precisamente o manifesto que uma pessoa cuidadosa escreve.

Corremo-lo contra um manifesto realista para o nosso próprio tenant, com o site de SharePoint devidamente delimitado, e ainda assim teve alguma coisa a dizer: TeamsMessages tinha entrado sem um array urls, portanto um agente pensado para uma biblioteca de documentos podia ler todos os canais e conversas a que o utilizador pertence. Ninguém decidiu aquilo. Foi uma linha que nunca lá esteve.

O que não apanha

Vale a pena dizê-lo com clareza, porque uma verificação que exagera aquilo que cobre é pior do que nenhuma.

Lê um ficheiro. Não sabe se o URL de SharePoint aponta para uma pasta com doze documentos ou para um site com quarenta mil, e essa diferença pesa mais do que tudo o que o script mede. Não consegue dizer se as instruções são boas. E não diz nada sobre se as pessoas que usam o agente deviam sequer ter acesso àquele conteúdo, que é uma questão de permissões anterior ao agente e que lhe sobrevive.

O que ele dispensa é a parte da revisão em que uma pessoa é pior: dar por uma coisa que não está lá.

Porque é que isto importa

O apelo de um agente declarativo é não haver código para manter. Um manifesto, algumas fontes de conhecimento, e algo genuinamente útil para uma equipa que nunca teria uma aplicação feita à medida.

É verdade, e é também por isso que o âmbito pesa mais aqui do que numa aplicação. Ninguém revê um ficheiro JSON como revê um pull request. Não há bateria de testes. Não há compilador que avise que o array que ficou de fora quer dizer o tenant inteiro.

Agora há, pelo menos, um script.

Continuar a ler

Desenvolvimento e automação

SPFx antes da versão 1.0: três web parts nossas nos samples oficiais da Microsoft

A developer preview do SharePoint Framework saiu em agosto de 2016. A versão 1.0 chegou em fevereiro de 2017. A nossa primeira contribuição para o repositório oficial de samples do Microsoft 365 é de outubro de 2016, cinco meses antes de haver uma 1.0 sobre a qual construir. Três das nossas web parts estão hoje nesse repositório, e é isto que cada uma faz.

·5 min de leitura
Desenvolvimento e automação

Continuamos a medir engenheiros pela parte que foi automatizada.

Em 2025 o DORA deitou fora o seu próprio quadro de pontuação. Os escalões low, medium, high e elite, que a indústria citou durante uma década, deram lugar a sete arquétipos de equipa sobre oito medidas. Isso não é uma nota de metodologia. É o quadro de medição mais usado no software a admitir que os instrumentos já não leem aquilo que decide se um sistema sobrevive.

·10 min de leitura
IA e agentes

Um agente é uma integração. Cem são um problema de governação.

Os agentes ganharam identidade de produção antes de terem forma estável de registar o que fizeram. O Microsoft Entra Agent ID já está disponível, com patrocinadores e datas de validade. As convenções do OpenTelemetry para os rastos dos agentes continuam experimentais. Essa diferença não é um detalhe: decide o que se pode pôr em produção este ano, e transforma uma questão de IA numa questão de governação.

·11 min de leitura