Todos os produtos
Search
Central de documentação

Vector Retrieval Service for Milvus:Crie uma barreira de segurança de conteúdo para perguntas e respostas de IA com Data inspection no Alibaba Cloud Milvus

Última atualização: Aug 13, 2026

Adicione uma barreira de segurança de conteúdo a um aplicativo de perguntas e respostas de IA ativando um único switch data_inspection em uma AI Function existente do Alibaba Cloud Milvus. Este tutorial demonstra como o switch inspeciona a entrada do usuário antes da chamada ao modelo e a saída do modelo antes do retorno do resultado, além de mostrar como o cliente converte os resultados de interceptação em três disposições mutuamente exclusivas: policy_blocked, manual_review e operational_error.

Visão geral da solução

Quando um modelo grande passa a atender tráfego externo, a segurança de conteúdo deixa de ser um diferencial e torna-se um requisito obrigatório para o lançamento. Assim que usuários reais enviam prompts em uma ponta e o modelo retorna texto na outra, duas exposições de risco se abrem simultaneamente:

  • Lado da entrada (usuário para modelo) — Usuários podem enviar conteúdo não compatível ou usar prompts de jailbreak para forçar o modelo a ultrapassar seus limites de segurança. Conteúdo gerado por usuários, como publicações, comentários e apelidos, também precisa passar por um controle de segurança antes do armazenamento.

  • Lado da saída (modelo para usuário) — Mesmo quando a entrada parece normal, o modelo ainda pode gerar conteúdo não compatível, como textos de marketing com promessas exageradas ou declarações tendenciosas. Isso é especialmente visível na geração de textos publicitários e roteiros, onde o modelo tem espaço para improvisar. Ambas as pontas precisam de um controle, e o valor para o negócio é direto. A segurança de conteúdo é um requisito rígido para o registro e lançamento de aplicativos de modelos grandes. Quando as máquinas bloqueiam a grande maioria dos conteúdos claramente não compatíveis, os revisores lidam apenas com uma pequena quantidade de amostras ambíguas, permitindo que a equipe de revisão deixe de analisar tudo para focar nos casos difíceis. Uma única captura de tela de um jailbreak bem-sucedido ou uma declaração inadequada pode se transformar em um incidente público, e a barreira de segurança interrompe esse risco antes que o conteúdo saia do sistema.

Implementar essa capacidade com uma abordagem tradicional enfrenta várias dificuldades:

  • As duas pontas são inspecionadas em momentos diferentes — A inspeção de entrada deve terminar antes da chamada ao modelo, e a inspeção de saída deve terminar antes do retorno do resultado. Uma única chamada de negócio abrange ambos os pontos ao redor da chamada do modelo.

  • É necessário integrar e encadear manualmente um serviço extra de segurança de conteúdo — A implementação típica faz com que o código de negócio chame uma API de segurança de conteúdo para inspecionar a entrada, depois chame o modelo grande e, em seguida, chame a API de inspeção novamente para a saída. Isso significa três chamadas remotas, três políticas de timeout e nova tentativa, e três caminhos de autenticação.

  • Os resultados de interceptação são difíceis de analisar programaticamente — Quando uma política de segurança é acionada, o servidor pode retornar um erro HTTP, um código de negócio diferente de zero e também uma recusa explícita como texto dentro de um HTTP 200. Verificar apenas o código de status HTTP ignora recusas dentro de respostas 200, e verificar apenas palavras-chave produz bloqueios falsos em textos de negócio normais que por acaso contêm palavras como "risco" ou "rejeitar".

  • O equilíbrio entre bloqueios falsos e falhas de bloqueio — Um controle muito rígido interrompe o negócio normal, e um muito flexível aumenta o risco de conformidade. Uma barreira de segurança não pode ter apenas estados de aprovação e bloqueio; ela também precisa de um meio-termo que encaminhe amostras ambíguas para revisão manual.

  • Conformidade de logs — A solução de problemas exige logs, mas assim que textos brutos de alto risco e informações pessoais sensíveis caem em logs de negócio em texto simples, o próprio sistema de logs torna-se uma nova superfície de vazamento. A abordagem no Alibaba Cloud Milvus consiste em adicionar um switch data_inspection aos params de uma AI Function existente, como a tarefa de geração de texto ai_text_generate. O switch aceita apenas três valores:

Valor

