一句话总结:Kimi 发布 Hosted Agents,把 Agent 运行所需的 Harness 做成云服务:7×24 常驻、独立沙箱、可导出审计、140+ Skills,还有推理前的 Inference Hook。我照着做了个最小版本,第一版只靠 Hook 拦截,绕过 Hook 直接调沙箱就读到了另一个会话的文件。补上路径检查之后才真正隔离。
看到「Harness 做成云服务」这句话的时候,我第一反应是:这不就是给 Agent 租了个服务器吗。
往下看细节才发现不止。它解决的是「Agent 一直活着」带来的三个副作用:串味、看不见、停不下。
我用 100 行做了个最小版,过程中撞出一个洞。
一、Hosted Agents 提供的是什么
先把它的清单列清楚。
| 能力 | 内容 |
|---|---|
| 运行形态 | 7×24 托管,支持长程任务与多智能体协作 |
| 扩展 | 140+ Skills、30+ 插件,可接自有工具和数据源 |
| 隔离 | 独立沙箱、网络权限控制 |
| 控制 | 可随时中止会话并导出审计 |
| 拦截 | Inference Hook,每次推理前按企业规则过一遍 |
| 模板 | 首批 8 个金融 Agent |
| 准入 | 目前只对企业认证用户开放 |
这份清单里有三项是「本地跑 Agent」做不到的:常驻、隔离、审计。
本地跑的时候,这三点都是默认缺失的——进程关了 Agent 就没了,所有会话共享一个工作目录,出了事只能翻自己的终端历史。
二、Harness 上云解决三个问题:常驻、隔离、审计
常驻。 本地跑 Agent 是「叫一次跑一次」。云端托管是它一直在,有消息就处理,没消息就待着。这个差别在长程任务上很致命——一个要跑几小时的任务,本地得开着电脑。
隔离。 这个最容易被低估。本地跑的时候,你同时开两个会话,它们共享一个工作目录。A 会话写了个 output.json,B 会话读到的就是 A 写的。这种串味在调试时还算好,在生产环境会出大问题。
审计。 出了问题要能回溯:谁在什么时候、用什么工具、做了什么、被拦了几次。这些在没有日志的系统里全靠回忆。
所以 Hosted Agents 不是「把 Agent 放到云上」,是「给 Agent 补上它一直缺的三个基础设施」。
三、第一版:只靠 Hook 拦,隔离绕过就破
我按这三个特性写了个最小版,一百行左右。
三个类:Hook 做推理前拦截,Sandbox 做目录隔离,Audit 记 JSONL 日志。
Hook 的规则很直白:
class Hook:
RULES = {
"凭证": ["sk-", "BEGIN PRIVATE KEY", "AKIA"],
"个人身份": ["身份证", "护照号", "银行卡"],
"越权路径": ["../", "/etc/", "C:\\Windows"],
}
def check(self, text):
for kind, pats in self.RULES.items():
for p in pats:
if p in text:
return kind, p
return None
Sandbox 一开始也简单:每个会话一个目录。
def write(self, name, content):
with open(os.path.join(self.dir, name), "w", encoding="utf-8") as f:
f.write(content)
def read(self, name):
p = os.path.join(self.dir, name)
return open(p, encoding="utf-8").read() if os.path.exists(p) else None
跑起来看着挺好:两个会话各写各的 notes.md,内容不串;带 sk- 和「身份证」的写入都被拦了。
然后我试了一件它没设计的事:绕过 Hook,直接调 Sandbox。
raw = a.read("../chan_beta/notes.md")
结果:
绕过 Hook 直调 Sandbox:'客服模板调研笔记'
-> 目录隔离被打穿了
os.path.join 不做路径规范化,../ 直接穿出去了。
问题在于我把「隔离」这件事交给了 Hook 的规则去管——只要文本里出现 ../ 就拦。但规则是字符串匹配,能拦住的只是「字面上的 ../」。换个写法(..%2f、多级拼接、符号链接)就绕过了。
规则层和隔离层混在一起,等于两层都只有一层。
四、第二版:在 Sandbox 里加真正的隔离检查
修法很直接:在 Sandbox 内部做路径解析和边界检查,不管名字长什么样。
def _resolve(self, name):
root = os.path.realpath(self.dir)
target = os.path.realpath(os.path.join(root, name))
if target != root and not target.startswith(root + os.sep):
raise PermissionError(f"越界访问:{name}")
return target
关键是 os.path.realpath——它会把 ..、符号链接、相对路径全部解析成真实的绝对路径,然后再判断这个路径在不在本会话目录下。
再跑一遍:
过 Hook 走一遍:None
绕过 Hook 直调 Sandbox:'<拒绝> 越界访问:../chan_beta/notes.md'
-> 目录隔离挡住了
这次是真挡住了,不依赖 Hook 的规则。

