Construire une place de marché de plug-ins d’agents
Boutique d’outils centralisée : publier, évaluer et distribuer des plug-ins d’agents.
Construire une place de marché de plug-ins d’agents est une leçon AI Agents gratuite sur CoddyKit. Ceci est la leçon 4 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage AI Agents, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours AI Agents comprend 4 leçons au total.
Qu’est-ce qu’une place de marché de modules d’extension pour agents ?
Une place de marché de modules d’extension est un registre centralisé où les développeurs publient des outils partageables, où d’autres développeurs les découvrent en effectuant des recherches et où les systèmes d’agents les installent automatiquement. Pensez à npm pour les outils d’agents : publiez une fois, utilisez partout.
Architecture de la place de marché
La place de marché comprend quatre services principaux : Registre (stocke les métadonnées des modules d’extension dans une base de données), Stockage (stocke les archives des modules d’extension dans un stockage d’objets), Recherche (indexation en texte intégral) et Sécurité (signe et vérifie les paquets). Tous sont exposés via une API REST.
# Marketplace REST API endpoints:
API_ROUTES = {
'POST /plugins': 'Publish a new plugin version',
'GET /plugins': 'List all plugins (paginated)',
'GET /plugins/search?q=weather': 'Search by keyword',
'GET /plugins/{name}': 'Get plugin metadata',
'GET /plugins/{name}/{version}': 'Get specific version metadata',
'GET /plugins/{name}/{version}/download': 'Download plugin archive',
'POST /plugins/{name}/{version}/reviews': 'Submit a rating/review',
'GET /plugins/{name}/reviews': 'Get all reviews',
'DELETE /plugins/{name}/{version}': 'Yank (hide) a version',
}
for route, description in API_ROUTES.items():
print(f'{route}: {description}')Publication d’un module d’extension
Le point d’accès de publication valide l’archive du module d’extension, vérifie le manifeste, vérifie l’identité de l’éditeur (au moyen d’une clé d’API), recherche les motifs connus comme malveillants, signe le paquet et le stocke. La publication est atomique : soit l’intégralité du téléversement réussit, soit rien n’est stocké.
import hashlib
import hmac
import os
def publish_plugin(
archive_path: str,
api_key: str,
registry_url: str = 'https://registry.agenttools.io'
) -> dict:
import requests
# Compute checksum for integrity
with open(archive_path, 'rb') as f:
data = f.read()
sha256 = hashlib.sha256(data).hexdigest()
# Sign the archive with the developer's API key
signature = hmac.new(
api_key.encode(), data, hashlib.sha256
).hexdigest()
response = requests.post(
f'{registry_url}/plugins',
files={'archive': (os.path.basename(archive_path), data, 'application/zip')},
headers={
'X-API-Key': api_key,
'X-Signature': signature,
'X-Checksum-SHA256': sha256
},
timeout=60
)
response.raise_for_status()
return response.json()Validation côté serveur lors de la publication
Le serveur du registre valide chaque soumission : le manifeste doit être analysable et complet, la version ne doit pas déjà exister, l’archive ne doit pas dépasser la limite de taille et une analyse de sécurité élémentaire doit réussir. Le serveur n’accepte le téléversement qu’une fois toutes les vérifications terminées.
import zipfile
import io
import json
MAX_ARCHIVE_SIZE_MB = 50
def validate_plugin_archive(archive_bytes: bytes) -> dict:
errors = []
# Size check
size_mb = len(archive_bytes) / 1024 / 1024
if size_mb > MAX_ARCHIVE_SIZE_MB:
errors.append(f'Archive too large: {size_mb:.1f}MB (max {MAX_ARCHIVE_SIZE_MB}MB)')
return {'valid': False, 'errors': errors}
# Must be a valid zip
try:
zf = zipfile.ZipFile(io.BytesIO(archive_bytes))
except zipfile.BadZipFile:
errors.append('Not a valid zip archive')
return {'valid': False, 'errors': errors}
file_list = zf.namelist()
# Must contain plugin.json
if 'plugin.json' not in file_list:
errors.append('Missing plugin.json in archive root')
# Basic security scan: no .pyc files, no hidden dirs
suspicious = [f for f in file_list if f.startswith('.') or '__pycache__' in f]
if suspicious:
errors.append(f'Suspicious files: {suspicious[:3]}')
return {'valid': len(errors) == 0, 'errors': errors, 'files': len(file_list)}
if __name__ == '__main__':
buf = io.BytesIO()
with zipfile.ZipFile(buf, 'w') as zf:
zf.writestr('plugin.json', '{"name": "demo-plugin"}')
archive_bytes = buf.getvalue()
print('Validation result:', validate_plugin_archive(archive_bytes))
Signature et vérification des paquets
Signez chaque paquet publié avec la clé de l’éditeur. Les clients vérifient la signature avant l’installation afin de s’assurer que le paquet n’a pas été altéré pendant son transfert ou dans le stockage.
import hashlib
import hmac
def sign_package(archive_bytes: bytes, secret_key: str) -> str:
return hmac.new(
secret_key.encode(), archive_bytes, hashlib.sha256
).hexdigest()
def verify_package(
archive_bytes: bytes,
signature: str,
secret_key: str
) -> bool:
expected = sign_package(archive_bytes, secret_key)
return hmac.compare_digest(expected, signature)
# Example:
package_data = b'fake plugin archive bytes'
developer_key = 'developer_secret_key_abc123'
sig = sign_package(package_data, developer_key)
print(f'Signature: {sig[:20]}...')
# Verify (registry or installer):
valid = verify_package(package_data, sig, developer_key)
print(f'Signature valid: {valid}')
# Tampered data:
tampered = b'tampered bytes'
print(f'Tampered valid: {verify_package(tampered, sig, developer_key)}')Découverte de modules d’extension par la recherche
L’API de recherche accepte les requêtes en texte libre et les filtres par étiquette. Elle renvoie des résultats classés selon un score pondéré : la correspondance exacte du nom obtient le meilleur classement, suivie des correspondances d’étiquettes, puis des correspondances de mots-clés dans la description.
import requests
def search_marketplace(
query: str,
tags: list = None,
page: int = 0,
per_page: int = 20,
registry_url: str = 'https://registry.agenttools.io'
) -> dict:
params = {
'q': query,
'page': page,
'per_page': per_page
}
if tags:
params['tags'] = ','.join(tags)
response = requests.get(
f'{registry_url}/plugins/search',
params=params,
timeout=10
)
response.raise_for_status()
return response.json()
# Usage:
results = search_marketplace('weather forecast', tags=['iot', 'weather'])
# {
# 'total': 12,
# 'results': [
# {'name': 'weather-tools', 'version': '2.1.0', 'score': 0.98, ...},
# ...
# ]
# }
print(f'Total results: {results.get("total", 0)}')Installation d’un module d’extension
Le processus d’installation se déroule ainsi : télécharger l’archive, vérifier la signature, l’extraire dans le répertoire des modules d’extension, valider le manifeste, vérifier les dépendances et l’enregistrer dans le registre local des outils. Toutes les étapes doivent réussir, sinon l’installation est annulée.
import requests
import zipfile
import io
import shutil
import os
def install_plugin(
plugin_name: str,
version: str = 'latest',
plugins_dir: str = 'plugins',
registry_url: str = 'https://registry.agenttools.io'
) -> bool:
# Step 1: Get metadata including expected signature
meta = requests.get(
f'{registry_url}/plugins/{plugin_name}/{version}',
timeout=10
).json()
# Step 2: Download archive
archive_resp = requests.get(
meta['download_url'], timeout=60
)
archive_bytes = archive_resp.content
# Step 3: Verify signature
if not verify_package(archive_bytes, meta['signature'], meta['public_key']):
print('Signature verification FAILED — aborting install')
return False
# Step 4: Extract
install_path = os.path.join(plugins_dir, plugin_name)
if os.path.exists(install_path):
shutil.rmtree(install_path) # remove old version
with zipfile.ZipFile(io.BytesIO(archive_bytes)) as zf:
zf.extractall(install_path)
print(f'Installed {plugin_name} v{version} to {install_path}')
return TrueSystème de notation et d’avis
Un système de notation aide les développeurs à choisir des modules d’extension de qualité. Mettez en place des notes de 1 à 5 étoiles avec des avis textuels facultatifs. Les données agrégées de notation (mean, count) sont stockées dans le registre et renvoyées dans les résultats de recherche.
import requests
def submit_review(
plugin_name: str,
stars: int,
review_text: str = '',
api_key: str = '',
registry_url: str = 'https://registry.agenttools.io'
) -> dict:
if not 1 <= stars <= 5:
raise ValueError('Stars must be 1-5')
payload = {
'stars': stars,
'review': review_text[:500] # truncate to max length
}
response = requests.post(
f'{registry_url}/plugins/{plugin_name}/reviews',
json=payload,
headers={'X-API-Key': api_key},
timeout=10
)
response.raise_for_status()
return response.json()
def get_reviews(plugin_name: str, registry_url: str = 'https://registry.agenttools.io') -> dict:
response = requests.get(
f'{registry_url}/plugins/{plugin_name}/reviews',
timeout=10
)
return response.json()
# Example response structure:
# {'average_stars': 4.3, 'total_reviews': 47,
# 'reviews': [{'stars': 5, 'review': 'Works great!', 'date': '...'}]}
print('Review system ready')Retrait (dépréciation) d’une version
Si une version publiée présente un bogue critique ou une vulnérabilité de sécurité, retirez-la : marquez-la comme masquée dans le registre afin que les nouvelles installations ne puissent pas l’utiliser, tout en permettant aux installations existantes de continuer à fonctionner. Cette méthode est plus sûre que la suppression, qui interromprait les agents existants.
import requests
def yank_version(
plugin_name: str,
version: str,
reason: str,
api_key: str,
registry_url: str = 'https://registry.agenttools.io'
) -> dict:
response = requests.delete(
f'{registry_url}/plugins/{plugin_name}/{version}',
json={'reason': reason},
headers={'X-API-Key': api_key},
timeout=10
)
response.raise_for_status()
return response.json()
# Yanked versions:
# - Still downloadable by users who have them pinned
# - Not returned in 'latest' queries
# - Search results show a 'yanked' badge with reason
# - Install of yanked version requires explicit --allow-yanked flag
print('Yanking removes a version from new installs without breaking existing ones.')
print('Always provide a reason: "Security vulnerability in API key handling"')Outil CLI de la place de marché
Une interface de ligne de commande permet d’utiliser facilement la place de marché depuis un terminal ou des pipelines d’intégration continue. Implémentez les commandes marketplace install, marketplace search, marketplace publish et marketplace list.
import argparse
import sys
def main():
parser = argparse.ArgumentParser(description='Agent Tool Marketplace CLI')
subparsers = parser.add_subparsers(dest='command')
# Search
search_parser = subparsers.add_parser('search', help='Search plugins')
search_parser.add_argument('query', help='Search query')
search_parser.add_argument('--tag', action='append', dest='tags')
# Install
install_parser = subparsers.add_parser('install', help='Install a plugin')
install_parser.add_argument('plugin', help='Plugin name')
install_parser.add_argument('--version', default='latest')
install_parser.add_argument('--plugins-dir', default='plugins')
# Publish
publish_parser = subparsers.add_parser('publish', help='Publish plugin')
publish_parser.add_argument('archive', help='Path to .zip archive')
publish_parser.add_argument('--api-key', required=True)
args = parser.parse_args()
if args.command == 'search':
results = search_marketplace(args.query, tags=args.tags or [])
for r in results.get('results', []):
print(f'{r["name"]} v{r["version"]} ({r.get("average_stars", "?")} stars)')
elif args.command == 'install':
install_plugin(args.plugin, args.version, args.plugins_dir)
elif args.command == 'publish':
result = publish_plugin(args.archive, args.api_key)
print('Published:', result)Gouvernance de la place de marché
Une place de marché publique a besoin de règles de gouvernance : gestion des espaces de noms (pour empêcher l’accaparement de noms), modération des contenus (aucun code malveillant), analyse de sécurité (analyse statique lors du téléversement) et processus de retrait (en cas d’incident juridique ou de sécurité). Ces enjeux sont autant organisationnels que techniques.
MARKETPLACE_POLICIES = {
'namespace_policy': (
'Plugin names must be unique across the registry. '
'Names are claimed on first publish. '
'Transfers require proof of original authorship.'
),
'security_scanning': (
'All uploads are scanned with bandit (Python security linter). '
'Critical vulnerabilities block publish. '
'High vulnerabilities generate a warning visible to installers.'
),
'rate_limits': {
'publish_per_hour': 10,
'search_per_minute': 60,
'install_per_minute': 30
},
'takedown_process': (
'Maintainers can yank versions at any time. '
'Legal/security takedowns processed within 48 hours. '
'Appeals via security@agenttools.io'
)
}
for policy, detail in MARKETPLACE_POLICIES.items():
if isinstance(detail, str):
print(f'{policy}: {detail[:80]}...')
else:
print(f'{policy}: {detail}')Vérification des connaissances
Quelle est la différence entre le retrait d’une version de module d’extension et sa suppression ?
Récapitulatif : création d’une place de marché de modules d’extension pour agents
Félicitations, vous avez terminé cette leçon ! Points clés :
- Flux de publication : valider l’archive → vérifier la signature → analyser → stocker et indexer
- Signature des paquets : la signature HMAC garantit l’intégrité ; les clients la vérifient avant l’installation
- Recherche : recherche en texte intégral avec filtrage par étiquette et classement par pertinence
- Installation : télécharger → vérifier → extraire → valider → enregistrer (annulation en cas d’échec)
- Notes : notes de 1 à 5 étoiles et avis textuels agrégés dans le registre
- Retrait : masquer les versions des nouvelles installations sans interrompre les installations existantes
Dernier cours : La route vers les systèmes autonomes.
Apprends AI Agents avec un tuteur IA — gratuit
Écris et exécute du vrai code dans ton navigateur, obtiens de l'aide instantanée d'un tuteur IA disponible 24h/24, et reprends là où tu t'es arrêté sur le web ou dans l'app.
- Cours
- 60
- Leçons
- 239
Questions Fréquemment Posées
La leçon « Construire une place de marché de plug-ins d’agents » est-elle gratuite ?
Oui — le texte complet de « Construire une place de marché de plug-ins d’agents » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours AI Agents, passe à CoddyKit PRO. Le cours AI Agents comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Construire une place de marché de plug-ins d’agents » ?
Boutique d’outils centralisée : publier, évaluer et distribuer des plug-ins d’agents. Tu pratiques AI Agents avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer AI Agents ?
Aucune expérience préalable n'est requise. AI Agents sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 4 sur 4.
Combien de temps prend la leçon « Construire une place de marché de plug-ins d’agents » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon AI Agents ?
Oui. Chaque leçon AI Agents inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Concevoir des outils d’agents partageables
- Découverte et enregistrement des plug-ins
- Gestion des versions et de la compatibilité des outils
- Construire une place de marché de plug-ins d’agents