P6 - Cấu hình OpenClaw 9Router Multi Account
Bước 0: Kích hoạt Codex trong cài đặt Chatgpt cho các tài khoản
Setting => Security => Enable device code author...
Bước 1: Cài đặt Node/npm và 9router
node -v
npm -v
npm install -g 9router
Nếu node/npm/git chưa được cài đặt thì:
Cài đặt Node 22 LTS:
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install nodejs -y
sudo apt install curl git -y
Bước 2: Chạy 9Router
Trên máy ảo, hãy thực hiện lệnh sau:
9router
Bảng điều khiển (đối với Windows, hãy mở trực tiếp từ liên kết này): http://localhost:20128/dashboard
Vì tôi đang sử dụng Ubuntu trên một máy khác, tôi sẽ SSH vào máy ảo Ubuntu AI này từ máy của mình.
Máy client: Mở CMD và SSH vào máy ảo AI.
ssh -N -L 20128:127.0.0.1:20128 bao@192.168.16.253 (IP VM AI)
Nhập pass SSH
Bước 3: Kết nối với nhà cung cấp trong bảng điều khiển
Truy cập:
• Dashboard → Providers → select Codex / ChatGPT (Codex) (or “Codex”)
• Click Connect → perform OAuth/device login as instructed
Nó sẽ hiển thị liên kết localhost, sao chép liên kết đó và dán vào terminal/SSH trên máy AI.
Thêm tài khoản thứ hai
Lặp lại thao tác “Kết nối” (hoặc “Thêm tài khoản”) để đăng nhập bằng tài khoản thứ hai.
Tương tự
bật chế độ luân phiên (round robin)
Tạo combo: tên openai
Bước 4: Tạo khóa API 9router
Đặt tên cho khóa; hệ thống sẽ tự động tạo khóa.
Key: sk-ccf64851645b8890-g9lfc4-4241aeb4
Kiểm tra key trên máy ảo
KEY='sk-ccf64851645b8890-g9lfc4-4241aeb4'
curl -sS -H "Authorization: Bearer $KEY" http://127.0.0.1:20128/v1/models | head -c 400; echo
Bước 5: Thay đổi nhà cung cấp OpenClaw openai-codex thành điểm cuối 9Router
Chạy lệnh hàng loạt
openclaw config set --batch-json "[
{\"path\":\"models.mode\",\"value\":\"merge\"},
{\"path\":\"models.providers.openai-codex\",\"value\":{
\"baseUrl\":\"http://127.0.0.1:20128/v1\",
\"api\":\"openai-completions\",
\"headers\":{\"Authorization\":\"Bearer $KEY\"},
\"models\":[]
}}
]"
Restart service
systemctl --user restart openclaw-gateway
Xác thực + restart gateway
openclaw config validate --json
valid: true
systemctl --user restart openclaw-gateway
Xác minh OpenClaw để xem mô hình thông qua 9Router:
openclaw models status –plain
Chỉnh sửa lại tệp cấu hình:
nano ~/.openclaw/openclaw.json
Thêm /cx vào tệp cấu hình.
Lưu tệp: Nhấn Ctrl O để vào Enter, Ctrl X để thoát.
Nếu bạn cần kiểm tra tệp mô hình được tạo lại:
cat ~/.openclaw/agents/main/agent/models.json | head -n 60
Việc tắt chế độ luân phiên (round robin) sẽ tự động chuyển sang tài khoản opanai1.
Bước 6: Tạo dịch vụ người dùng systemd.
#0: Bật CLI Tools Openclaw
#1 Tạo unit file
mkdir -p ~/.config/systemd/user
nano ~/.config/systemd/user/9router.service
Dán nội dung sau (khuyến nghị chỉ liên kết cục bộ để đảm bảo an toàn):
[Unit]
Description=9Router (OpenAI-compatible router dashboard)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
Environment=PATH=%h/.npm-global/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
WorkingDirectory=%h
ExecStart=%h/.npm-global/bin/9router --host 127.0.0.1 --port 20128 --no-browser --log --skip-update
Restart=on-failure
RestartSec=2
[Install]
WantedBy=default.target
Save file
#2 Reload systemd user + enable + start
systemctl --user daemon-reload
systemctl --user enable --now 9router
#3 Làm cho openclaw-gateway chờ 9router
mkdir -p ~/.config/systemd/user/openclaw-gateway.service.d
Sao chép lệnh chính xác như sau:
cat > ~/.config/systemd/user/openclaw-gateway.service.d/override.conf <<'EOF'
[Unit]
Wants=9router.service
After=9router.service
EOF
Restart service
systemctl --user daemon-reload
systemctl --user restart 9router
systemctl --user restart openclaw-gateway
#4 Kiểm tra trạng thái + xem nhật ký thời gian thực
systemctl --user status 9router --no-pager -l
journalctl --user -u 9router -f
#5 Các lệnh quản lý nhanh
systemctl --user restart 9router
systemctl --user stop 9router
systemctl --user start 9router
5) Kiểm tra điểm cuối trực tiếp
curl -sS http://127.0.0.1:20128/v1/models | head -c 300; echo
6) (Quan trọng) Tự động chạy sau khi khởi động lại
Các dịch vụ người dùng đôi khi chỉ chạy khi người dùng có phiên làm việc. Nếu bạn muốn dịch vụ khởi động và chạy tự động ngay cả khi không có quyền truy cập SSH, bạn cần bật linger (yêu cầu sudo):
sudo loginctl enable-linger bao
Bước 7: Thiết lập một tập lệnh để giám sát hạn mức theo tài khoản người dùng
1. Tạo thư mục bin (nếu chưa tồn tại)
mkdir -p ~/bin
2. Tạo Python file
nano ~/bin/9router-account-usage.py
(Nội dung: phân tích tệp ~/.9router/log.txt, xóa dòng PENDING, thêm theo tài khoản, in bảng cột)
#!/usr/bin/env python3
"""Summarize 9Router usage per account from ~/.9router/log.txt.
Expected log format (from 9Router):
DD-MM-YYYY HH:MM:SS | <model> | CODEX | <account> | <prompt_tokens or -> | <completion_tokens or -> | <status>
We count only completed (non-PENDING) rows.
"""
from __future__ import annotations
import argparse
import datetime as dt
import os
import re
import sys
from collections import defaultdict
LOG_PATH_DEFAULT = os.path.expanduser("~/.9router/log.txt")
LINE_RE = re.compile(
r"^(?P<date>\d{2}-\d{2}-\d{4})\s+(?P<time>\d{2}:\d{2}:\d{2})\s+\|\s+"
r"(?P<model>[^|]+?)\s+\|\s+"
r"(?P<provider>[^|]+?)\s+\|\s+"
r"(?P<account>[^|]+?)\s+\|\s+"
r"(?P<prompt>[^|]+?)\s+\|\s+"
r"(?P<completion>[^|]+?)\s+\|\s+"
r"(?P<status>.+?)\s*$"
)
def parse_int(s: str) -> int:
s = s.strip()
if s == "-" or s == "":
return 0
try:
return int(s)
except ValueError:
return 0
def parse_args() -> argparse.Namespace:
ap = argparse.ArgumentParser(description="9Router per-account usage table")
ap.add_argument("--log", default=LOG_PATH_DEFAULT, help=f"Log file path (default: {LOG_PATH_DEFAULT})")
ap.add_argument("--date", default="today", help="Filter date: YYYY-MM-DD or 'today' (default: today)")
ap.add_argument("--tz", default=None, help="Timezone name (best-effort). Default: local time on server")
ap.add_argument("--top", type=int, default=50, help="Max accounts to show (default: 50)")
return ap.parse_args()
def resolve_date(date_arg: str) -> dt.date:
if date_arg == "today":
return dt.date.today()
return dt.date.fromisoformat(date_arg)
def main() -> int:
args = parse_args()
path = os.path.expanduser(args.log)
if not os.path.exists(path):
print(f"Log not found: {path}", file=sys.stderr)
return 2
target_date = resolve_date(args.date)
# account -> metrics
m = defaultdict(lambda: {
"requests": 0,
"ok": 0,
"err": 0,
"prompt_tokens": 0,
"completion_tokens": 0,
"models": defaultdict(int),
"last": None,
})
with open(path, "r", encoding="utf-8", errors="replace") as f:
for line in f:
line = line.rstrip("\n")
if not line.strip():
continue
mo = LINE_RE.match(line)
if not mo:
continue
status = mo.group("status").strip()
if status.upper() == "PENDING":
continue
# parse date in log: DD-MM-YYYY
d_str = mo.group("date")
t_str = mo.group("time")
d = dt.datetime.strptime(d_str, "%d-%m-%Y").date()
if d != target_date:
continue
account = mo.group("account").strip()
model = mo.group("model").strip()
prompt = parse_int(mo.group("prompt"))
completion = parse_int(mo.group("completion"))
rec = m[account]
rec["requests"] += 1
if "200" in status or status.upper().startswith("OK") or "OK" in status.upper():
rec["ok"] += 1
else:
rec["err"] += 1
rec["prompt_tokens"] += prompt
rec["completion_tokens"] += completion
rec["models"][model] += 1
rec["last"] = f"{d_str} {t_str}"
rows = []
for acct, rec in m.items():
total_tokens = rec["prompt_tokens"] + rec["completion_tokens"]
top_models = ",".join([f"{k}:{v}" for k, v in sorted(rec["models"].items(), key=lambda kv: (-kv[1], kv[0]))[:3]])
rows.append((
acct,
rec["requests"],
rec["ok"],
rec["err"],
rec["prompt_tokens"],
rec["completion_tokens"],
total_tokens,
top_models,
rec["last"] or "-",
))
rows.sort(key=lambda r: (-r[1], r[0]))
rows = rows[: args.top]
# print table
header = [
"ACCOUNT",
"REQ",
"OK",
"ERR",
"PROMPT_TOK",
"COMP_TOK",
"TOTAL_TOK",
"TOP_MODELS",
"LAST",
]
# compute widths
widths = [len(h) for h in header]
for r in rows:
for i, val in enumerate(r):
widths[i] = max(widths[i], len(str(val)))
def fmt_row(vals):
return " ".join(str(v).ljust(widths[i]) for i, v in enumerate(vals))
print(fmt_row(header))
print(" ".join("-" * w for w in widths))
for r in rows:
print(fmt_row(r))
# totals
tot_req = sum(r[1] for r in rows)
tot_prompt = sum(r[4] for r in rows)
tot_comp = sum(r[5] for r in rows)
print()
print(f"TOTAL requests={tot_req} prompt_tokens={tot_prompt} completion_tokens={tot_comp} total_tokens={tot_prompt+tot_comp}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
Save file: Ctrl O enter, Ctrl X exit
3. Phân quyền
chmod +x ~/bin/9router-account-usage.py
4. Tạo file ~/bin/9router-account-usage để gọi hàm ngắn gọn hơn.
cat > ~/bin/9router-account-usage <<'SH'
#!/usr/bin/env bash
set -euo pipefail
exec "$HOME/bin/9router-account-usage.py" "$@"
SH
Phân quyền
chmod +x ~/bin/9router-account-usage
5. Test
~/bin/9router-account-usage --date today | head -n 60
~/bin/9router-account-usage --date today
Tham khảo thêm bài viết cùng chủ đề
P10 – Gỡ OpenClaw Windows Sạch Sẽ
P10 – Gỡ OpenClaw Windows Sạch Sẽ https://youtu.be/1ljEMzohiSY 🚀 AI Tutorial – P10: Gỡ OpenClaw trên Windows (Clean Removal & Fix Issues) Nếu bạn đang gặp lỗi với OpenClaw hoặc muốn cài lại từ đầu, việc gỡ openclaw đúng cách là rất quan trọng. Nếu chỉ xóa file đơn giản,...
Read MoreP9 – Cài đặt Telegram Bot Local AI với Ollama nhanh
P9 – Cài đặt Telegram Bot Local AI với Ollama nhanh https://youtu.be/YuiLJDLIVr0 🚀 AI Tutorial – P9: Cài Đặt Telegram Bot Local AI với Ollama Việc cài đặt telegram bot local giúp bạn đưa AI vào thực tế một cách nhanh chóng và hiệu quả. Khi kết hợp Ollama với...
Read MoreP8 – Cài đặt OpenClaw Local AI với Ollama & Qwen
P8 – Cài đặt OpenClaw Local AI với Ollama & Qwen 🚀 AI Tutorial – P8: Cài Đặt OpenClaw Local với Ollama, Qwen & Open WebUI Việc cài đặt openclaw local giúp bạn xây dựng một hệ thống AI chạy hoàn toàn nội bộ, không phụ thuộc vào API bên...
Read More