agent 能用 bash,团队知识的权限该守在哪里?

Flashduty AI SRE 技术揭秘第 13 篇:区分知识广告面、落盘面和加载面,解释 bash 可读文件时的团队权限检查、按需加载与权限边界。

作者 快猫星云

交给 agent 的文件,要在落盘之前确定范围。

Flashduty AI SRE 是一个专注 SRE 领域、帮你做故障根因分析、日常运维工作的 AI Agent 产品,注册即可免费试用。这是系列文章《Flashduty AI SRE 技术揭秘》第 13 篇,本系列会持续分享我们在 agent 工程上的真实设计、踩过的坑和做过的取舍。

核心要点

  • Agent 能运行 bash 时,知识权限必须在文件准备阶段限定范围;只拦平台 read 工具,挡不住已落盘文件被 cat 读取。
  • 广告面决定主动介绍什么,落盘面决定本地有什么,加载面决定获准引入什么;按需加载时,盘上文件可能只是获准范围的一部分。
  • 团队会话不跨团队加载;个人会话按创建者的真实成员关系判断。后续权限变化不能追溯撤回已进入上下文的内容。

支付团队的故障复盘,应该出现在支付团队的排障会话里;搜索团队开始排查时,不该把这些资料也一并加载进来。对于一个普通的文档网站,这件事可以放在下载接口里处理:先查权限,再返回内容。可我们的 agent 还能在执行环境里运行 bash,问题就多了一条路。

我们把负责执行命令和文件操作的程序叫 runner。知识包里的操作手册、架构说明、历史复盘,会被同步到 runner 的工作目录,agent 可以用 ls 找文件、用 grep 搜内容,也可以直接运行 cat 读取。这些命令并不调用平台的团队权限判定函数。 操作系统当然有文件权限,但它不会自动理解平台里的“支付团队成员”是什么身份。

如果把所有团队的文件都放到同一个工作目录,再只给平台的 read 工具加检查,就会留下一个缺口:read 拒绝了,cat 仍可能读到。我们因此把检查往前移,在准备文件时就限定范围。一个会话没有资格加载的知识,不应该被这个会话的预取流程带到盘上。

先分清:告诉它什么、给它什么、允许它加载什么

为了解释这套设计,需要区分三个范围。原来的设计文档把它们叫作广告面、落盘面和加载面,名字有点抽象,可以换成三个问题来看。

开场时告诉 agent 哪些知识可用? 这是广告面。第 7 篇删掉的是开场说明里逐文件列出的目录,开场说明本身仍然保留,代码里叫 manifest。它是一段由服务端组装、放进模型请求前部的参考上下文,不需要 agent 先去磁盘上打开一个目录文件。

这段说明主要包含固定的浏览指引、当前生效的知识范围,以及这些范围下已有的 DUTY.md 正文。DUTY.md 是团队维护的知识入口,可以介绍系统背景、排障约定和相关文档,但不要求枚举每一个文件。也就是说,agent 开始工作时就能看到入口说明;想知道目录里具体有哪些文件,再用 ls、glob 等工具去查。文件发生变化,不必在开场说明里同步维护另一份逐文件目录。

主动介绍的范围仍然只有账号级共享知识,以及会话所绑定团队的知识。其他团队不会因为有知识包,就自动被列进这段开场说明。

工作目录里已经放了哪些知识文件? 这是落盘面。文件一旦在那里,agent 就能通过普通文件工具浏览,所以预先放进去的范围必须受控。

会话提出请求后,系统允许它加载哪些团队的资源? 这是加载面。平台用一个统一的函数 CanLoadScope 判断,知识读取、技能解析、MCP 工具解析和子 agent 派发等入口都消费这套规则。这里的 scope,就是资源归属的范围:账号级,或者某个团队级。

这三个范围不必一样大。比如,一个人同时属于支付和搜索两个团队。他发起个人会话,可能确实需要把两边的资料放在一起分析;系统可以允许这件事,但没必要在开场时把两个团队的入口全部列出来。

所以我们的安排是:开场主动介绍得少一些,文件按需要准备,加载资格另外判断。 知识总量在阈值内时,系统会预先准备这个会话有资格加载的全部知识,减少后续读文件的往返。超过阈值,就先准备账号级、会话绑定团队和已经挂载团队的知识包;其他有资格加载的团队,可以通过平台的 read 工具按需引入。不能把它概括成“盘上文件始终等于可加载的全集”,按需加载时,盘上本来就可能只有一部分。

