Agent 엔지니어링

Milvus 실습

삽입부터 삭제까지 동일한 Collection 이름과 실제 텍스트 Embedding을 사용합니다. 로컬 Milvus가 19530 포트에서 실행 중이라고 가정합니다.

공식 Docker Standalone 안내서로 서비스를 시작한 뒤 pymilvussentence-transformers를 설치하세요. 실제 모델 출력에서 차원을 구하며, 랜덤 벡터로 시맨틱 검색을 흉내 내지 않습니다.
연결, 정의, 배치 삽입
from pymilvus import MilvusClient, DataType
from sentence_transformers import SentenceTransformer
COLLECTION = "support_kb"
client = MilvusClient(uri="http://localhost:19530")
print(client.list_collections())  # 연결 확인. 상태 확인은 보통 9091/healthz
RESET_LAB = False  # 기존 실습 데이터를 버려도 되는지 확인한 뒤에만 True
if client.has_collection(collection_name=COLLECTION):
    if not RESET_LAB:
        raise RuntimeError("support_kb가 이미 있습니다. 이름을 바꾸거나 RESET_LAB을 명시적으로 활성화하세요")
    client.drop_collection(collection_name=COLLECTION)  # 컬렉션 전체 삭제
encoder = SentenceTransformer("BAAI/bge-m3")
docs = [{"id": 1, "text": "환불은 영업일 기준 3일 안에 입금됩니다", "category": "refund"},
        {"id": 2, "text": "비밀번호 변경 후 다시 로그인하세요", "category": "account"}]
vectors = encoder.encode([d["text"] for d in docs], normalize_embeddings=True).tolist()
schema = MilvusClient.create_schema(auto_id=False, enable_dynamic_field=False)
schema.add_field(field_name="id", datatype=DataType.INT64, is_primary=True)
schema.add_field(field_name="vector", datatype=DataType.FLOAT_VECTOR, dim=len(vectors[0]))
schema.add_field(field_name="text", datatype=DataType.VARCHAR, max_length=1000)
schema.add_field(field_name="category", datatype=DataType.VARCHAR, max_length=64)
client.create_collection(collection_name=COLLECTION, schema=schema)
client.insert(collection_name=COLLECTION, data=[{**d, "vector": v} for d, v in zip(docs, vectors)])
인덱스, Load, 필터 Top-K Search
index = client.prepare_index_params()
index.add_index(field_name="vector", index_type="HNSW", metric_type="COSINE",
                params={"M": 16, "efConstruction": 200})
client.create_index(collection_name=COLLECTION, index_params=index)
client.load_collection(collection_name=COLLECTION)
query_vector = encoder.encode(["환불 금액은 언제 들어오나요?"], normalize_embeddings=True).tolist()
hits = client.search(collection_name=COLLECTION, data=query_vector, anns_field="vector", limit=3,
    filter='category == "refund"', output_fields=["text", "category"],
    search_params={"metric_type": "COSINE", "params": {"ef": 64}})
Query와 Delete
rows = client.query(collection_name=COLLECTION, filter='category == "account"', output_fields=["id", "text"])
client.delete(collection_name=COLLECTION, filter="id == 2")
# 파괴적 작업이므로 일회성 실습 환경에서만 사용:
# client.drop_collection(collection_name=COLLECTION)

쓰기

배치 삽입하되 안정적인 ID와 upsert 또는 애플리케이션 중복 제거로 멱등성을 구현합니다. insert만으로는 중복이 제거되지 않습니다. 콘텐츠와 Embedding 모델 버전을 함께 기록하세요.

삭제

삭제를 되돌릴 수 있게 하려면 먼저 active=false로 논리 삭제합니다. 물리 삭제 공간은 이후 Compaction으로 회수됩니다.

체크 같은 모델·차원·COSINE metric, 실제 Embedding, Search 전 Load. HNSW를 조정하기 전 FLAT으로 소규모 정확도 기준선을 만드세요.