Todos os produtos
Search
Central de documentação

Vector Retrieval Service for Milvus:Crie um assistente de solução de problemas de desenvolvimento com a base de conhecimento do Alibaba Cloud Milvus

Última atualização: Aug 27, 2026

Neste tutorial, você cria um assistente de solução de problemas de desenvolvimento em uma base de conhecimento do Alibaba Cloud Milvus que armazena referências de API, notas de arquitetura, Runbooks e postmortems. Pesquise por nome de API ou código de erro para obter etapas ordenadas de solução de problemas com citações das fontes.

Visão geral da solução

Este tutorial tem como base o Build an intelligent customer service Q&A application with the Alibaba Cloud Milvus knowledge base. O pipeline de ponta a ponta é idêntico: defina tags no console → importe arquivos de source em lote por tag → publique uma versão → recupere dados pelo SDK (com filtragem opcional de tags) → faça o LLM gerar respostas com citações de source → disponibilize a página de perguntas e respostas com Flask. Reutilize o código de engenharia (kb_client.py, upload.py, app.py, templates/index.html e start.sh) desse tutorial, com duas modificações descritas em Step 4: Adapt the reused code. Este documento aborda apenas as alterações necessárias para o cenário de solução de problemas de desenvolvimento: tags de módulo, parâmetros de recuperação para termos exatos, tratamento de arquivos de source HTML e restrições sobre comandos de alto risco e resultados vazios.

Para a primeira versão, selecione apenas de 5 a 20 arquivos de source de um único sistema e expanda o escopo após a validação. Todo o processo leva cerca de 20 a 30 minutos. O tempo de análise dos documentos depende da quantidade e do tamanho dos arquivos de source.

Pré-requisitos

  • Crie uma base de conhecimento do Milvus e registre o ID da base de conhecimento (por exemplo, kd-803ae9b10cc31).

  • Crie um usuário RAM com sua conta Alibaba Cloud, selecione Use Permanent AccessKey Access e anexe a política de sistema AliyunMilvusFullAccess.

  • Prepare um endpoint de LLM compatível com o protocolo OpenAI chat/completions e sua chave de API.

  • Instale o Python 3.8 ou posterior localmente.

  • Configure o diretório do projeto e os cinco arquivos reutilizados (kb_client.py, upload.py, app.py, templates/index.html e start.sh) seguindo o tutorial referenciado em Solution overview.

  • Garanta que os arquivos de source não contenham segredos, tokens ou contas de rede interna.

Etapa 1: Definir tags de módulo

Em Basic Information, na página de detalhes da base de conhecimento, escolha Tags > Gerencie e adicione as três tags a seguir. Defina o tipo de campo de cada tag como string. A lista suspensa de tipos fica no lado direito da caixa de entrada do nome da tag. As opções são string, int64, list, float32 e bool.

Nome da tag

Descrição

Valor de exemplo

module

Sistema ou service ao qual o arquivo de source pertence

order-service, user-service

docType

Tipo de documento

API, Runbook, ErrorCode, Postmortem

version

Versão da API ou do documento

v2

A tag version tem o mesmo nome do parâmetro version, que representa a versão publicada da base de conhecimento em uma solicitação de recuperação. No entanto, um não afeta o outro: a tag é usada em tagFilter.field, enquanto a versão de recuperação fica no nível superior da solicitação. Se houver preocupação com confusão, nomeie a tag como apiVersion.

Aviso

A caixa de diálogo de gerenciamento de tags é salva como um todo. Após abrir a caixa de diálogo, a lista de tags carrega de forma assíncrona. Aguarde até que todas as tags existentes sejam exibidas antes de adicionar novas tags e clique em OK. Caso contrário, o salvamento pode sobrescrever tudo com "lista vazia + novas tags" e apagar as definições de tags existentes.

