Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Operate Milvus vector database with pymilvus — collections, vector search, hybrid search, indexes, RBAC, partitions, and more via Python code.
.claude/skills/leoyeai-milvus/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-03 | ✗→✓ | ▲ Improved | 153% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 204% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 212% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 146% | 0% |
| case-15 | ✗→✓ | ▲ Improved | 172% | 0% |
Operate Milvus vector databases directly through Python code using the pymilvus SDK. This skill covers the full lifecycle — connecting, schema design, collection management, vector CRUD, search, hybrid search, indexing, partitions, databases, and RBAC.
Use this skill when the user wants to:
pymilvus (pip install pymilvus)| Area | What You Can Do | |------|----------------| | Connection | Connect to Milvus Lite, Standalone, Cluster, or Zilliz Cloud | | Collections | Create (quick or custom schema), list, describe, drop, rename, load, release | | Vectors | Insert, upsert, search, hybrid search, query, get, delete | | Indexes | Create (AUTOINDEX, HNSW, IVF_FLAT, etc.), list, describe, drop | | Partitions | Create, list, load, release, drop | | Databases | Create, list, switch, drop | | RBAC | Users, roles, privileges management |
pythonfrom pymilvus import MilvusClient # Milvus Lite (embedded, file-based — great for dev/test) client = MilvusClient(uri="./milvus_demo.db") # Standalone / Cluster Milvus client = MilvusClient(uri="http://localhost:19530", token="root:Milvus") # Zilliz Cloud client = MilvusClient( uri="https://in03-xxxx.api.gcp-us-west1.zillizcloud.com:19530", token="your_api_key" )
Parameters:
| Parameter | Type | Description | |-----------|------|-------------| | uri | str | "./file.db" for Milvus Lite, "http://host:19530" for server | | token | str | API key or "username:password" | | user | str | Username (alternative to token) | | password | str | Password (alternative to token) | | db_name | str | Target database (default: "default") | | timeout | float | Operation timeout in seconds |
pythonfrom pymilvus import AsyncMilvusClient async with AsyncMilvusClient(uri="http://localhost:19530") as client: results = await client.search(...)
pythonclient.create_collection( collection_name="my_collection", dimension=768, metric_type="COSINE" # Optional: "COSINE" (default), "L2", "IP" )
This automatically creates:
id field (INT64, primary key, auto_id)vector field (FLOAT_VECTOR, dim=dimension)pythonfrom pymilvus import DataType # Step 1: Define schema schema = client.create_schema(auto_id=False, enable_dynamic_field=True) schema.add_field("id", DataType.INT64, is_primary=True) schema.add_field("text", DataType.VARCHAR, max_length=512) schema.add_field("embedding", DataType.FLOAT_VECTOR, dim=768) # Step 2: Define index index_params = client.prepare_index_params() index_params.add_index( field_name="embedding", index_type="AUTOINDEX", metric_type="COSINE" ) # Step 3: Create collection client.create_collection( collection_name="my_collection", schema=schema, index_params=index_params )
Scalar types:
| DataType | Notes | |----------|-------| | DataType.BOOL | Boolean | | DataType.INT8 / INT16 / INT32 / INT64 | Integers | | DataType.FLOAT / DOUBLE | Floating point | | DataType.VARCHAR | String (requires max_length) | | DataType.JSON | JSON object | | DataType.ARRAY | Array (requires element_type, max_capacity) |
Vector types:
| DataType | Notes | |----------|-------| | DataType.FLOAT_VECTOR | Float32 vector (requires dim) | | DataType.FLOAT16_VECTOR | Float16 vector (requires dim) | | DataType.BFLOAT16_VECTOR | BFloat16 vector (requires dim) | | DataType.BINARY_VECTOR | Binary vector (requires dim) | | DataType.SPARSE_FLOAT_VECTOR | Sparse vector (no dim needed) |
pythonschema.add_field( field_name="my_field", datatype=DataType.VARCHAR, is_primary=False, auto_id=False, max_length=256, # Required for VARCHAR dim=768, # Required for vector types (except sparse) element_type=DataType.INT64, # Required for ARRAY max_capacity=100, # Required for ARRAY nullable=False, default_value=None, is_partition_key=False, description="" )
python# List all collections collections = client.list_collections() # Describe a collection info = client.describe_collection(collection_name="my_collection") # Check if collection exists exists = client.has_collection(collection_name="my_collection") # Rename a collection client.rename_collection(old_name="old_name", new_name="new_name") # Drop a collection client.drop_collection(collection_name="my_collection") # Load collection into memory (required before search/query) client.load_collection(collection_name="my_collection") # Release collection from memory client.release_collection(collection_name="my_collection") # Get load state state = client.get_load_state(collection_name="my_collection") # Get collection statistics stats = client.get_collection_stats(collection_name="my_collection")
enable_dynamic_field=True to allow inserting fields not defined in the schema.Target collection must exist and be loaded.
pythondata = [ {"id": 1, "text": "AI advances", "embedding": [0.1, 0.2, ...]}, {"id": 2, "text": "ML basics", "embedding": [0.3, 0.4, ...]}, ] res = client.insert(collection_name="my_collection", data=data) # Returns: {"insert_count": 2, "ids": [1, 2]}
pythonres = client.upsert(collection_name="my_collection", data=data) # Returns: {"upsert_count": 2}
pythonresults = client.search( collection_name="my_collection", data=[[0.1, 0.2, ...]], # List of query vectors anns_field="embedding", # Vector field name limit=10, # Top-K output_fields=["text", "id"], # Fields to return filter='age > 20 and status == "active"', # Optional scalar filter search_params={ "metric_type": "COSINE", "params": {"nprobe": 10} # Index-specific params } ) # Returns: List[List[dict]] # Each hit: {"id": ..., "distance": ..., "entity": {"text": ...}}
pythonfrom pymilvus import AnnSearchRequest, RRFRanker, WeightedRanker req1 = AnnSearchRequest( data=[[0.1, 0.2, ...]], anns_field="dense_embedding", param={"metric_type": "COSINE", "params": {"nprobe": 10}}, limit=10 ) req2 = AnnSearchRequest( data=[{1: 0.5, 100: 0.3}], # Sparse vector anns_field="sparse_embedding", param={"metric_type": "IP"}, limit=10 ) # RRF reranking results = client.hybrid_search( collection_name="my_collection", reqs=[req1, req2], ranker=RRFRanker(k=60), limit=10, output_fields=["text"] ) # Or weighted reranking results = client.hybrid_search( collection_name="my_collection", reqs=[req1, req2], ranker=WeightedRanker(0.7, 0.3), limit=10 )
pythonresults = client.query( collection_name="my_collection", filter='id in [1, 2, 3]', output_fields=["text", "embedding"], limit=100 )
pythonresults = client.get( collection_name="my_collection", ids=[1, 2, 3], output_fields=["text"] )
python# By primary keys client.delete(collection_name="my_collection", ids=[1, 2, 3]) # By filter expression client.delete(collection_name="my_collection", filter='status == "obsolete"')
| Expression | Example | |---|---| | Comparison | age > 20 | | Equality | status == "active" | | IN list | id in [1, 2, 3] | | AND/OR | age > 20 and status == "active" | | String match | text like "hello%" | | Array contains | ARRAY_CONTAINS(tags, "ml") | | JSON field | json_field["key"] > 100 | | Match all | id > 0 |
data parameter in search must match the collection's vector dimension exactly.output_fields to control which fields are returned.pythonindex_params = client.prepare_index_params() # Vector index index_params.add_index( field_name="embedding", index_type="HNSW", # See index types table below metric_type="COSINE", # "COSINE", "L2", "IP" params={"M": 16, "efConstruction": 256} ) # Optional: scalar index index_params.add_index( field_name="text", index_type="" # Auto-select for scalars ) client.create_index( collection_name="my_collection", index_params=index_params )
| Index Type | For | Key Params | Notes | |------------|-----|------------|-------| | AUTOINDEX | Dense vectors | Auto-tuned | Recommended for most cases | | FLAT | Dense vectors | None | Brute force, 100% recall | | IVF_FLAT | Dense vectors | nlist | Good balance | | IVF_SQ8 | Dense vectors | nlist | Compressed, less memory | | HNSW | Dense vectors | M, efConstruction | High recall, more memory | | DISKANN | Dense vectors | None | Disk-based, large datasets | | SPARSE_INVERTED_INDEX | Sparse vectors | drop_ratio_build | For sparse vectors | | SPARSE_WAND | Sparse vectors | drop_ratio_build | Faster sparse search |
| Metric | Description | Use With | |--------|-------------|----------| | "COSINE" | Cosine similarity (larger = more similar) | Dense vectors | | "L2" | Euclidean distance (smaller = more similar) | Dense vectors | | "IP" | Inner product (larger = more similar) | Dense & Sparse vectors |
python# List indexes indexes = client.list_indexes(collection_name="my_collection") # Describe an index info = client.describe_index(collection_name="my_collection", index_name="my_index") # Drop an index client.drop_index(collection_name="my_collection", index_name="my_index")
AUTOINDEX is recommended for most use cases."IP" metric type.python# Create a partition client.create_partition(collection_name="my_collection", partition_name="partition_A") # List partitions partitions = client.list_partitions(collection_name="my_collection") # Returns: ["_default", "partition_A"] # Check if partition exists exists = client.has_partition(collection_name="my_collection", partition_name="partition_A") # Load specific partitions client.load_partitions(collection_name="my_collection", partition_names=["partition_A"]) # Release specific partitions client.release_partitions(collection_name="my_collection", partition_names=["partition_A"]) # Drop a partition client.drop_partition(collection_name="my_collection", partition_name="partition_A")
_default partition.is_partition_key=True on a field to enable automatic partitioning by field value.python# Create a database client.create_database(db_name="my_database") # List all databases databases = client.list_databases() # Returns: ["default", "my_database"] # Switch to a database client.using_database(db_name="my_database") # Drop a database (must drop all collections first) client.drop_database(db_name="my_database") # Or connect to a specific database at init client = MilvusClient(uri="http://localhost:19530", db_name="my_database")
"default" database.python# Create a user client.create_user(user_name="analyst", password="SecureP@ss123") # List users users = client.list_users() # Describe a user (shows assigned roles) info = client.describe_user(user_name="analyst") # Update password client.update_password(user_name="analyst", old_password="SecureP@ss123", new_password="NewP@ss456") # Grant role to user client.grant_role(user_name="analyst", role_name="read_only") # Revoke role from user client.revoke_role(user_name="analyst", role_name="read_only") # Drop a user client.drop_user(user_name="analyst")
python# Create a role client.create_role(role_name="read_only") # List roles roles = client.list_roles() # Grant privilege (v2 API — recommended) client.grant_privilege_v2( role_name="read_only", privilege="Search", # e.g., "Search", "Insert", "Query", "Delete" collection_name="my_collection", # Use "*" for all collections db_name="default" # Use "*" for all databases ) # Built-in privilege groups client.grant_privilege_v2( role_name="admin_role", privilege="ClusterAdmin", # See privilege groups below collection_name="*", db_name="*" ) # Revoke privilege client.revoke_privilege_v2( role_name="read_only", privilege="Search", collection_name="my_collection", db_name="default" ) # Describe role (see granted privileges) info = client.describe_role(role_name="read_only") # Drop a role client.drop_role(role_name="read_only")
| Group | Scope | |-------|-------| | ClusterAdmin | Full cluster access | | ClusterReadOnly | Read-only cluster access | | ClusterReadWrite | Read-write cluster access | | DatabaseAdmin | Full database access | | DatabaseReadOnly | Read-only database access | | DatabaseReadWrite | Read-write database access | | CollectionAdmin | Full collection access | | CollectionReadOnly | Read-only collection access | | CollectionReadWrite | Read-write collection access |
Search, Query, Insert, Delete, Upsert, CreateIndex, DropIndex, CreateCollection, DropCollection, Load, Release, CreatePartition, DropPartition
"*" for collection_name/db_name to grant on all resources.pythonfrom pymilvus import MilvusClient, DataType # 1. Connect client = MilvusClient(uri="http://localhost:19530") # 2. Create collection schema = client.create_schema(auto_id=True, enable_dynamic_field=True) schema.add_field("id", DataType.INT64, is_primary=True) schema.add_field("text", DataType.VARCHAR, max_length=2048) schema.add_field("embedding", DataType.FLOAT_VECTOR, dim=768) schema.add_field("source", DataType.VARCHAR, max_length=256) index_params = client.prepare_index_params() index_params.add_index(field_name="embedding", index_type="AUTOINDEX", metric_type="COSINE") client.create_collection(collection_name="knowledge_base", schema=schema, index_params=index_params) # 3. Insert documents (after embedding with your model) client.insert("knowledge_base", data=[ {"text": "chunk text...", "embedding": [...], "source": "doc1.pdf"}, ]) # 4. Retrieve relevant context results = client.search( collection_name="knowledge_base", data=[query_embedding], limit=5, output_fields=["text", "source"], search_params={"metric_type": "COSINE"} )
python# Simplest possible setup client = MilvusClient(uri="./search.db") client.create_collection(collection_name="docs", dimension=768) client.insert("docs", data=[{"id": i, "vector": emb, "text": txt} for i, (emb, txt) in enumerate(zip(embeddings, texts))]) results = client.search("docs", data=[query_vector], limit=10, output_fields=["text"])
pip install pymilvus.uri="./file.db") — no server needed.pymilvus[model] for built-in embedding support.enable_dynamic_field=True when the schema may evolve.AUTOINDEX unless the user has specific performance requirements.| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-05 | pass→pass | 10,824 | 4,013 | -63% | 1 | 1 | 0% | 1,977 | 5,964 | +202% | 0 | 0 | — |
case-01 | fail→fail | 19,140 | 14,127 | -26% | 1 | 1 | 0% | 3,914 | 7,955 | +103% | 0 | 0 | — |
case-02 | pass→pass | 18,307 | 13,550 | -26% | 1 | 1 | 0% | 3,301 | 7,803 | +136% | 0 | 0 | — |
case-03 | fail→pass | 14,425 | 9,782 | -32% | 1 | 1 | 0% | 2,897 | 7,319 | +153% | 0 | 0 | — |
case-04 | fail→pass | 11,440 | 5,587 | -51% | 1 | 1 | 0% | 2,066 | 6,275 | +204% | 0 | 0 | — |
case-06 | pass→pass | 8,397 | 3,162 | -62% | 1 | 1 | 0% | 1,304 | 5,787 | +344% | 0 | 0 | — |
case-07 | fail→pass | 13,900 | 11,410 | -18% | 1 | 1 | 0% | 2,290 | 7,154 | +212% | 0 | 0 | — |
case-08 | pass→pass | 14,307 | 6,038 | -58% | 1 | 1 | 0% | 2,574 | 6,328 | +146% | 0 | 0 | — |
case-09 | pass→pass | 14,899 | 4,945 | -67% | 1 | 1 | 0% | 2,996 | 6,288 | +110% | 0 | 0 | — |
case-10 | pass→pass | 5,502 | 2,118 | -62% | 1 | 1 | 0% | 920 | 5,619 | +511% | 0 | 0 | — |
case-11 | fail→pass | 15,185 | 5,492 | -64% | 1 | 1 | 0% | 2,563 | 6,304 | +146% | 0 | 0 | — |
case-12 | pass→pass | 15,299 | 10,542 | -31% | 1 | 1 | 0% | 2,806 | 7,271 | +159% | 0 | 0 | — |
case-13 | pass→pass | 7,031 | 5,243 | -25% | 1 | 1 | 0% | 1,383 | 6,270 | +353% | 0 | 0 | — |
case-14 | pass→pass | 8,811 | 4,937 | -44% | 1 | 1 | 0% | 1,595 | 6,123 | +284% | 0 | 0 | — |
case-15 | fail→pass | 12,789 | 7,365 | -42% | 1 | 1 | 0% | 2,364 | 6,441 | +172% | 0 | 0 | — |
case-16 | fail→pass | 10,864 | 3,268 | -70% | 1 | 1 | 0% | 1,804 | 5,816 | +222% | 0 | 0 | — |
case-17 | pass→pass | 11,326 | 4,212 | -63% | 1 | 1 | 0% | 2,103 | 5,945 | +183% | 0 | 0 | — |
case-18 | fail→pass | 11,960 | 5,612 | -53% | 1 | 1 | 0% | 2,478 | 6,341 | +156% | 0 | 0 | — |
case-19 | pass→pass | 6,544 | 3,385 | -48% | 1 | 1 | 0% | 1,170 | 5,850 | +400% | 0 | 0 | — |
case-20 | pass→pass | 5,682 | 5,264 | -7% | 1 | 1 | 0% | 1,012 | 6,076 | +500% | 0 | 0 | — |
case-21 | pass→pass | 6,821 | 5,605 | -18% | 1 | 1 | 0% | 1,326 | 6,135 | +363% | 0 | 0 | — |
case-22 | pass→pass | 8,859 | 9,227 | +4% | 1 | 1 | 0% | 1,666 | 6,884 | +313% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +32 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.