Files
Cyrene_v2/internal/runtime/loop/tool_policy.go
T
AskaEth 91e8d7a029 docs(runtime): internal/runtime/loop 44 个文件注释改中文(逐行 1:1,代码零字节改动)
主人 2026-10-03 拍板「注释一律中文」;本模块按长期分批协议(一个模块一次派工)执行。
注释行数 1329 → 1329 逐行 1:1(含中文 33 → 1304);行尾注释 5 条一并译。
校验:机械校验(非注释改动行)= 空;独立 Go AST 程序(剥注释复渲)证明 44/44 文件代码全等;
全树 go test ./... 58 包 ok / 0 FAIL;交叉编译绿。
gofmt:capacity_loop_test.go / flow.go 改动前即未格式化,按铁律保留原样未动。
2026-10-03 22:28:21 +08:00

87 lines
3.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package loop
import (
"context"
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/agent"
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/model"
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/tool"
)
// auditToolRejectNotAllowed 是群聊/陌生人环境中被
// design/11 §3 安全门拒绝的工具调用的审计 "what"。
const auditToolRejectNotAllowed = "tool.reject.not_allowed"
// AuditEvent 向审计 sink 上报被安全门拦截的工具拒绝
// (design/11 §3 §8)。循环在受限(群聊/陌生人)环境中拒绝
// 工具调用时发出一个,使该拒绝可审计,且
// 前端可以渲染「xx 工具在群聊被拒」的小字。它经
// Options.OnAudit 接到 gateway 审计轨迹。
type AuditEvent struct {
// Who 是实施行为的 actor 环境标签("group_member"/"stranger")。
Who string
// What 是操作,如 "tool.reject.not_allowed"。
What string
// Result 是结果详情 —— 指名被拒工具的 not_allowed 提示。
Result string
}
// restrictSchemas 仅返回 ctx 上会话环境可调用的工具 schema,
// 依据 design/11 §3 安全门:群聊/陌生人会话只能
// 调用其可编辑标记允许该环境的工具(默认
// 关闭),而 owner 环境保留全集。它是对 capacity
// 允许集视图的最终裁决 —— 是安全红线而非便利
// 提示 —— 因此它最后运行,且只能收缩暴露面。
func (l *Loop) restrictSchemas(ctx context.Context, d *tool.Dispatcher, schemas []model.ToolSpec) []model.ToolSpec {
policy := d.PolicyEngine(sessionEnvFrom(ctx).ActorEnv)
kept := make([]model.ToolSpec, 0, len(schemas))
for _, s := range schemas {
if policy.Allow(s.Name) {
kept = append(kept, s)
}
}
return kept
}
// applyToolSecurityGate 拦截所有工具在 ctx 会话环境中
// 不被允许的调用(design/11 §3)。它就地把 actions 改写,
// 把每个可运行的调用改为携带 not_allowed 提示的跳过,
// 并为该拒绝发出一个 AuditEvent。nil 接收者或 nil dispatcher 为空操作。
func (l *Loop) applyToolSecurityGate(ctx context.Context, d *tool.Dispatcher, calls []agent.ToolCall, actions []toolCallAction) {
env := sessionEnvFrom(ctx)
policy := d.PolicyEngine(env.ActorEnv)
for i := range calls {
if !actions[i].run {
continue
}
if policy.Allow(calls[i].Name) {
continue
}
reason := notAllowedReason(calls[i].Name)
actions[i] = toolCallAction{run: false, reason: reason}
l.emitAudit(AuditEvent{
Who: string(env.ActorEnv),
What: auditToolRejectNotAllowed,
Result: reason,
})
}
}
// notAllowedReason 是受限(群聊/陌生人)会话调用其可编辑
// 标记拒绝该环境的工具时回灌给模型的软提示
// (design/11 §3)。它是软结果 —— 注入对话,
// 绝不 panic 或硬崩溃 —— 使模型能重新评估。
func notAllowedReason(name string) string {
return "not_allowed: tool " + name + " is not allowed in a group/stranger conversation"
}
// emitAudit 把 AuditEvent 转发给配置的 OnAudit sink。未接线
// sink 时为空操作,因此在未挂审计 sink 的叶子测试中
// 安全门仍能工作(软 not_allowed 结果)。
func (l *Loop) emitAudit(ev AuditEvent) {
if l.onAudit == nil {
return
}
l.onAudit(ev)
}