A tag module é a decisão de design mais crítica neste cenário. Sistemas diferentes costumam ter caminhos de API com exatamente o mesmo nome. Sem a filtragem por módulo, eles interferem uns nos outros. Em nosso teste, tanto user-service quanto order-service possuíam o mesmo caminho GET /api/v2/orders/{orderId}. Ao pesquisarmos esse caminho:

Condição de recuperação

Resultado

Sem filtro

O arquivo de source do user-service ficou em primeiro lugar (relevância 0,246) — o módulo errado foi atingido

module = order-service

O arquivo de source do order-service ficou em primeiro lugar, e o arquivo de source do user-service foi completamente excluído

Etapa 2: Preparar arquivos de source e o manifesto de importação

Nesta etapa, colete os arquivos de source, anote cada um deles no manifesto de importação e configure como a base de conhecimento os fragmentará.

  • Coloque os arquivos de source no diretório documents/. Os formatos Markdown, HTML, PDF, DOCX e TXT são suportados. Mantenha códigos de erro completos, caminhos de API e números de versão nos arquivos de source. Em nossos testes, tanto códigos de erro quanto caminhos completos foram recuperados com exatidão.

  • Crie o arquivo documents.jsonl e anote cada arquivo de source com seu módulo, tipo de documento e versão:

    {"path": "documents/order-api.md", "metadata": {"module": "order-service", "docType": "API", "version": "v2"}}
    {"path": "documents/order-timeout-runbook.md", "metadata": {"module": "order-service", "docType": "Runbook", "version": "v2"}}
    {"path": "documents/order-error-codes.html", "metadata": {"module": "order-service", "docType": "ErrorCode", "version": "v2"}}
    {"path": "documents/order-postmortem-2026-06.md", "metadata": {"module": "order-service", "docType": "Postmortem", "version": "v2"}}
  • Importe a referência de API, o Runbook, a tabela de códigos de erro e o postmortem como um conjunto. Distinga-os com docType. Em nosso teste, ao perguntarmos sobre um código de erro, todos os quatro tipos de arquivos de source foram atingidos simultaneamente. O modelo produziu uma resposta completa no formato "significado → etapas de solução de problemas → casos históricos".

Tratar arquivos de source HTML

Arquivos HTML podem ser carregados e analisados diretamente. O conteúdo dentro de <table> entra no índice. Em nosso teste, a consulta por ORD-42901, que existe apenas em uma tabela de códigos de erro em HTML, atingiu exatamente esse arquivo.

No entanto, o HTML tem uma granularidade de fragmentação mais grossa que o Markdown. Neste teste, um arquivo HTML contendo duas tabelas foi analisado em 1 fragmento. Isso está relacionado à forma como o HTML é processado. Antes de escolher um formato de arquivo de source, entenda dois pontos:

  • Blocos de código não preservam seu layout original. As tags <pre> e <code> são reconhecidas como blocos, mas o texto é extraído recursivamente e unido com espaços. Indentação, quebras de linha e delimitadores de código são perdidos. Para arquivos de source com grandes quantidades de código ou comandos, converta-os para Markdown antes da importação. Caso contrário, os trechos de código recuperados podem não ser utilizáveis diretamente.

  • Tabelas são indexadas como um todo. Uma <table> é anexada como um único segmento independente em sua forma de string HTML original e não é dividida por linha. Portanto, tabelas grandes formam facilmente fragmentos grossos. Conclusão: textos descritivos simples e tabelas pequenas podem usar HTML diretamente. Quando precisar de fidelidade exata, recuperação por segmento de código ou divisão de tabelas grandes, prefira Markdown ou arquivos de source estruturados. Após a importação, verifique amostras dos fragmentos na página Data Management.

Configure a política de fragmentação