Momento da inspeção

Cenário típico

Descrição

input

Antes da chamada ao modelo

Atendimento ao cliente inteligente e assistentes de IA que recebem prompts de usuários; conteúdo UGC antes do armazenamento

Bloqueia prompts de jailbreak e entradas não compatíveis. Quando acionado, o modelo não é chamado, o que economiza computação e melhora a segurança.

output

Antes do retorno do resultado

Publicação de textos de marketing e campanhas, geração de roteiros

Adequado para cenários onde a entrada é confiável e a única preocupação é a saída não compatível do modelo.

both

Uma vez em cada ponta

Diálogo aberto de alto risco, perguntas e respostas livres voltadas ao público

A barreira mais forte para quando nenhuma das pontas é confiável, sendo também a mais custosa.

A barreira de segurança e a chamada ao modelo são concluídas em uma única passagem dentro do Milvus, portanto, os dados nunca saem da instância do Milvus. O administrador configura as credenciais centralmente no lado do Provider e elas nunca são gravadas em nenhum corpo de requisição, de modo que o lado do negócio não precisa mais construir ou encadear um serviço externo de segurança de conteúdo.

O data_inspection adiciona um controle a uma chamada existente e não substitui os parâmetros obrigatórios da própria tarefa. Por exemplo, a geração de texto ainda requer texts, e omiti-lo retorna texts is required for task [ai_text_generate].

Pré-requisitos

  • Uma instância do Milvus 2,6 criada. A AI Function depende do kernel 2,6, e nenhuma vinculação separada de serviço de modelo é necessária após a criação.

  • Para acessar a instância pela Internet, o Public Access está ativado na aba Security Configuration da página de detalhes da instância, e o IP de saída do cliente foi adicionado à lista de permissões de acesso público.

  • Um modelo de texto alvo configurado no lado do Provider, com a capacidade Data inspection (DATA_INSPECTION) disponível. O switch data_inspection chama esse modelo, e o administrador configura o modelo e suas credenciais centralmente no lado do Provider. Se o modelo alvo não estiver configurado, as chamadas falharão com um operational_error (o modelo não existe, retornado como HTTP 500 com código de negócio 65535).

  • pymilvus instalado. Os exemplos neste tutorial foram verificados com pymilvus [TODO: confirm version].

Importante

A interface RESTful e o gRPC compartilham a porta 19530, portanto, especifique a porta explicitamente, por exemplo http://c-xxx.milvus.aliyuncs.com:19530. Omitir a porta reverte para a porta 80 e causa timeout de conexão.

Procedimento

Este tutorial anexa a barreira de segurança de duas maneiras. Escolha aquela que corresponde à forma como seu aplicativo chama o modelo:

  • Collection com uma função TextTransform — A barreira de segurança executa quando você grava dados em uma collection. Use esta opção para pipelines que inspecionam e depois persistem conteúdo, como moderação de UGC antes do armazenamento. A maioria das etapas deste tutorial usa essa abordagem.

  • **Chamada REST síncrona de text_generate** — A barreira executa em linha e retorna a disposição na resposta. Utilize esta opção para cenários de requisição/resposta em tempo real, como perguntas e respostas interativas. A Etapa 4 também demonstra esse caminho. (Recomendado) Para perguntas e respostas interativas de IA, use a chamada REST síncrona de text_generate. Para conteúdo que deve ser inspecionado antes do armazenamento, use a Collection com uma função TextTransform.

Etapa 1: Prepare o código compartilhado

O código a seguir contém as configurações de conexão, um wrapper REST, um fallback de compatibilidade para o tipo TEXTTRANSFORM e uma função auxiliar que cria uma Collection com uma barreira de segurança. O switch da barreira é a única linha data_inspection dentro de params.

from __future__ import annotations

import json
from typing import Any
from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen

from pymilvus import DataType, Function, FunctionType, MilvusClient
from pymilvus.exceptions import MilvusException

# ==================== Connection settings ====================
MILVUS_URI = "http://c-xxx.milvus.aliyuncs.com:19530"  # The port must be 19530
MILVUS_TOKEN = "root:xxx"
MILVUS_REST_BASE_URL = MILVUS_URI
MODEL_NAME = "<your-configured-text-model>"  # Replace with a text model already configured in the Provider

TEXTTRANSFORM_FUNCTION_TYPE = 9

