ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

OpenHuman 会话工具边界策略(agent_policy):基于渠道权限天花板的确定性工具分级与系统提示注入

OpenHuman 会话工具边界策略(agent_policy):基于渠道权限天花板的确定性工具分级与系统提示注入 OpenHuman 会话工具边界策略agent_policy基于渠道权限天花板的确定性工具分级与系统提示注入【免费下载链接】openhumanOpenHuman is an open source personal AI for Mac, Windows and Linux — local-first memory, agent orchestration, and deep research.项目地址: https://gitcode.com/GitHub_Trending/op/openhumanOpenHuman 是一个面向 macOS、Windows 与 Linux 的开源个人 AI本地优先记忆、Agent 编排与深度研究。在单 Agent 会话中模型能看到的工具集合与其运行时实际能调用的工具必须保持一致且都要被渠道channel配置的权限天花板约束。src/openhuman/tools/agent_policy/模块正是负责这项工作的纯逻辑域它为每个 Agent 会话生成一份确定、不可变的ToolPolicySession快照每个工具的允许/拒绝/隐藏决策、允许/阻断/隐藏工具名集合、粗粒度任务风险等级并把活动边界渲染成系统提示中的一小节。读完本文你将掌握 OpenHuman 中渠道权限 → 每工具决策 → 风险等级 → 提示注入的完整链路以及空配置回退、宽松解析、可见性/权限双轴分类等关键设计。模块定位一份纯逻辑、无副作用的安全快照agent_policy是crate::openhuman::tools下的独立子模块其唯一职责是把输入映射为输出不涉及持久化、RPC 或事件。模块文档明确写道This domain is pure logic — no persistence, no RPC, no events.一次build_session调用接收六个输入活跃的agent_id发起会话的channel如web、cli、telegramentrypoint如chat、agent配置好的channel - permission字符串映射channel_permissions: HashMapString, String可用工具注册表tools: [Boxdyn Tool]可选的显式可见工具名集合visible_tool_names: HashSetString。输出是一份确定性、不可变的ToolPolicySession同一输入必然产生同一输出快照没有变更mutationAPI从生成到会话结束始终保持一致。这样设计的好处是会话中途无论提示被拼接多少次模型看到的是同一份边界不会出现分类结果漂移。模块由四个文件构成目录文件角色mod.rs仅导出模块文档 mod声明 pub use重导出引擎、提示渲染器与类型types.rs无 Serde 的纯领域类型TaskRiskLevel、TaskProfile、ToolPolicyAction、ToolPolicyDecision、ToolCapability、ToolPolicySession含查询辅助方法并持有NO_TOOLS_ALLOWED_SENTINEL哨兵常量engine.rsToolPolicyEngine::build_session分类逻辑私有辅助函数permission_for_channel/parse_permission_level内联#[cfg(test)]测试套件prompt.rsrender_tool_policy_boundaryTOOL_POLICY_BOUNDARY_HEADING常量UTF-8 安全的truncate_utf8内联#[cfg(test)]测试套件核心职责拆解README 将模块职责归纳为六条逐一对应到代码中的具体实现解析渠道权限天花板从channel - permission字符串映射中解析出某渠道的PermissionLevelpermission_for_channel并处理各类回退。逐工具分类把注册表中的每个工具对照天花板与可选可见性集合产出ToolPolicyActionAllow/RequireApproval/Deny/HideFromPrompt。构建不可变快照生成附加到 Agent 会话上的ToolPolicySessionprofile、capabilities、允许/阻断/隐藏工具名集合、决策映射。推导任务风险等级由最高允许权限推导粗粒度TaskRiskLevelLow/Medium/High/Critical。渲染系统提示边界输出有界的一节## Tool Policy Boundary列出活跃的 agent/channel/entrypoint、允许权限、风险、允许工具与受限数量摘要。运行时 fail-closed对未知或未列出的工具名默认决策为Deny保证查不到就是拒绝。权限模型PermissionLevel天花板与排序模块依赖crate::openhuman::tools::PermissionLevel与Tooltraitname()、permission_level()这是它与 openhuman/core 之间唯一的耦合点见 tools/mod.rs 的重导出。权限层级通过PermissionLevel的 Ord 排序实现天花板比较这也是整个分类的核心。权限到风险等级的映射在 types.rs 中明确定义允许权限PermissionLevel风险等级TaskRiskLevelDisplay 输出None/ReadOnlyLowlowWriteMediummediumExecuteHighhighDangerousCriticalcriticalTaskRiskLevel是Copy Eq枚举其Display实现把等级渲染为小写字符串供提示文本直接使用。分类算法两条相互独立的轴ToolPolicyEngine::build_sessionengine.rs对每个工具计算两个布尔量然后三选一explicitly_hidden !visible_tool_names.is_empty() !visible_tool_names.contains(name)可见性轴。只要显式可见集合非空且工具不在其中该工具就被判定为HideFromPrompt。exceeds_permission required_permission allowed_permission权限轴。工具所需权限高于渠道天花板即Deny。分类优先级为先看可见性HideFromPrompt再看权限Deny否则 Allow。也就是说if explicitly_hidden HideFromPrompt入 hidden_tool_names else if exceeds_permission Deny入 blocked_tool_names else Allow入 allowed_tool_names注意两条轴完全独立一个工具可以因为超过权限天花板被Deny进blocked_tool_names也可以因为不在显式可见集合中被HideFromPrompt进hidden_tool_names两者可能同时发生但归类互不影响。README 特别指出Hidden takes precedence over deny in the classification order与上述代码顺序一致。ToolPolicyAction::RequireApproval在 match 中已被处理路由到blocked_tool_names但当前build_session永远不会产生该动作——这是为未来审批流预留的扩展位。每个工具都会生成一条ToolPolicyDecision与一条ToolCapability存入快照并通过log::trace!(target: openhuman::tools::agent_policy, ...)输出[tool-policy] classified tool ...级别的诊断日志便于按日志目标过滤排查RUST_LOG 可指定openhuman::tools::agent_policy。渠道权限解析的宽松规则私有函数parse_permission_levelengine.rs采用宽松解析trim后转小写、去掉-/_然后匹配别名规范化 token结果 PermissionLevelnoneNonereadonly/readReadOnlywriteWriteexecute/execExecutedangerous/dangerDangerous其他任何 tokenNone回退 ReadOnly因此配置里写read_only、read-only、ReadOnly、READ都能正确归一化为ReadOnly写danger等价于dangerous完全无法识别的 token 不会报错而是回退到ReadOnly——默认偏保守。两个关键回退语义Legacy 逃生门 vs ReadOnly 兜底permission_for_channelengine.rs实现了最容易踩坑的语义差异空映射 完全放行legacy 逃生门如果channel_permissions映射为空直接返回PermissionLevel::Dangerous即不施加任何限制保留升级前的行为。这保证了老安装以及未 seed 映射的单元测试夹具不会在升级瞬间被锁死。只要映射非空缺失渠道一律 ReadOnly一旦存在任意渠道策略那么映射中缺失的渠道、或值无法解析的渠道都回退到PermissionLevel::ReadOnly而不是无限制。真正的加固落在配置层AgentConfig::migrate_channel_permissions_if_legacy在启动时对 legacy 安装执行迁移用安全的逐渠道默认值 seed 映射使天花板在升级后的第一次启动就生效。这正是引擎保持纯逻辑、加固下沉到配置层的分层设计。快照查询 API 与 fail-closed 兜底ToolPolicySession在 types.rs 提供五个查询辅助方法is_allowed(name) - bool判断工具名是否在allowed_tool_names中has_restrictions() - boolblocked_tool_names或hidden_tool_names任一非空即为 truerestricted_tool_count() - usize阻断数 隐藏数之和visible_tool_names_for_prompt() - HashSetString见下文哨兵机制decision_for(name) - ToolPolicyDecision查决策映射查不到时默认返回Denyrequired_permission: Noneallowed_permission取 profile 值——这就是 fail-closed 兜底未知工具名在运行时永远得不到放行。ToolPolicyDecision::is_denied()的定义值得注意任何非Allow的动作包括RequireApproval、Deny、HideFromPrompt都视为拒绝。也就是说被隐藏的工具在运行时同样不可调用。空-但-受限的哨兵机制当存在限制但没有任何工具被允许时visible_tool_names_for_prompt()会插入哨兵常量const NO_TOOLS_ALLOWED_SENTINEL: str __openhuman_no_policy_allowed_tools__;这样提示渲染能区分空但受限{__openhuman_no_policy_allowed_tools__}与完全不受限空集合避免模型把受限的空表面误解成不受限。系统提示渲染## Tool Policy Boundaryrender_tool_policy_boundary(session, max_bytes) - OptionStringprompt.rs在会话无限制!has_restrictions()时返回None不注入任何提示否则渲染一段紧凑的系统提示小节## Tool Policy Boundary - Agent: orchestrator - Channel: web - Entry point: chat - Allowed permission: read_only - Risk: low - Allowed tools: read_notes - Restricted tools: 1 omitted by policy渲染内容的要点TOOL_POLICY_BOUNDARY_HEADING ## Tool Policy Boundary是固定的标题常量逐行列出Agent、Channel、Entry point、Allowed permissionPermissionLevel的 Display、RiskTaskRiskLevel的 Display允许工具非空时才输出Allowed tools:行逗号连接BTreeSet保证有序受限数量 0 时输出Restricted tools: N omitted by policy摘要行不逐个列出被隐藏/阻断的工具名——既告诉模型边界存在又避免把敏感工具名泄露进上下文输出经过truncate_utf8保证不超过max_bytes。truncate_utf8prompt.rs保证截断发生在字符边界上不会把多字节 UTF-8 字符切断当空间足够时追加\n[...truncated]标记仅当max_bytes marker.len()时max_bytes 0时直接清空。实际接入点在会话轮次提示构建处render_tool_policy_boundary(self.tool_policy_session, 2048)turn/context.rs即每轮对话的系统提示固定预留最多 2048 字节给边界小节。会话集成builder / runtime / turn / types 四处接线README 列出模块在会话生命周期中的四个使用方源码均可以证实builder/setters.rs在构建会话时调用ToolPolicyEngine::build_session(...)生成工具策略会话与渠道策略会话注意 setters 里出现了两次调用对应工具级与会话级两种策略维度builder/mod.rstool_policy: ToolPolicySession作为构建参数传递runtime.rs 与 runtime_impl_01_part_01.rs运行时重建ToolPolicySession保证提示中缺失的工具是真被策略拒绝而不只是没出现在提示里types.rsToolPolicySession作为pub(super)字段挂在会话类型上随会话生命周期存活turn/context.rs每轮把边界小节注入提示。从源码结构看这种构建时生成快照 → 运行时查询决策 → 每轮注入提示的三角结构确保了提示可见性与运行时执行严格对齐模型只能看到被允许的工具运行时查询同样以快照为准双端共用同一份不可变数据。测试验证从单元测试看行为契约模块附带两套内联测试直接固化了上文全部行为engine_tests.rs 覆盖四类关键行为未知渠道回退 ReadOnlywebwrite映射下查询unknown-channel断言allowed_permission ReadOnly、read_notes允许、write_notes不允许——验证有配置后缺失渠道不再放行。空映射保留 legacy 全量表面空映射下allowed_permission Dangerous三个工具全部允许且!has_restrictions()——验证逃生门语义。超过天花板即过滤webwrite下run_scriptExecute被拒绝——验证权限轴。显式可见集合收窄允许面cliexecutevisible{run_script}时read_notes/write_notes进入hidden_tool_names而非blocked_tool_namesblocked_tool_names为空——验证可见性轴独立于权限轴。fail-closed 默认拒绝decision_for(missing_tool).is_denied() true未知工具名默认 Deny。prompt_tests.rs 覆盖提示渲染契约受限会话渲染出## Tool Policy Boundary、Agent: orchestrator、Allowed tools: read_notes、Restricted tools: 1 omitted by policy且不包含被拒绝工具名write_notes80 个长工具名的会话在max_bytes192下渲染结果len 192且位于字符边界——验证 UTF-8 安全截断空工具表与无限制会话均返回None——验证无限制不注入提示。实战注意事项与设计启示结合 README 的 gotchas 与源码以下是接入或排查时最值得记住的几点空映射 ≠ 安全刚升级但还没 seed 渠道策略的实例Dangerous意味着工具表面完全放行。务必依赖migrate_channel_permissions_if_legacy在首次启动完成 seed或在部署时预置映射。写配置时名字可以很随意read_only、read-only、READ都解析为ReadOnlydangerdangerous无法识别的值回退ReadOnly不会硬失败。两条轴别混淆blocked_tool_names超权限与hidden_tool_names不可见加起来才是restricted_tool_count()is_denied()把隐藏也算作拒绝运行时不可调用。提示中的边界是有字节上限的render_tool_policy_boundary的max_bytes参数会话接入时传 2048由truncate_utf8强制保证工具名列表过长时会安全截断。快照不可变、决策确定性调试时看到的分类结果就是模型与运行时共用的那份数据要改变策略只能重建会话快照不存在运行时篡改入口。agent_policy的克制值得借鉴把策略分类做成纯函数式、确定性、不可变的快照域把迁移、seed 等副作用隔离在配置层把运行时默认值设为Denyfail-closed再用 UTF-8 安全的提示渲染控制上下文开销——四个文件就支撑起提示可见性 运行时执行边界 渠道权限天花板的完整闭环。【免费下载链接】openhumanOpenHuman is an open source personal AI for Mac, Windows and Linux — local-first memory, agent orchestration, and deep research.项目地址: https://gitcode.com/GitHub_Trending/op/openhuman创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表