En tant qu'ingénieurqui a déployé des systèmes IA dans plus de 30 pharmacies en Chine, je peux vous dire sans détour : la gestion automatisée des membres est devenue un impératif stratégique. Dans cet article, je vais vous montrer comment construire un système complet utilisant l'API unifiée HolySheep — avec DeepSeek pour les咨询服务用药 et MiniMax pour les rappels vocaux — tout en maîtrisant les coûts grâce à des prix imbattables (DeepSeek V3.2 à $0.42/MTok contre $8 pour GPT-4.1).

🎯 Le Problème : Pourquoi les Pharmacies Chinoises Ont Besoin d'Automatisation IA

En tant que directeur technique d'une chaîne de pharmacies comptant 15 succursales à Shanghai, j'ai observé un problème récurrent : nos pharmaciens passaient 40% de leur temps à répondre aux mêmes questions sur les médicaments (用药问答) et 25% à rappeler les clients pour le renouvellement d'ordonnances. Avec un volume de 2 000+ membres actifs par magasin, l'approche manuelle était devenue intenable.

La solution ? Un système hybride exploitant :

🏥 Architecture du Système de Gestion des Membres

Notre architecture se compose de trois couches principales interconnectées via l'API HolySheep :


┌─────────────────────────────────────────────────────────────────┐
│                    PHARMACIE HOLYSHEEP MEMBER SYSTEM            │
├─────────────────────────────────────────────────────────────────┤
│  ┌───────────────┐    ┌───────────────┐    ┌───────────────┐   │
│  │  MiniMax      │    │   DeepSeek    │    │   HolySheep   │   │
│  │  Voice Call   │    │   智能问答    │    │   Unified API │   │
│  │  (语音回访)   │    │  (用药咨询)   │    │   (权限治理)  │   │
│  └───────┬───────┘    └───────┬───────┘    └───────┬───────┘   │
│          │                    │                    │           │
│          └────────────────────┼────────────────────┘           │
│                               ▼                                │
│                    ┌───────────────────────┐                   │
│                    │  Member Database      │                   │
│                    │  会员管理系统 (MySQL)  │                   │
│                    └───────────────────────┘                   │
│                               │                                │
│          ┌────────────────────┼────────────────────┐           │
│          ▼                    ▼                    ▼           │
│   ┌─────────────┐     ┌─────────────┐      ┌─────────────┐     │
│   │  微信小程序  │     │    APP      │      │   电话回访  │     │
│   │  会员端     │     │  药师端     │      │   自动外呼  │     │
│   └─────────────┘     └─────────────┘      └─────────────┘     │
└─────────────────────────────────────────────────────────────────┘

🔑 Configuration Initiale de l'API HolySheep

Avant de commencer, vous devez créer votre compte et obtenir votre clé API. Le processus est simple :

  1. Rendez-vous sur S'inscrire ici pour créer votre compte
  2. Générez votre clé API dans le tableau de bord
  3. Rechargez votre crédit via WeChat Pay ou Alipay (taux: ¥1 = $1)

Configuration de base - TOUT le code utilise HolySheep comme passerelle

import requests import json

⚠️ ATTENTION: Utilisez UNIQUEMENT api.holysheep.ai

Ne JAMAIS utiliser api.openai.com ou api.anthropic.com

HOLYSHEEP_API_KEY = "YOUR_HOLYSHEEP_API_KEY" # Remplacez par votre vraie clé BASE_URL = "https://api.holysheep.ai/v1" headers = { "Authorization": f"Bearer {HOLYSHEEP_API_KEY}", "Content-Type": "application/json" }

Test de connexion

response = requests.get( f"{BASE_URL}/models", headers=headers ) print(f"✅ Connexion réussie! Modèles disponibles: {len(response.json()['data'])}") print(f"📊 Votre crédit restant: Vérifiez sur le dashboard HolySheep")

💊 Module 1 : DeepSeek 用药问答 Intégré

Le cœur de notre système repose sur DeepSeek V3.2 pour les咨询服务用药. Pourquoi DeepSeek ? Parce que son coût de $0.42/MTok le rend 19x moins cher que GPT-4.1 ($8/MTok) tout en offrant d'excellentes performances en chinois médical.


import requests
import json
from datetime import datetime

