- 只翻注释/文档字符串:剥离注释后代码行逐字节不变(机械校验只剩允许的行尾注释形态) - 裁决编号与 design 节号(02.4.3 / 11.4.2 / design/05 §9 等)逐字保留 - 复核:go build 绿;orchestration 及其子包(audit/comm)测试绿;gofmt 干净
72 lines
3.4 KiB
Go
72 lines
3.4 KiB
Go
package orchestration
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
|
||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/loop"
|
||
)
|
||
|
||
// 本文件持有 design/22 §8-2 里「派出去的子代理以目标角色身份运行」那一半:带目标
|
||
// 角色派发的子代理必须以那个角色的指令作答、使用那个角色的记忆作用域,而本进程跑
|
||
// 不了的角色必须明确报错,不得静默降级为默认角色。
|
||
//
|
||
// 解析本身留在循环里(internal/runtime/loop/persona.go):每一处按角色作用域的读取
|
||
// —— 提示词(agent 池)、记忆命名空间、情绪、思考旋钮 —— 都派生自会话 env 的
|
||
// PersonaID 这一个输入,因此四者不可能互相矛盾。编排层因此只有两件事要做:校验
|
||
// id,并把它盖到子代理的运行上下文上。两者都不碰身份车道(ActorEnv / MemWritable),
|
||
// 那条车道由渠道层从结构上决定,从来不是角色的分内事(项目红线:身份识别靠元数据)。
|
||
|
||
// PersonaKnown 报告 personaID 是否为本进程能以自身指令运行的角色
|
||
// (Options.PersonaExists)。空 id 平凡已知:调用方没有指名任何角色,即既有的单角色
|
||
// 派发。resolver 为 nil 时所有具名角色都算未知,因此指名角色的派发会失败,而不是
|
||
// 悄悄按默认角色运行。
|
||
func (s *Scheduler) PersonaKnown(personaID string) bool {
|
||
return personaID == "" || (s.personaExists != nil && s.personaExists(personaID))
|
||
}
|
||
|
||
// effectiveRole 返回记在子代理上的绑定角色。被派发的 persona 就是绑定本身(子代理
|
||
// 以该角色身份作答),因此它填补空的 RoleID;指名了不存在的 persona 的显式 RoleID
|
||
// 则保持调用方所设。记录下来的角色正是进度事件与 ws subagent_event 帧所报告的内容,
|
||
// 所以前端不会看到「以 yuki 身份运行却角色为空」的子代理。
|
||
func effectiveRole(spec AgentSpec) string {
|
||
if spec.RoleID == "" {
|
||
return spec.PersonaID
|
||
}
|
||
return spec.RoleID
|
||
}
|
||
|
||
// checkPersona 拒绝本进程跑不了的角色,包装 ErrUnknownPersona,使调用方能以
|
||
// errors.Is 归类。
|
||
func (s *Scheduler) checkPersona(personaID string) error {
|
||
if s.PersonaKnown(personaID) {
|
||
return nil
|
||
}
|
||
return fmt.Errorf("orchestration: spawn: persona %q: %w", personaID, ErrUnknownPersona)
|
||
}
|
||
|
||
// CheckPersonas 在整批派发前校验每个 spec 的目标角色,因此一个未知 id 会拒绝整批
|
||
// 派发,而不会留下批次里先建好的、结果永远不上报的子代理。派发工具在解码参数后立即
|
||
// 调用它:那时 id 已知,但什么都还没启动。
|
||
func (s *Scheduler) CheckPersonas(specs []AgentSpec) error {
|
||
for _, spec := range specs {
|
||
if err := s.checkPersona(spec.PersonaID); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// withPersonaEnv 返回把目标角色盖到会话 env 上的 ctx,使循环为子代理运行的每一步
|
||
// 解析该角色的提示词与记忆作用域。env 的其余部分(身份车道与记忆写入门)原样拷贝
|
||
// —— 角色决定她怎么回答,绝不决定她能碰什么。空 id 返回未改动的 ctx,因此无角色
|
||
// 的派发同之前一样继续继承派发回合的 env。
|
||
func withPersonaEnv(ctx context.Context, personaID string) context.Context {
|
||
if personaID == "" {
|
||
return ctx
|
||
}
|
||
env := loop.SessionEnvFrom(ctx)
|
||
env.PersonaID = personaID
|
||
return loop.WithSessionEnv(ctx, env)
|
||
}
|