主人 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 改动前即未格式化,按铁律保留原样未动。
87 lines
3.2 KiB
Go
87 lines
3.2 KiB
Go
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)
|
||
}
|