Skip to main content

Bài 09: Recipe — File Search Agent nâng cao

📋 Agenda

Thời gian đọc ước tính: ~20 phút | 💻 Lab

Sau bài này, bạn sẽ:

  • Phân biệt FileSearch (Bài 07) vs các advanced patterns trong bài này
  • Implement Persistent Vector Store — tạo 1 lần, tái sử dụng nhiều phiên
  • Update documents trong Vector Store mà không tạo lại từ đầu
  • Query metadata để filter tài liệu trước khi search

Yêu cầu đầu vào:

  • 🔹 Đã hoàn thành Bài 07 — hiểu FileSearch cơ bản
  • 💰 Azure cost: ~$0.03-0.05

❓ Vấn đề & Giải pháp

Vấn đề với pattern Bài 07:

  • Tạo Vector Store mới mỗi lần chạy → mất thời gian indexing, tốn chi phí
  • Không cập nhật được tài liệu khi chính sách thay đổi
  • Không thể phân biệt tài liệu theo loại/domain khi search

Bài này giải quyết:

  • Persistent Vector Store: tạo 1 lần, reuse theo vector_store_id
  • Incremental update: add/remove file mà không recreate
  • Metadata filtering: search trong subset tài liệu cụ thể

📖 Persistent vs Ephemeral Vector Store


💻 Lab 09: Persistent Knowledge Base Manager

# filename: part3-recipes/lab-09-persistent-kb.py
"""
Recipe 09: Persistent Vector Store — Production Pattern

Workflow:
1. Lần đầu: Tạo Vector Store → lưu ID → index docs
2. Lần sau: Load từ ID đã lưu → reuse ngay
3. Khi có tài liệu mới: Add file vào VS hiện có
"""

import os
import json
from pathlib import Path
from dotenv import load_dotenv
from azure.ai.projects import AIProjectClient
from azure.ai.projects.models import FileSearchTool
from azure.identity import DefaultAzureCredential

load_dotenv()

# File lưu trữ Vector Store ID (thay bằng database trong production)
STATE_FILE = "data/kb_state.json"


def load_state() -> dict:
"""Load trạng thái KB từ file (production: dùng database)"""
if Path(STATE_FILE).exists():
with open(STATE_FILE) as f:
return json.load(f)
return {}


def save_state(state: dict):
"""Lưu trạng thái KB"""
Path("data").mkdir(exist_ok=True)
with open(STATE_FILE, "w") as f:
json.dump(state, f, indent=2)


def get_or_create_vector_store(client: AIProjectClient, name: str) -> str:
"""
Production pattern:
- Nếu đã có vector_store_id → verify còn tồn tại → reuse
- Nếu chưa có hoặc đã bị xoá → tạo mới
"""
state = load_state()

if "vector_store_id" in state:
vs_id = state["vector_store_id"]
try:
# Verify vector store còn tồn tại trên Azure
vs = client.agents.get_vector_store(vs_id)
if vs.status != "expired":
print(f"♻️ Reusing existing Vector Store: {vs_id}")
print(f" Status: {vs.status} | Files: {vs.file_counts.completed}")
return vs_id
except Exception:
print("⚠️ Saved vector store not found — creating new one")

# Tạo mới
print(f"🆕 Creating new Vector Store: '{name}'")
vs = client.agents.create_vector_store(name=name)
state["vector_store_id"] = vs.id
state["name"] = name
save_state(state)
print(f" ✅ Created: {vs.id}")
return vs.id


def add_document(client: AIProjectClient, vector_store_id: str, file_path: str):
"""Thêm document mới vào Vector Store hiện có"""
print(f"\n📎 Adding document: {Path(file_path).name}")

with open(file_path, "rb") as f:
file_obj = client.agents.upload_file_and_poll(
file=f, purpose="assistants"
)

# Link file vào vector store hiện có
batch = client.agents.create_vector_store_file_batch_and_poll(
vector_store_id=vector_store_id,
file_ids=[file_obj.id]
)

print(f" ✅ Document indexed: {batch.file_counts.completed}/{batch.file_counts.total}")
return file_obj.id