Etapas de solução de problemas e exemplos de código não devem ser cortados no meio. Na página de detalhes da base de conhecimento, clique em Create Policy em Processing Policy para ajustar a granularidade dos fragmentos. A unidade do comprimento máximo do segmento é caracteres (padrão 512). Defina entre 580 e 770 caracteres (cerca de 384 a 512 tokens) para que "uma etapa completa de solução de problemas" ou "um exemplo de código" caiba no mesmo fragmento. A proporção de caracteres para tokens varia conforme o idioma e o tokenizador dos seus arquivos de source. Use o valor em caracteres como base e verifique amostras dos fragmentos após a importação.

Etapa 3: Configure parâmetros de recuperação e o prompt

Consultas de solução de problemas de desenvolvimento consistem principalmente em códigos de erro, caminhos de API e comandos. Elas pertencem à categoria de correspondência exata de termos. Portanto, os parâmetros diferem muito de outros cenários.

{
  "retrieval": {
    "page_size": 6,
    "candidate_count": 64,
    "min_score": 0.05,
    "semantic_weight": 0.15,
    "enable_query_expansion": false,
    "rerank_model_name": "",
    "tag_filter": {
      "relation": "and",
      "conditions": [
        {"field": "module", "op": "=", "value": "order-service"}
      ]
    }
  },
  "scenario": {
    "title": "Development docs and troubleshooting assistant",
    "system_prompt": "You are a development documentation assistant. Answer only based on the retrieved materials. Preserve API names, error codes, commands, and code as-is. List troubleshooting steps in order and cite them as [Source N]. When write operations or high-risk commands are involved, explicitly state the risk level and require manual confirmation. When the retrieved materials are empty, reply only that the materials are insufficient and warn against performing any change operation.",
    "image_enabled": false,
    "sample_questions": [
      "What are the required parameters for the order query API?",
      "In what order should I troubleshoot a connection timeout?",
      "In which documents does this error code appear?"
    ]
  }
}

A lista a seguir descreve os parâmetros ajustados para este cenário. Os campos restantes no exemplo são mantidos do tutorial referenciado.

  • semantic_weight: 0.15: Fator de ponderação da pontuação final. A pontuação é calculada como score ≈ (1-semantic_weight) × keywordScore + semantic_weight × semanticScore (um recurso de classificação também pode ser adicionado). O min_score realiza a pós-filtragem nessa pontuação final. Este cenário usa 0,15 para que a pontuação de palavras-chave de termos exatos, como códigos de erro e caminhos de API, domine a classificação. Como semantic_weight interage com min_score, ajuste os dois juntos conforme descrito em Tune semantic_weight and min_score together.

  • enable_query_expansion: false: A expansão de consulta fica desativada para evitar a reescrita de códigos de erro e nomes de API. Em nosso teste, após ativá-la, o keywordScore da consulta ORD-50021 caiu de 0,283 para 0,226. Além disso, o número de resultados recuperados para a consulta ORD-42901 caiu de 2 para 1. Mantenha essa opção desativada para recuperação de termos exatos.

  • tag_filter fixa o module: Isso impede que APIs com nomes idênticos de sistemas diferentes interfiram umas nas outras. Consulte Step 1: Define module tags para ver o efeito. Se um único ponto de entrada precisar abranger vários módulos, use {"field": "module", "op": "in", "value": ["order-service", "user-service"]}.

  • Um rerank_model_name em branco significa que a reclassificação não está ativada. A correspondência exata para códigos de erro depende da pontuação de palavras-chave, então a reclassificação traz benefícios limitados.

  • op suporta apenas =, in e not in: Em nossos testes, , >, , <, , empty, not empty, start with e end with são ignorados silenciosamente e todos os dados são retornados (nenhum erro é relatado). contains e not contains são meramente aliases de in/not in, não representando contenção de string; passar fragmentos de string retorna 0 resultados. Aliases como eq, == e like retornam 400 Unsupported tag filter operator, e todas as perguntas falham. Após configurar as condições de filtro, compare o número total de resultados com os resultados não filtrados para confirmar que o filtro realmente entrou em vigor.

Ajustar semantic_weight e min_score conjuntamente

