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
@@ -66,10 +66,12 @@ type FlowGraph struct {
|
||||
Edges []FlowEdge `json:"edges"`
|
||||
// SessionID 是拥有这张图的会话(帧壳与落盘行的归属坐标)。
|
||||
SessionID string `json:"sessionId,omitempty"`
|
||||
// TurnID 是这张图所属**路**的权威回合标识(design/15 §2.5.9):真并行时
|
||||
// 同一会话有多条路,每条路各有自己的图与本键。**缺席**语义:这条路没有
|
||||
// 权威标识(单路的历史帧、未接编排器的调用方、旧后端)——客户端据此把图
|
||||
// 归到会话那一张(兼容口径),绝不臆造一个 id。
|
||||
// TurnID 是这张图所属**路**的权威回合标识(design/15 §2.5.9):编排器给**每一路**
|
||||
// 都发(普通单路回合、队首续跑路、真并行路都有),因此它就是这张图属于哪一回合的
|
||||
// 答案,也是落盘 `flow_graphs.lane_turn_id` 与 `chat_messages.turn_id` 的同值。
|
||||
// **缺席**语义:这条路没有权威标识(未接编排器的调用方、后台思考、旧后端)——
|
||||
// 客户端据此把图归到会话那一张(兼容口径),读侧据此让该轮的 `toolCalls` 整体
|
||||
// 缺席;两侧都**绝不**臆造一个 id。
|
||||
TurnID string `json:"turnId,omitempty"`
|
||||
// Round 是这张图所属的**会话轮次号**(每会话单调递增;同一轮的各条路共享它)。
|
||||
// 客户端据此只保留最新一轮的泳道,避免旧轮的路留在页面上。
|
||||
@@ -105,7 +107,10 @@ type RunGraph struct {
|
||||
// 未经 SetSeq 建图的调用方(图构建器单测)保留 `{会话}-{轮次}` 的老形状。
|
||||
seq int
|
||||
// turnID 是本图所属**路**的权威回合标识(design/15 §2.5.9)。空串表示这条路
|
||||
// 没有权威标识(单路 / 未接编排器 / 旧调用方),此时图上该键缺席。
|
||||
// 没有权威标识(未接编排器 / 旧调用方),此时图上该键缺席。它有一个可见后果:
|
||||
// 图 id 的后缀取它(见 graphID),因此有权威标识的单路图也不再是 `{会话}-{序号}`
|
||||
// 而是 `{会话}-{序号}#{回合}` —— 落盘唯一键 `(session_id, turn_id)` 因此跨进程
|
||||
// 不撞(序号只在进程内递增、重启归零,而回合标识每回合全新)。
|
||||
turnID string
|
||||
nodes map[string]*FlowNode
|
||||
order []string
|
||||
@@ -151,7 +156,7 @@ func (g *RunGraph) Start(round int, now time.Time) {
|
||||
}
|
||||
|
||||
// SetTurnID 盖上这张图所属**路**的权威回合标识(design/15 §2.5.9)。空串是
|
||||
// 合法入参(单路 / 未接编排器),此时快照里该键缺席(omitempty),客户端回落
|
||||
// 合法入参(未接编排器的调用方),此时快照里该键缺席(omitempty),客户端回落
|
||||
// 到「按会话归一张图」的兼容口径。
|
||||
func (g *RunGraph) SetTurnID(turnID string) { g.turnID = turnID }
|
||||
|
||||
@@ -195,9 +200,11 @@ func (g *RunGraph) SeedOutput(text string) string {
|
||||
const laneIDSep = "#"
|
||||
|
||||
// Snapshot returns a copy of the graph safe to encode and publish. 图 id 是
|
||||
// `{会话}-{轮次}`;这张图属于一条**路**时追加 `#{路标识}` —— 真并行下同一会话
|
||||
// 同一轮的两路必须各有各的 id,否则落盘(`flow_graphs` 的 `(session_id, turn_id)`
|
||||
// 唯一键)会把其中一路整行顶掉、客户端也分不出两张图(design/15 §2.5.10-1)。
|
||||
// `{会话}-{轮次}`;这张图属于一条**有权威回合标识的路**时追加 `#{回合标识}` ——
|
||||
// 真并行下同一会话同一轮的两路必须各有各的 id,否则落盘(`flow_graphs` 的
|
||||
// `(session_id, turn_id)` 唯一键)会把其中一路整行顶掉、客户端也分不出两张图
|
||||
// (design/15 §2.5.10-1);单路(编排器也给它盖了权威标识)同样带后缀,因此它的
|
||||
// 图 id 跨进程不撞(见 RunGraph.turnID)。
|
||||
func (g *RunGraph) Snapshot() *FlowGraph {
|
||||
nodes := make([]FlowNode, 0, len(g.order))
|
||||
for _, id := range g.order {
|
||||
@@ -215,9 +222,10 @@ func (g *RunGraph) Snapshot() *FlowGraph {
|
||||
}
|
||||
}
|
||||
|
||||
// graphID 拼出这张图的 id:`{会话}-{序号}[#{路标识}]`。序号取会话级建图序号
|
||||
// graphID 拼出这张图的 id:`{会话}-{序号}[#{权威回合标识}]`。序号取会话级建图序号
|
||||
// (未设置时退回轮次号,见 RunGraph.seq),因此**同一轮里的每一条路也各有各的 id**;
|
||||
// 路标识只在「这条路有权威标识」时追加。
|
||||
// 回合标识只在「这条路有权威标识」时追加(单路在有 ctx 通道时也有 —— 见
|
||||
// loop.FlowPublisher.StartRoundWithTurnID)。
|
||||
func (g *RunGraph) graphID() string {
|
||||
n := g.seq
|
||||
if n == 0 {
|
||||
|
||||
Reference in new issue
Block a user