Hermes Agent Profile架构与多用户方案设计
目录
一、Profile架构核心机制
1.1 核心设计理念
Hermes Agent的Profile机制基于环境变量隔离的轻量级设计,核心是通过HERMES_HOME环境变量实现完全独立的运行环境。
关键代码路径: hermes_constants.py
def get_hermes_home() -> Path:
"""Return the Hermes home directory (default: ~/.hermes)."""
return Path(os.getenv("HERMES_HOME", Path.home() / ".hermes"))
1.2 Profile目录结构
每个Profile是一个完全独立的HERMES_HOME目录:
~/.hermes/ # 默认Profile (default)
├── config.yaml # 模型、提供商、工具配置
├── .env # API密钥、Bot Token
├── SOUL.md # 个性设定
├── memories/ # 记忆系统
│ ├── MEMORY.md # Agent笔记
│ └── USER.md # 用户画像
├── sessions/ # 会话数据
├── skills/ # 技能目录
├── state.db # SQLite数据库
├── home/ # 子进程HOME目录(隔离git/ssh配置)
├── cron/ # 定时任务
├── logs/ # 日志
└── gateway.pid # Gateway进程ID
~/.hermes/profiles/ # 命名Profile存储位置
├── coder/ # coder profile
│ ├── config.yaml
│ ├── .env
│ ├── SOUL.md
│ └── ... (同上结构)
├── assistant/ # assistant profile
└── work/ # work profile
1.3 Profile核心组件
| 组件 | 隔离级别 | 说明 |
|---|---|---|
| 配置系统 | 完全隔离 | 独立的config.yaml, .env |
| 记忆系统 | 完全隔离 | 独立的MEMORY.md, USER.md |
| 会话系统 | 完全隔离 | 独立的state.db, sessions/ |
| 技能系统 | 完全隔离 | 独立的skills/目录 |
| 凭证池 | 完全隔离 | 独立的auth.json |
| Gateway服务 | 完全隔离 | 独立的进程、Bot Token |
| 定时任务 | 完全隔离 | 独立的cron/目录 |
| 子进程环境 | 完全隔离 | 独立的home/目录 (git/ssh配置) |
1.4 Profile操作机制
命令别名机制:
每个Profile自动在~/.local/bin/创建包装脚本:
# ~/.local/bin/coder
#!/bin/sh
exec hermes -p coder "$@"
切换机制:
| 方式 | 示例 | 说明 |
|---|---|---|
| Flag指定 | hermes -p coder chat |
通过-p或–profile参数 |
| 别名调用 | coder chat |
通过Profile别名直接调用 |
| 粘性默认 | hermes profile use coder |
设置默认Profile,后续hermes命令自动使用 |
Profile管理命令:
hermes profile list # 列出所有Profile
hermes profile create <name> # 创建新Profile
hermes profile create <name> --clone # 克隆配置
hermes profile create <name> --clone-all # 完全克隆
hermes profile delete <name> # 删除Profile
hermes profile rename <old> <new> # 重命名
hermes profile export <name> # 导出为tar.gz
hermes profile import <file> # 导入
hermes profile use <name> # 设置默认Profile
二、Profile隔离机制详解
2.1 路径解析隔离
所有119+个文件通过get_hermes_home()解析路径,自动实现隔离:
# 任何需要访问数据的地方
from hermes_constants import get_hermes_home
home = get_hermes_home()
config_path = home / "config.yaml"
关键实现:
- 所有路径都基于
HERMES_HOME动态计算 - 切换Profile只需改变
HERMES_HOME环境变量 - 代码无需任何修改,自动适配当前Profile
2.2 进程级隔离
Gateway进程隔离:
coder gateway start # 启动coder的Gateway (独立进程)
assistant gateway start # 启动assistant的Gateway (独立进程)
每个Gateway是独立的Python进程,拥有自己的:
- 进程ID (存储在各自的
gateway.pid) - 端口监听
- Bot Token连接
Bot Token安全锁:
如果两个Profile使用相同的Bot Token,第二个Gateway会被阻止启动,并显示明确的错误信息。支持的系统:
- Telegram
- Discord
- Slack
- Signal
2.3 子进程HOME隔离
每个Profile有独立的home/目录,用于子进程工具配置隔离:
def get_subprocess_home() -> str | None:
"""Return a per-profile HOME directory for subprocesses."""
hermes_home = os.getenv("HERMES_HOME")
if not hermes_home:
return None
profile_home = os.path.join(hermes_home, "home")
if os.path.isdir(profile_home):
return profile_home
return None
这确保每个Profile有独立的:
- Git身份配置 (
~/.gitconfig) - SSH密钥 (
~/.ssh/) - GitHub CLI token (
~/.config/gh/) - npm配置 (
~/.npmrc)
Docker部署优势:
在Docker环境中,子进程HOME位于持久化卷内,确保工具配置在容器重启后保留。
2.4 会话隔离
SQLite数据库结构:
每个Profile有独立的state.db,包含:
| 表 | 说明 |
|---|---|
sessions |
会话元数据(ID、来源、模型、标题、时间戳) |
messages |
完整消息历史(角色、内容、工具调用、token计数) |
messages_fts |
FTS5全文搜索索引 |
Gateway会话追踪:
| 平台 | 会话Key格式 | 说明 |
|---|---|---|
| Telegram DM | agent:main:telegram:dm:<chat_id> |
每个DM独立会话 |
| Discord DM | agent:main:discord:dm:<chat_id> |
每个DM独立会话 |
| 群组聊天 | agent:main:<platform>:group:<chat_id>:<user_id> |
群组内按用户隔离 |
| 频道 | agent:main:<platform>:channel:<chat_id>:<user_id> |
频道内按用户隔离 |
配置选项:
# config.yaml
gateway:
group_sessions_per_user: true # 群组内按用户隔离会话
session_reset_policy: idle # 会话重置策略: idle/daily/both/none
idle_reset_minutes: 60 # 60分钟无活动重置
2.5 凭证池隔离
每个Profile有独立的凭证池(auth.json),支持:
- 多API密钥轮换
- OAuth令牌管理
- 自动速率限制恢复
- 计费配额自动切换
# 查看凭证池
hermes auth list
# 添加凭证
hermes auth add openrouter --api-key sk-or-v1-xxx
hermes auth add anthropic --type oauth
三、多用户使用场景分析
3.1 场景1:单机多用户(不同OS用户)
现状评估:
| 项目 | 状态 | 说明 |
|---|---|---|
| 支持程度 | ✅ 完全支持 | 每个OS用户有自己的~/.hermes |
| 数据隔离 | ✅ 天然隔离 | 不同home目录 |
| 共享机制 | ❌ 不支持 | 无跨用户共享机制 |
| 管理成本 | ⚠️ 较高 | 每用户需独立安装配置 |
适用场景:
- 个人开发机多用户
- 服务器多用户环境
- 需要完全隔离的场景
3.2 场景2:单用户多Profile(不同用途Agent)
现状评估:
| 项目 | 状态 | 说明 |
|---|---|---|
| 支持程度 | ✅ 完全支持 | Profile机制的核心场景 |
| 数据隔离 | ✅ 完全隔离 | 配置、记忆、会话、凭证全隔离 |
| 管理成本 | ✅ 低 | 统一安装,Profile独立管理 |
| 资源占用 | ✅ 低 | 空闲Profile不占用资源 |
典型用途:
| Profile | 用途 | 特点 |
|---|---|---|
coder |
编程助手 | 技术文档、代码规范记忆 |
assistant |
个人助理 | 日程、偏好记忆 |
research |
研究助手 | 学术资源、文献记忆 |
work |
工作专用 | 公司项目、流程记忆 |
3.3 场景3:团队共享(共享特定Profile)
现状评估:
| 项目 | 状态 | 说明 |
|---|---|---|
| Profile迁移 | ✅ 支持 | export/import功能 |
| 实时共享 | ❌ 不支持 | 无多用户并发访问控制 |
| 协作机制 | ❌ 不支持 | 无协作编辑/同步 |
现有方案:
# 导出Profile
hermes profile export work ./work-backup.tar.gz
# 在另一台机器导入
hermes profile import ./work-backup.tar.gz work
3.4 场景4:多用户协作(同一Profile协作)
现状评估:
| 项目 | 状态 | 说明 |
|---|---|---|
| 多用户访问 | ✅ 支持 | Gateway模式支持多用户同时对话 |
| 会话隔离 | ✅ 支持 | 会话按user_id自动隔离 |
| 记忆隔离 | ❌ 不支持 | 记忆系统不区分用户 |
| 配置隔离 | ❌ 不支持 | 配置对所有用户相同 |
Gateway多用户模式:
通过Telegram/Discord/Slack等消息平台,多用户可以:
- 同时与同一个Agent对话
- 各自的会话历史独立
- 共享Agent的配置和记忆
限制:
- 记忆系统共享(无法区分用户偏好)
- SOUL.md对所有用户相同
- 无法区分用户权限
四、多用户方案设计
4.1 方案A:基于现有Profile机制(推荐)
A-1: 单机多OS用户模式
架构图:
Linux Server
├── 用户 alice
│ └── ~/.hermes/ # alice的Agent
│ ├── config.yaml
│ ├── .env (alice的API keys)
│ ├── memories/
│ └── profiles/
│ ├── work/
│ └── personal/
│
├── 用户 bob
│ └── ~/.hermes/ # bob的Agent
│ ├── config.yaml
│ ├── .env (bob的API keys)
│ └── profiles/
│ └── dev/
│
└── 用户 shared (共享服务账户)
└── ~/.hermes/ # 团队共享Agent
├── config.yaml (团队配置)
└── .env (团队共享凭证)
部署步骤:
# 1. 创建独立的OS用户
sudo useradd -m -s /bin/bash hermes-team
sudo useradd -m -s /bin/bash alice
sudo useradd -m -s /bin/bash bob
# 2. 为每个用户安装Hermes
sudo -u alice bash
cd ~alice
curl -fsSL https://raw.githubusercontent.com/nousresearch/hermes-agent/main/setup-hermes.sh | bash
exit
sudo -u bob bash
cd ~bob
curl -fsSL https://raw.githubusercontent.com/nousresearch/hermes-agent/main/setup-hermes.sh | bash
exit
# 3. 配置各自的API密钥
sudo -u alice hermes setup
sudo -u bob hermes setup
sudo -u hermes-team hermes setup
# 4. 启动各自的Gateway(如果需要)
sudo -u alice hermes gateway install # systemd服务
sudo -u bob hermes gateway install
sudo -u hermes-team hermes gateway install
Systemd服务配置:
每个用户的Gateway会创建独立的systemd服务:
# alice的服务
~/.config/systemd/user/hermes-gateway-default.service
# bob的服务
~/.config/systemd/user/hermes-gateway-default.service
# hermes-team的服务
~/.config/systemd/user/hermes-gateway-default.service
优点:
- ✅ 完全隔离,安全性最高
- ✅ 无需代码修改
- ✅ 每个用户完全控制自己的Agent
缺点:
- ❌ 无共享机制
- ❌ 管理成本高(每个用户需要独立安装)
A-2: Gateway多用户访问模式
架构图:
┌─────────────────────────────────────┐
│ Hermes Gateway (shared) │
│ ~/.hermes/ │
│ ├── config.yaml │
│ ├── memories/ (团队共享记忆) │
│ └── state.db │
└─────────────────────────────────────┘
│
┌────────────────┼────────────────┐
│ │ │
┌─────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐
│ Alice │ │ Bob │ │ Carol │
│ Telegram │ │ Discord │ │ Slack │
│ DM/Group │ │ Server │ │ Workspace │
└───────────┘ └───────────┘ └───────────┘
会话隔离机制:
Gateway自动按用户ID隔离会话:
| 平台 | 会话Key格式 | 说明 |
|---|---|---|
| Telegram DM | agent:main:telegram:dm:<chat_id> |
每个DM独立会话 |
| Discord DM | agent:main:discord:dm:<chat_id> |
每个DM独立会话 |
| 群组聊天 | agent:main:<platform>:group:<chat_id>:<user_id> |
群组内按用户隔离 |
| 频道 | agent:main:<platform>:channel:<chat_id>:<user_id> |
频道内按用户隔离 |
配置示例:
# ~/.hermes/config.yaml
gateway:
group_sessions_per_user: true # 群组内按用户隔离会话
session_reset_policy: idle # 会话重置策略
idle_reset_minutes: 60 # 60分钟无活动重置
部署示例(Telegram):
# 1. 安装Hermes
curl -fsSL https://raw.githubusercontent.com/nousresearch/hermes-agent/main/setup-hermes.sh | bash
# 2. 配置
hermes setup
# 选择模型、输入API密钥
# 3. 配置Telegram Bot
# 访问 @BotFather 创建Bot,获取Token
hermes config set telegram.bot_token "123456:ABC-DEF..."
# 4. 启动Gateway
hermes gateway start
# 5. 团队成员添加Bot
# Alice、Bob、Carol各自在Telegram中搜索并添加Bot即可使用
优点:
- ✅ 多用户可同时访问
- ✅ 会话天然隔离
- ✅ 无需额外配置
- ✅ 跨平台支持(Telegram/Discord/Slack等)
缺点:
- ⚠️ 记忆系统共享(不区分用户)
- ⚠️ 配置共享(所有用户看到相同的SOUL.md)
- ⚠️ 无法区分用户偏好
A-3: 混合方案(多Profile + Gateway)
为不同团队/用途创建独立Profile,每个Profile通过Gateway服务特定用户群。
架构图:
~/.hermes/profiles/
├── team-dev/ # 开发团队Profile
│ ├── config.yaml
│ ├── .env (开发团队Bot Token)
│ ├── SOUL.md (开发团队个性)
│ └── memories/
│ └── MEMORY.md (开发团队知识)
│
├── team-support/ # 支持团队Profile
│ ├── config.yaml
│ ├── .env (支持团队Bot Token)
│ ├── SOUL.md (支持团队个性)
│ └── memories/
│
└── personal-alice/ # Alice个人Profile
├── config.yaml
├── .env (Alice的Bot Token)
└── memories/
运行方式:
# 启动开发团队Gateway(使用Telegram Dev Group Bot)
team-dev gateway start
# 启动支持团队Gateway(使用Discord Support Bot)
team-support gateway start
# Alice个人使用CLI
alice chat
团队Profile配置示例:
# 创建开发团队Profile
hermes profile create team-dev
# 配置开发团队专用设置
team-dev config set model.model anthropic/claude-sonnet-4
team-dev config set telegram.bot_token "DEV_TEAM_BOT_TOKEN"
# 设置开发团队个性
cat > ~/.hermes/profiles/team-dev/SOUL.md << 'EOF'
You are a coding assistant for the development team.
You have access to:
- GitHub repository: github.com/company/project
- CI/CD: Jenkins at ci.company.com
- Issue tracker: Jira at jira.company.com
Team conventions:
- Python code follows PEP8 with 120 char line limit
- Commit messages follow conventional commits
- All PRs require at least one review
EOF
# 启动服务
team-dev gateway install
优点:
- ✅ 灵活性高,适应不同团队需求
- ✅ 各团队完全隔离
- ✅ 每个团队有自己的Agent个性
- ✅ 凭证和配额独立管理
缺点:
- ⚠️ 需要为每个Profile配置独立的Bot Token
- ⚠️ 管理多个Profile有一定复杂度
4.2 方案B:增强型多用户支持(需开发)
如果需要更强的多用户支持(用户级记忆隔离、权限控制),需要开发增强功能。
B-1: 用户级记忆隔离
设计思路:
扩展记忆系统,支持按用户ID存储:
~/.hermes/memories/
├── MEMORY.md # Agent公共记忆
├── USER.md # 默认用户画像
└── users/
├── alice_12345/
│ ├── MEMORY.md # Alice专用记忆
│ └── USER.md # Alice画像
├── bob_67890/
│ ├── MEMORY.md # Bob专用记忆
│ └── USER.md # Bob画像
└── shared/
└── MEMORY.md # 团队共享记忆
实现要点:
- 修改memory tool - 添加
user_id参数
# tools/memory_tool.py (伪代码)
def memory(
action: str,
target: str,
content: str = None,
old_text: str = None,
user_id: str = None # 新增参数
) -> dict:
"""Memory tool with user isolation support."""
home = get_hermes_home()
# 确定记忆文件路径
if user_id:
# 用户专用记忆
memory_path = home / "memories" / "users" / user_id / f"{target}.md"
memory_path.parent.mkdir(parents=True, exist_ok=True)
else:
# 公共记忆
memory_path = home / "memories" / f"{target}.md"
# 执行记忆操作
if action == "add":
return _add_memory(memory_path, content)
elif action == "replace":
return _replace_memory(memory_path, old_text, content)
elif action == "remove":
return _remove_memory(memory_path, old_text)
- Gateway传递user_id
# gateway/session.py (伪代码)
class GatewaySession:
def get_memory_context(self) -> str:
"""Build memory context for system prompt."""
user_id = self.get_user_id() # 从消息来源获取
# 加载公共记忆
public_memory = load_memory("memory")
public_user = load_memory("user")
# 加载用户专用记忆
user_memory = load_memory("memory", user_id=user_id)
user_profile = load_memory("user", user_id=user_id)
# 合并渲染
return render_memory_block(
public_memory, public_user,
user_memory, user_profile
)
- 系统Prompt注入
══════════════════════════════════════════════
MEMORY (your personal notes) [67% — 1,474/2,200 chars]
══════════════════════════════════════════════
[公共记忆内容]
§
══════════════════════════════════════════════
USER MEMORY (alice_12345) [45% — 618/1,375 chars]
══════════════════════════════════════════════
[Alice的专用记忆]
配置选项:
# config.yaml
memory:
user_isolation: true # 启用用户级记忆隔离
shared_memory_fallback: true # 用户无专用记忆时使用公共记忆
B-2: 用户权限控制
设计思路:
添加权限配置,控制不同用户的访问范围:
# ~/.hermes/config.yaml
multi_user:
enabled: true
auth_method: telegram_id # 或 discord_id, slack_id
users:
# 管理员 - 完全访问
alice_12345:
role: admin
permissions: [chat, memory, skills, config, tools]
# 普通用户 - 限制访问
bob_67890:
role: user
permissions: [chat, memory]
skills_allowed: [general, research]
max_tokens_per_day: 100000
# 访客 - 最小权限
carol_11111:
role: guest
permissions: [chat]
skills_allowed: [general]
max_tokens_per_day: 20000
# 默认权限(未配置的用户)
default_role: guest
实现要点:
- 用户认证层
# gateway/auth.py (伪代码)
class UserAuthorizer:
def __init__(self, config: dict):
self.config = config.get("multi_user", {})
def get_user_role(self, platform: str, user_id: str) -> str:
"""Get user role from config."""
user_key = f"{platform}_{user_id}"
users = self.config.get("users", {})
if user_key in users:
return users[user_key].get("role", "guest")
return self.config.get("default_role", "guest")
def check_permission(self, user_key: str, permission: str) -> bool:
"""Check if user has specific permission."""
users = self.config.get("users", {})
if user_key not in users:
return permission in self.config.get("default_permissions", ["chat"])
return permission in users[user_key].get("permissions", [])
def check_skill_access(self, user_key: str, skill_name: str) -> bool:
"""Check if user can use a specific skill."""
users = self.config.get("users", {})
if user_key not in users:
return skill_name in self.config.get("default_skills", ["general"])
allowed = users[user_key].get("skills_allowed", ["general"])
return skill_name in allowed or "all" in allowed
- 工具调用检查
# agent/tool_executor.py (伪代码)
async def execute_tool(tool_name: str, params: dict, context: dict):
"""Execute tool with permission check."""
user_id = context.get("user_id")
user_key = f"{context.get('platform')}_{user_id}"
authorizer = context.get("authorizer")
# 检查工具权限
if not authorizer.check_permission(user_key, "tools"):
return {"error": "You don't have permission to use tools"}
# 检查技能访问(如果是技能调用)
if tool_name == "skill":
skill_name = params.get("skill")
if not authorizer.check_skill_access(user_key, skill_name):
return {"error": f"Access denied to skill: {skill_name}"}
# 执行工具
return await _do_execute_tool(tool_name, params)
- 配额管理
# agent/quota_manager.py (伪代码)
class QuotaManager:
def __init__(self, db_path: str):
self.db = sqlite3.connect(db_path)
self._init_tables()
def check_quota(self, user_key: str) -> bool:
"""Check if user has remaining quota for today."""
today = datetime.now().date().isoformat()
cursor = self.db.execute(
"SELECT tokens_used FROM daily_quota WHERE user_key = ? AND date = ?",
(user_key, today)
)
row = cursor.fetchone()
used = row[0] if row else 0
# 从config获取用户配额
quota = self._get_user_quota(user_key)
return used < quota
def record_usage(self, user_key: str, tokens: int):
"""Record token usage."""
today = datetime.now().date().isoformat()
self.db.execute(
"""INSERT INTO daily_quota (user_key, date, tokens_used)
VALUES (?, ?, ?)
ON CONFLICT(user_key, date)
DO UPDATE SET tokens_used = tokens_used + ?""",
(user_key, today, tokens, tokens)
)
self.db.commit()
B-3: 共享Profile机制
设计思路:
允许多用户共享特定Profile,支持读写权限控制:
# 创建共享Profile
hermes profile create team-dev --shared
# 设置共享权限
hermes profile share team-dev --add alice --permissions rw
hermes profile share team-dev --add bob --permissions r
hermes profile share team-dev --add carol --permissions r
# 查看共享状态
hermes profile show team-dev
# Output:
# Profile: team-dev
# Path: ~/.hermes/profiles/team-dev
# Shared with:
# alice (rw)
# bob (r)
# carol (r)
实现要点:
- Profile存储位置调整
# hermes_cli/profiles.py (修改)
def _get_profiles_root() -> Path:
"""Return the directory where named profiles are stored."""
# 检查是否是共享Profile
shared_root = Path("/var/lib/hermes/profiles")
if shared_root.exists():
return shared_root
return _get_default_hermes_home() / "profiles"
def get_shared_profile_dir(name: str) -> Path:
"""Get shared profile directory."""
return Path("/var/lib/hermes/profiles") / name
- Unix Group权限控制
# 创建共享Profile时
sudo mkdir -p /var/lib/hermes/profiles/team-dev
sudo groupadd hermes-team-dev
sudo chgrp hermes-team-dev /var/lib/hermes/profiles/team-dev
sudo chmod 775 /var/lib/hermes/profiles/team-dev
# 添加用户到组
sudo usermod -aG hermes-team-dev alice
sudo usermod -aG hermes-team-dev bob
# 设置ACL实现读写分离
sudo setfacl -m u:alice:rwx /var/lib/hermes/profiles/team-dev
sudo setfacl -m u:bob:rx /var/lib/hermes/profiles/team-dev
- Profile锁机制
# hermes_cli/profiles.py (新增)
import fcntl
class ProfileLock:
"""File-based lock for shared profile access."""
def __init__(self, profile_dir: Path):
self.lock_file = profile_dir / ".profile.lock"
self.lock_fd = None
def acquire_read(self):
"""Acquire shared lock for reading."""
self.lock_fd = open(self.lock_file, 'r')
fcntl.flock(self.lock_fd.fileno(), fcntl.LOCK_SH)
def acquire_write(self):
"""Acquire exclusive lock for writing."""
self.lock_fd = open(self.lock_file, 'w')
fcntl.flock(self.lock_fd.fileno(), fcntl.LOCK_EX)
def release(self):
"""Release the lock."""
if self.lock_fd:
fcntl.flock(self.lock_fd.fileno(), fcntl.LOCK_UN)
self.lock_fd.close()
4.3 方案C:企业级多租户方案(需开发)
适合企业部署,支持租户隔离、计费、审计。
C-1: 架构设计
Hermes Enterprise Gateway
│
├── Tenant Manager
│ ├── tenant_config.yaml # 租户配置
│ ├── tenant_credentials.json # 租户凭证池
│ └── tenant_billing.db # 租户计费数据
│
├── Tenant Isolation Layer
│ ├── tenants/
│ │ ├── tenant_a/
│ │ │ ├── memories/
│ │ │ ├── sessions/
│ │ │ ├── state.db
│ │ │ └── config.yaml
│ │ ├── tenant_b/
│ │ │ ├── memories/
│ │ │ ├── sessions/
│ │ │ ├── state.db
│ │ │ └── config.yaml
│
└── Multi-Tenant Gateway
├── Telegram Bot (tenant_a_bot)
├── Discord Bot (tenant_b_bot)
└── API Server (各租户独立endpoint)
C-2: 租户配置设计
# /etc/hermes/tenant_config.yaml
version: 1
tenants:
tenant_a:
name: "Acme Corporation"
plan: enterprise
created_at: "2026-01-01"
# API密钥池
credentials:
anthropic:
- sk-ant-api03-xxx
- sk-ant-api03-yyy
openrouter:
- sk-or-v1-xxx
# 配额限制
quotas:
max_tokens_per_month: 10_000_000
max_users: 100
max_sessions_per_user_per_day: 50
rate_limit_requests_per_minute: 60
# 功能权限
features:
- chat
- memory
- skills
- tools
- delegation
# 用户映射
users:
telegram:
"12345": { name: "Alice", role: admin }
"67890": { name: "Bob", role: user }
discord:
"111222333": { name: "Carol", role: user }
# 品牌定制
branding:
agent_name: "Acme Assistant"
welcome_message: "Welcome to Acme's AI assistant!"
response_style: professional
tenant_b:
name: "Beta Startup"
plan: pro
created_at: "2026-02-15"
credentials:
openrouter:
- sk-or-v1-zzz
quotas:
max_tokens_per_month: 1_000_000
max_users: 10
rate_limit_requests_per_minute: 30
features:
- chat
- memory
users:
telegram:
"99999": { name: "Dave", role: admin }
# 全局设置
global:
default_plan: free
billing:
provider: stripe
plans:
free:
price: 0
max_tokens_per_month: 100_000
pro:
price: 29
max_tokens_per_month: 1_000_000
enterprise:
price: 299
max_tokens_per_month: 10_000_000
C-3: 租户隔离实现
# gateway/tenant_manager.py
import os
from pathlib import Path
from contextlib import contextmanager
from typing import Optional
class TenantManager:
"""Manage multi-tenant isolation."""
def __init__(self, config_path: str = "/etc/hermes/tenant_config.yaml"):
self.config = self._load_config(config_path)
self.tenants_root = Path("/var/lib/hermes/tenants")
def get_tenant(self, tenant_id: str) -> Optional[dict]:
"""Get tenant configuration."""
return self.config.get("tenants", {}).get(tenant_id)
def get_tenant_dir(self, tenant_id: str) -> Path:
"""Get tenant data directory."""
return self.tenants_root / tenant_id
@contextmanager
def tenant_context(self, tenant_id: str):
"""Context manager for tenant isolation."""
tenant_dir = self.get_tenant_dir(tenant_id)
# 保存原始HERMES_HOME
original_home = os.environ.get("HERMES_HOME")
try:
# 设置租户专属HERMES_HOME
os.environ["HERMES_HOME"] = str(tenant_dir)
yield tenant_dir
finally:
# 恢复原始HERMES_HOME
if original_home:
os.environ["HERMES_HOME"] = original_home
else:
os.environ.pop("HERMES_HOME", None)
def identify_tenant(self, platform: str, user_id: str) -> Optional[str]:
"""Identify tenant from platform user."""
for tenant_id, tenant in self.config.get("tenants", {}).items():
users = tenant.get("users", {}).get(platform, {})
if user_id in users:
return tenant_id
return None
C-4: 多租户Gateway实现
# gateway/multi_tenant_server.py
from fastapi import FastAPI, Request, HTTPException, Header
from typing import Optional
app = FastAPI()
tenant_manager = TenantManager()
@app.post("/v1/chat")
async def chat(
request: Request,
x_tenant_id: Optional[str] = Header(None),
x_user_id: Optional[str] = Header(None),
):
"""Multi-tenant chat endpoint."""
# 1. 验证租户
tenant_id = x_tenant_id
if not tenant_id:
raise HTTPException(401, "Missing tenant ID")
tenant = tenant_manager.get_tenant(tenant_id)
if not tenant:
raise HTTPException(401, "Invalid tenant")
# 2. 验证用户
if not x_user_id:
raise HTTPException(401, "Missing user ID")
user_info = tenant.get("users", {}).get(x_user_id)
if not user_info:
raise HTTPException(401, "User not authorized for this tenant")
# 3. 检查配额
if not check_quota(tenant_id, x_user_id):
raise HTTPException(429, "Quota exceeded")
# 4. 检查速率限制
if not check_rate_limit(tenant_id, x_user_id):
raise HTTPException(429, "Rate limit exceeded")
# 5. 在租户上下文中运行Agent
body = await request.json()
with tenant_manager.tenant_context(tenant_id):
response = await run_agent(
messages=body.get("messages", []),
user_id=x_user_id,
tenant_config=tenant
)
# 6. 记录使用
record_usage(tenant_id, x_user_id, response.tokens_used)
return response
async def run_agent(messages: list, user_id: str, tenant_config: dict):
"""Run agent in tenant context."""
from run_agent import run as run_hermes
# 加载租户配置
model = tenant_config.get("model", "anthropic/claude-sonnet-4")
# 运行Agent
result = await run_hermes(
messages=messages,
model=model,
user_context={"user_id": user_id}
)
return result
C-5: 计费系统
# billing/usage_tracker.py
import sqlite3
from datetime import datetime
from typing import Dict, List
class UsageTracker:
"""Track usage for billing."""
def __init__(self, db_path: str = "/var/lib/hermes/billing.db"):
self.db = sqlite3.connect(db_path)
self._init_tables()
def _init_tables(self):
self.db.execute("""
CREATE TABLE IF NOT EXISTS usage (
id INTEGER PRIMARY KEY AUTOINCREMENT,
tenant_id TEXT NOT NULL,
user_id TEXT NOT NULL,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
input_tokens INTEGER,
output_tokens INTEGER,
model TEXT,
request_type TEXT,
metadata JSON
)
""")
self.db.execute("""
CREATE INDEX IF NOT EXISTS idx_tenant_timestamp
ON usage(tenant_id, timestamp)
""")
self.db.commit()
def record_usage(
self,
tenant_id: str,
user_id: str,
input_tokens: int,
output_tokens: int,
model: str,
request_type: str = "chat"
):
"""Record a usage event."""
self.db.execute(
"""INSERT INTO usage
(tenant_id, user_id, input_tokens, output_tokens, model, request_type)
VALUES (?, ?, ?, ?, ?, ?)""",
(tenant_id, user_id, input_tokens, output_tokens, model, request_type)
)
self.db.commit()
def get_monthly_usage(self, tenant_id: str, year: int, month: int) -> Dict:
"""Get usage summary for a month."""
start = datetime(year, month, 1)
if month == 12:
end = datetime(year + 1, 1, 1)
else:
end = datetime(year, month + 1, 1)
cursor = self.db.execute(
"""SELECT
SUM(input_tokens) as total_input,
SUM(output_tokens) as total_output,
COUNT(*) as request_count
FROM usage
WHERE tenant_id = ? AND timestamp >= ? AND timestamp < ?""",
(tenant_id, start.isoformat(), end.isoformat())
)
row = cursor.fetchone()
return {
"tenant_id": tenant_id,
"period": f"{year}-{month:02d}",
"input_tokens": row[0] or 0,
"output_tokens": row[1] or 0,
"request_count": row[2] or 0,
}
def generate_invoice(self, tenant_id: str, year: int, month: int) -> Dict:
"""Generate invoice for a billing period."""
usage = self.get_monthly_usage(tenant_id, year, month)
tenant = tenant_manager.get_tenant(tenant_id)
# 获取定价
plan = tenant.get("plan", "free")
pricing = BILLING_PLANS[plan]
# 计算费用
base_fee = pricing["price"]
token_cost = 0
# 超额计费
total_tokens = usage["input_tokens"] + usage["output_tokens"]
if total_tokens > pricing["max_tokens_per_month"]:
overage = total_tokens - pricing["max_tokens_per_month"]
token_cost = overage * 0.00001 # $0.00001 per token
return {
"tenant_id": tenant_id,
"tenant_name": tenant["name"],
"period": usage["period"],
"base_fee": base_fee,
"token_cost": token_cost,
"total": base_fee + token_cost,
"usage": usage
}
C-6: 审计日志
# audit/logger.py
import json
import sqlite3
from datetime import datetime
from pathlib import Path
class AuditLogger:
"""Comprehensive audit logging for enterprise compliance."""
def __init__(self, db_path: str = "/var/lib/hermes/audit.db"):
self.db = sqlite3.connect(db_path)
self._init_tables()
def log_event(
self,
tenant_id: str,
user_id: str,
event_type: str,
resource: str,
action: str,
details: dict = None,
ip_address: str = None
):
"""Log an audit event."""
self.db.execute(
"""INSERT INTO audit_log
(tenant_id, user_id, event_type, resource, action, details, ip_address, timestamp)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
(tenant_id, user_id, event_type, resource, action,
json.dumps(details) if details else None,
ip_address, datetime.utcnow().isoformat())
)
self.db.commit()
def query_events(
self,
tenant_id: str = None,
user_id: str = None,
event_type: str = None,
start_time: datetime = None,
end_time: datetime = None,
limit: int = 100
) -> List[dict]:
"""Query audit events with filters."""
query = "SELECT * FROM audit_log WHERE 1=1"
params = []
if tenant_id:
query += " AND tenant_id = ?"
params.append(tenant_id)
if user_id:
query += " AND user_id = ?"
params.append(user_id)
if event_type:
query += " AND event_type = ?"
params.append(event_type)
if start_time:
query += " AND timestamp >= ?"
params.append(start_time.isoformat())
if end_time:
query += " AND timestamp < ?"
params.append(end_time.isoformat())
query += f" ORDER BY timestamp DESC LIMIT {limit}"
cursor = self.db.execute(query, params)
columns = [desc[0] for desc in cursor.description]
return [dict(zip(columns, row)) for row in cursor.fetchall()]
# 使用示例
audit = AuditLogger()
# 记录事件
audit.log_event(
tenant_id="tenant_a",
user_id="alice_12345",
event_type="authentication",
resource="session",
action="login",
details={"method": "telegram"},
ip_address=None
)
audit.log_event(
tenant_id="tenant_a",
user_id="alice_12345",
event_type="data_access",
resource="memory",
action="write",
details={"target": "memory", "content_length": 256}
)
# 查询事件
events = audit.query_events(
tenant_id="tenant_a",
event_type="data_access",
start_time=datetime(2026, 1, 1)
)
五、方案对比与推荐
5.1 方案对比表
| 方案 | 适用场景 | 开发成本 | 部署复杂度 | 隔离程度 | 共享能力 | 推荐度 |
|---|---|---|---|---|---|---|
| A-1: 多OS用户 | 个人/小团队,完全隔离 | 无 | 低 | ⭐⭐⭐⭐⭐ | ❌ | ⭐⭐⭐⭐⭐ |
| A-2: Gateway共享 | 团队协作,共享Agent | 无 | 低 | ⭐⭐⭐ | ✅ 会话级 | ⭐⭐⭐⭐ |
| A-3: 多Profile混合 | 不同团队不同Agent | 无 | 中 | ⭐⭐⭐⭐ | ✅ Profile级 | ⭐⭐⭐⭐⭐ |
| B-1: 用户级记忆 | 需要区分用户偏好 | 中 | 中 | ⭐⭐⭐⭐ | ✅ 记忆级 | ⭐⭐⭐ |
| B-2: 权限控制 | 需要访问控制 | 中 | 中 | ⭐⭐⭐⭐ | ✅ 功能级 | ⭐⭐⭐ |
| B-3: 共享Profile | 多用户协作同一Agent | 高 | 中 | ⭐⭐⭐ | ✅ Profile级 | ⭐⭐ |
| C: 企业多租户 | SaaS/企业部署 | 高 | 高 | ⭐⭐⭐⭐⭐ | ✅ 租户级 | ⭐⭐ |
5.2 详细对比
隔离维度对比
| 隔离维度 | A-1 多OS用户 | A-2 Gateway | A-3 多Profile | B-1 用户记忆 | B-2 权限 | C 企业多租户 |
|---|---|---|---|---|---|---|
| 配置隔离 | ✅ 完全 | ❌ 共享 | ✅ 完全 | ❌ 共享 | ❌ 共享 | ✅ 完全 |
| 记忆隔离 | ✅ 完全 | ❌ 共享 | ✅ 完全 | ✅ 用户级 | ❌ 共享 | ✅ 完全 |
| 会话隔离 | ✅ 完全 | ✅ 自动 | ✅ 完全 | ✅ 完全 | ✅ 完全 | ✅ 完全 |
| 凭证隔离 | ✅ 完全 | ❌ 共享 | ✅ 完全 | ❌ 共享 | ❌ 共享 | ✅ 完全 |
| 进程隔离 | ✅ 完全 | ❌ 共享 | ✅ 完全 | ❌ 共享 | ❌ 共享 | ✅ 完全 |
功能维度对比
| 功能维度 | A-1 多OS用户 | A-2 Gateway | A-3 多Profile | B-1 用户记忆 | B-2 权限 | C 企业多租户 |
|---|---|---|---|---|---|---|
| 用户管理 | OS级别 | 无 | 无 | 需开发 | 需开发 | 完整 |
| 权限控制 | OS级别 | 无 | 无 | 无 | 需开发 | 完整 |
| 配额管理 | ❌ | ❌ | ❌ | ❌ | 需开发 | 完整 |
| 计费系统 | ❌ | ❌ | ❌ | ❌ | ❌ | 完整 |
| 审计日志 | OS级别 | ❌ | ❌ | ❌ | 需开发 | 完整 |
| 品牌定制 | ❌ | ❌ | ✅ SOUL.md | ❌ | ❌ | 完整 |
5.3 推荐方案
小团队(< 5人)
推荐:方案A-1 或 A-3
- 使用多OS用户或多Profile即可满足需求
- 无需额外开发
- 管理成本可接受
部署示例:
# 方案A-1: 每个团队成员一个OS用户
for user in alice bob carol; do
sudo useradd -m -s /bin/bash $user
sudo -u $user bash -c "curl -fsSL https://... | bash && hermes setup"
done
# 方案A-3: 每个团队成员一个Profile
hermes profile create alice --clone
hermes profile create bob --clone
hermes profile create carol --clone
中型团队(5-20人)
推荐:方案A-2 或 A-3
- Gateway共享 + 多Profile组合
- 开发团队、支持团队、管理团队各自Profile
- 通过消息平台统一访问
部署示例:
# 为不同团队创建Profile
hermes profile create team-dev
hermes profile create team-support
hermes profile create team-management
# 配置各自的Bot
team-dev config set telegram.bot_token "DEV_BOT_TOKEN"
team-support config set discord.bot_token "SUPPORT_BOT_TOKEN"
team-management config set slack.bot_token "MGMT_BOT_TOKEN"
# 启动Gateway
team-dev gateway install
team-support gateway install
team-management gateway install
大型团队/企业(> 20人)
推荐:方案B-1 + B-2
- 需要开发用户级记忆隔离
- 需要权限控制
- 基于现有Profile机制扩展
开发清单:
-
用户级记忆隔离
- 修改
tools/memory_tool.py - 修改
agent/memory_manager.py - Gateway传递user_id
- 修改
-
权限控制系统
- 创建
gateway/auth.py - 修改工具调用检查
- 配置schema扩展
- 创建
-
配额管理
- 创建
agent/quota_manager.py - 集成到Gateway
- 创建
SaaS产品
推荐:方案C
- 需要完整的多租户架构
- 计费、审计、配额管理
- 租户数据完全隔离
开发清单:
- 租户管理模块
- 多租户Gateway重构
- 计费系统集成
- 审计日志系统
- 管理后台
六、实施建议
6.1 立即可行(无需开发)
团队共享Agent部署
步骤1:创建共享账户
# 在Linux服务器上创建专用账户
sudo useradd -m -s /bin/bash hermes-team
# 切换到该账户
sudo -u hermes-team bash
步骤2:安装Hermes
cd ~
curl -fsSL https://raw.githubusercontent.com/nousresearch/hermes-agent/main/setup-hermes.sh | bash
source ~/.bashrc
步骤3:配置
# 运行设置向导
hermes setup
# 配置Telegram Bot(或Discord/Slack)
hermes config set telegram.bot_token "YOUR_BOT_TOKEN"
# 定制Agent个性
cat > ~/.hermes/SOUL.md << 'EOF'
You are the team assistant for [Team Name].
You help with:
- Code reviews and debugging
- Documentation
- Research and analysis
- Task management
Team conventions:
- [Add your conventions here]
EOF
# 添加团队记忆
hermes memory add memory "Team uses GitHub: github.com/org/repo"
hermes memory add memory "CI/CD: GitHub Actions"
hermes memory add user "Team prefers concise responses"
步骤4:启动服务
# 安装为系统服务
hermes gateway install
# 启动服务
hermes gateway start
# 查看状态
hermes gateway status
步骤5:团队成员使用
团队成员只需在各自的设备上:
- 打开Telegram(或Discord/Slack)
- 搜索Bot名称
- 发送消息开始使用
多团队独立Agent部署
步骤1:创建多个Profile
# 开发团队
hermes profile create team-dev
team-dev config set telegram.bot_token "DEV_BOT_TOKEN"
cat > ~/.hermes/profiles/team-dev/SOUL.md << 'EOF'
You are a coding assistant for the development team.
Focus on: code quality, architecture, debugging.
EOF
# 支持团队
hermes profile create team-support
team-support config set discord.bot_token "SUPPORT_BOT_TOKEN"
cat > ~/.hermes/profiles/team-support/SOUL.md << 'EOF'
You are a customer support assistant.
Focus on: user issues, documentation, quick solutions.
EOF
步骤2:启动多个Gateway
# 启动开发团队Gateway
team-dev gateway install
team-dev gateway start
# 启动支持团队Gateway
team-support gateway install
team-support gateway start
步骤3:验证
# 查看运行状态
hermes profile list
# 输出示例:
# Profile Gateway Model Skills
# ────────────────────────────────────────────────
# default running claude-sonnet 15
# team-dev running claude-sonnet 12
# team-support running claude-sonnet 8
6.2 需要开发(短期)
用户级记忆隔离实现
预估工时:2-3天
修改文件清单:
tools/memory_tool.py- 添加user_id参数agent/memory_manager.py- 用户级记忆加载gateway/session.py- 传递user_idhermes_constants.py- 新增路径函数
测试要点:
- 不同用户记忆隔离验证
- 公共记忆 + 用户记忆合并验证
- 系统prompt注入正确性验证
6.3 需要开发(长期)
企业多租户系统
预估工时:4-6周
开发阶段:
| 阶段 | 内容 | 工时 |
|---|---|---|
| Phase 1 | 租户管理模块、数据隔离 | 1.5周 |
| Phase 2 | 多租户Gateway重构 | 1周 |
| Phase 3 | 权限控制、配额管理 | 1周 |
| Phase 4 | 计费系统集成 | 0.5周 |
| Phase 5 | 审计日志系统 | 0.5周 |
| Phase 6 | 管理后台、文档 | 1.5周 |
技术选型建议:
- API框架:FastAPI
- 数据库:PostgreSQL(租户元数据)+ SQLite(租户数据)
- 计费:Stripe API
- 缓存:Redis(配额计数)
- 部署:Docker + Kubernetes
附录
A. 相关代码文件索引
| 文件 | 功能 | 关键函数 |
|---|---|---|
hermes_constants.py |
核心路径定义 | get_hermes_home(), get_default_hermes_root() |
hermes_cli/profiles.py |
Profile管理 | create_profile(), delete_profile(), list_profiles() |
tools/memory_tool.py |
记忆工具 | memory() |
gateway/session.py |
Gateway会话管理 | 会话key生成、用户ID提取 |
agent/credential_pool.py |
凭证池管理 | API密钥轮换、错误恢复 |
hermes_cli/gateway.py |
Gateway服务管理 | 服务安装、启动、停止 |
B. 配置文件参考
Profile基础配置
# ~/.hermes/config.yaml
model:
default: anthropic/claude-sonnet-4
provider: openrouter
gateway:
group_sessions_per_user: true
session_reset_policy: idle
idle_reset_minutes: 60
memory:
memory_enabled: true
user_profile_enabled: true
memory_char_limit: 2200
user_char_limit: 1375
credential_pool_strategies:
openrouter: fill_first
anthropic: least_used
多用户增强配置(方案B)
# ~/.hermes/config.yaml
multi_user:
enabled: true
auth_method: telegram_id
users:
alice_12345:
role: admin
permissions: [chat, memory, skills, config]
bob_67890:
role: user
permissions: [chat, memory]
skills_allowed: [general, research]
max_tokens_per_day: 100000
memory:
user_isolation: true
shared_memory_fallback: true
C. 命令速查表
Profile管理
# 创建
hermes profile create <name> # 空白Profile
hermes profile create <name> --clone # 克隆配置
hermes profile create <name> --clone-all # 完全克隆
# 查看
hermes profile list # 列出所有
hermes profile show <name> # 详情
# 操作
hermes profile use <name> # 设置默认
hermes profile rename <old> <new> # 重命名
hermes profile delete <name> # 删除
# 导入导出
hermes profile export <name> [file] # 导出
hermes profile import <file> [name] # 导入
Gateway管理
# 启动停止
hermes gateway start # 前台运行
hermes gateway stop # 停止
hermes gateway restart # 重启
# 服务管理
hermes gateway install # 安装系统服务
hermes gateway uninstall # 卸载系统服务
hermes gateway status # 查看状态
会话管理
# 列出
hermes sessions list # 最近会话
hermes sessions list --source telegram # 按平台筛选
# 操作
hermes sessions rename <id> "title" # 重命名
hermes sessions delete <id> # 删除
hermes sessions prune --older-than 30 # 清理旧会话
# 导出
hermes sessions export backup.jsonl # 全部导出
文档结束
更多推荐


所有评论(0)