知识范围对照:开场介绍更窄,全量预取时落盘范围与获准加载范围一致

图中两个等宽托盘表示全量预取完成时的情况。按需加载时,盘上可能只准备了获准范围的一部分;开场介绍仍可更窄。

范围 回答的问题 本文设计的约束
广告面 开场主动介绍什么? 账号级共享与会话绑定团队的入口,不枚举其他团队知识包
落盘面 runner 工作目录里有什么? 文件准备时限定资格;按需加载可以只落盘部分获准知识
加载面 请求后允许引入什么? 统一由 CanLoadScope 判断;资格检查不能由本地读取成功替代

团队会话和个人会话,为什么用不同规则

账号级共享知识,以及会话自己绑定团队的知识,可以直接加载。区别出现在请求其他团队资源的时候。

团队会话的规则固定:支付团队的会话不能加载搜索团队的资源。即便当前发消息的人同时属于两个团队,或者是账号管理员,也没有豁免。这样,一段归属于支付团队的排障记录,其知识来源不会因为换了一个人接手就突然扩展。

个人会话允许组合多个团队的知识,不过查的是会话创建者的真实成员关系。创建者属于支付和搜索,就可以申请加载这两个团队的资料;没有加入存储团队,就不能加载存储团队的资料。管理员和账号 owner 也走相同的判断。

为什么不查当前发消息的人?因为会话会积累上下文。如果换一个人说话,就按新说话者的身份扩大知识范围,这段会话的能力会随着参与者漂移。把判定固定到创建者,至少让“这是谁发起的个人工作空间”有一个确定的答案。

成员关系查询会在单次请求内缓存,后续请求重新获取,并不是创建会话时保存一份名单就一直使用。创建者无法解析,或成员服务查询失败时,涉及其他团队的加载默认拒绝。查不到身份,不能当成已经获得授权。

不主动介绍其他团队,减少了什么暴露

允许按需加载,不代表要提前告诉模型所有可能存在的资料。开场说明里,我们删掉了“其他团队还有哪些知识包”的目录。

假设一个个人会话无权加载存储团队的知识。告诉模型“存储团队有一份容量事故复盘,不过你不能读”,虽然没给正文,也已经透露了这份资源的存在。对当前任务而言,这条消息没有必要。开场说明只介绍当前范围;如果用户提供了一个其他团队的知识路径,agent 可以调用平台的 read 工具,系统再判断是否允许加载。

这也影响报错。个人会话没有资格读取某个团队时,返回的错误按“文件不存在”的形状呈现,不明确确认那个团队有没有知识包。文件尚未落盘时,权限判断发生在查询知识包之前;没有通过,就不去查包,也不把它从存储里取下来。这减少了查询本身带来的额外暴露,但不等于证明整个系统不存在任何时序侧信道。

团队会话则给明确的操作指引:当前会话归属于哪个团队,为什么不能加载另一个团队的资源,以及可以怎样继续。比如请管理员将合适的资源设为共享,改到有资格的个人会话里组合分析,或者把故障转派给对方团队。团队归属本来就是这段会话的明确语义,解释清楚有助于用户继续工作。

文件已经在盘上,read 为什么还要检查一次

文件准备和内容返回是两个时刻。文件可能在先前预取时已经落盘,之后成员关系发生变化;也可能同步尚未完成,盘上状态与当前计划暂时不同。不能因为底层文件读取成功,就认定这次读取已经获得授权。

这里的 read 是平台提供给 agent 的独立工具,不是 shell 命令,也不是 shell 里的 read 内建指令。它先尝试读取本地文件;如果返回“文件不存在”,并且路径位于受管理的知识目录下,就尝试补取:检查加载资格,查找对应知识包,从对象存储下载这个文件,写到 runner,再重试读取。下载失败、远端确实没有文件或者没有权限,都不会凭空产生内容。

cat、grep 和 ls 没有这段补取逻辑。磁盘上不存在的文件,cat 只会报找不到,grep 搜不出正文,ls 也不会列出远端尚未落盘的目录。只有 agent 另行调用平台的 read 工具,才可能触发按需下载;平台不会因为看见一条失败的 shell 命令,就自动把它改成下载请求。

