5. 用户与身份

无论你是智能体的唯一使用者,还是与几个人共享,OpenHermit 都需要知道现在说话的是谁。它根据你在每个入口的身份来判断 —— 你的 Telegram 用户 ID、你的 Web 浏览器指纹、你的 CLI 用户名 —— 然后基于你在这个智能体上的角色决定你能做什么。本章覆盖两个问题:它怎么识别你,以及每种角色能干什么。本章最后有针对最常见身份纠葛的配方,例如想让”Telegram 上的你”和”Web 上的你”算同一个人。


5.1 三种角色

每个智能体的成员都有三种角色之一。

Owner。 创建智能体的人(或原 owner 提升的人)。Owner 可以修改任意设置、增删成员、查看智能体上的所有会话、使用所有工具。每个智能体至少有一个 owner;也可以有多个。

User。 受信任的成员。User 可以正常对话、使用智能体的工具(网络搜索、文件操作、exec、记忆),可以查看自己参与的会话。他们不能改配置、不能管理技能或 MCP 服务、不能管理成员、不能查看自己没参与过的会话。

Guest。 低信任成员,通常在 public 智能体上从未知身份自动创建。Guest 可以聊天、可以使用受限的工具集(仅 Web —— 不能改文件、不能 exec、不能写记忆),只能看到自己参与过的会话。


5.2 OpenHermit 如何识别你

你没有一个唯一账号。你有一个或多个身份,每个身份都是 (channel, channel_user_id) 对:

  • CLI → (cli, <你的操作系统用户名>)
  • Web → (web, <浏览器设备指纹>)
  • Telegram → (telegram, <Telegram 用户 ID>)
  • Discord → (discord, <Discord 用户 ID>)
  • Slack → (slack, <Slack 用户 ID>)

你所有的身份都指向同一个内部用户,而你在某个智能体上的角色是绑在用户上的 —— 不是绑在任何一个身份上。新增一个身份,角色不变。

某个新身份第一次给智能体发消息时,OpenHermit 会根据智能体的 访问级别 决定怎么处理:创建一个 guest、要求 access token,或者直接拒绝。一旦身份记录在案,owner 就可以提升它的角色、把它链到某个现有用户,或与其他身份合并。


5.3 你大概率不止一个身份

如果你在 CLI 上搭好一个智能体,然后又从浏览器打开它,这就是两个身份。如果你还从 Telegram 给它发消息,那就是第三个。默认情况下,OpenHermit 不知道它们是同一个人 —— 在它眼里,三个身份就是三个用户,可能各自的角色都不同。

这通常无伤大雅,直到某天让你大吃一惊:

  • 你在 CLI 教过智能体的事,从 Telegram 来它一无所知。
  • 你在 CLI 是 owner,从 Telegram 却只能看到 guest 视角。
  • 成员列表里你的名字出现了两次。

解决方法是身份关联。5.5 里有配方。


5.4 关联身份的两种方式

有两条路,选哪条取决于谁在做关联。

Owner 发起。 Owner 在管理界面中选中一个身份,或在一个已经被识别为 owner 的通道里直接告诉智能体”Telegram 上叫 William 的用户也是我”。智能体当场关联完毕。这条路适合 owner 整理成员列表 —— 包括自己的重复身份 —— 需要 owner 角色。

基于 Token 的跨通道证明。 任何用户(包括 guest)都可以通过证明自己同时控制两边来关联自己的多个身份。流程:

  1. 在通道 A,让智能体签发关联 token。它会调用 identity_link_request 并打印一个短 token,有效期约 10 分钟。
  2. 你自己把这个 token 带到通道 B —— 复制下来,在另一个客户端里输入。
  3. 在通道 B,把这个 token 给智能体,并请它确认关联。智能体调用 identity_link_confirm。工具会校验”兑换这一侧的通道”与”签发那一侧的通道”是不同通道;两边都在你手上,这就是证明。
  4. 现在两个身份都挂在同一个用户下。如果一边是 guest 一边是真用户,guest 会被并入真用户。如果两边都已经是非 guest 的、角色各不相同的用户,工具会拒绝,转而请 owner 手动合并 —— 防止某个 guest 的身份意外”占用”别人的用户。

