Files
Cyrene_v2/internal/runtime/loop/spanlog.go
T
AskaEth 91e8d7a029 docs(runtime): internal/runtime/loop 44 个文件注释改中文(逐行 1:1,代码零字节改动)
主人 2026-10-03 拍板「注释一律中文」;本模块按长期分批协议(一个模块一次派工)执行。
注释行数 1329 → 1329 逐行 1:1(含中文 33 → 1304);行尾注释 5 条一并译。
校验:机械校验(非注释改动行)= 空;独立 Go AST 程序(剥注释复渲)证明 44/44 文件代码全等;
全树 go test ./... 58 包 ok / 0 FAIL;交叉编译绿。
gofmt:capacity_loop_test.go / flow.go 改动前即未格式化,按铁律保留原样未动。
2026-10-03 22:28:21 +08:00

121 lines
3.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package loop
import (
"sort"
"time"
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/agent"
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/trace"
)
// 记录在 span 上的日志事件类型,使 flow 图的节点 log[] 展示
// 每一步发生了什么(design/19 §1 节点实时日志)。仅当该步真正
// 完成时才追加事件,因此日志绝不宣告未运行的工作。
const (
// EventLLMDone 在模型调用返回后记录到 llm_call span 上。
EventLLMDone = "llm.done"
// EventToolDone 在工具调用落定后记录到 tool_call span 上。
EventToolDone = "tool.done"
)
// maxLogSummaryRunes 在发布前限制日志取值的长度。图在
// 输出时还会再次脱敏与截断;这里只是让单个 span 保持小。
const maxLogSummaryRunes = 240
// recordLLMDone 把一次已完成模型调用的脱敏摘要追加到其
// span:服务该 turn 的模型、两个 stop reason、请求了多少工具调用
// 以及该调用耗时。不记录任何提示词或补全文本,
// 因此 llm 节点的日志绝不携带对话内容。ag 是该 turn 的 agent
// (按角色池条目),因此多角色进程上报的是实际
// 服务该调用的模型,而不是默认角色的模型。
func (l *Loop) recordLLMDone(span *trace.Span, out *stepOutcome, ag *agent.Agent) {
if span == nil || out == nil {
return
}
props := map[string]any{
"stop_reason": out.stopReason,
"tool_calls": len(out.toolCalls),
"elapsed_ms": elapsedMS(span),
}
if m := modelName(out, ag); m != "" {
props["model"] = m
}
if out.modelStopReason != "" {
props["model_stop_reason"] = out.modelStopReason
}
_ = span.Record(trace.Event{Type: EventLLMDone, Props: props})
}
// recordToolDone 把一次工具调用的脱敏摘要追加到其 span:工具
// 名、是否成功、参数名(绝不记录其值)、失败时的
// 错误种类,以及截断后的结果摘要。参数值被
// 刻意省略 —— 工具参数正是凭据、消息正文
// 或含机密的文件路径会出现的地方 —— 且这里记录的一切
// 在发布前都会由图再次清洗。
func (l *Loop) recordToolDone(span *trace.Span, tc agent.ToolCall, out ToolOutcome) {
if span == nil {
return
}
props := map[string]any{
"tool": tc.Name,
"ok": out.Err == nil,
"args_keys": argKeys(tc.Args),
"elapsed_ms": elapsedMS(span),
}
if out.Err != nil {
if out.ErrKind != "" {
props["error_kind"] = out.ErrKind
}
props["error"] = trimSummary(out.Err.Error())
} else {
props["result_summary"] = trimSummary(formatToolResult(out.Result))
}
_ = span.Record(trace.Event{Type: EventToolDone, Props: props})
}
// modelName 报告服务某步的模型:流式路径知道时用路由出的
// 请求模型,否则用该 turn 的 agent 配置的模型。两者
// 都不可用时为空。
func modelName(out *stepOutcome, ag *agent.Agent) string {
if out.model != "" {
return out.model
}
if ag == nil {
return ""
}
return ag.Model()
}
// elapsedMS 报告 span 迄今已打开多久。span 的 Duration 仅由
// Finish 打上,因此在 Finish 之前记录的日志事件在此测量它。
func elapsedMS(span *trace.Span) int64 {
if span == nil || span.StartedAt.IsZero() {
return 0
}
return time.Since(span.StartedAt).Milliseconds()
}
// argKeys 按排序返回工具调用的参数名。只记录
// 参数名;值留在 trace 之外(见 recordToolDone)。
func argKeys(args map[string]any) []string {
if len(args) == 0 {
return nil
}
keys := make([]string, 0, len(args))
for k := range args {
keys = append(keys, k)
}
sort.Strings(keys)
return keys
}
// trimSummary 把日志取值缩短到 maxLogSummaryRunes 个 rune,
// 且不切开多字节 rune。
func trimSummary(s string) string {
r := []rune(s)
if len(r) <= maxLogSummaryRunes {
return s
}
return string(r[:maxLogSummaryRunes]) + "…"
}