图 1:Hook 管「内容里有没有敏感词」,Sandbox 管「路径出没出圈」,各管一段
五、140 个 Skill 怎么选:检索比列表重要
数量上来之后有个新问题:140 个 Skill,模型怎么知道该用哪个?
全塞进上下文不现实——140 个 Skill 的描述加起来轻松上万 token,还没开始干活就烧掉了。而且中间那些真正相关的会被淹没(这就是上下文压缩那篇讲的分层问题)。
合理的做法是先检索再注入:
- 拿当前任务描述去检索
- 取 top-k 个 Skill 的描述注入
- 模型决定调哪个,再按需加载完整内容
这个「先检索、后加载」的两段式,和 Skill 本身的分层加载是同一个逻辑。 三者配合那篇讲过:描述层常驻、正文层按需、附件层引用。
Hosted Agents 说「140+ Skills、30+ 插件可接入」,真正的工作量不在「接进来」,在「进来之后怎么让它不占地方」。
六、坑清单
1. 隔离不能只靠规则。
这是本文那个洞的根因。规则是字符串匹配,隔离要的是路径解析。前者防「写出来的样子」,后者防「真实的落点」。两件事必须分开做,各在各自的层。
2. 审计日志要记「被拦的」,不只是「发生的」。
我第一版只记成功的操作,blocked 是后补的。实际排查时,被拦的记录比成功的更有用——它告诉你「有人试过什么」。
3. 同名文件是串味的高发区。
两个会话都写 notes.md,如果共享目录,后写的覆盖先写的,而且不报错。这类 bug 在单会话测试里永远发现不了。
4. 中止要能立刻生效。
「可随时中止」这句话,实现上有个细节:正在跑的工具调用要不要等它跑完?我的最小版是同步的,所以「中止」就是抛异常。异步并发的话,得有个取消信号能被每一层检查到,否则「已中止」的会话还在后台写文件。
5. 导出审计的格式要能直接进分析工具。
我用的是 JSONL(一行一条),可以直接 json.loads 逐行读,也能直接喂给 pandas。用嵌套 JSON 数组的话,大文件要整个读进内存才能解析。
七、结语
这次最有价值的不是那个最小版,是撞洞的过程。
我一开始把「安全」当成一个功能——加个 Hook,配几条规则,齐了。实际上安全是分层的:每一层只管自己那一小段,任何一层单独存在都不够。
Hook 管「内容里有没有敏感词」,Sandbox 管「路径出没出圈」,权限管「这个工具能不能调」。三段各管一件事,任何一段被绕过,还有另外两段兜着。
Hosted Agents 那种产品,卖的就是这三段的组合。 自己搭也能搭,但得先知道有哪三段。
参考资料
- 腾讯研究院 AI 速递 20261106 - Hosted Agents 的功能清单与 Inference Hook 描述来源
- AI科技日报 2026 年 11 月 6 日 - 140+ Skills、8 个金融模板等细节
- Python 文档 - os.path.realpath - 路径规范化与符号链接解析的官方说明
- 参考资料
发表评论
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。