Token 流程不需要 owner 审批;安全性来自”你必须在两个通道上都真的拿到 token”。Owner 发起的那条路,是给那些有管理权限、想跳过 token 那一套的人。


5.5 能力对照表

这是后续章节会反复引用的”真理源”。每一个 ✓ 都是”默认允许”;空格代表”被角色阻挡”。自定义策略可以进一步收紧,见 第 15 章 · 策略与审批

能力OwnerUserGuest
发送消息、对话
使用网络工具(搜索、抓取)
读写工作区文件
在沙箱中执行命令(exec)
读写长期记忆
读写指令
列出会话全部自己的自己的
主动向其他会话发消息
管理定时任务只读只读
管理技能
管理 MCP 服务
管理通道
管理密钥
增删成员、修改角色
合并身份✓(任意)自己的

5.6 How-to 配方

5.6.1 让 Telegram 把你识别成”你自己”,而不是 guest

场景 —— 你从 Web 创建了智能体(Web 身份就是 owner),然后你打开 Telegram 跟 bot 对话。Telegram 看到一个新身份,要么扔掉你的消息(在 protectedprivate 访问下),要么创建一个 guest 用户。

前置条件 —— 智能体已经接入 Telegram(第 17 章 · 通道)。你已经从 Telegram 给 bot 发过至少一条消息,所以智能体已经看到了那个身份。

做法

让智能体来做。 在 Web(你是 owner)里,用大白话描述另一侧的身份:

在 Telegram 上跟你聊天的那个叫 “William” 的用户,也是我的账号。

智能体会找到对应的 Telegram 身份,把它关联到你的用户,并确认。你不需要查 Telegram 用户的数字 ID —— 只要显示名足够无歧义,叫名字就够了。

Web 管理界面。 打开智能体的 Manage 页,定位到成员区(通常通过 UI 暴露的 /api/agents/<id>/members 流程进入;如果你的管理界面没有直接入口,可以走下面的 API)。找到你自己的条目,新增一个身份,选 channel = telegram,粘贴 ID。

HTTP API。 Owner-only:

curl -X POST "$GATEWAY/api/agents/<agent-id>/members" \
  -H "Authorization: Bearer <your-jwt>" \
  -H "Content-Type: application/json" \
  -d '{"channel":"telegram","channelUserId":"656756615","userId":"<your-user-id>"}'

验证 —— 从 Telegram 发一条消息。问智能体”我是谁?” —— 它应该回答你的 owner 显示名,而不是 “guest”。

常见问题 —— 如果在你关联之前,智能体已经从你的 Telegram 身份自动创建过一个 guest 用户,那你还需要把这个重复用户合并到自己;见 5.6.4。


5.6.2 用 Token 关联自己的两个通道

场景 —— Web 上你是被识别的真用户,Telegram 上目前是 guest。没有 owner 帮你;你自己来做。

前置条件 —— 你已经从两个通道分别给智能体发过至少一条消息,这样每个身份都已经存在。

做法

  1. 在已经被识别为”自己”的那个通道(比如 Web),告诉智能体:

    生成一个 link token,我要把另一个通道关联到这个账号。

    智能体会调用 identity_link_request,回一个短 token,例如 Xq4Aw8Zk,有效期约 10 分钟。

  2. 切换到另一个通道(Telegram),发一条带 token 的消息给智能体:

    用 token Xq4Aw8Zk 把这个 Telegram 账号关联过来。

    智能体会调用 identity_link_confirm。因为兑换的通道(Telegram)和签发的通道(Web)不同,证明检查通过;Telegram 身份被挂到你的用户下,原来的 guest 记录被并入。

