From 95f517d61dcd1510e49475cbf124d1396dd903e5 Mon Sep 17 00:00:00 2001 From: AskaEth Date: Tue, 13 Oct 2026 21:01:54 +0800 Subject: [PATCH] =?UTF-8?q?refactor(embedding):=20=E5=88=A0=E6=8E=89?= =?UTF-8?q?=E7=94=9F=E4=BA=A7=E4=B8=8D=E5=8F=AF=E8=BE=BE=E7=9A=84=E5=86=B7?= =?UTF-8?q?=E5=90=AF=E5=8A=A8=20WARN=20=E5=8F=8A=E5=85=B6=20logger=20?= =?UTF-8?q?=E7=BC=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit review round2 的 p2:`api.go` 那句「一次性冷启动放宽被花掉」的 WARN 在生产里 **不可能被人看见** —— 本包默认丢弃日志器,`WithLogger` 全树零调用者,生产走 `embedding.New(cfg)` 不带任何选项。而它的注释写着「这是唯一能审计一次性放宽的 地方」,与实际不符(本项目规则:注释不得说与实现不符的话)。 - 删该 WARN(失败并非静默:四个调用方本来就把错误原文写进各自的 WARN, 路径见 review 召回 / 整理召回 / 向量索引写入 / 检索)。 - 连带删掉 `WithLogger` 与 `log` 字段:WARN 是 `e.log` 的唯一使用者,留着它会 变成一个**空转的公开选项 + 又一次与实现不符的注释**(与上一轮删 `WithRequestTimeout` 同一条理由:不留没人用且未测的缝)。 - `callTimeout` 不再返回 `relaxed`(它只为那条 WARN 存在)。 - `NewAPI` 的注释改为把「本包不记日志、失败沿错误返回」写成**实话**。 行为零变化(生产本就是丢弃日志器),只删死代码与不实注释。 --- internal/runtime/embedding/api.go | 42 ++++++++----------------------- 1 file changed, 10 insertions(+), 32 deletions(-) diff --git a/internal/runtime/embedding/api.go b/internal/runtime/embedding/api.go index 41bde16..9986e7b 100644 --- a/internal/runtime/embedding/api.go +++ b/internal/runtime/embedding/api.go @@ -7,7 +7,6 @@ import ( "errors" "fmt" "io" - "log/slog" "net/http" "strings" "sync" @@ -94,7 +93,6 @@ var ErrNoAPIURL = errors.New("embedding: api url is empty") type APIEmbedder struct { cfg config.EmbeddingConfig client *http.Client - log *slog.Logger // warmupLeft 是**一次性**冷启动放宽的开关:为真时下一个请求用 warmupTimeout, // 取走即置假(CompareAndSwap ⇒ 并发下也只有一个请求能拿到)。 // 它**不是**「未成功就一直放宽」—— 失败也照样花掉这一次,因此服务挂死时 @@ -127,24 +125,17 @@ func WithRoundTripper(rt http.RoundTripper) APIOption { } } -// WithLogger sets the logger used for embed failures. A nil logger is ignored -// and the default discard logger is kept. -func WithLogger(l *slog.Logger) APIOption { - return func(e *APIEmbedder) { - if l != nil { - e.log = l - } - } -} - // NewAPI builds an APIEmbedder from cfg using the default HTTP client. -// 单次超时不可由选项更换:它是 budget.go 那条推演的输入,批次上限与它同源, -// 因此**只允许改常量**(改了之后批次上限自动跟着改,测试的编译期/常量断言立刻复核)。 +// +// 本包**不记日志**(没有任何 logger 缝):失败一律沿错误返回,由四个调用方 +// (审查召回 / 整理召回 / 向量索引写入 / 检索)各自带 source 记账 —— 它们本来 +// 就会把错误原文写进各自的 WARN,因此这里再记一条只会是重复的第二份。 +// 单次超时也不可由选项更换:它是 budget.go 那条推演的输入,批次上限与它同源, +// 因此**只允许改常量**(改了之后批次上限自动跟着改,测试的常量断言立刻复核)。 func NewAPI(cfg config.EmbeddingConfig, opts ...APIOption) *APIEmbedder { e := &APIEmbedder{ cfg: cfg, client: defaultHTTPClient(), - log: slog.New(slog.NewTextHandler(io.Discard, nil)), } // 冷启动放宽是一个**一次性**许可:见 APIEmbedder.warmupLeft。 e.warmupLeft.Store(true) @@ -200,21 +191,18 @@ func (e *APIEmbedder) Embed(ctx context.Context, texts []string) ([]Vector, erro // (成功、失败都算花掉)。因此: // - 本进程的第一个请求最多等 warmupTimeout(冷启动), // - 从第二个请求起一律 DefaultRequestTimeout —— 服务挂死时不会每次都慢。 -// -// 返回值 relaxed 表示这一次是不是拿到了放宽,供失败时留痕。 -func (e *APIEmbedder) callTimeout() (timeout time.Duration, relaxed bool) { +func (e *APIEmbedder) callTimeout() time.Duration { if e.warmupLeft.CompareAndSwap(true, false) { - return warmupTimeout, true + return warmupTimeout } - return DefaultRequestTimeout, false + return DefaultRequestTimeout } // embedChunk 发**一次** /embeddings 请求。chunk 必须已经落在 batchLimitsFor 推出来 // 的边界内(Embed 负责切),因此它的耗时上界不超过单次超时的一半。 func (e *APIEmbedder) embedChunk(ctx context.Context, texts []string) ([]Vector, error) { // 先取超时(取走即花掉那次放宽),再建 ctx:放宽许可与这次请求一一对应。 - timeout, relaxed := e.callTimeout() - callCtx, cancel := context.WithTimeout(ctx, timeout) + callCtx, cancel := context.WithTimeout(ctx, e.callTimeout()) defer cancel() req, err := e.newEmbeddingRequest(callCtx, texts) @@ -224,16 +212,6 @@ func (e *APIEmbedder) embedChunk(ctx context.Context, texts []string) ([]Vector, resp, err := e.client.Do(req) if err != nil { - // 冷启动放宽被花掉且没成功时留一条 WARN:它是「本进程第一次请求」这个 - // 判断唯一能被事后复核的地方(此后一切失败都按常态超时快速失败)。 - // 只记两个预算与错误(错误文本里已带端点,不另记 URL,避免重复落一条 - // 可能含 userinfo 的地址)。 - if relaxed { - e.log.Warn("embedding: one-shot cold-start allowance spent without a successful response", - "warmup_timeout", warmupTimeout.String(), - "steady_timeout", DefaultRequestTimeout.String(), - "error", err) - } return nil, fmt.Errorf("embedding: request: %w", err) } defer func() { _ = resp.Body.Close() }()