O parâmetro semantic_weight altera apenas a pontuação ponderada final; ele não modifica as duas subpontuações (keywordScore e semanticScore) em scoreDetails dos resultados de recuperação. A pontuação ponderada é aproximadamente:

score ≈ semantic_weight × semanticScore + (1 - semantic_weight) × keywordScore

Observe dois pontos antes de ajustar:

  1. Reduzir semantic_weight não necessariamente diminui a pontuação total. A pontuação total cai apenas quando o semanticScore do lote de resultados é maior que seu keywordScore. É por isso que este documento também reduz min_score para 0,05. Calibre os dois parâmetros juntos verificando scoreDetails.

  2. Ao definir semantic_weight como 0, a implementação atual deixa de aplicar o limiar min_score. Não use 0 para significar "recuperação pura por palavras-chave". Para materiais de desenvolvimento, o keywordScore geralmente é muito menor que o semanticScore (em nossos testes, cerca de 0,16–0,28 e 0,64–0,78, respectivamente). Portanto, sob essa distribuição, reduzir semantic_weight puxa a pontuação total para baixo. Isso não é uma regra universal: somente quando o semanticScore do lote de resultados é maior que seu keywordScore é que reduzir o peso empurra a pontuação total para baixo; caso contrário, ela aumenta. Se min_score não for reduzido ao mesmo tempo, até mesmo resultados semanticamente relevantes serão filtrados:

Consulta

semantic_weight=0.15

semantic_weight=0.7

/api/v2/orders/{orderId}

8 resultados, pontuação máxima 0,269

8 resultados, pontuação máxima 0,602

kubectl -n order rollout undo deploy/order-api

4 resultados

8 resultados

Consulta em linguagem natural "Por que a API de pedidos desacelerou significativamente recentemente?"

3 resultados

8 resultados

Ao usar semantic_weight=0.15, defina min_score para cerca de 0,05 (o exemplo acima está configurado dessa forma). Se você deixar min_score em 0,15 ou mais, a recuperação de consultas baseadas em comandos e em linguagem natural cai pela metade. Durante o ajuste, fixe um parâmetro e observe as duas subpontuações por meio de scoreDetails antes de decidir.

Restringir comandos de alto risco e resultados vazios

O assistente de solução de problemas gera comandos executáveis diretamente. Portanto, restrinja dois aspectos.

Comandos de alto risco. Marque o nível de risco e os requisitos de confirmação em uma tabela dentro dos arquivos de source. O modelo os transmitirá fielmente. Em nosso teste, um comando de reinicialização marcado como "alto risco, confirmação por duas pessoas necessária" no Runbook, quando consultado, foi retornado pelo modelo junto com as declarações explícitas "o nível de risco é alto", "confirmação por duas pessoas é necessária" e "não pule a solução de problemas e execute a reinicialização diretamente".

Resultados de recuperação vazios. O código deve interceptar esse caso, conforme descrito em Step 4: Adapt the reused code.

Etapa 4: Adaptar o código reutilizado

Adapte o projeto reutilizado com duas modificações antes de validar o assistente: um fallback para resultado vazio em app.py e uma correção de código de retorno em kb_client.py.

**Interceptar resultados de recuperação vazios em app.py**

No arquivo app.py reutilizado, a função llm_answer() ainda chama o LLM quando os resultados da recuperação estão vazios. Nesse ponto, o contexto é uma string vazia, e o modelo responde inteiramente com base em seu próprio conhecimento. Em nosso teste, ao perguntarmos "Como me recupero de um split-brain em cluster Redis?", algo que a base de conhecimento não cobre, o modelo gerou um plano operacional completo com parâmetros e não deu nenhum aviso. Em cenários de solução de problemas, os usuários podem copiar os comandos e executá-los diretamente no ambiente de produção. Portanto, intercepte isso no nível do código:

def llm_answer(question: str, results: list[dict[str, Any]]) -> str:
    if not LLM.get("enabled", True):
        return "The LLM is not enabled. Review the retrieval results below."
    if not results:
        return "No relevant document was retrieved from the knowledge base, so no troubleshooting steps can be provided. Do not perform any change operation based on this. We recommend that you contact the module owner."
    ...

Restringir isso apenas no prompt não é confiável. Mesmo que você declare "avise explicitamente quando os materiais não cobrirem a pergunta", o modelo ainda assim responderá.

O último critério de aceitação em Step 5: Upload, publish, and verify abrange esta modificação.

**Corrigir a verificação de código de retorno em kb_client.py**

No arquivo kb_client.py reutilizado, a função _check() avalia getattr(body, "code", 0) != 0. O code de uma resposta bem-sucedida é None, então recuperações bem-sucedidas são relatadas como "Failed: None". Altere a verificação para getattr(body, "code", None) not in (None, 0, "0"). Após a correção, recuperações bem-sucedidas não relatarão mais "Failed: None".

Etapa 5: Carregar, publicar e verificar

  1. Carregue os arquivos de source. O campo MetaFields aplica-se a todo o lote; o script primeiro agrupa por tag e depois envia em lotes.

    python upload.py --manifest documents.jsonl
  2. Na página Data Management do console, confirme se o status do arquivo de source é Completed. Uma resposta bem-sucedida da API de upload significa apenas que a análise assíncrona foi enviada.

  3. Na página Version Management, clique em Publish Version. Após concluir o assistente, confirme se o status da nova versão é Published.

    Importante

    Podem existir no máximo 3 versões publicadas ao mesmo tempo. Depois que o limite é atingido, o botão Publish Version fica acinzentado, mas a página ainda mostra "There are currently N pending changes to publish". Exclua versões antigas desnecessárias nos registros de versão primeiro. Documentos de desenvolvimento mudam frequentemente (cada lançamento pode atualizar a referência de API e os Runbooks). Portanto, mantenha apenas "a versão atual + a versão histórica mais recente".

  4. Inicie o service:

    python app.py
  5. Envie uma pergunta de teste:

    curl -sS http://127.0.0.1:7860/api/ask \
      -H 'Content-Type: application/json' \
      -d '{"question":"In what order should I troubleshoot a connection timeout?"}'
  6. Valide com base nos cinco critérios de aceitação a seguir:

    • A página abre normalmente e, após enviar uma pergunta, a resposta e as fontes de recuperação são retornadas juntas.

    • Cada [Source N] na resposta pode ser associado ao fragmento correspondente do arquivo de source na seção de fontes abaixo.

    • Ao perguntar com um código de erro completo, todos os arquivos de source nos quais o código de erro aparece são listados. Em nosso teste, a consulta por ORD-50021 listou corretamente quatro arquivos de source — a tabela de códigos de erro, a referência de API, o Runbook e o postmortem — juntamente com a localização em cada um.

    • Ao perguntar sobre uma operação arriscada, a resposta inclui o nível de risco e o requisito de confirmação manual.

    • Ao perguntar sobre algo que a base de conhecimento não cobre, a resposta é "materials are insufficient" com um aviso para não realizar nenhuma operação de alteração, e nenhum comando fabricado aparece. Teste este caso na prática.