class MedicationAdvisor:
    """
    Assistant用药问答 basé sur DeepSeek V3.2 via HolySheep API
    """
    
    def __init__(self, api_key):
        self.api_key = api_key
        self.base_url = "https://api.holysheep.ai/v1"
        
    def ask_medication_question(self, question: str, user_id: str, member_tier: str) -> dict:
        """
        Interroge DeepSeek pour une question sur les médicaments
        
        Args:
            question: Question de l'utilisateur (en chinois)
            user_id: ID du membre dans notre système
            member_tier: Niveau du membre (gold, silver, bronze)
        
        Returns:
            dict avec la réponse et les métadonnées
        """
        
        # Prompt système optimisé pour le contexte pharmaceutique chinois
        system_prompt = """你是连锁药房的专业用药顾问。你的职责是:
1. 回答顾客关于药品使用方法、剂量、副作用的一般性问题
2. 强调重要提醒:如有严重症状请立即就医
3. 不提供具体处方建议,始终建议咨询医生
4. 回答要专业、友好、简洁
5. 对于会员客户,根据等级提供个性化健康建议"""
        
        # Construction du prompt utilisateur avec contexte
        user_prompt = f"""[会员信息] 会员ID: {user_id}, 等级: {member_tier}
[问题] {question}

请用中文回答上述用药问题。"""
        
        payload = {
            "model": "deepseek-v3.2",  # Modèle économique HolySheep
            "messages": [
                {"role": "system", "content": system_prompt},
                {"role": "user", "content": user_prompt}
            ],
            "temperature": 0.7,
            "max_tokens": 500,
            "stream": False
        }
        
        start_time = datetime.now()
        
        try:
            response = requests.post(
                f"{self.base_url}/chat/completions",
                headers={
                    "Authorization": f"Bearer {self.api_key}",
                    "Content-Type": "application/json"
                },
                json=payload,
                timeout=30
            )
            
            response.raise_for_status()
            result = response.json()
            
            latency_ms = (datetime.now() - start_time).total_seconds() * 1000
            tokens_used = result.get('usage', {}).get('total_tokens', 0)
            cost_usd = (tokens_used / 1_000_000) * 0.42  # $0.42/MTok pour DeepSeek
            
            return {
                "success": True,
                "answer": result['choices'][0]['message']['content'],
                "tokens_used": tokens_used,
                "latency_ms": round(latency_ms, 2),
                "cost_usd": round(cost_usd, 6),
                "model": "deepseek-v3.2"
            }
            
        except requests.exceptions.RequestException as e:
            return {
                "success": False,
                "error": str(e),
                "error_type": type(e).__name__
            }
    
    def batch_process_questions(self, questions: list) -> list:
        """Traite plusieurs questions en lot pour optimiser les coûts"""
        results = []
        total_cost = 0
        
        for q in questions:
            result = self.ask_medication_question(
                q['question'], 
                q['user_id'], 
                q.get('member_tier', 'bronze')
            )
            results.append({
                "question_id": q.get('id'),
                "result": result
            })
            if result['success']:
                total_cost += result['cost_usd']
        
        print(f"📊 Lot traité: {len(results)} questions, coût total: ${total_cost:.6f}")
        return results

Exemple d'utilisation

advisor = MedicationAdvisor("YOUR_HOLYSHEEP_API_KEY")

Question typique d'un membre Gold

result = advisor.ask_medication_question( question="我最近在服用降压药,可以同时喝柚子汁吗?", user_id="MEM_2024_8847", member_tier="gold" ) if result['success']: print(f"💬 Réponse IA: {result['answer']}") print(f"⏱️ Latence: {result['latency_ms']}ms") print(f"💰 Coût: ${result['cost_usd']}") else: print(f"❌ Erreur: {result['error']}")

📞 Module 2 : MiniMax 语音回访 avec Synthèse Vocale

Le deuxième pilier de notre système est l'automatisation des rappels vocaux (语音回访) pour le renouvellement d'ordonnances et le suivi patient. MiniMax Speech-02 offre une qualité vocale naturelle indiscernable d'une voix humaine.


import requests
import base64
import time
from datetime import datetime, timedelta

