package loop import "context" // flowBranchKey 是标记某个 Run 为另一会话 flow 图分支的 context key。 // 它由 orchestration 层(经 WithFlowBranch)在驱动 // 子代理 Run 之前设置。 type flowBranchKey struct{} // WithFlowBranch 返回 ctx,把当前 Run 标记为分支:它的 span 并入 // parentSession 的实时 flow 图,且该 Run 自身既不开始也不结束 // 轮次 —— 由所属会话的轮次负责。orchestration 层正是靠它把 // 子代理的工作归属到派生它的会话,使并行的 // 子代理渲染为同一张图的泳道,而不是用各自的图 // 替换前端唯一的 flowGraph(design/19 §2)。 // // 空的 parentSession 仍把该 Run 标记为分支:它不拥有轮次、 // 不发布任何东西。这正是无人归属到会话的后台子代理的 // 状态,它不得悄悄变成属于自己的一张图。nil ctx // 原样返回。 func WithFlowBranch(ctx context.Context, parentSession string) context.Context { if ctx == nil { return ctx } return context.WithValue(ctx, flowBranchKey{}, parentSession) } // flowBranch 报告 WithFlowBranch 记录的父会话以及 // 该 Run 是否本就是分支。非分支的 Run 拥有自己的轮次并在 // 自己的 session id 下发布。 func flowBranch(ctx context.Context) (string, bool) { if ctx == nil { return "", false } v := ctx.Value(flowBranchKey{}) if v == nil { return "", false } s, _ := v.(string) return s, true } // flowOwner 返回应当接收该 Run span 的 flow 图所属会话: // 当该 Run 是子代理分支时取分支父级(分支未归属时为空, // 此时抑制发布),否则取该 Run 自身的 sessionID。 func flowOwner(ctx context.Context, sessionID string) string { if parent, isBranch := flowBranch(ctx); isBranch { return parent } return sessionID } // FlowOwnerFromContext 返回拥有从 ctx 启动的工作的 flow 图所属会话: // 当 ctx 运行于子代理分支内时取分支父级,否则 // 取外层 Run 被驱动的 session id(两者都未设置时为空)。 // // 派发后台工作的工具 —— 子代理派生工具 —— 把它 // 作为父会话传入,使在另一个子代理内部派生的子代理 // 保持在同一个顶层图的归属之下,而不是归属到一个不拥有 // 任何图的 id。 func FlowOwnerFromContext(ctx context.Context) string { if parent, isBranch := flowBranch(ctx); isBranch { return parent } return SessionFromContext(ctx) }