验证 —— 从 Telegram 问智能体”我是谁?” —— 它应该报出你的真用户,而不是 “guest”。

常见问题

  • “Token expired.” 它只有约 10 分钟有效。再签一个。
  • “Same channel.” Token 必须在与签发通道不同的另一个通道兑换。
  • “Already linked to a different user.” 如果两边都已经是已建立的非 guest 用户,token 流程会故意拒绝 —— 让 owner 来做合并(5.6.4)。

5.6.3 让浏览器中的会话也把你识别成 owner

场景 —— 你在 CLI 上跑了 hermit setup,那让你成了 owner。现在你在浏览器里打开 Web UI;Web 身份是一个不同的 (web, <fingerprint>) 元组,你被识别为 guest 或未认证。

前置条件 —— 无。

做法

从 CLI 启动 Web 登录。 最快路径:

hermit web start
# 打开打印出来的本地 Web URL,使用同一份网关凭据登录。

这条命令启动 Web 服务并打印监听 URL;它本身不会建立浏览器身份的关联。如果登录后浏览器仍然以一个独立 guest 出现,用 5.6.2 的身份关联流程。

手动关联 Web 身份。 和 5.6.1 一样,只是 channel = web,值是管理界面里显示的设备指纹。

验证 —— 管理界面显示完整的管理面(Manage 选项卡可见且可编辑),而不是只有聊天视图。

常见问题 —— 换浏览器,或开 incognito 窗口,会得到一个新的 Web 身份。如果想让它也指向同一个用户,需要再关联一次。


5.6.4 合并意外变成两个用户的身份

场景 —— 成员列表里有两条明显是同一个人的条目(你,带着两个通道身份,因为没有及时关联,意外被建成两个用户)。

前置条件 —— 你是 owner。

做法

告诉智能体。 “把用户 u_abc 合并到用户 u_xyz。”

智能体会调用 user_merge。返回之后,所有原本挂在 u_abc 上的身份都解析到 u_xyz。旧会话和消息保留原来的发送方归属,所以历史完整。

HTTP API。 如果管理界面暴露了 Merge 操作,直接用。否则通过智能体调用。

验证 —— 成员列表现在显示一条记录,带有两个身份。

常见问题 —— 合并是单向的。旧用户记录会被标记 merged_into;身份解析会跟随关联,但要”反合并”需要手工动数据库。


5.6.5 把 user 提升为 owner

场景 —— 你想给某人一个智能体的完整管理权。

前置条件 —— 你是 owner。

做法

告诉智能体。 “把用户 u_xyz 在这个智能体上的角色设成 owner。”

智能体会调用 user_role_set。被提升的用户保留所有已有身份;他/她下一条消息发出时,就会拿到 owner 的工具集。

验证 —— 被提升的人现在可以打开 Manage 选项卡并编辑设置。

常见问题 —— 没有”防误降 owner”的保险栏。要是你把 owner 给错了人,只能再降回去 —— 提升时要慎重。


5.7 常见问题

我创建了这个智能体,它却叫我 “guest”,为什么? 你大概率是从某一个通道创建的(比如 CLI),却从另一个通道发消息(比如 Telegram)。两个通道是两个独立身份,直到你把它们关联起来。见 5.6.1。

Guest 能不经过 owner 自动变成 user 吗? 不能。角色变更只能由 owner 来做。

两个用户合并时,他们的记忆也会合并吗? 长期记忆是按智能体存,而不是按用户存,所以没有”按用户的记忆”需要合并。会话参与关系会重新计算:任何一个旧用户参与过的会话,合并后都属于新用户。

同一个人能在一个智能体上是 owner,在另一个上是 guest 吗? 可以 —— 角色是按智能体存的。同一个用户在同一个实例的不同智能体上,可以有不同角色。

群聊里,智能体回复时”用户”指谁? Runner 会把每一条消息归属到它的发送者。智能体看到的每条消息都带着发送者显示名标签(在群通道上,也能看到”对话里有多个人”)。


5.8 指引