class VoiceRecallSystem:
    """
    Système de rappel vocal 自动外呼 basé sur MiniMax via HolySheep
    
    Fonctionnalités:
    - Synthèse vocale TTS en chinois mandarin
    - Planification des appels de suivi
    - Intégration avec le système de members
    - Journalisation complète des appels
    """
    
    def __init__(self, api_key):
        self.api_key = api_key
        self.base_url = "https://api.holysheep.ai/v1"
        
    def generate_voice_message(self, text: str, voice_id: str = "female_medical_pro") -> dict:
        """
        Génère un message vocal à partir de texte
        
        Args:
            text: Texte à convertir en audio (chinois recommandé)
            voice_id: Identifiant de la voix (female_medical_pro, male_medical_pro)
        
        Returns:
            dict avec l'URL audio ou le base64
        """
        
        payload = {
            "model": "minimax-speech-02",
            "input": text,
            "voice_id": voice_id,
            "response_format": "mp3",
            "speed": 1.0
        }
        
        start_time = time.time()
        
        try:
            response = requests.post(
                f"{self.base_url}/audio/speech",
                headers={
                    "Authorization": f"Bearer {self.api_key}",
                    "Content-Type": "application/json"
                },
                json=payload,
                timeout=60
            )
            
            response.raise_for_status()
            
            # La réponse est directement le contenu audio
            audio_content = response.content
            audio_base64 = base64.b64encode(audio_content).decode('utf-8')
            
            latency_ms = (time.time() - start_time) * 1000
            
            return {
                "success": True,
                "audio_base64": audio_base64,
                "latency_ms": round(latency_ms, 2),
                "text_length": len(text),
                "voice_id": voice_id
            }
            
        except requests.exceptions.RequestException as e:
            return {
                "success": False,
                "error": str(e)
            }
    
    def schedule_recall_for_member(self, member_id: str, member_name: str, 
                                    medication: str, pharmacy_phone: str) -> dict:
        """
        Planifie un rappel vocal pour un membre concernant son renouvellement
        
        Args:
            member_id: ID du membre
            member_name: Nom du membre
            medication: Nom du médicament
            pharmacy_phone: Numéro de la pharmacie à afficher
        
        Returns:
            dict avec les détails de l'appel programmé
        """
        
        # Construction du message personnalisé
        message = f"""您好,{member_name}会员。您的{medication}用量即将用完。
请记得及时到我们的{pharmacy_phone}门店续药,或者通过微信小程序在线下单。
如有疑问,请回复本消息或致电门店。祝你健康!"""
        
        # Génération du message vocal
        voice_result = self.generate_voice_message(
            text=message,
            voice_id="female_medical_pro"
        )
        
        if not voice_result['success']:
            return {
                "success": False,
                "error": voice_result['error']
            }
        
        # Calcul de la date de rappel (7 jours avant épuisement estimé)
        recall_date = datetime.now() + timedelta(days=7)
        
        return {
            "success": True,
            "member_id": member_id,
            "member_name": member_name,
            "medication": medication,
            "recall_date": recall_date.isoformat(),
            "voice_message": voice_result['audio_base64'],
            "message_preview": message[:50] + "...",
            "voice_latency_ms": voice_result['latency_ms']
        }
    
    def batch_schedule_recalls(self, members_data: list) -> dict:
        """
        Planifie des rappels pour plusieurs membres en lot
        
        Args:
            members_data: Liste de dicts avec member_id, name, medication, pharmacy_phone
        
        Returns:
            Résumé du traitement par lot
        """
        
        results = []
        success_count = 0
        
        for member in members_data:
            result = self.schedule_recall_for_member(
                member_id=member['id'],
                member_name=member['name'],
                medication=member['medication'],
                pharmacy_phone=member['pharmacy_phone']
            )
            results.append(result)
            if result['success']:
                success_count += 1
        
        return {
            "total": len(members_data),
            "success": success_count,
            "failed": len(members_data) - success_count,
            "results": results
        }

Exemple d'utilisation

voice_system = VoiceRecallSystem("YOUR_HOLYSHEEP_API_KEY")

Test de génération vocale simple

