feat(ws): tool_execution.end 带可选 resultSummary(Y1,答应安卓端的那条)
- 形状:data.resultSummary(可选)。与回看面 toolCalls[].resultSummary 代码级同源:同一个常量
(500 rune)与同一个截断函数,摘要由 **loop 侧**生成(trace.RedactText + renderToolResultSummary)
再随 StreamEvent 下发,gateway 只搬运不加工(与回看面同口径)
- 缺席语义:结果真拿到才带;失败/取消/超时/未落图 ⇒ 整键缺席(绝不用空串顶位)
- 线上帧证据:真 WS + 真 JWT + 真 loop ⇒ 客户端收到 resultSummary;同源钉用真图快照反读逐字比对
- 3 个变异(空摘要也写键/去掉脱敏/失败当结果)
⚠️ 待裁:失败路径现按派工书「整键缺席」,但回看面对失败给的是 error 摘要 ⇒ 实时无结果区、事后有;
若要实时也显示失败原因,一行可改(toolEndFrameSummary 的 return "")
This commit is contained in:
1 parent
a820d9968e
commit
215eedf8e9
6 files changed
+433
-7
No files matched your search
@@ -298,7 +298,7 @@ func (l *Loop) invokeOne(ctx context.Context, d *tool.Dispatcher, tc agent.ToolC
|
||||
return ToolOutcome{Err: fmt.Errorf("no tool dispatcher configured for %q", tc.Name), ErrKind: tool.KindExecution}
|
||||
}
|
||||
_, toolSpan := l.span(ctx, "tool."+tc.Name, trace.SpanKindTool)
|
||||
l.emitToolExecution(ctx, tc, model.StreamKindToolExecutionStart, model.ToolPhaseRunning)
|
||||
l.emitToolExecution(ctx, tc, model.StreamKindToolExecutionStart, model.ToolPhaseRunning, "")
|
||||
// 路标识桥(design/15 §2.5.7、主人裁决 3):网关在**每一条在途路**上把互斥
|
||||
// 身份经 loop.WithToolLegID 挂在 ctx 上(普通回合 / 并行路 / 队首续跑路都挂,
|
||||
// 取值 = 该路的 turnID),这里把它桥接成工具层的 tool.WithLegID —— 工具侧
|
||||
@@ -313,7 +313,7 @@ func (l *Loop) invokeOne(ctx context.Context, d *tool.Dispatcher, tc agent.ToolC
|
||||
if err != nil {
|
||||
phase = model.ToolPhaseError
|
||||
}
|
||||
l.emitToolExecution(ctx, tc, model.StreamKindToolExecutionEnd, phase)
|
||||
l.emitToolExecution(ctx, tc, model.StreamKindToolExecutionEnd, phase, toolEndFrameSummary(result, err))
|
||||
out := ToolOutcome{Result: result, Err: err, ErrKind: errorKind(err)}
|
||||
l.spanProp(toolSpan, "phase", phase)
|
||||
l.recordToolDone(toolSpan, tc, out)
|
||||
@@ -331,13 +331,13 @@ func (l *Loop) invokeOne(ctx context.Context, d *tool.Dispatcher, tc agent.ToolC
|
||||
// tool_execution 三事件(start / update / end)转发给前端;否则为
|
||||
// 空操作(被静音的内部流程也会跳过它)。phase 是结构化字段
|
||||
// (running/done/error),绝不塞进 Delta;工具名走 Tool 字段,
|
||||
// ID 复用 provider 的 tool call id,使执行帧能与其 tool_call.* 帧
|
||||
// 关联。
|
||||
func (l *Loop) emitToolExecution(ctx context.Context, tc agent.ToolCall, kind model.StreamKind, phase string) {
|
||||
// ID 复用 provider 的 tool call id,使执行帧能与其 tool_call.* 帧关联;
|
||||
// resultSummary 只在 end 帧、且只在结果真正拿到时非空(口径见 tool_frame_summary.go)。
|
||||
func (l *Loop) emitToolExecution(ctx context.Context, tc agent.ToolCall, kind model.StreamKind, phase, resultSummary string) {
|
||||
if l.onStream == nil || isMutedBroadcast(ctx) {
|
||||
return
|
||||
}
|
||||
ev := model.StreamEvent{Kind: kind, Phase: phase, Tool: tc.Name, ID: tc.ID}
|
||||
ev := model.StreamEvent{Kind: kind, Phase: phase, Tool: tc.Name, ID: tc.ID, ResultSummary: resultSummary}
|
||||
ev.SessionID = loopSession(ctx)
|
||||
ev.TurnID = loopTurn(ctx)
|
||||
l.onStream(ev)
|
||||
|
||||
Reference in new issue
Block a user