因此,按需加载还要求 agent 已经知道要读哪个路径。它可以从知识入口里的引用、用户提供的路径等线索得到,也可以按开场指引读取明确团队的 knowledge/team_<id>/DUTY.md 入口。read 负责按路径补取,不负责搜索整个远端知识库。没有落盘、也没有路径线索的资料,不能指望一次本地 grep 就发现。

文件已经存在时,底层读取可能直接成功,但 read 在把内容返回给模型之前,还要经过同一套加载资格判断。拒绝就丢弃刚读到的内容,返回错误。这条返回前的检查,与文件缺失时的补取是两条不同路径。

被拒绝的文件也不会进入“已读集合”。第 7 篇讲过,系统会对 agent 读过的文件发送变更提醒。既然这次内容没有交给模型,后续就不该通过变更通知再告诉它这份文件的情况。

不过,这道检查保护的是 read 的返回路径,不能替 bash 补上一套权限系统。 团队成员关系变化后,已有文件怎样清理、同步失败怎样处理,仍属于执行环境管理的责任。已经进入模型上下文的内容,也不会因为后来退团就自动被遗忘。落盘控制限制后续能拿到的字节,并不提供追溯撤回。

一次读取,怎样变成持续可用的知识入口

个人会话第一次成功读取另一个有资格的团队时,系统还会记下一件事:这个团队已经被当前会话使用了。我们把这叫作挂载。

这里的“挂载”是会话状态里的登记,不要求读者把它理解成操作系统的磁盘挂载。read 可以先补取所请求的单个文件,团队被记入已挂载集合;后续模型调用前的文件同步会将该团队知识包纳入准备范围,让其他文件也可供 ls、grep 浏览。当前工具循环补一段临时提醒,从下一轮起,这个团队的入口文件 DUTY.md 会出现在稳定的挂载说明中。后续即使对话经过压缩,这份登记也还在,系统可以重新生成入口说明。

这样,模型先通过一次实际读取表达“我需要这份资料”,平台再检查资格,并把后续入口补齐。开场不需要堆一份长目录,也不用因为提前介绍了某个团队,就暗示所有会话都可以访问它。

这套设计能承诺到哪里

文件准备流程有一条必须守住的约束:不能因为方便浏览,就预取这个会话无资格加载的团队知识。全量预取尤其需要检查这一点,因为 grep 和 cat 不会替平台再问一次成员关系。

与此同时,团队范围在我们产品里的定位是知识组织和加载控制。它让支付会话默认使用支付资料,让维护归属可预期;不能据此承诺同一账号内的同事之间具备完整的保密隔离。成员关系变化、工作区残留和已经进入上下文的信息,都不能用一句“有权限检查”带过。第 15 篇会专门讲这个边界。

下一篇先看另一个容易混淆的问题:能参与使用一个资源,是否就能编辑它?我们给团队资源采用的规则是,想治理哪个团队,先实际加入那个团队,管理员也一样。


这是「 Flashduty AI SRE 技术揭秘 」系列的第十三篇。我们在做一个专注 SRE 领域、帮你做故障根因分析的 AI Agent 产品,本系列会持续分享我们在 agent 工程上的真实设计、踩过的坑和做过的取舍。

常见问题

只给 read 加权限检查,为什么还不够?

因为 bash 的 cat、grep 等命令不会调用平台的团队权限判定。无资格的文件应在准备阶段就被排除;read 返回前仍需检查当次加载资格。

个人会话按谁的团队成员关系判断?

按会话创建者,而不是当前发消息的人。成员关系只在单次请求内缓存,后续请求重新查询;涉及其他团队时,身份或成员服务查询失败就拒绝加载。

退出团队后,模型会自动忘记读过的知识吗?

不会。加载检查可以限制后续请求,但无法撤回已经进入模型上下文的内容;磁盘残留的清理还属于执行环境管理的责任。

延伸阅读

延伸路径

继续看解决方案和产品对比

如果你正在做监控、可观测性或故障定位相关选型,建议从解决方案和产品对比继续往下看。

快猫星云 联系方式 快猫星云 联系方式
快猫星云 联系方式
快猫星云 联系方式
快猫星云 联系方式
快猫星云