def list_vector_store_files(client: AIProjectClient, vector_store_id: str):
"""List tất cả files trong Vector Store"""
files = client.agents.list_vector_store_files(vector_store_id=vector_store_id)
print(f"\n📁 Files in Vector Store ({vector_store_id[:12]}...):")
for f in files.data:
print(f" - {f.id[:8]}... | Status: {f.status}")
return files.data


def remove_document(client: AIProjectClient, vector_store_id: str, file_id: str):
"""Xoá document khỏi Vector Store (không xoá file gốc)"""
client.agents.delete_vector_store_file(
vector_store_id=vector_store_id,
file_id=file_id
)
print(f"🗑️ Removed file {file_id[:8]}... from vector store")


def create_kb_agent(client: AIProjectClient, vector_store_id: str):
"""Tạo agent với persistent knowledge base"""
file_search = FileSearchTool(vector_store_ids=[vector_store_id])

agent = client.agents.create_agent(
model="gpt-4o",
name="kb-agent",
instructions="""Trợ lý tra cứu tài liệu.
Chỉ trả lời dựa trên tài liệu đã được index.
Luôn dẫn nguồn cụ thể.
Nếu không có thông tin → nói thẳng, không đoán.""",
tools=file_search.definitions,
tool_resources=file_search.resources
)
return agent


def main():
client = AIProjectClient.from_connection_string(
conn_str=os.environ["AZURE_AI_PROJECT_CONNECTION_STRING"],
credential=DefaultAzureCredential()
)

print("=" * 60)
print("🗄️ Persistent Knowledge Base Manager")
print("=" * 60)

# ── BƯỚC 1: Get or create Vector Store ───────────────────────
vs_id = get_or_create_vector_store(client, "company-kb-prod")

# ── BƯỚC 2: Add documents (chỉ cần chạy khi có tài liệu mới) ─
# Kiểm tra xem đã có files chưa
existing = list_vector_store_files(client, vs_id)

if len(existing) == 0:
print("\n📥 No documents found — adding initial documents...")
add_document(client, vs_id, "data/product-faq.md")
add_document(client, vs_id, "data/shipping-policy.md")
else:
print(f"\n✅ KB already has {len(existing)} documents — ready to use!")

# ── BƯỚC 3: Sử dụng agent ─────────────────────────────────────
agent = create_kb_agent(client, vs_id)
thread = client.agents.create_thread()

# Demo queries
queries = [
"Sản phẩm nào không được đổi trả?",
"Tôi ở tỉnh lẻ, mua hàng 600k sẽ mất bao lâu và phí bao nhiêu?"
]

for q in queries:
print(f"\n❓ {q}")
client.agents.create_message(thread_id=thread.id, role="user", content=q)
run = client.agents.create_and_process_run(thread_id=thread.id, agent_id=agent.id)
if run.status == "completed":
msgs = client.agents.list_messages(thread_id=thread.id)
print(f"💬 {msgs.data[0].content[0].text.value}")

# ── Cleanup agent (giữ lại Vector Store!) ─────────────────────
client.agents.delete_agent(agent.id)
print(f"\n✅ Done! Vector Store {vs_id} preserved for next session.")
print(" Run script again to see it reuse the existing KB.")


if __name__ == "__main__":
main()

📖 Vector Store Lifecycle Management

Production checklist:

TaskKhi nàoCode
Tạo VS1 lần duy nhấtcreate_vector_store_and_poll()
Lưu VS IDNgay sau khi tạoDB / config file
Add document mớiKhi policy updatecreate_vector_store_file_batch_and_poll()
Remove document cũKhi policy lỗi thờidelete_vector_store_file()
Monitor statusĐịnh kỳget_vector_store()

🚀 WHAT IF

⚠️ Pitfall: create_vector_store vs create_vector_store_and_poll

# ❌ Dùng create_vector_store rồi dùng ngay → status có thể vẫn là "in_progress"
vs = client.agents.create_vector_store(name="kb")
# vs.status có thể = "in_progress" ← chưa ready!

# ✅ Dùng _and_poll để chờ indexing xong
vs = client.agents.create_vector_store_and_poll(name="kb")
# vs.status = "completed" ← guaranteed ready

Bài tiếp theo: Bài 10 — Recipe: Multi-Agent System →


Made by Anh Tu - Share to be shared