docs(cyrene): cmd/cyrene 存量英文注释中文化(22 个非测试文件,仅注释)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

- 四组并行完成:main/wire*.go、wire_capacity|channels|runtime、channels|persona_*|emotion|observability、
  voice|plugins|stream|skills|vision|subagent_tool|extractor|mcp|sessionread_tool|terminal_tool。
- 机械校验(git diff -U0 去 // 行与空行)仅剩 wire_frontend.go:84 一处行尾注释:代码前缀
  「\ta.gw = gw 」经 cat -A 与 HEAD 逐字节比对一致(allow 清单已声明)。
- 验收:隔离 worktree(HEAD + 本批 patch)go build/vet 绿、全树 58 包 ok、
  linux/amd64 + darwin/arm64 交叉编译绿;gofmt -l 干净。
- 备注:整树两次红均为负载敏感 flake(backthink / orchestration 的 timeout 类用例单独 -count=20 全过)。
This commit is contained in:
AskaEth committed 2026-10-04 16:58:06 +08:00
1 parent 5c2189efb0
commit 52f08dc343
22 files changed
+455 -586

No files matched your search

+28 -34
View File
@@ -10,20 +10,18 @@ import (
"git.yeij.top/AskaEth/Cyrene_v2/internal/services/gateway"
)
// newAppTraceWriter builds the composition root's trace writer: the JSONL stderr
// recorder (the local structured log of every span) plus room for the gateway's
// observability sink, which can only be registered once the gateway exists (see
// wireGatewayTraceSink).
// newAppTraceWriter 构建组合根的 trace writer:JSONL stderr 记录器(每个 span 的
// 本地结构化日志),外加为 gateway 可观测性出口预留的位置 —— 后者只能在 gateway
// 存在之后注册(见 wireGatewayTraceSink)。
func newAppTraceWriter() *traceFanout {
return newTraceFanout(trace.NewJSONRecorder(os.Stderr))
}
// wireGatewayTraceSink registers the gateway's observability sink on the app's
// trace fanout. It is called from wireFrontend — the first moment the gateway
// exists — because the gateway is built after the loop that emits the spans.
// Without it the admin trace ring buffer is never fed and
// GET /api/v1/admin/observability/trace answers an empty list forever
// (design/19 §2). It is a no-op on a boot without a tracer or gateway.
// wireGatewayTraceSink 把 gateway 的可观测性出口注册到应用的 trace 扇出上。它由
// wireFrontend 调用 —— 那是 gateway 首次存在的时刻 —— 因为 gateway 构建在发出 span
// 的循环之后。没有它,管理面板的 trace 环形缓冲区永远不会被喂数据,
// GET /api/v1/admin/observability/trace 会永远返回空列表(design/19 §2)。在没有
// tracer 或 gateway 的启动中是空操作。
func wireGatewayTraceSink(a *app, gw *gateway.Server) {
if a.traceWriter == nil || gw == nil {
return
@@ -32,20 +30,18 @@ func wireGatewayTraceSink(a *app, gw *gateway.Server) {
a.log.Info("observability trace sink wired")
}
// traceFanout is the composition root's trace.Writer. It fans every completed
// span out to the sinks installed at boot (the JSONL stderr recorder) plus any
// sink registered later in the boot sequence: the gateway's observability buffer
// only exists once wireFrontend has built the gateway, while the loop that emits
// the spans is built earlier in wireRuntime.
// traceFanout 是组合根的 trace.Writer。它把每个已完成的 span 扇出到启动时安装的
// 出口(JSONL stderr 记录器),以及启动流程中稍后注册的出口:gateway 的可观测性
// 缓冲区要等 wireFrontend 构建好 gateway 才存在,而发出 span 的循环在 wireRuntime
// 中更早就构建了。
//
// It is safe for concurrent use — the loop finishes spans from several
// goroutines (per-step, per-tool-call, sub-agents).
// 它可安全并发使用 —— 循环会从多个 goroutine 结束 span(按步、按工具调用、子代理)。
type traceFanout struct {
mu sync.RWMutex
writers []trace.Writer
}
// newTraceFanout builds a fanout over the given writers. Nil writers are dropped.
// newTraceFanout 在给定 writers 之上构建扇出。nil writer 会被丢弃。
func newTraceFanout(writers ...trace.Writer) *traceFanout {
f := &traceFanout{}
for _, w := range writers {
@@ -54,7 +50,7 @@ func newTraceFanout(writers ...trace.Writer) *traceFanout {
return f
}
// Add installs one more sink. A nil writer is ignored.
// Add 再安装一个出口。nil writer 被忽略。
func (f *traceFanout) Add(w trace.Writer) {
if w == nil {
return
@@ -64,9 +60,9 @@ func (f *traceFanout) Add(w trace.Writer) {
f.writers = append(f.writers, w)
}
// WriteSpan writes rec to every sink and returns the first error. Every sink is
// tried even after a failure, so one broken sink cannot hide a span from the
// others; the tracer only logs the returned error (trace.Tracer.emit).
// WriteSpan 把 rec 写给每个出口并返回第一个错误。即使失败也仍会尝试所有出口,
// 因此单个坏掉的出口无法把 span 从其他出口那里藏起来;tracer 只记录返回的错误
// (trace.Tracer.emit)。
func (f *traceFanout) WriteSpan(rec trace.SpanRecord) error {
f.mu.RLock()
defer f.mu.RUnlock()
@@ -82,12 +78,11 @@ func (f *traceFanout) WriteSpan(rec trace.SpanRecord) error {
return firstErr
}
// wireGatewayLogBuffer hands the composition root's log ring buffer to the
// gateway, so GET /api/v1/admin/observability/logs serves the recent backend log
// tail. The logger is built before the gateway (its records must be captured
// from the first log line), so the buffer is injected here, the first moment the
// gateway exists — like wireGatewayTraceSink does for spans. It is a no-op on a
// boot without a gateway.
// wireGatewayLogBuffer 把组合根的日志环形缓冲区交给 gateway,使
// GET /api/v1/admin/observability/logs 能提供最近的后端日志尾部。logger 构建在
// gateway 之前(其记录必须从第一行日志起就被捕获),因此缓冲区在这里注入 ——
// gateway 首次存在的时刻 —— 与 wireGatewayTraceSink 对 span 的做法一致。在没有
// gateway 的启动中是空操作。
func wireGatewayLogBuffer(a *app, buf *logger.RingBuffer) {
if a == nil || a.gw == nil || buf == nil {
return
@@ -96,14 +91,13 @@ func wireGatewayLogBuffer(a *app, buf *logger.RingBuffer) {
a.log.Info("observability log buffer wired", "capacity", buf.Len())
}
// gatewaySpanSink adapts *gateway.Server to trace.Writer through its ObserveSpan
// entry point, so an emitted span lands in the admin panel's trace ring buffer
// AND in the LLM/subagent metric counters (ObserveSpan appends to the buffer and
// observes the metrics; the buffer is what GET /api/v1/admin/observability/trace
// serves).
// gatewaySpanSink 通过 *gateway.Server 的 ObserveSpan 入口把它适配成 trace.Writer,
// 使发出的 span 落在管理面板的 trace 环形缓冲区**并且**计入 LLM/子代理指标计数器
// (ObserveSpan 既追加到缓冲区,也观测指标;缓冲区正是
// GET /api/v1/admin/observability/trace 所提供的内容)。
type gatewaySpanSink struct{ srv *gateway.Server }
// WriteSpan feeds rec to the gateway. A missing server is a no-op.
// WriteSpan 把 rec 喂给 gateway。server 缺失时是空操作。
func (g gatewaySpanSink) WriteSpan(rec trace.SpanRecord) error {
if g.srv == nil {
return nil