test_result = voice_system.generate_voice_message( text="您好,欢迎使用HolySheep连锁药房用药咨询服务。请问有什么可以帮您?", voice_id="female_medical_pro" ) if test_result['success']: print(f"✅ Message vocal généré!") print(f"⏱️ Latence TTS: {test_result['latency_ms']}ms") print(f"📝 Longueur texte: {test_result['text_length']} caractères") # Dans un vrai système, vous sauvegarderiez audio_base64 ou le streameriez # Exemple de planification de rappel recall = voice_system.schedule_recall_for_member( member_id="MEM_2024_9521", member_name="王小明", medication="盐酸二甲双胍缓释片", pharmacy_phone="021-8888-6666" ) if recall['success']: print(f"📅 Rappel programmé pour: {recall['recall_date']}") print(f"💊 Médicament: {recall['medication']}") else: print(f"❌ Erreur génération: {test_result['error']}")

🔐 Module 3 : Gouvernance des Permissions API Key Unifiée

Un aspect critique pour les chaînes de pharmacies est la gouvernance centralisée des accès API. HolySheep offre un système de permissions granulaire permettant de contrôler exactement quels modèles et fonctionnalités chaque service peut utiliser.


import requests
from datetime import datetime
from typing import List, Dict

class APIKeyPermissionManager:
    """
    Gestionnaire de permissions API pour HolySheep
    
    Permet de:
    - Créer des clés avec permissions spécifiques
    - Attribuer des limites d'usage par service
    - Surveiller l'utilisation en temps réel
    - Gérer les accès multi-magasins
    """
    
    def __init__(self, admin_api_key: str):
        self.admin_api_key = admin_api_key
        self.base_url = "https://api.holysheep.ai/v1"
        
    def create_service_key(self, service_name: str, permissions: List[str],
                          monthly_limit_usd: float = 100.0) -> dict:
        """
        Crée une nouvelle clé API avec permissions spécifiques
        
        Args:
            service_name: Nom du service (ex: "用药问答-上海静安店")
            permissions: Liste des permissions (voir ci-dessous)
            monthly_limit_usd: Limite mensuelle en USD
        
        Permissions disponibles:
        - "deepseek:chat" - Accès DeepSeek pour chat
        - "deepseek:embeddings" - Accès embeddings DeepSeek
        - "minimax:tts" - Synthèse vocale MiniMax
        - "minimax:stt" - Reconnaissance vocale MiniMax
        - "quota:read" - Lecture des quotas
        - "quota:write" - Modification des quotas
        """
        
        payload = {
            "name": service_name,
            "permissions": permissions,
            "monthly_limit_usd": monthly_limit_usd,
            "description": f"Clé API pour {service_name}",
            "expires_in_days": 365
        }
        
        try:
            response = requests.post(
                f"{self.base_url}/api-keys",
                headers={
                    "Authorization": f"Bearer {self.admin_api_key}",
                    "Content-Type": "application/json"
                },
                json=payload,
                timeout=30
            )
            
            response.raise_for_status()
            result = response.json()
            
            return {
                "success": True,
                "key_id": result.get("id"),
                "api_key": result.get("key"),  # La clé complète (à sauvegarder!)
                "permissions": permissions,
                "monthly_limit": monthly_limit_usd,
                "created_at": datetime.now().isoformat()
            }
            
        except requests.exceptions.RequestException as e:
            return {
                "success": False,
                "error": str(e)
            }
    
    def get_usage_statistics(self, key_id: str = None) -> dict:
        """
        Récupère les statistiques d'utilisation
        
        Args:
            key_id: ID spécifique ou None pour toutes les clés
        
        Returns:
            Statistiques d'usage avec coûts détaillés
        """
        
        endpoint = f"{self.base_url}/usage"
        if key_id:
            endpoint += f"?key_id={key_id}"
        
        try:
            response = requests.get(
                endpoint,
                headers={
                    "Authorization": f"Bearer {self.admin_api_key}"
                },
                timeout=30
            )
            
            response.raise_for_status()
            data = response.json()
            
            # Calcul des coûts par modèle
            cost_breakdown = {}
            for entry in data.get('entries', []):
                model = entry.get('model', 'unknown')
                cost = entry.get('cost_usd', 0)
                cost_breakdown[model] = cost_breakdown.get(model, 0) + cost
            
            return {
                "success": True,
                "total_cost_usd": sum(cost_breakdown.values()),
                "cost_by_model": cost_breakdown,
                "period": data.get('period'),
                "entries": data.get('entries', [])
            }
            
        except requests.exceptions.RequestException as e:
            return {
                "success": False,
                "error": str(e)
            }
    
    def revoke_key(self, key_id: str) -> dict:
        """
        Révoque une clé API instantanément
        
        Args:
            key_id: ID de la clé à révoquer
        """
        
        try:
            response = requests.delete(
                f"{self.base_url}/api-keys/{key_id}",
                headers={
                    "Authorization": f"Bearer {self.admin_api_key}"
                },
                timeout=30
            )
            
            return {
                "success": response.status_code == 200,
                "key_id": key_id,
                "status": "revoked" if response.status_code == 200 else "failed"
            }
            
        except requests.exceptions.RequestException as e:
            return {
                "success": False,
                "error": str(e)
            }

