ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

OpenCode v2 提供商策略(provider.use):experimental.policies 的设计与源码解析

OpenCode v2 提供商策略(provider.use):experimental.policies 的设计与源码解析 OpenCode v2 提供商策略provider.useexperimental.policies 的设计与源码解析【免费下载链接】opencodeThe open source coding agent.项目地址: https://gitcode.com/GitHub_Trending/openc/opencode本文以 specs/v2/provider-policy.md 这份策略规范为主体完整解析 OpenCode v2 中策略系统Policy的定位、语句格式、通配符匹配、最后匹配生效评估算法、跨配置文档的优先级顺序以及它与 provider 配置的边界和遗留配置的迁移方式。读完后你将能够独立编写experimental.policies规则来控制哪些 LLM 提供商可用并理解规则在 OpenCode 运行时中如何被加载与求值。策略是什么把 provider 配置与 provider 授权分离策略Policy控制的是对某个具名资源执行某个操作是否被允许。它可以写在配置文件里但策略求值本身是独立的运行时关注点规范原文。第一个策略消费者是提供商可用性action: provider.use resource: provider ID, such as openai or company-ai规范明确了一条核心原则provider 配置与 provider 策略保持分离providers负责描述端点、选项和模型覆盖这个 provider 怎么连experimental.policies负责决定使用某个 provider 的操作是否被允许这个 provider 能不能用。因此一个 provider 完全可以配置正确、凭据有效却依然被策略拒绝使用。这一点在用户文档 packages/web/src/content/docs/policies.mdx 中也有对应表述被策略拒绝的 provider 在模型选择和模型使用中均不可用即使它已正确配置并持有凭据。策略与权限permissions也是两个概念权限控制会话中工具能做什么策略控制 OpenCode 能否使用某个资源如 LLM provider。设计目标与非目标规范列出的目标替代遗留的enabled_providers和disabled_providers配置当用户不指定任何策略时默认体验保持不变支持对 action 与 resource 的通配符匹配提供一套足够小的策略词汇未来可覆盖plugin.load、mcp.connect等操作让 user 策略覆盖 repository 策略并预留组织托管策略organization-managed policy覆盖两者的位置保持求值简单匹配语句按顺序应用最后一条匹配生效。非目标同样清晰划定了策略能力的边界策略不配置端点、凭据、模型或 provider 选项策略不能把不可用的资源变为可用策略当前不提供条件conditions、主体principals、审批提示或强制配置值本规范不定义组织托管策略的投递方式。语句格式experimental.policies每条策略语句有三个字段写在配置文件的experimental.policies数组中{ experimental: { policies: [ { effect: deny, action: provider.use, resource: openai, }, ], }, }对应的接口形状interface PolicyInfo { effect: allow | deny action: string resource: string }这个格式与官方用户文档中的配置示例一致effect取allow或denyaction是被控制的操作resource是资源 ID 或通配符模式{ $schema: https://opencode.ai/config.json, experimental: { policies: [ { effect: deny, action: provider.use, resource: openai } ] } }模块划分Policy 模块拥有通用接口领域定义自己的语句模式规范对模块职责有明确划分Policy模块拥有共享的Policy.Info接口、Policy.Effect类型和求值器各领域定义自己支持的类型化语句模式——例如Catalog.ProviderPolicy会把action固定为provider.use。配置模式再把各领域定义的语句模式汇总成experimental.policies接受的联合类型因为配置文件是该能力还处于实验期时唯一可以书写语句的地方。这一设计在源码中可以直接看到。packages/core/src/policy.ts 实现了通用策略核心Effect是allow | deny的字面量模式带Policy.Effect标识Info是一个模式类字段为action: string、effect: Effect、resource: stringService暴露load载入语句、evaluate对给定 action/resource 求值并返回 effect参数里带调用方提供的 fallback和hasStatements三个方法。领域侧的绑定在 packages/core/src/catalog.tsPolicyActions Schema.Literals([provider.use])即目录Catalog领域目前只注册了provider.use这一个 action。而 packages/core/src/config/experimental.ts 中的配置模式正是按规范描述的方式聚合的PolicyAction取各领域导出 action 的Schema.UnionConfigV2.Experimental.Policy在PolicyV2.Info字段基础上把action收窄为该联合Experimental再挂上可选的policies: Policy[]。源码里的注释也印证了扩展方式每个核心域导出它支持的策略 action往这个联合里加一个 action 就使其在配置中合法同时 Policy 模块本身保持通用。通配符匹配action 与 resource 独立匹配action和resource都使用 OpenCode 既有的通配符匹配行为实现复用 packages/core/src/util/wildcard 工具用户文档进一步说明*匹配零或多个字符?匹配单个字符。规范给出的示例ActionResourceMatchesprovider.useopenai仅匹配 provider ID 为openai的使用provider.usecompany-*匹配company-us、company-eu这类 provider ID 的使用provider.**若未来引入更多 provider 操作则匹配任意 provider 上的任意操作一个重要的设计决定不存在模式特定优先级。具体 resource 不会自动胜过通配 resource——结果完全由书写/求值顺序控制。单元测试 packages/core/test/policy.test.ts 验证了 action 与 resource 是相互独立匹配的加载{ effect: deny, action: provider.*, resource: company-* }后provider.use / company-stable求值为deny而plugin.load / company-stable求值为 fallbackallow——action 不匹配时即使 resource 匹配也不会命中。求值算法按序遍历最后一条匹配生效对一个操作 资源求值的完整规则从allow开始考察所有action与resource均匹配请求内容的语句每条匹配语句用其effect替换当前决定最后一条匹配语句决定结果。概念性实现function evaluate(action: string, resource: string, fallback: Policy.Effect, statements: Policy.Info[]) { return ( statements.findLast( (statement) Wildcard.match(action, statement.action) Wildcard.match(resource, statement.resource), )?.effect ?? fallback ) }这段概念代码与仓库实际实现几乎逐行一致packages/core/src/policy.ts 中evaluate就是statements.findLast(...)?.effect ?? fallback通配判断为Wildcard.match(action, statement.action) Wildcard.match(resource, statement.resource)。注意 fallback 由调用方提供而不是策略引擎硬编码目录Catalog对 provider 使用提供allow作为默认因此没有任何 provider 策略语句时本来可用的 provider 照常可用——这正是不写策略则默认体验不变目标的落地。测试用例也验证了这一点evaluate(provider.use, anthropic, allow)返回allowevaluate(provider.use, anthropic, deny)返回deny语句为空时结果完全取决于 fallback 参数。最后匹配生效同样有测试覆盖先后加载allow openai和deny openai两条语句后provider.use / openai求值为deny。单个配置文档内的顺序同一份配置文件中语句保持用户书写的顺序。例 1除 Anthropic 外拒绝所有 provider——先写宽泛 deny再写具体 allow{ experimental: { policies: [ { effect: deny, action: provider.use, resource: *, }, { effect: allow, action: provider.use, resource: anthropic, }, ], }, }结果provider.use / anthropic - allow provider.use / openai - deny例 2允许内部 provider但排除实验性的{ experimental: { policies: [ { effect: deny, action: provider.use, resource: * }, { effect: allow, action: provider.use, resource: company-* }, { effect: deny, action: provider.use, resource: company-experimental-* }, ], }, }结果company-stable: allowed company-experimental-fast: denied openai: denied两个例子共同说明了实践惯例宽规则在前、例外规则在后利用最后匹配生效逐层修正结果。跨配置文档的顺序user 策略覆盖 repository 策略普通设置与策略的优先级需求不同普通设置正向读取因此位置特定的设置如项目级覆盖用户全局设置策略按倒序遍历所书写的配置文档因此用户全局策略可以覆盖仓库策略每个文档内部的语句保持其书写顺序。规范强调这至少保证了一件事仓库无法悄悄重新启用你在全局拒绝的东西。示例——项目配置{ experimental: { policies: [{ effect: allow, action: provider.use, resource: openai }], }, }用户全局配置{ experimental: { policies: [{ effect: deny, action: provider.use, resource: openai }], }, }结果provider.use / openai - deny用户全局的 deny 位于倒序遍历的更靠后位置覆盖了项目里的 allow。用户文档也对这一行为做了面向使用者的说明如果全局配置和项目配置的策略匹配到同一个 provider你的全局策略优先于项目策略这可以防止仓库重新启用你全局拒绝的 provider。另外直接项目文件与.opencode文件之间的相对优先级被有意推迟待.opencode配置评审后再定。组织托管策略与插件边界组织托管策略不属于普通书写的配置。当它实现时托管语句必须追加在倒序后的书写语句之后使其拥有最终决定权repository policy - user-global policy - organization-managed policy规范同时划出了两条治理红线插件不得添加、删除或覆盖策略语句。插件可以提供功能或已配置的 provider但是否允许某操作由策略通过受管执行路径裁决提供商策略不是可执行插件的完整沙箱。被拒绝的 provider 不能通过正常的 provider/model 路径被使用若插件代码的合规管控成为需求则需要独立的治理机制。策略与 provider 配置的协作一个 provider 条目负责配置策略语句负责准入。组合示例{ providers: { company-ai: { endpoint: { type: openai/responses, url: https://ai.company.example/v1/responses, }, }, }, experimental: { policies: [ { effect: deny, action: provider.use, resource: * }, { effect: allow, action: provider.use, resource: company-ai }, ], }, }这里providers条目把company-ai配置出来策略语句使它成为唯一被允许使用的 provider。并且策略的生效不依赖 provider 的来源——无论 provider 如何变得已知或可用策略都会应用包括models.dev 目录数据环境凭据已保存的账户内建 provider 插件显式的 provider 配置。应用时机先组装目录再执行策略规范要求一个关键的应用顺序先组装 provider 记录与模型覆盖再检查 provider 策略。否则后续加载 provider 时可能重建一个已经被过滤掉的 provider。预期流程构建 provider/model 目录条目应用已配置的 provider 与模型覆盖对每个 provider ID 调用Policy.Service求值provider.use阻止被拒绝的 provider 被选择或使用。从源码结构看这一顺序与实现相符packages/core/src/catalog.ts 中 Catalog 层在 Effect 生成函数里注入了Policy.Service与EventV2.Service、Integration.Service并列其节点依赖列表为[EventV2.node, Policy.node, Integration.node]即策略服务作为目录构建的依赖参与运行。至于被拒绝的 provider 是整体移除还是保留为禁用记录用于诊断规范明确这留给实现决定。从遗留配置迁移enabled_providers / disabled_providers策略是disabled_providers与enabled_providers的替代方案。官方用户文档也建议用策略取代这两个旧设置来控制 provider 访问。遗留拒绝列表{ disabled_providers: [openai, google], }等价的 v2 策略逐条 deny{ experimental: { policies: [ { effect: deny, action: provider.use, resource: openai }, { effect: deny, action: provider.use, resource: google }, ], }, }遗留允许列表allowlist 需要先全拒、后放行的模式表达{ enabled_providers: [anthropic, openai], }等价的 v2 策略{ experimental: { policies: [ { effect: deny, action: provider.use, resource: * }, { effect: allow, action: provider.use, resource: anthropic }, { effect: allow, action: provider.use, resource: openai }, ], }, }迁移时的注意点deny 列表是逐 provider 一对一翻译语义直观而 allowlist 必须依赖最后匹配生效的顺序语义deny *在前、allow例外在后如果调整语句顺序会直接改变放行集合。小结与实践要点experimental.policies目前规范与 packages/core/src/catalog.ts 中的PolicyActions一致只支持一个 actionprovider.useresource 为 provider ID求值引擎是确定性的最后匹配生效无模式优先级编写规则时把宽规则放前、例外放后不写任何策略时行为不变Catalog 提供allowfallback跨文档优先级为 user-global 覆盖 repository策略倒序遍历文档未来组织托管策略将拥有最终决定权插件无权改写策略provider 的配置providers与准入experimental.policies正交凭据有效也可能被策略拒绝策略在 provider/model 目录组装并应用覆盖之后统一求值避免被过滤的 provider 在后续加载中被重建。相关代码与文档入口规范 specs/v2/provider-policy.md、策略核心 packages/core/src/policy.ts、策略测试 packages/core/test/policy.test.ts、实验性配置模式 packages/core/src/config/experimental.ts、目录构建 packages/core/src/catalog.ts、用户文档 packages/web/src/content/docs/policies.mdx。【免费下载链接】opencodeThe open source coding agent.项目地址: https://gitcode.com/GitHub_Trending/openc/opencode创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表