La base de connaissances Alibaba Cloud Milvus permet de transformer les lois et règlements publics, les jugements judiciaires et les politiques de conformité des entreprises en un assistant documentaire consultable : retrouvez des affaires similaires à partir des détails d'un dossier, résumez les opinions judiciaires et affichez les sources originales.
Solution
Le pipeline de bout en bout est identique à celui décrit dans Créer une application intelligente de questions-réponses pour le service client à l'aide de la base de connaissances Alibaba Cloud Milvus : définissez des tags dans la console → importez les documents sources par lots selon les tags → publiez une version → récupérez les données via le SDK avec filtrage optionnel par tag → un LLM génère des réponses en citant les sources → Flask sert la page de questions-réponses. Réutilisez directement le code d'ingénierie de ce tutoriel : kb_client.py, upload.py, app.py, templates/index.html et start.sh. Cette rubrique couvre uniquement les éléments à adapter pour un scénario juridique : les tags d'affaire (incluant un type numérique), les paramètres de récupération, les contraintes de prompt et la gestion des résultats vides que vous devez ajouter.
Pour la première version, choisissez un seul type de délit ou un seul sujet de conformité et importez uniquement 10 à 50 documents sources. Élargissez la portée après validation. L'ensemble du processus prend environ 25 à 40 minutes. Le temps d'analyse des documents dépend du nombre et de la taille des documents sources.
Cette solution constitue un outil d'aide à la recherche documentaire et ne produit pas d'avis juridiques. Maintenez une étape de révision manuelle tant pour les résultats de recherche que pour les résumés générés par le modèle.
Prérequis
Une base de connaissances Milvus a été créée et vous avez noté l'ID de la base de connaissances (par exemple,
kd-803ae9b10cc31).Vous avez créé un utilisateur RAM sous votre compte Alibaba Cloud, sélectionné Use permanent AccessKey et joint la stratégie système
AliyunMilvusFullAccess.Vous avez préparé l'endpoint et la clé API d'un grand modèle de langage prenant en charge le protocole OpenAI
chat/completions.Python 3.8 ou une version ultérieure est installé localement et le répertoire du projet a été configuré en suivant les instructions.
La vérification des autorisations et la désidentification de vos documents sources sont terminées. Pour plus de détails, consultez la section Remarques pour les scénarios juridiques.
Étape 1 : Définir les tags d'affaire
Sur la page des détails de la base de connaissances, sous Basic Information, choisissez Tags > Manage et ajoutez les quatre tags suivants. Sélectionnez le type de champ dans la liste déroulante située à droite de la zone de saisie du nom du tag. Les options disponibles sont string, int64, list, float32 et bool.
|
Nom du tag |
Type de champ |
Description |
Exemples de valeurs |
|
docType |
string |
Type de document |
Jugement judiciaire, Lois et règlements, Politique de conformité |
|
court |
string |
Tribunal de jugement |
Premier tribunal populaire de la ville XX |
|
caseType |
string |
Chef d'accusation |
Fraude contractuelle, Accident de la circulation |
|
judgmentYear |
int64 |
Année du jugement |
2025 |
Définir l'année du jugement comme int64 n'active pas les requêtes par plage. L'API de récupération ne prend pas en charge les expressions de plage d'entiers. L'intérêt de définir l'année du jugement comme int64 réside dans le fait que le serveur convertit les chaînes numériques en entiers, permettant ainsi aux opérateurs =, in et not in de fonctionner correctement. Par conséquent, pour une exigence telle que « affaires des trois dernières années », le client doit d'abord calculer la liste des années, puis la transmettre avec in, par exemple [2024, 2025, 2026]. N'utilisez pas ≥ ou ≤ dans les conditions tag_filter. Lorsque la plage d'années est large, il n'existe pas d'alternative aussi efficace : divisez-la en plusieurs requêtes.
La boîte de dialogue de gestion des tags est enregistrée dans son ensemble et la liste des tags se charge de manière asynchrone après l'ouverture de la boîte de dialogue. Attendez que tous les tags existants soient affichés avant d'ajouter de nouveaux tags et de cliquer sur Done. Sinon, l'enregistrement risque d'écraser l'ensemble avec une « liste vide + nouveaux tags », effaçant ainsi les définitions de tags existantes.
Prenez note des points suivants concernant les types et les valeurs des tags :
Pour un tag de type
int64, vous pouvez écrire un nombre JSON brut (tel que2025) directement dansdocuments.jsonl. Lors du filtrage,valuecorrespond, que vous transmettiez un nombre ou une chaîne.Les valeurs des tags peuvent être une chaîne vide. Les lois et règlements ainsi que les politiques de conformité n'ont pas de tribunal de jugement, vous pouvez donc écrire
"court": "". La console affiche ceci commecourt=et vous pouvez ultérieurement utilisercourt = ""comme condition de filtre pour cibler exactement ces documents sources.
Étape 2 : Préparer les documents sources et le manifeste d'importation
Placez les documents sources dans le répertoire
documents/. Les formats PDF, DOCX, Markdown, TXT et autres sont pris en charge. Conservez le numéro d'affaire, le tribunal et la date du jugement dans le corps du texte ou dans le nom du fichier. Lors de nos tests, après avoir écrit le numéro d'affaire sur la première ligne du corps du texte, nous pouvions retrouver le jugement correspondant directement par numéro d'affaire.-
Créez
documents.jsonlet annotez chaque document source avec le type de document, le tribunal, le chef d'accusation et l'année du jugement :{"path": "documents/criminal-case-001.md", "metadata": {"docType": "Court judgment", "court": "The First People's Court of XX City", "caseType": "Contract fraud", "judgmentYear": 2025}} {"path": "documents/criminal-case-002.md", "metadata": {"docType": "Court judgment", "court": "The Second People's Court of XX City", "caseType": "Contract fraud", "judgmentYear": 2023}} {"path": "documents/law-excerpt.md", "metadata": {"docType": "Laws and regulations", "court": "", "caseType": "Criminal", "judgmentYear": 2024}} {"path": "documents/compliance-policy.md", "metadata": {"docType": "Compliance policy", "court": "", "caseType": "Compliance", "judgmentYear": 2024}} Les jugements judiciaires doivent préserver le contexte des faits de l'affaire, le raisonnement du jugement et la conclusion. Sur la page des détails de la base de connaissances, cliquez sur Create Policy sous Processing Policy pour ajuster la granularité des chunks. L'unité de la longueur maximale du chunk est le caractère (512 par défaut). Définissez-la entre 770 et 1 150 caractères (environ 512 à 768 tokens) afin que les points clés du litige et le raisonnement du jugement se trouvent autant que possible dans le même chunk.
Importez des affaires ayant des conclusions différentes pour un même chef d'accusation. La valeur de la recherche juridique réside précisément dans la présentation des divergences. Lors de nos tests, après avoir importé deux jugements de fraude contractuelle avec des conclusions opposées, le modèle a cité les deux et a explicitement souligné que « dans une affaire, la co-auteurship était reconnue, tandis que dans l'autre, elle ne l'était pas en raison d'un manque de preuves de conspiration ». Cela offre une référence plus précieuse que l'importation de matériaux aboutissant à une conclusion unique. Exigez dans le prompt que ces affaires soient présentées séparément, afin que le modèle ne présente pas la conclusion d'une affaire individuelle comme une règle générale.
Étape 3 : Configurer les paramètres de récupération et le prompt
Dans config.json, ajustez aliyun.knowledge_base_version, retrieval et scenario pour le scénario juridique. L'exemple suivant montre uniquement ces trois blocs. Conservez les autres blocs de votre config.json issus du tutoriel référencé, y compris l'ID de la base de connaissances ainsi que l'endpoint et la clé API du LLM que vous avez préparés dans les prérequis.
{
"aliyun": {
"knowledge_base_version": "LATEST_PUBLISHED"
},
"retrieval": {
"page_size": 8,
"candidate_count": 80,
"min_score": 0.25,
"semantic_weight": 0.4,
"enable_query_expansion": true,
"rerank_model_name": "qwen3-rerank",
"tag_filter": {
"relation": "and",
"conditions": [
{"field": "docType", "op": "=", "value": "Court judgment"}
]
}
},
"scenario": {
"title": "Legal and compliance case retrieval",
"system_prompt": "You are a legal materials retrieval assistant. Do not provide final legal opinions. Summarize facts, key issues in dispute, and judicial opinions and their bases strictly from the retrieved materials, and annotate each item with [Source N]. When different cases reach conflicting conclusions, present them separately. If the retrieved materials are empty or irrelevant to the question, reply only with 'Insufficient materials; manual review is recommended', and never cite any law, regulation, judicial interpretation, or case that does not appear in the retrieved materials.",
"image_enabled": false,
"sample_questions": [
"When someone defrauds payment for goods by fabricating the ability to perform a contract, how is joint crime in contract fraud determined?",
"How does voluntary surrender after causing a traffic accident affect sentencing?",
"Which cases discuss the distinction between a principal offender and an accessory?"
]
}
}
Prenez note des points suivants concernant les paramètres clés :
semantic_weight=0.4: la recherche juridique utilise intensivement les numéros d'affaire, les types de délits et la terminologie juridique ; réduisez donc le poids sémantique et augmentez en conséquence le poids des mots-clés. Lors de nos tests, une requête avec un numéro d'affaire complet ciblait précisément le jugement correspondant. Vous pouvez ajuster cette valeur en utilisantscoreDetailsdans les résultats de récupération, qui contientkeywordScoreetsemanticScore.candidate_count=80avecqwen3-rerankactivé : élargissez l'ensemble des candidats et laissez le modèle de reclassement classer les candidats par similarité avec les affaires similaires. Notez que les scores de reclassement et les scores vectoriels ne sont pas sur la même échelle. Combinés avecmin_score, ils peuvent filtrer certains résultats. Lors du réglage, fixez d'abord l'un des deux.Maintenez
knowledge_base_versiondéfini surLATEST_PUBLISHEDsauf si le suivi de conformité exige une version figée. Pour plus de détails sur le figage et la suppression des versions, consultez la section Gérer les versions publiées.
Seuls trois opérateurs fonctionnent pour le filtrage par tag
Lors de nos tests, dans la version actuelle, seuls =, in et not in dans tag_filter.conditions prennent réellement effet en tant que valeur op :
|
Opérateur |
Comportement |
Exemple |
|
|
Correspondance exacte ; pour un tag |
|
|
|
Correspondance énumérée |
|
|
|
Exclure une énumération |
|
|
|
La condition est ignorée et toutes les données sont renvoyées (aucune erreur n'est levée) |
— |
|
|
La condition est ignorée et toutes les données sont renvoyées |
— |
|
|
Alias de |
— |
Si vous transmettez un opérateur qui ne prend pas effet, l'API ne lève pas d'erreur et renvoie toutes les données sans filtrage. Dans un scénario juridique, cela signifie que les affaires qui auraient dû être exclues entrent toujours dans le contexte du LLM, et rien ne semble incorrect sur la page. Par conséquent :
N'utilisez pas > ou ≥ pour filtrer par plage d'années. Utilisez plutôt
inavec une énumération des années, par exemple{"field": "judgmentYear", "op": "in", "value": [2023, 2024, 2025]}.N'utilisez pas empty pour vérifier si un tag est vide. Utilisez plutôt
{"field": "court", "op": "=", "value": ""}.-
Après avoir configuré une condition de filtre, comparez toujours le nombre total de résultats avec le résultat non filtré pour confirmer que le filtre prend effectivement effet. Si les deux sont identiques, la condition n'a pas été appliquée.
Si
opest écrit sous forme d'alias tel queeq,==oulike, l'API renvoie400 Unsupported tag filter operatoret chaque question échoue. La liste « Supported operators » indiquée dans ce message d'erreur inclut des opérateurs du tableau ci-dessus qui ne prennent pas effet ; ils ne peuvent donc pas être considérés comme une liste utilisable.
Étape 4 : Intercepter les résultats de récupération vides dans app.py
Dans le app.py réutilisé, llm_answer() appelle toujours le LLM lorsque les résultats de récupération sont vides. Le contexte est alors une chaîne vide et le modèle répond entièrement à partir de ses propres connaissances. Lors de nos tests, nous avons posé une question non couverte par la base de connaissances, telle que « Comment les dommages-intérêts sont-ils calculés pour la violation de la propriété intellectuelle ? ». Le modèle a produit de longues règles de calcul des dommages et a fabriqué des annotations de sources telles que [Source 1 : Interprétation de ... Dommages punitifs, Article 2], bien que sources fût vide. Dans un scénario juridique, ce type de sortie est hautement trompeur et doit être intercepté au niveau du code :
def llm_answer(question: str, results: list[dict[str, Any]]) -> str:
if not LLM.get("enabled", True):
return "The LLM is disabled; please see the retrieval results below."
if not results:
return "No materials related to this question were found in the knowledge base, so I cannot answer. Add relevant materials and try again, or escalate to manual review."
...
Les contraintes de prompt seules ne sont pas fiables. Même lorsque system_prompt indique explicitement « indiquer clairement lorsque les matériaux sont insuffisants », le modèle répond quand même. Après avoir ajouté la solution de repli ci-dessus, les questions non couvertes renvoient systématiquement le message d'invite, tandis que les questions normales avec résultats de récupération restent inchangées.
Étape 5 : Télécharger, publier et vérifier
-
Téléchargez les documents sources.
MetaFieldss'applique à l'ensemble du lot ; le script regroupe les documents par tag et les soumet par lots.python upload.py --manifest documents.jsonl Sur la page Data Management de la console, confirmez que le statut du document est Processed. Une réponse réussie de l'API de téléchargement signifie uniquement que l'analyse asynchrone a été soumise.
Sur la page Version Management, cliquez sur Publish Version, suivez l'assistant et confirmez que le statut de la nouvelle version est Published. Si le bouton est grisé, supprimez d'abord les anciennes versions qui ne sont plus nécessaires. Consultez la section Gérer les versions publiées.
-
Démarrez le service.
python app.py -
Une fois le service démarré, posez une question de test :
curl -sS http://127.0.0.1:7860/api/ask \ -H 'Content-Type: application/json' \ -d '{"question":"When someone defrauds payment for goods by fabricating the ability to perform a contract, how is joint crime in contract fraud determined?"}'
La page s'ouvre normalement et la soumission d'une question renvoie à la fois la réponse et les sources de récupération.
Chaque
[Source N]dans la réponse correspond à un chunk source dans la section des sources ci-dessous.Une requête avec un numéro d'affaire complet cible précisément le jugement judiciaire correspondant.
Lorsque vous posez une question sur un contenu non couvert par la base de connaissances, la réponse renvoie le message « Insufficient materials » et ne contient aucune citation juridique. Assurez-vous de tester cela en pratique — il s'agit du critère d'acceptation le plus critique pour ce scénario.
Après avoir ajouté davantage de matériaux et publié une nouvelle version, la page peut récupérer le nouveau contenu.
Gérer les versions publiées
LATEST_PUBLISHED utilise automatiquement la dernière version publiée. Vous pouvez également spécifier un numéro de version explicite (tel que v2) pour figer une version. Les scénarios de conformité exigent généralement de tracer quelle version des matériaux a soutenu quelle décision ; enregistrez donc le numéro de version effectivement utilisé dans les journaux d'appels de l'application.
Après la suppression d'une version, celle-ci devient immédiatement irrécupérable et le backend nettoie les données de manière asynchrone par la suite. Avant de supprimer une version, basculez toute application encore figée sur celle-ci vers une nouvelle version. Si vous supprimez une version utilisée par l'application, la récupération renvoie immédiatement 404 Knowledge base version ... does not exist et chaque question échoue sur la page.
Au maximum trois versions publiées peuvent exister simultanément. Une fois la limite atteinte, le bouton Publish Version est grisé, tandis que la page affiche toujours « There are N pending changes ». Supprimez d'abord les anciennes versions qui ne sont plus nécessaires depuis les enregistrements de version. Si vous figez une version, vous devez également établir un processus de mise à jour de la configuration avant de supprimer une ancienne version.
Remarques pour les scénarios juridiques
Avant de partager l'application avec les utilisateurs finaux, examinez les considérations suivantes :
Matériaux autorisés uniquement — Utilisez uniquement des matériaux que vous êtes autorisé à rendre publics et à traiter, et effectuez la vérification des autorisations avant le téléchargement. Si un jugement judiciaire contient des informations personnelles, désidentifiez-le d'abord. Les chunks de documents sont envoyés au LLM comme contexte, ce qui constitue une sortie de données.
Avertissement permanent — La page doit comporter un avertissement permanent. La page d'exemple indique seulement « Answers are generated from published knowledge base content ». Modifiez l'avis dans
templates/index.htmlpour indiquer clairement : « Ces résultats constituent uniquement une aide à la recherche documentaire et ne constituent pas des avis juridiques. Faites-les examiner par un professionnel. »Points d'entrée séparés par type de document — Par exemple, restreignez un point d'entrée de consultation publique avec
docType = Laws and regulationsafin qu'il ne récupère que les lois et règlements publics, tandis qu'un point d'entrée d'analyse interne autorise également les jugements judiciaires.Déploiement en production — Ne continuez pas à utiliser le serveur de développement Flask. Basculez vers un serveur WSGI de production et ajoutez la gestion des secrets, l'authentification, l'audit et la limitation du débit.
FAQ
|
Symptôme |
Cause et solution |
|
Chaque question renvoie |
Le numéro de version codé en dur dans la configuration a été supprimé ou n'a jamais été publié. Utilisez |
|
Les questions renvoient une erreur 500 et les journaux indiquent |
|
|
Le nombre de résultats avec filtrage par tag est exactement le même que sans filtrage |
Vous avez utilisé un opérateur qui ne prend pas effet (tel que |
|
Le filtrage par plage d'années n'a aucun effet |
Les opérateurs de comparaison numérique ne prennent pas effet. Utilisez |
|
Le filtrage par tag renvoie toujours 0 résultat |
Le nom du tag est orthographié différemment de ce qui a été écrit. L'API ne lève pas d'erreur et renvoie simplement 0 résultat. Vérifiez-le sous Tags > Manage sur la page des détails de la base de connaissances. Lors de nos tests, l'opérateur |
|
J'ai posé une question non couverte par la base de connaissances, mais j'ai obtenu des citations juridiques plausibles |
|
|
Le champ |
La version actuelle ne remplit pas rétroactivement les tags. Le champ |
|
Le téléchargement renvoie |
Chaque fichier du lot a été dédupliqué en raison de noms de fichiers en double. Renommez les fichiers ou supprimez d'abord les anciennes données dans la console. |
|
La récupération signale un échec avec |
La logique |
|
Après avoir activé le reclassement, il y a moins de résultats |
Les scores de reclassement et les scores vectoriels sont sur des échelles différentes. Combinés avec |