# The contract identifier returned by the Provider when a safety policy is triggered.
# It is the only reliable basis for deciding whether content was blocked.
BLOCK_MARKERS = ("DataInspectionFailed", "inappropriate content")

client = MilvusClient(uri=MILVUS_URI, token=MILVUS_TOKEN)

def texttransform_function_type() -> Any:
    """Get the FunctionType for TEXTTRANSFORM; add a member dynamically when the enum is missing in older versions."""
    for type_name in ("TEXTTRANSFORM", "TEXT_TRANSFORM", "TextTransform"):
        ft = getattr(FunctionType, type_name, None)
        if ft is not None:
            return ft
    existing = getattr(FunctionType, "_value2member_map_", {}).get(TEXTTRANSFORM_FUNCTION_TYPE)
    if existing is not None:
        return existing
    extension = int.__new__(FunctionType, TEXTTRANSFORM_FUNCTION_TYPE)
    extension._name_ = "TEXTTRANSFORM"
    extension._value_ = TEXTTRANSFORM_FUNCTION_TYPE
    FunctionType._value2member_map_[TEXTTRANSFORM_FUNCTION_TYPE] = extension
    FunctionType._member_map_["TEXTTRANSFORM"] = extension
    return extension

def post_json(path: str, body: dict[str, Any], timeout: int = 120) -> tuple[int, dict[str, Any]]:
    """REST wrapper: returns (http_status, data), and still tries to parse the response body when HTTP is not 2xx."""
    request = Request(
        f"{MILVUS_REST_BASE_URL.rstrip('/')}{path}",
        data=json.dumps(body, ensure_ascii=False).encode("utf-8"),
        headers={"Authorization": f"Bearer {MILVUS_TOKEN}", "Content-Type": "application/json"},
        method="POST",
    )
    try:
        with urlopen(request, timeout=timeout) as response:
            return response.status, json.loads(response.read().decode("utf-8"))
    except HTTPError as exc:
        return exc.code, json.loads(exc.read().decode("utf-8"))

def build_guard_collection(name: str, func_name: str, in_field: str, out_field: str,
                           prompt: str, data_inspection: str) -> None:
    """Create a write-oriented Collection with a TextTransform guardrail."""
    if client.has_collection(name):
        client.drop_collection(name)

    schema = MilvusClient.create_schema(auto_id=True, enable_dynamic_field=False)
    schema.add_field("id", DataType.INT64, is_primary=True)
    schema.add_field(in_field, DataType.VARCHAR, max_length=1024)
    schema.add_field(out_field, DataType.VARCHAR, max_length=4096)
    # A Collection must have at least one vector field. This example does not run vector search,
    # so a 2-dimensional placeholder field satisfies the constraint.
    # With nullable=True declared, the field does not need to be passed on insert.
    schema.add_field("dummy_vector", DataType.FLOAT_VECTOR, dim=2, nullable=True)
    schema.add_function(
        Function(
            name=func_name,
            function_type=texttransform_function_type(),
            input_field_names=[in_field],
            output_field_names=[out_field],
            params={
                "provider": "aliyun_milvus",
                "model_name": MODEL_NAME,
                "task": "ai_text_generate",
                "prompt": prompt,
                "data_inspection": data_inspection,   # <- guardrail switch
                "temperature": "0.2",
                "enable_thinking": "false",
                "timeout_sec": "45",
            },
        )
    )
    index_params = client.prepare_index_params()
    index_params.add_index(field_name="dummy_vector", index_type="AUTOINDEX",
                           metric_type="COSINE")
    client.create_collection(collection_name=name, schema=schema, index_params=index_params)

Uma Collection deve conter pelo menos um campo vetorial, caso contrário, a criação falha com schema does not contain vector field. Este exemplo não executa busca vetorial, portanto, um campo vetorial de espaço reservado bidimensional satisfaz a restrição e é declarado como nullable=True para evitar a passagem de um valor em cada gravação.

Etapa 2: Converta os resultados de interceptação em três disposições

Esta é a dificuldade de engenharia mais frequentemente subestimada quando uma barreira de segurança entra em produção. Quando uma política de segurança é acionada, o servidor pode retornar um erro HTTP, um código de negócio diferente de zero ou uma recusa explícita como texto dentro de um HTTP 200. O cliente precisa, portanto, converter os sinais observados em três disposições mutuamente exclusivas:

