feat(flow): toolCalls 覆盖率追到全部编排回合(L3)—— 图带权威回合标识
- 新增 StartRoundWithTurnID(ctx, owner, text):从 ctx 取权威回合标识(与 chat_messages.turn_id
同值);Run 的唯一生产建图入口切到它;原 StartRound 语义逐字不变(1 行包装)
- 归属桶(并行按路分图)保持不变;落盘查询键 lane_turn_id 对单路也有值 ⇒ 消息面按等值查得到图
- 无权威标识时**保持缺席**(不拿桶里的路标识顶上:不许臆造)
- 不需要迁移 0024(0023 的列+索引已在位;老单路行标识根本没存过,回填=编造)
形状变化(已登记契约):graph.id 对单路也追加 #{回合标识}(web-7 → web-7#turn-…),
顺带修掉「重启后单路轮次 upsert 掉旧行」的隐患。3 个变异自检(退回老入口/无视 ctx/臆造回退)。
This commit is contained in:
1 parent
6475eb875d
commit
4a937bd269
6 files changed
+353
-21
No files matched your search
@@ -1,6 +1,7 @@
|
||||
package loop
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"sort"
|
||||
"sync"
|
||||
@@ -129,11 +130,39 @@ func NewFlowPublisher(publish func(subject string, payload any), allow func(sess
|
||||
// id(引入本切片之前的所有调用点原样可用),真并行路由经 flowOwner 传
|
||||
// `{会话}#{路}`。两条路各起各的图 ⇒ 后起的那一路绝不重置前一路的图。
|
||||
//
|
||||
// 本入口**没有 ctx 通道**,因此这一路的身份只能取**归属桶里的路标识**:真并行路的桶
|
||||
// 正是网关按该路的 turnId 拼的,两者逐字同值;单路的桶里没有路标识 ⇒ 图上该键缺席。
|
||||
// 要覆盖**全部编排回合**(普通单路回合与队首续跑路也算)请用 StartRoundWithTurnID,
|
||||
// 它是 `Run` 走的入口。
|
||||
func (p *FlowPublisher) StartRound(owner, inputText string) {
|
||||
_, lane := splitFlowOwner(owner)
|
||||
p.startRound(owner, lane, inputText)
|
||||
}
|
||||
|
||||
// StartRoundWithTurnID 与 StartRound 同一件事,但这一路的**权威回合标识**取自 ctx
|
||||
// (`TurnIDFrom`,design/15 §2.5.9):编排器给**每一路**都盖它 —— 普通单路回合、队首
|
||||
// 续跑路、真并行路都有,因此它是「这张图属于哪一回合」的唯一正当来源,也是落盘
|
||||
// `flow_graphs.lane_turn_id` 与消息面 `toolCalls[]`(按 `chat_messages.turn_id` 等值
|
||||
// 回看)能对上的前提。
|
||||
//
|
||||
// ctx 上没有权威标识时(后台思考、未接编排器的调用方)**保持缺席**:快照上的 `turnId`
|
||||
// 键与落盘行的 `lane_turn_id` 都是空 ⇒ 读侧查不到这一轮的图。那是契约允许的诚实空态,
|
||||
// **绝不**拿会话 id、时间戳之类去臆造一个身份。
|
||||
//
|
||||
// 归属桶(owner)的语义一字未改:真并行两路仍各起各的图(桶是 `{会话}#{路}`),单路的
|
||||
// 桶仍是会话 id。本方法只决定**这张图属于哪一回合**,不决定它是不是另一张图。
|
||||
func (p *FlowPublisher) StartRoundWithTurnID(ctx context.Context, owner, inputText string) {
|
||||
p.startRound(owner, TurnIDFrom(ctx), inputText)
|
||||
}
|
||||
|
||||
// startRound 是两条入口的共用实现:turnID 是这一路的权威回合标识(空 = 缺席,绝不
|
||||
// 臆造),owner 是归属桶。
|
||||
//
|
||||
// 轮次号只在**上一轮的路全部收尾**之后推进(inflight 为空),因此「同一条消息
|
||||
// 引出的两路」共享同一个轮次号;新一轮开始时丢掉更早轮次的桶,内存因此不随路数
|
||||
// 无界增长(图的历史在落盘里,见 flowgraph 包)。
|
||||
func (p *FlowPublisher) StartRound(owner, inputText string) {
|
||||
session, lane := splitFlowOwner(owner)
|
||||
func (p *FlowPublisher) startRound(owner, turnID, inputText string) {
|
||||
session, _ := splitFlowOwner(owner)
|
||||
if session == "" || owner == "" || !p.allowed(session) {
|
||||
return
|
||||
}
|
||||
@@ -141,7 +170,7 @@ func (p *FlowPublisher) StartRound(owner, inputText string) {
|
||||
defer p.mu.Unlock()
|
||||
round := p.beginRoundLocked(session, owner)
|
||||
g := trace.NewRunGraph(session)
|
||||
g.SetTurnID(lane)
|
||||
g.SetTurnID(turnID)
|
||||
g.SetSeq(p.seq[session])
|
||||
g.Start(round, time.Now())
|
||||
g.SeedInput(inputText)
|
||||
|
||||
Reference in new issue
Block a user