docs(comments): 注释中文化 · internal/runtime/orchestration(29 文件,纯替换)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

- 只翻注释/文档字符串:剥离注释后代码行逐字节不变(机械校验只剩允许的行尾注释形态)
- 裁决编号与 design 节号(02.4.3 / 11.4.2 / design/05 §9 等)逐字保留
- 复核:go build 绿;orchestration 及其子包(audit/comm)测试绿;gofmt 干净
This commit is contained in:
AskaEth committed 2026-10-04 13:30:04 +08:00
1 parent fe90f64a56
commit eb1828ee7a
29 files changed
+751 -949

No files matched your search

+25 -33
View File
@@ -7,33 +7,28 @@ import (
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/loop"
)
// This file owns the "派出去的子代理以目标角色身份运行" half of design/22 §8-2: a
// sub-agent dispatched with a target persona must answer with that role's
// instructions and use that role's memory scope, and a role this process cannot run
// must be rejected loudly instead of silently degrading into the default role.
// 本文件持有 design/22 §8-2 里「派出去的子代理以目标角色身份运行」那一半:带目标
// 角色派发的子代理必须以那个角色的指令作答、使用那个角色的记忆作用域,而本进程跑
// 不了的角色必须明确报错,不得静默降级为默认角色。
//
// The resolution itself stays in the loop (internal/runtime/loop/persona.go): every
// role-scoped read — prompt (agent pool), memory namespace, emotion, thinking knobs
// — is derived from the session env's PersonaID, one input, so the four can never
// disagree. The orchestration layer therefore has exactly two jobs: validate the id,
// and stamp it onto the sub-agent's run context. Neither touches the identity lane
// (ActorEnv / MemWritable), which is decided structurally by the channel layer and
// is never a persona's business (project red line: 身份识别靠元数据).
// 解析本身留在循环里(internal/runtime/loop/persona.go):每一处按角色作用域的读取
// —— 提示词(agent 池)、记忆命名空间、情绪、思考旋钮 —— 都派生自会话 env 的
// PersonaID 这一个输入,因此四者不可能互相矛盾。编排层因此只有两件事要做:校验
// id,并把它盖到子代理的运行上下文上。两者都不碰身份车道(ActorEnv / MemWritable),
// 那条车道由渠道层从结构上决定,从来不是角色的分内事(项目红线:身份识别靠元数据)。
// PersonaKnown reports whether personaID is a role this process can run with its
// own instructions (Options.PersonaExists). A blank id is trivially known: the
// caller is naming no role, which is the pre-existing single-role dispatch. A nil
// resolver makes every named role unknown, so a role-naming dispatch fails instead
// of quietly running as the default role.
// PersonaKnown 报告 personaID 是否为本进程能以自身指令运行的角色
// (Options.PersonaExists)。空 id 平凡已知:调用方没有指名任何角色,即既有的单角色
// 派发。resolver 为 nil 时所有具名角色都算未知,因此指名角色的派发会失败,而不是
// 悄悄按默认角色运行。
func (s *Scheduler) PersonaKnown(personaID string) bool {
return personaID == "" || (s.personaExists != nil && s.personaExists(personaID))
}
// effectiveRole returns the role to record on the sub-agent as its binding. A
// dispatched persona IS the binding (the sub-agent answers as that role), so it fills
// a blank RoleID; an explicit RoleID that names no persona is left as the caller set
// it. The recorded role is what the progress events and the ws subagent_event frame
// report, so the frontend never shows a sub-agent running as yuki with an empty role.
// effectiveRole 返回记在子代理上的绑定角色。被派发的 persona 就是绑定本身(子代理
// 以该角色身份作答),因此它填补空的 RoleID;指名了不存在的 persona 的显式 RoleID
// 则保持调用方所设。记录下来的角色正是进度事件与 ws subagent_event 帧所报告的内容,
// 所以前端不会看到「以 yuki 身份运行却角色为空」的子代理。
func effectiveRole(spec AgentSpec) string {
if spec.RoleID == "" {
return spec.PersonaID
@@ -41,8 +36,8 @@ func effectiveRole(spec AgentSpec) string {
return spec.RoleID
}
// checkPersona rejects a role the process cannot run, wrapping ErrUnknownPersona so
// a caller can classify it with errors.Is.
// checkPersona 拒绝本进程跑不了的角色,包装 ErrUnknownPersona,使调用方能以
// errors.Is 归类。
func (s *Scheduler) checkPersona(personaID string) error {
if s.PersonaKnown(personaID) {
return nil
@@ -50,10 +45,9 @@ func (s *Scheduler) checkPersona(personaID string) error {
return fmt.Errorf("orchestration: spawn: persona %q: %w", personaID, ErrUnknownPersona)
}
// CheckPersonas validates every spec's target role before a batch is spawned, so one
// unknown id rejects the whole dispatch instead of leaving the earlier sub-agents of
// the batch running with no result ever reported. The dispatch tool calls it right
// after decoding the arguments, where the ids are known but nothing has started yet.
// CheckPersonas 在整批派发前校验每个 spec 的目标角色,因此一个未知 id 会拒绝整批
// 派发,而不会留下批次里先建好的、结果永远不上报的子代理。派发工具在解码参数后立即
// 调用它:那时 id 已知,但什么都还没启动。
func (s *Scheduler) CheckPersonas(specs []AgentSpec) error {
for _, spec := range specs {
if err := s.checkPersona(spec.PersonaID); err != nil {
@@ -63,12 +57,10 @@ func (s *Scheduler) CheckPersonas(specs []AgentSpec) error {
return nil
}
// withPersonaEnv returns ctx with the target role stamped onto the session env, so
// the loop resolves that role's prompt and memory scope for every step of the
// sub-agent's run. The rest of the env (the identity lane and the memory-write gate)
// is copied unchanged — a persona decides how she answers, never what she may touch.
// A blank id returns ctx untouched, so a persona-less dispatch keeps inheriting the
// spawning turn's env exactly as before.
// withPersonaEnv 返回把目标角色盖到会话 env 上的 ctx,使循环为子代理运行的每一步
// 解析该角色的提示词与记忆作用域。env 的其余部分(身份车道与记忆写入门)原样拷贝
// —— 角色决定她怎么回答,绝不决定她能碰什么。空 id 返回未改动的 ctx,因此无角色
// 的派发同之前一样继续继承派发回合的 env。
func withPersonaEnv(ctx context.Context, personaID string) context.Context {
if personaID == "" {
return ctx