Disposição

Significado

Ação recomendada

policy_blocked

Uma política de segurança foi acionada, a barreira está funcionando conforme projetado e o resultado é esperado pelo negócio.

Registre um log de auditoria e retorne um aviso de conformidade ao usuário. Nenhum alerta de operações é necessário.

manual_review

A camada de protocolo não consegue decidir, por exemplo, HTTP 200 com estrutura normal, mas conteúdo suspeito.

Envie a amostra para a fila de revisão manual. "Nenhum erro levantado" não significa "passou com segurança".

operational_error

Uma falha real de serviço, como modelo indisponível, erro de parâmetro ou falha de rede.

Alertar o engenheiro de plantão e parar de tentar novamente. Não tente novamente indefinidamente.

# ==================== Three-state disposition classification ====================
# Both call paths (gRPC exception / REST response body) first check the Provider contract identifier,
# then fall back to the HTTP status and business code, so the same interception yields a consistent
# disposition on both paths.

def classify_grpc(exc: MilvusException) -> str:
    """Converge a gRPC exception into a three-state disposition."""
    msg = str(exc)
    if any(marker in msg for marker in BLOCK_MARKERS):
        return "policy_blocked"      # A safety policy was triggered, which is an expected business result
    return "operational_error"       # Everything else counts as a service fault

def classify_rest(status: int, data: dict[str, Any]) -> str:
    """Converge a REST response into a three-state disposition."""
    raw = json.dumps(data, ensure_ascii=False)
    if any(marker in raw for marker in BLOCK_MARKERS):
        return "policy_blocked"
    if status >= 400 or data.get("code", 0) != 0:
        return "operational_error"
    # HTTP 200 with a normal structure: the model may have returned an explicit refusal in the body.
    # The protocol layer cannot decide, so route it to manual review instead of treating it as a safe pass.
    return "manual_review"

A função classify_rest é deliberadamente conservadora. Quando a resposta é HTTP 200 com uma estrutura normal, a camada de protocolo sozinha não pode certificar que o corpo é seguro, pois o modelo pode ter embutido uma recusa explícita nele. A função retorna, portanto, manual_review para este caso indecidível, em vez de aprová-lo silenciosamente. Combine classify_rest com sua própria validação de resposta para que uma resposta que você possa confirmar como uma resposta de negócio genuína seja tratada como aprovação, e apenas as respostas que permanecerem indecisas cheguem à fila de revisão. É isso que mantém a tabela de cenários "Passou" para entradas normais consistente com o objetivo de encaminhar apenas um pequeno número de amostras ambíguas para revisão manual.

Por que a decisão deve ser baseada no identificador de contrato do Provider e não apenas no código de status HTTP. Testes verificaram que "uma entrada não compatível bloqueada pela barreira" e "uma falha de serviço causada por um nome de modelo inexistente" retornam exatamente a mesma combinação de códigos de status:

Cenário

Status HTTP

Código de negócio

**Corpo da resposta contém DataInspectionFailed**

Disposição correta

Entrada não compatível bloqueada

500

65535

Sim

policy_blocked

Nome do modelo não existe

500

65535

Não

operational_error

Parâmetro obrigatório texts ausente

400

1100

Não

operational_error

Entrada normal

200

0

Não

Passou

O código de status HTTP e o código de negócio nas duas primeiras linhas são idênticos, portanto, não conseguem distinguir "conteúdo bloqueado" de "falha de serviço". O significado operacional dos dois é completamente diferente: classificar erroneamente um bloqueio de política como uma falha de serviço transforma cada interceptação de segurança de conteúdo em um falso alerta de falha de serviço, o que, com o tempo, oculta falhas reais. A decisão deve, portanto, confiar no identificador de contrato DataInspectionFailed no corpo da resposta. Pelo mesmo motivo, não dependa de valores específicos de código de status HTTP, pois eles podem mudar entre versões de gateway.

Não use palavras-chave como "risco", "rejeitar" ou "não pode" para decidir se o conteúdo foi bloqueado, pois textos de negócio normais também podem conter essas palavras e causar bloqueios falsos. Palavras-chave são, no máximo, um sinal auxiliar e não devem alterar a disposição final.

Etapa 3: Inspecione conteúdo compatível nos modos de entrada e saída