Observações para cenários de desenvolvimento

  • Ponto de entrada por módulo — Depois que tag_filter fixa o module, o ponto de entrada responde apenas a perguntas sobre aquele módulo. Mantenha as perguntas de exemplo dentro do mesmo módulo também.

  • Termos exatos completos — Mantenha códigos de erro, caminhos de API, comandos e números de versão completos nos arquivos de source. Esses termos exatos são os principais pontos de entrada de recuperação neste cenário.

  • Marcação de risco — Marque níveis de risco e requisitos de confirmação nos arquivos de source. Não dependa do modelo para julgar quais comandos são perigosos.

  • Sem segredos nos uploads — Não carregue segredos, tokens, contas de rede interna ou strings de conexão de banco de dados de produção. O conteúdo dos fragmentos é enviado ao LLM como contexto.

  • Descrições de API obsoletas — Mantenha as descrições de operações de API obsoletas. Em nosso teste, depois que a referência de API declarou "a API v1 está obsoleta e seus nomes de parâmetros são incompatíveis", o modelo não misturou parâmetros antigos ao responder perguntas sobre os parâmetros da nova API.

  • O campo tags nos resultados de recuperação não reflete o módulo e a versão gravados. É um campo diferente dos MetaFields de AddDocuments, e nenhum parâmetro pode ativar seu retorno. Um valor vazio não significa que o upload falhou. Quando precisar anotar módulo e versão na resposta, una os resultados de recuperação com documents.jsonl por documentId e anexe as informações ao contexto.

  • Implantação em produção — Não continue usando o servidor de desenvolvimento Flask para implantação online. Mude para um servidor WSGI de produção e adicione gerenciamento de segredos, autenticação, auditoria e limitação de taxa.

Perguntas frequentes

Sintoma

Causa e solução

A pergunta retorna 500, e o log mostra 400 Unsupported tag filter operator

O operador op usa aliases como eq, == ou like. Use =, in ou not in em vez disso.

Um filtro de tag foi adicionado, mas o número de resultados é exatamente o mesmo sem filtragem

Um operador ineficaz (como >, , ou empty) foi usado. Apenas =, in e not in têm efeito.

APIs com nomes idênticos de outros sistemas são recuperadas

O filtro module não está configurado, ou o valor do filtro não corresponde ao valor escrito no momento do upload.

Muito menos resultados de recuperação do que o esperado

Quando semantic_weight é baixo, as pontuações gerais são reduzidas e, combinadas com min_score, mais resultados são filtrados. Reduza min_score conforme descrito em Tune semantic_weight and min_score together.

A recuperação de código de erro é imprecisa

Confirme se enable_query_expansion está definido como false. Quando ativado, códigos de erro podem ser reescritos.

A filtragem de tags sempre retorna 0 resultados

A ortografia do nome da tag não corresponde àquela escrita no momento do upload (a API não relata erro e simplesmente retorna 0 resultados). Verifique na página de detalhes da base de conhecimento escolhendo Tags > Gerencie.

Uma pergunta não coberta pela base de conhecimento retornou comandos aparentemente executáveis

A função llm_answer() ainda chama o LLM quando os resultados da recuperação estão vazios. Adicione o fallback para resultado vazio conforme descrito em Step 4: Adapt the reused code.

Detalhes em arquivos de source HTML não podem ser recuperados

O HTML tem uma granularidade de fragmentação mais grossa. Reduza o comprimento máximo do segmento. Converta HTML com grandes quantidades de código para Markdown primeiro.

A recuperação relata "Failed: None"

A verificação em _check() do código reutilizado está incorreta (getattr(body, "code", 0) != 0); o code de uma resposta bem-sucedida é None. Aplique a correção em Step 4: Adapt the reused code.

A recuperação retorna 404 Knowledge base version ... does not exist

Nenhuma versão foi publicada ainda, ou o número de versão codificado na configuração foi excluído. Use LATEST_PUBLISHED em vez disso.

Próximas etapas

  • Expanda a cobertura módulo por módulo: crie um ponto de entrada independente para cada valor de module e mantenha as perguntas de exemplo dentro do mesmo módulo.

  • Prepare-se para a produção: mude do servidor de desenvolvimento Flask para um servidor WSGI de produção e adicione gerenciamento de segredos, autenticação, auditoria e limitação de taxa.

  • Gerencie versões conforme seus documentos mudam: publique novas versões quando as referências de API ou Runbooks forem atualizados e mantenha apenas a versão atual mais a versão histórica mais recente.