1. Giới thiệu
Tổng quan
Trong phòng thí nghiệm này, bạn sẽ triển khai một phiên bản hoàn toàn liên tục và bảo mật của Hermes Agent (của Nous Research) vào Cloud Run Instances. Bạn sẽ tương tác với tác nhân AI bằng Bảng điều khiển web Hermes và hỗ trợ không gian làm việc liên tục bằng Google Cloud Storage.
Mặc dù Hermes hỗ trợ chế độ Cổng có thể chạy dưới dạng Dịch vụ Cloud Run có khả năng tự động cấp tài nguyên bổ sung, nhưng Hermes cũng đóng vai trò là một tác nhân có trạng thái quét các kỹ năng khi khởi động và xử lý việc thực thi ở chế độ nền. Các phiên bản Cloud Run cung cấp một môi trường có thể định địa chỉ riêng lẻ và tồn tại lâu dài, rất phù hợp với khối lượng công việc này.
Bạn sẽ thực hiện
- Chuẩn bị một bộ chứa Cloud Storage để duy trì trạng thái và cấu hình của vùng chứa.
- Tạo một trình giám sát Python tuỳ chỉnh (
run_hermes.py) và một tập lệnh khởi động (start_hermes.sh) để xử lý quá trình khởi động. - Triển khai Hermes Agent bằng cách dùng
gcloud beta run instances deploy. - Truy cập và xác thực vào Trang tổng quan Hermes.
Kiến thức bạn sẽ học được
- Cách triển khai Hermes Agent cho các phiên bản Cloud Run.
- Cách gắn bộ chứa Cloud Storage vào các phiên bản Cloud Run bằng GCSFuse.
- Cách định cấu hình SQLite và bộ nhớ đệm tạm thời một cách an toàn để bỏ qua các hạn chế về khoá tệp GCSFuse.
2. Thiết lập và yêu cầu
Thiết lập dự án GCP
- Đăng nhập vào Google Cloud Console.
- Tạo hoặc chọn một dự án trên Google Cloud.
- Đảm bảo bạn đã bật tính năng thanh toán cho dự án trên đám mây của mình trên Google Cloud.
Mở Cloud Shell
Kích hoạt Google Cloud Shell từ thanh công cụ trên cùng của Cloud Console.
Thiết lập dự án và cài đặt gcloud beta
Trước tiên, hãy đặt dự án và khu vực của bạn làm biến môi trường.
export PROJECT_ID=<YOUR_PROJECT_ID>
export REGION="us-west2"
export BUCKET_NAME="hermes-state-${PROJECT_ID}"
Đồng thời định cấu hình dự án cho gcloud.
gcloud config set project $PROJECT_ID
Đảm bảo bạn đã cài đặt thành phần beta cho gcloud beta run instances:
gcloud components install beta --quiet
Phiên bản gcloud của bạn là phiên bản mới nhất.
gcloud components updates
Bật các API bắt buộc của Google Cloud
Trong Cloud Shell, hãy bật API Cloud Run, Cloud Storage và Secret Manager:
gcloud services enable \
run.googleapis.com \
secretmanager.googleapis.com \
storage.googleapis.com \
compute.googleapis.com \
aiplatform.googleapis.com
3. Tạo Tài khoản dịch vụ chuyên dụng
Để tuân thủ nguyên tắc về đặc quyền tối thiểu, hãy tạo một tài khoản dịch vụ IAM chuyên dụng cho tác nhân Hermes và cấp cho tài khoản đó các quyền cần thiết để gọi các mô hình Vertex AI:
export SERVICE_ACCOUNT_NAME="hermes-sa"
gcloud iam service-accounts create ${SERVICE_ACCOUNT_NAME} \
--display-name="Hermes Service Account"
export SERVICE_ACCOUNT="${SERVICE_ACCOUNT_NAME}@${PROJECT_ID}.iam.gserviceaccount.com"
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
--member="serviceAccount:${SERVICE_ACCOUNT}" \
--role="roles/aiplatform.user"
4. Lưu trữ thông tin đăng nhập trong Secret Manager
Chúng ta sẽ lưu trữ thông tin đăng nhập nhạy cảm như mật khẩu trang tổng quan trong Google Cloud Secret Manager để Cloud Run có thể chèn thông tin đăng nhập đó một cách an toàn vào vùng chứa tại thời điểm khởi động.
Tạo một mật khẩu ngẫu nhiên an toàn cho trang tổng quan của bạn và lưu trữ mật khẩu đó trong Secret Manager:
export DASHBOARD_PASSWORD=$(openssl rand -hex 16)
echo "Generated Hermes Dashboard Password: ${DASHBOARD_PASSWORD}"
echo -n "${DASHBOARD_PASSWORD}" | gcloud secrets create hermes-dashboard-password \
--data-file=- \
--replication-policy="automatic"
gcloud secrets add-iam-policy-binding hermes-dashboard-password \
--member="serviceAccount:${SERVICE_ACCOUNT}" \
--role="roles/secretmanager.secretAccessor"
5. Chuẩn bị bộ chứa Cloud Storage và tệp cấu hình
Hermes cần bộ nhớ ổn định được gắn dưới dạng /opt/data. Chúng ta sẽ sử dụng một bộ chứa Google Cloud Storage (GCS) và gắn bộ chứa đó bằng cách sử dụng tính năng gắn ổ đĩa Cloud Storage.
1. Tạo bộ chứa Cloud Storage
gcloud storage buckets create gs://${BUCKET_NAME} --location=${REGION}
# Grant the service account permissions to mount the bucket
gcloud storage buckets add-iam-policy-binding gs://${BUCKET_NAME} \
--member="serviceAccount:${SERVICE_ACCOUNT}" \
--role="roles/storage.objectAdmin"
2. Tạo config.yaml
Tạo tệp config.yaml. Nhớ thêm _config_version: 12 để đảm bảo cấu hình được tải đúng cách:
_config_version: 12
model:
default: "google/gemini-3.8-flash"
provider: "vertex"
dashboard:
enabled: true
database:
journal_mode: delete
3. Tạo tập lệnh Supervisor (run_hermes.py)
Cloud Storage không hỗ trợ các cơ chế khoá tệp cụ thể mà cơ sở dữ liệu SQLite cần để chạy một cách an toàn. Để ngăn chặn tình trạng hỏng cơ sở dữ liệu, chúng ta cần một tập lệnh "giám sát" tuỳ chỉnh (run_hermes.py). Tập lệnh này định cấu hình Hermes để lưu trữ các khoá cơ sở dữ liệu tạm thời trong bộ nhớ cục bộ của vùng chứa thay vì trên Cloud Storage trước khi khởi động tác nhân.
Tạo run_hermes.py cục bộ:
import os
import shutil
import subprocess
import sys
import threading
import time
print(
"=== INITIALIZING HERMES SUPERVISOR ===", flush=True
)
# 1. Local Directory Setup
# Creates temporary, local folders (in /tmp) for the agent's caches and working directories.
# See more below in comment section NOTE ON CLOUD STORAGE FUSE
home_dir = "/tmp/hermes_home"
hermes_dir = os.path.join(home_dir, ".hermes")
os.makedirs(hermes_dir, exist_ok=True)
os.makedirs("/tmp/logs", exist_ok=True)
os.makedirs("/tmp/skills", exist_ok=True)
os.makedirs("/tmp/uv_cache", exist_ok=True)
os.makedirs("/tmp/cache", exist_ok=True)
os.makedirs("/opt/data/workspace", exist_ok=True)
os.makedirs("/opt/data/.hermes", exist_ok=True)
# 2. State Restoration & Database Config
# Copies your existing configurations and chat history (state.db) from Cloud Storage into the local folders.
# It also forces the SQLite database into TRUNCATE mode, a crucial step to prevent database corruption
# when eventually saving back to Cloud Storage. See more in section 3. Note on Cloud Storage Fuse below
if os.path.exists("/opt/data/config.yaml"):
shutil.copy("/opt/data/config.yaml", os.path.join(hermes_dir, "config.yaml"))
print(f"Synced config.yaml -> {hermes_dir}/config.yaml", flush=True)
elif os.path.exists("/opt/data/.hermes/config.yaml"):
shutil.copy("/opt/data/.hermes/config.yaml", os.path.join(hermes_dir, "config.yaml"))
print(f"Synced config.yaml from .hermes -> {hermes_dir}/config.yaml", flush=True)
if os.path.exists("/opt/data/.env"):
shutil.copy("/opt/data/.env", os.path.join(hermes_dir, ".env"))
print(f"Synced .env -> {hermes_dir}/.env", flush=True)
elif os.path.exists("/opt/data/.hermes/.env"):
shutil.copy("/opt/data/.hermes/.env", os.path.join(hermes_dir, ".env"))
print(f"Synced .env from .hermes -> {hermes_dir}/.env", flush=True)
if os.path.exists("/opt/data/.hermes/state.db"):
shutil.copy("/opt/data/.hermes/state.db", os.path.join(hermes_dir, "state.db"))
print(f"Synced state.db -> {hermes_dir}/state.db (restored previous chats!)", flush=True)
# 3. Note on Cloud Storage Fuse
# Cloud Storage FUSE is optimized for object storage, but is not fully POSIX compliant.
# This means GCS lacks the byte-range file locking required by active caches and default SQLite (WAL mode)
# which SQLite depends on to prevent data collisions.
# Without these locks, SQLite experiences database corruption and blocked I/O operations.
# To ensure stability, we route these active I/O processes to local container memory (/tmp).
# See section 5 Enable Autosave below on how /tmp is uploaded to Cloud Storage.
# Read more: https://cloud.google.com/storage/docs/cloud-storage-fuse/overview#differences-and-limitations
db_path = os.path.join(hermes_dir, "state.db")
try:
import sqlite3
conn = sqlite3.connect(db_path)
conn.execute("PRAGMA journal_mode=TRUNCATE;")
conn.close()
print("Configured SQLite database to TRUNCATE mode for direct single-file persistence", flush=True)
except Exception as e:
print(f"Warning: Failed to configure TRUNCATE mode: {e}", flush=True)
subprocess.run(["chmod", "-R", "777", "/tmp"], check=False)
# 4. Update system environment variables
# Hermes needs to know to look at the new local /tmp folders rather than defaulting to the mounted bucket.
env = dict(os.environ)
env["HOME"] = home_dir
env["HERMES_HOME"] = hermes_dir
env["PATH"] = "/opt/hermes/.venv/bin:/opt/hermes/bin:" + env.get("PATH", "")
env["PYTHONUNBUFFERED"] = "1"
env["HERMES_STATE_PATH"] = hermes_dir
env["HERMES_SKILLS_PATH"] = "/tmp/skills"
env["UV_CACHE_DIR"] = "/tmp/uv_cache"
env["XDG_CACHE_HOME"] = "/tmp/cache"
env["SQLITE_BUSY_TIMEOUT"] = "30000"
env["HERMES_ALLOW_ROOT_GATEWAY"] = "1"
env["HERMES_WORKSPACE"] = "/opt/data/workspace"
env["HERMES_WRITE_SAFE_ROOT"] = "/opt/data"
python_bin = "/opt/hermes/.venv/bin/python3"
# 5. Enable Autosave
# Spawn a background worker thread to watch your local database and config files every 5 seconds.
# As you chat with your agent, this worker thread automatically copies the updated database content
# back to Cloud Storage to persist it.
def sync_to_gcs_loop():
files_to_sync = ["state.db", "config.yaml", ".env"]
last_mtimes = {}
# Initialize last_mtimes
for f in files_to_sync:
path = os.path.join(hermes_dir, f)
if os.path.exists(path):
last_mtimes[f] = os.path.getmtime(path)
else:
last_mtimes[f] = 0
while True:
time.sleep(5)
for f in files_to_sync:
src_path = os.path.join(hermes_dir, f)
if os.path.exists(src_path):
try:
mtime = os.path.getmtime(src_path)
if mtime > last_mtimes.get(f, 0):
dst_path = os.path.join("/opt/data/.hermes", f)
shutil.copy2(src_path, dst_path)
last_mtimes[f] = mtime
print(f"Auto-saved {f} to GCS volume mount", flush=True)
except Exception as e:
print(f"Error auto-saving {f} to GCS: {e}", flush=True)
threading.Thread(target=sync_to_gcs_loop, daemon=True).start()
# 6. Launch the Hermes Gateway (the AI backend) and the Web Dashboard (the UI)
# These are launched as parallel processes, sending logs to Cloud Run via stdout & stderr
print("=== STARTING GATEWAY IN BACKGROUND ===", flush=True)
gw = subprocess.Popen(
[python_bin, "-m", "hermes_cli.main", "gateway", "run"],
env=env,
cwd="/opt/data/workspace",
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
bufsize=1,
)
def stream_gw():
for line in iter(gw.stdout.readline, ""):
if line:
print(f"[GATEWAY] {line.rstrip()}", flush=True)
threading.Thread(target=stream_gw, daemon=True).start()
print("=== STARTING DASHBOARD ON 0.0.0.0:8080 ===", flush=True)
sys.stdout.flush()
dash = subprocess.Popen(
[
python_bin,
"-m",
"hermes_cli.main",
"dashboard",
"--host",
"0.0.0.0",
"--port",
"8080",
"--skip-build",
],
env=env,
cwd="/opt/data/workspace",
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
bufsize=1,
)
for line in iter(dash.stdout.readline, ""):
if line:
print(f"[DASHBOARD] {line.rstrip()}", flush=True)
rc = dash.wait()
print(f"DASHBOARD EXITED WITH RETURN CODE: {rc}", flush=True)
while True:
time.sleep(10)
4. Tạo tập lệnh khởi động (start_hermes.sh)
Tạo start_hermes.sh cục bộ.
#!/bin/sh
set -e
export PYTHONUNBUFFERED=1
exec python3 /opt/data/run_hermes.py
5. Tải tệp lên Cloud Storage
Sao chép các tệp cấu hình vào thư mục gốc của bộ chứa GCS:
gcloud storage cp config.yaml run_hermes.py start_hermes.sh gs://${BUCKET_NAME}/
6. Triển khai Hermes trên các phiên bản Cloud Run
Chúng tôi sử dụng gcloud beta run instances deploy để triển khai vùng chứa. Lệnh này bao gồm các cấu hình cụ thể để giải quyết các vấn đề đã biết với GCSFuse và giới hạn của vùng chứa.
Đảm bảo các biến môi trường (PROJECT_ID, REGION, BUCKET_NAME, SERVICE_ACCOUNT) được xuất trong phiên hoạt động của thiết bị đầu cuối.
Triển khai phiên bản:
gcloud beta run instances deploy hermes-instance \
--image nousresearch/hermes-agent:latest \
--service-account ${SERVICE_ACCOUNT} \
--command "/bin/sh" \
--args "/opt/data/start_hermes.sh" \
--port 8080 \
--cpu 2 \
--memory 4Gi \
--ingress all \
--no-invoker-iam-check \
--add-volume name=hermes-storage,mount-path=/opt/data,type=cloud-storage,mount-options="uid=2000;gid=2000;file-mode=0777;dir-mode=0777;implicit-dirs",bucket=$BUCKET_NAME \
--set-secrets "HERMES_DASHBOARD_BASIC_AUTH_PASSWORD=hermes-dashboard-password:latest" \
--set-env-vars "PYTHONUNBUFFERED=1,VERTEX_PROJECT_ID=$PROJECT_ID,VERTEX_LOCATION=global,HERMES_DASHBOARD_BASIC_AUTH_USERNAME=admin,HERMES_ALLOW_ROOT_GATEWAY=1,HERMES_WORKSPACE=/opt/data/workspace,HERMES_WRITE_SAFE_ROOT=/opt/data" \
--region $REGION \
--project $PROJECT_ID
Các cấu hình quan trọng được đề cập ở trên:
--service-account: Đính kèm tài khoản dịch vụhermes-sachuyên dụng.- Tập lệnh giám sát:
start_hermes.shgọi trình giám sát Python tuỳ chỉnhrun_hermes.pyđể chuyển các giới hạn khoá SQLite và vấn đề về lưu vào bộ nhớ đệm từ FUSE của GCS vào tmpfs cục bộ. --set-secrets: Chèn thông tin đăng nhập trực tiếp từ Secret Manager vào các biến môi trường.
7. Tương tác trực tiếp thông qua giao diện người dùng web Hermes
Sau khi triển khai, bạn có thể truy cập vào trang tổng quan của mình tại URL .run.app đã tạo. Khi được nhắc xác thực, hãy nhập admin làm Tên người dùng và ${DASHBOARD_PASSWORD} làm Mật khẩu.
Trò chuyện với trợ lý ảo
Bạn có thể thử những thao tác như echo "hello" để xác nhận rằng nhân viên hỗ trợ đang hoạt động.
Kiểm thử bộ nhớ ổn định
Bạn có thể kiểm thử bộ nhớ liên tục trong Bộ chứa Google Cloud bằng cách yêu cầu nhân viên hỗ trợ
Write "hello world" to a file named hello.txt in your workspace.
Sau đó, trong trình bao, bạn có thể xác minh rằng tệp đã được ghi bằng cách chạy
gcloud storage cat gs://$BUCKET_NAME/workspace/hello.txt
Cuối cùng, để xác minh rằng các cuộc trò chuyện và tệp của bạn vẫn tồn tại trên các phiên bản Cloud Run mới (vì phiên bản Cloud Run có thời gian chạy liên tục lên đến 7 ngày, với chính sách khởi động lại tự động được định cấu hình theo mặc định), bạn có thể chạy lại lệnh gcloud beta run instances deploy chính xác như trước. Sau đó, bạn sẽ thấy các phiên trò chuyện của mình. Bạn có thể hỏi nhân viên hỗ trợ
Read the contents of the file hello.txt in your workspace.
và bạn sẽ thấy "hello world".
8. Dọn dẹp
Để tránh phát sinh phí cho tài khoản Google Cloud của bạn đối với các tài nguyên được dùng trong lớp học lập trình này, hãy làm như sau:
- Xoá phiên bản Cloud Run:
gcloud beta run instances delete hermes-instance --region ${REGION} --quiet - Xoá các giá trị bí mật trong Secret Manager:
gcloud secrets delete hermes-dashboard-password --quiet - Xoá bộ chứa Cloud Storage:
gcloud storage rm -r gs://${BUCKET_NAME} - Xoá tài khoản dịch vụ chuyên dụng:
gcloud iam service-accounts delete ${SERVICE_ACCOUNT} --quiet
9. Kết luận
Xin chúc mừng! Bạn đã triển khai thành công một phiên bản bảo mật, hoàn toàn liên tục của Hermes Agent trên Cloud Run Instances được hỗ trợ bởi Cloud Storage!
Kiến thức bạn học được
- Cách triển khai Hermes Agent cho các phiên bản Cloud Run.
- Cách gắn bộ chứa Cloud Storage vào các phiên bản Cloud Run bằng GCSFuse.
- Cách định cấu hình SQLite và bộ nhớ đệm tạm thời một cách an toàn để bỏ qua các hạn chế về khoá tệp GCSFuse.