O modo input inspeciona a entrada do usuário antes da chamada ao modelo, e o modo output inspeciona a saída do modelo antes do retorno do resultado. Conteúdo compatível passa normalmente.

# ==================== Step 3a: input mode, inspection before the request ====================
build_guard_collection(
    "guard_input", "inspect_customer_request", "request", "response",
    "Answer in one sentence in a customer service tone: ${request}", "input",
)
client.insert("guard_input", [{"request": "How long does a refund take to arrive after it is approved?"}])
client.flush("guard_input")
for row in client.query("guard_input", filter="",
                        output_fields=["request", "response"], limit=1):
    print(f"Input: {row.get('request')}")
    print(f"Output: {row.get('response')}")
# The guardrail was not triggered, so the content passes through and the model returns normally.
# With a jailbreak prompt instead, the model is never called and the guardrail blocks it up front.

# ==================== Step 3b: output mode, inspection before publishing model output ====================
build_guard_collection(
    "guard_output", "inspect_generated_copy", "draft_request", "publish_copy",
    "Generate a membership campaign summary suitable for publishing in an app: ${draft_request}", "output",
)
client.insert("guard_output",
              [{"draft_request": "Generate a benefits summary for Members Day in 30 words or fewer, with no exaggerated promises."}])
client.flush("guard_output")
for row in client.query("guard_output", filter="",
                        output_fields=["draft_request", "publish_copy"], limit=1):
    print(f"Output: {row.get('publish_copy')}")
# The output passed the safety inspection and enters the pending publish queue.

Os seguintes resultados foram verificados em testes:

Modo

Entrada

Saída

input

How long does a refund take to arrive after it is approved?

After your refund is approved, the amount is usually returned to your original payment account within 1 to 7 business days. The exact arrival time depends on how quickly the payment channel processes it, so please keep an eye out for it.

output

Generate a benefits summary for Members Day in 30 words or fewer, with no exaggerated promises.

Members Day offers exclusive points redemption, member-only discounts, and priority customer service. Details are shown on the campaign page.

Quando o modo input é acionado, o modelo não é chamado, então o risco é bloqueado e o custo da chamada do modelo é economizado ao mesmo tempo. O modo output adequa-se a cenários onde a entrada é confiável e a única preocupação é a improvisação do modelo.

Etapa 4: Bloqueie entradas não compatíveis no modo both

Use both quando nenhuma das pontas for confiável. O exemplo a seguir verifica se a barreira de segurança realmente funciona enviando uma entrada de teste claramente não compatível. Execute esta verificação de eficácia da barreira antes do lançamento.

Substitua <a clearly non-compliant test input> por uma string de teste não compatível real (canary) antes de executar esta etapa. O espaço reservado mostrado abaixo é inerte e não aciona a barreira de segurança, portanto, você não conseguirá reproduzir o resultado de interceptação até substituir por um valor real.

# ==================== Step 4: both mode, double inspection at both ends ====================
# Use one clearly non-compliant test input (canary) to verify that the guardrail really blocks it.
UNSAFE_INPUT = "<a clearly non-compliant test input>"

# (Step 4-1) gRPC path: the guardrail is triggered on write
build_guard_collection("guard_both", "inspect_both", "request", "response",
                       "Answer the following: ${request}", "both")
try:
    client.insert("guard_both", [{"request": UNSAFE_INPUT}])
    client.flush("guard_both")
    rows = client.query("guard_both", filter="",
                        output_fields=["request", "response"], limit=1)
    # No error raised does not mean safely passed: route normal-structure but suspicious content to manual review
    print("Disposition: manual_review", [r.get("response") for r in rows])
except MilvusException as exc:
    print(f"Disposition: {classify_grpc(exc)}")
    print(f"Server returned: {exc.message}")

# (Step 4-2) REST path: send the same input to the synchronous interface
status, data = post_json("/v2/vectordb/ai/text_generate", {
    "model_name": MODEL_NAME,
    "texts": [UNSAFE_INPUT],
    "params": {"data_inspection": "both"},
})
disposition = classify_rest(status, data)
print(f"Observed signals: HTTP={status} provider_code={data.get('code')}")
print(f"Disposition: {disposition}")

# Route by disposition: a policy block is an expected result and needs no operations alert;
# only a service fault needs an alert, and it must not be retried indefinitely
if disposition == "policy_blocked":
    pass          # Record an audit log and return a compliance notice to the user