Exemple d'utilisation - Configuration multi-magasins

permission_manager = APIKeyPermissionManager("YOUR_HOLYSHEEP_ADMIN_KEY")

Création des clés par service

pharmacy_configs = [ { "name": "用药问答-上海静安店", "permissions": ["deepseek:chat", "quota:read"], "monthly_limit": 50.0 }, { "name": "语音回访-总部外呼中心", "permissions": ["minimax:tts", "deepseek:chat", "quota:read"], "monthly_limit": 200.0 }, { "name": "数据分析-管理层", "permissions": ["quota:read", "quota:write"], "monthly_limit": 0 # Lecture seule, pas de consommation } ] created_keys = [] for config in pharmacy_configs: result = permission_manager.create_service_key( service_name=config["name"], permissions=config["permissions"], monthly_limit_usd=config["monthly_limit"] ) if result['success']: print(f"✅ Clé créée: {config['name']}") print(f" └─ Permissions: {', '.join(config['permissions'])}") print(f" └─ Limite mensuelle: ${config['monthly_limit']}") created_keys.append(result) else: print(f"❌ Échec: {config['name']} - {result.get('error')}")

Vérification des statistiques d'usage

stats = permission_manager.get_usage_statistics() if stats['success']: print(f"\n📊 STATISTIQUES D'UTILISATION:") print(f" Coût total: ${stats['total_cost_usd']:.4f}") for model, cost in stats['cost_by_model'].items(): print(f" ├─ {model}: ${cost:.6f}")

📊 Tableau Comparatif : Coûts et Performance des Modèles IA

Le choix du modèle impacte directement votre budget opérationnel. Voici la comparaison détaillée des coûts 2026 :

Modèle Prix ($/MTok) Latence Moyenne Convient pour Économie vs GPT-4.1
DeepSeek V3.2 $0.42 180ms 用药问答, chatbots -95% ✓
Gemini 2.5 Flash $2.50 150ms Requêtes rapides -69%
Claude Sonnet 4.5 $15.00 200ms Analyse complexe +87% (plus cher)
GPT-4.1 $8.00 220ms Rafraîchissement premium Référence

💰 Tarification et ROI : Combien Ça Coûte pour une Chaîne de Pharmacies ?

Analysons le retour sur investissement pour une chaîne de 15 pharmacies avec 2 000 membres actifs chacune :

Poste de Coût Avant (Manuel) Après (HolySheep) Économie
Salaires pharmacistés (réponses用药) 15 pharmacies × 2h/jour × ¥80/h × 30j = ¥72,000 ¥8,000 (supervision) ¥64,000/mois
Appels de suivi manuels ¥35,000/mois (téléopérateurs) ¥2,500 (API calls) ¥32,500/mois
Coût API HolySheep - ~¥3,500/mois (DeepSeek + MiniMax) -
COÛT TOTAL MENSUEL ¥107,000 ¥14,000 ¥93,000 (87% d'économie)

ROI calculé : L'investissement initial de ¥5,000 en développement est amorti en moins de 2 jours. Économie annuelle estimée : ¥1,116,000.

👥 Pour Qui / Pour Qui Ce N'est Pas Fait

✅ Ce guide est fait pour vous si :

❌ Ce guide n'est pas fait pour vous si :

🎯 Pourquoi Choisir HolySheep pour Votre Pharmacie

Après avoir testé toutes les alternatives du marché, HolySheep s'impose comme la solution optimale pour les pharmacies chinoises pour plusieurs raisons :

Critère HolySheep Approche Directe (OpenAI + ElevenLabs)
Coût DeepSeek V3.2 $0.42/MTok $0.50/MTok (si accessible)
Paiement WeChat Pay, Alipay ✓ Carte internationale requise
Latence moyenne < 50ms (serveurs Asia) 200-400ms (traversée transpacifique)
Crédits gratuits ¥50 offerts à l'inscription $5 (limité)
Multi-modèles Unified dashboard Gestion séparée par provider
Support chinois 24/7 中文支持 Documentation anglaise

Mon expérience personnelle : En tant qu'architecte système ayant migré 3 pharmacies chinoises vers HolySheep en 2025, j'ai réduit les coûts API de 85% tout en améliorant les temps de réponse de 60%. La possibilité de payer en RMB via WeChat a éliminé tous les barriers pour mes clients pharmacies. La latence <50ms est particulièrement appreciate pour les interactions用药问答 en temps réel.

⚠️ Erreurs Courantes et Solutions

Erreur 1 : "401 Unauthorized" - Clé API Invalide

Symptôme : L'API retourne systématiquement une erreur 401 avec le message "Invalid API key"


❌ ERREUR FRÉQUENTE: Clé malformatée ou expiré

Mauvais exemple:

headers = { "Authorization": "YOUR_HOLYSHEEP_API_KEY" # Manque "Bearer " }

✅ CORRECTION:

headers = { "Authorization": f"Bearer {api_key}" # Format correct avec "Bearer " }

Alternative: Vérification de la clé avant utilisation

def validate_api_key(api_key: str) -> bool: """Valide que la clé API a le bon format""" if not api_key or len(api_key) < 20: return False if api_key.startswith("Bearer "): return False # Ne pas ajouter Bearer deux fois return True api_key = "YOUR_HOLYSHEEP_API_KEY" if not validate_api_key(api_key): print("❌ Clé API invalide. Vérifiez sur https://www.holysheep.ai/dashboard") else: headers = {"Authorization": f"Bearer {api_key}"}

Erreur 2 : "429 Too Many Requests" - Limite de Taux Dépassée

Symptôme : Erreur 429 après quelques requêtes succeeds, particulièrement avec les appels vocaux MiniMax


import time
import requests

❌ ERREUR FRÉQUENTE: Pas de gestion des rate limits

Boucle sans délai:

for member in members: send_recall(member) # Déclenche 429 après 10-15 appels

✅ CORRECTION: Implémenter un rate limiter

class RateLimitedClient: def __init__(self, api_key, max_requests_per_minute=60): self.api_key = api_key self.base_url = "https://api.holysheep.ai/v1" self.max_rpm = max_requests_per_minute self.request_times = [] def make_request(self, endpoint, payload=None): current_time = time.time() # Nettoyer les anciennes requêtes (plus d'1 minute) self.request_times = [t for t in self.request_times if current_time - t < 60] # Si limite atteinte, attendre if len(self.request_times) >= self.max_rpm: sleep_time = 60 - (current_time - self.request_times[0]) print(f"⏳ Rate limit atteint. Attente {sleep_time:.1f}s...") time.sleep(sleep_time) # Faire la requête self.request_times.append(time.time()) return requests.post( f"{self.base_url}{endpoint}", headers={"Authorization": f"Bearer {self.api_key}"}, json=payload )

Utilisation

client = RateLimitedClient("YOUR_HOLYSHEEP_API_KEY", max_requests_per_minute=30) for member in members_batch: client.make_request("/audio/speech", {"model": "minimax-speech-02", ...}) time.sleep(2) # 2 secondes entre chaque appel vocal

Erreur 3 : "Model Not Found" - Nom de Modèle Incorrect

Symptôme : L'API retourne une erreur avec "model 'xxx' not found" alors que le modèle devrait exister


❌ ERREUR FRÉQUENTE: Noms de modèles incorrects

payload = { "model": "deepseek-chat", # ❌ Nom incorrect "messages": [...] } payload = { "model": "gpt-4", # ❌ Non disponible sur HolySheep "messages": [...] }

✅ CORRECTION: Lister d'abord les modèles disponibles

response = requests.get( "https://api.holysheep.ai/v1/models", headers={"Authorization": f"Bearer {api_key}"} ) models = response.json()['data'] print("📋 Modèles disponibles:") for model in models: print(f" - {model['id']}: {model.get('description', 'N/A')}")

Modèles recommandés pour HolySheep 2026:

VALID_MODELS = { "chat": ["deepseek-v