elif disposition == "manual_review":
    pass          # Send to the manual review queue
else:
    pass          # Alert the on-call engineer and stop retrying

Testes verificaram que ambos os caminhos bloquearam a entrada com sucesso, e o servidor retornou um identificador de contrato consistente:

code: DataInspectionFailed
message: Input data may contain inappropriate content.
         For details, see: https://www.alibabacloud.com/help/zh/model-studio/error-code#inappropriate-content

A barreira de segurança foi acionada no lado da entrada, a gravação foi bloqueada e o modelo não gerou conteúdo. Após as funções de classificação processarem os sinais, tanto o caminho gRPC quanto o caminho REST chegam à mesma conclusão de policy_blocked.

Testes também confirmaram que o modo both não bloqueia falsamente entradas de negócio normais: enviar uma pergunta normal de atendimento ao cliente para a mesma interface retornou HTTP 200 com uma resposta completa.

Etapa 5: Registre disposições com segurança

Depois que a barreira de segurança estiver em produção, os logs devem localizar problemas sem se tornarem uma nova superfície de vazamento de dados. Siga estas recomendações:

  • Registre apenas campos legíveis por máquina e redigidos: trace id, estágio de inspeção, status HTTP, código do provider, enum de disposição e ação de disposição.

  • Reduza ou redija texto bruto e informações pessoais sensíveis. Durante a revisão, recupere os dados por meio de um canal autorizado usando o trace id, em vez de manter texto bruto nos logs de negócio.

  • Pare e alerte quando uma política for acionada ou ocorrer um operational_error, e não tente novamente indefinidamente.

  • Combine com AI_PII_MASK: redija antes de registrar para manter a superfície de exposição de dados sensíveis a menor possível.

{
  "trace_id": "req-20260808-abc123",
  "stage": "both",
  "http_status": 500,
  "provider_code": 65535,
  "disposition": "policy_blocked",
  "note": "blocked by data inspection on input side"
}

Limpeza

Os exemplos neste tutorial criam três collections: guard_input, guard_output e guard_both. Ao terminar, exclua-as para remover os dados de teste:

for name in ("guard_input", "guard_output", "guard_both"):
    if client.has_collection(name):
        client.drop_collection(name)

Valor da solução

Dimensão

Antes da barreira de segurança

**Após ativar o DATA_INSPECTION**

Jailbreak e prompts não compatíveis no lado da entrada

Depende de revisão manual posterior, causando atraso no tratamento

Bloqueado antes da chamada ao modelo

Conteúdo não compatível saindo no lado da saída

Encontrado posteriormente e tratado reativamente

Interceptado antes do retorno do resultado, nunca chegando a sair

Volume de revisão manual

Cada item é revisado manualmente

Apenas o pequeno número de amostras de manual_review é revisado

Número de sistemas

Sistema de negócio mais um serviço externo de segurança de conteúdo, encadeados por três chamadas remotas

Um sistema (Milvus, com a barreira anexada à chamada)

Dados e credenciais

Texto bruto flui para um serviço externo e a autenticação é dispersa

Os dados permanecem dentro da instância e as credenciais são configuradas centralmente pelo Provider

Uma vez que a barreira de segurança de conteúdo se resume a um único switch, ela pode ser escolhida flexivelmente por cenário:

  • Perguntas e respostas de atendimento ao cliente inteligente — Adicione input à geração de texto para bloquear prompts de jailbreak e perguntas não compatíveis.

  • Geração de textos de marketing e roteiros — Adicione output à geração de texto para interceptar conteúdo não compatível antes da publicação.

  • Assistentes de IA abertos voltados ao público — Use both para proteção dupla em cada ponta. Direções que valem a pena expandir:

  • **Combinar com AI_PII_MASK** — Redija primeiro, depois inspecione e então registre, para manter a superfície de exposição de dados sensíveis a menor possível.

  • Abranger mais tarefas — À medida que mais tarefas de AI Function suportarem data_inspection, o mesmo modelo input/output/both se transfere para mais cenários generativos.

  • Fechar o ciclo de políticas — Transforme amostras de manual_review em um conjunto de avaliação e continue calibrando o equilíbrio entre bloqueios falsos e falhas de bloqueio para que a barreira se torne mais precisa ao longo do tempo.