docs(comments): 注释中文化 · internal/runtime/memory(31 文件,纯替换)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

- 只翻注释/文档字符串:820 增 / 820 删 严格 1:1;剥掉整行注释与行尾注释尾巴后,
  逐文件与 HEAD 比对「代码部分差异 = 0」;裁决/design 编号计数逐项与 HEAD 一致
- 保留可执行 SQL 样例、SQLite 报错原文、pgvector/HNSW/ANN/BLOB 等技术词原文
- 复核:go build 绿 + memory/projectindex/vector 三包测试绿;gofmt 仅剩 2 个既有未格式化文件(改动前即如此,未顺手修)
This commit is contained in:
AskaEth committed 2026-10-04 13:32:54 +08:00
1 parent eb1828ee7a
commit 61cf80d5ee
31 files changed
+820 -820

No files matched your search

+55 -55
View File
@@ -1,22 +1,22 @@
// Package vector implements dense-vector similarity search for Cyrene's
// long-term memory layer (DevDocs/design/03-记忆.md §3).
// vector 包为 Cyrene 的长期记忆层(DevDocs/design/03-记忆.md §3)
// 实现稠密向量相似度检索。
//
// It exposes an Index abstraction over two storage backends:
// 它在两种存储后端之上暴露 Index 抽象:
//
// - SQLiteIndex (the default): vectors are stored as BLOBs in a SQLite
// vec_items table and similarity is computed in Go as cosine similarity via
// a linear scan over the persisted vectors. This is the zero-extra-
// dependency path used when DB_PG_URL is empty.
// - PGVectorIndex (optional enhancement): uses PostgreSQL + pgvector, storing
// vectors in a vector column and answering similarity with the cosine-
// distance operator <=> in SQL. It is selected only when DB_PG_URL is set;
// when it is, semantic search is genuinely backed by pgvector and does not
// silently fall back to keyword matching.
// - SQLiteIndex(默认):向量以 BLOB 存在 SQLite 的
// vec_items 表中,相似度在 Go 侧通过对已持久化向量
// 线性扫描计算余弦相似度。这是 DB_PG_URL 为空时使用的
// 零额外依赖路径。
// - PGVectorIndex(可选增强):使用 PostgreSQL + pgvector,把
// 向量存入 vector 列,并用 SQL 中的余弦
// 距离运算符 <=> 作答相似度。仅当设置了 DB_PG_URL 时才选中;
// 此时语义检索真正由 pgvector 支撑,不会
// 静默退化为关键词匹配。
//
// Both backends are linear-scan for milestone M4 (small datasets, no ANN
// training). The PostgreSQL path may later be upgraded to an HNSW index as data
// grows; the Search SQL is unchanged. Vectors are produced by an
// embedding.Embedder wired in via New.
// 两个后端在 M4 里程碑都是线性扫描(数据集小,不做 ANN
// 训练)。随着数据增长,PostgreSQL 路径日后可升级为 HNSW 索引;
// Search SQL 不变。向量由经 New 接入的
// embedding.Embedder 产出。
package vector
import (
@@ -33,22 +33,22 @@ import (
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/embedding"
)
// Vector is a dense embedding: one float32 per dimension.
// Vector 是一个稠密向量:每维一个 float32。
type Vector []float32
// Hit is a single similarity result: a stored id and its similarity score.
// Higher Score means more similar, so results sort in descending Score order.
// Hit 是一条相似度结果:一个已存 id 及其相似度得分。
// Score 越高越相似,因此结果按 Score 降序排列。
type Hit struct {
ID string
Score float64
}
// Index is a dense-vector similarity index over stored (id, vector) pairs.
// Index 是建立在已存 (id, vector) 对上的稠密向量相似度索引。
//
// Embed turns a batch of texts into vectors using the wired embedder. Add
// persists an (id, vector) pair, upserting on a repeated id. Search returns the
// topK ids most similar to q, ranked by descending similarity. Dim reports the
// fixed vector dimension. Close releases the underlying database.
// Embed 用接入的 embedder 把一批文本转成向量。Add
// 持久化一个 (id, vector) 对,id 重复时 upsert。Search 返回与 q
// 最相似的前 topK 个 id,按相似度降序排列。Dim 报告
// 固定向量维度。Close 释放底层数据库。
type Index interface {
Embed(ctx context.Context, texts []string) ([]Vector, error)
Add(ctx context.Context, id string, v Vector) error
@@ -57,51 +57,51 @@ type Index interface {
Close() error
}
// Package-level sentinel errors. Each is wrapped with %w at the call site so
// callers can use errors.Is.
// 包级哨兵错误。每个都在调用点用 %w 包裹,调用方
// 可用 errors.Is 判断。
var (
// ErrNoEmbedder is returned when an index is asked to Embed but no embedder
// has been wired in (e.g. a constructor called directly rather than via New).
// ErrNoEmbedder 在索引被要求 Embed 却没有接入 embedder
// 时返回(例如直接调用构造函数而非经 New)。
ErrNoEmbedder = errors.New("memory/vector: no embedder configured")
// ErrNilDB is returned when a constructor receives a nil *sql.DB.
// ErrNilDB 在构造函数收到 nil *sql.DB 时返回。
ErrNilDB = errors.New("memory/vector: nil db")
// ErrInvalidDim is returned when a dimension is not positive.
// ErrInvalidDim 在维度非正时返回。
ErrInvalidDim = errors.New("memory/vector: dimension must be positive")
// ErrDimMismatch is returned when an added or queried vector's length does
// not match the index dimension.
// ErrDimMismatch 在新增或查询的向量长度与
// 索引维度不匹配时返回。
ErrDimMismatch = errors.New("memory/vector: vector dimension mismatch")
// ErrMissingID is returned when an id is blank.
// ErrMissingID 在 id 为空时返回。
ErrMissingID = errors.New("memory/vector: id is required")
// ErrInvalidPGURL is returned when the PostgreSQL URL is blank.
// ErrInvalidPGURL 在 PostgreSQL URL 为空时返回。
ErrInvalidPGURL = errors.New("memory/vector: invalid postgres url")
)
// New builds the Index selected by cfg: PGVectorIndex when cfg.PGURL is set,
// otherwise SQLiteIndex. embedder must be non-nil and report a positive Dim();
// it is wired into the returned index so Index.Embed produces vectors with it.
// log may be nil; logs are tagged with source "memory/vector".
// New 构建 cfg 选定的 Index:设置了 cfg.PGURL 时为 PGVectorIndex,
// 否则为 SQLiteIndex。embedder 必须非 nil 且 Dim() 为正;
// 它被接入返回的索引,使 Index.Embed 用它产向量。
// log 可为 nil;日志带 source "memory/vector" 标签。
//
// SQLiteIndex is created on a connection opened at cfg.SQLitePath (":memory:"
// for an isolated test database or a file path). PGVectorIndex connects to
// cfg.PGURL with the pgx driver and requires a PostgreSQL server with the
// pgvector extension installed.
// SQLiteIndex 建立在 cfg.SQLitePath 打开的连接上(":memory:"
// 用于隔离的测试数据库,或一个文件路径)。PGVectorIndex 用
// pgx 驱动连接 cfg.PGURL,要求已安装
// pgvector 扩展的 PostgreSQL 服务。
//
// New performs one-time setup with a background context and has no ctx
// parameter by design; every subsequent operation accepts a context.Cancel.
// New 用后台 context 做一次性准备,按设计不接收 ctx
// 参数;后续每个操作都接受 context.Cancel。
func New(cfg config.DBConfig, embedder embedding.Embedder, log *slog.Logger) (Index, error) {
if embedder == nil {
return nil, ErrNoEmbedder
}
dim := embedder.Dim()
if dim <= 0 {
// API-backed embedders do not know their vector dimension until the first
// successful response. Probe once so the index is built with the real
// dimension instead of erroring out and falling back to in-memory.
// 走 API 的 embedder 在首次成功响应前不知道自己的向量维度。
// 探测一次,使索引以真实
// 维度构建,而不是报错并回退到内存内实现。
if _, err := embedder.Embed(context.Background(), []string{"probe"}); err != nil {
return nil, fmt.Errorf("memory/vector: probe embedder dim: %w", err)
}
@@ -118,8 +118,8 @@ func New(cfg config.DBConfig, embedder embedding.Embedder, log *slog.Logger) (In
idx.withEmbedder(embedder)
return idx, nil
}
// db.Open logs internally; hand it a sourced logger so its records carry
// provenance, while the raw log goes to NewSQLite which sources it itself.
// db.Open 内部会记日志;交给它一个带来源的 logger,使其记录携带
// 出处,而原始 log 传给 NewSQLite,由后者自行加来源。
d, err := db.Open(context.Background(), cfg.SQLitePath, sourcedLogger(log))
if err != nil {
return nil, fmt.Errorf("memory/vector: open sqlite: %w", err)
@@ -133,16 +133,16 @@ func New(cfg config.DBConfig, embedder embedding.Embedder, log *slog.Logger) (In
return idx, nil
}
// usePG reports whether cfg should select the PostgreSQL+pgvector backend.
// Surrounding whitespace counts as unset.
// usePG 报告 cfg 是否应选择 PostgreSQL+pgvector 后端。
// 首尾空白视为未设置。
func usePG(pgURL string) bool {
return strings.TrimSpace(pgURL) != ""
}
// sourcedLogger returns log, or a discard logger when nil, tagged with source
// "memory/vector" so every record carries provenance. It is idempotent in
// output: re-applying the same source value on an already-sourced logger yields
// the same emitted attribute.
// sourcedLogger 返回 log,nil 时返回丢弃型 logger,并打上 source
// "memory/vector" 标签,使每条记录都携带出处。它在
// 输出上幂等:对已带来源的 logger 再次施加相同 source 值,
// 得到的输出属性相同。
func sourcedLogger(log *slog.Logger) *slog.Logger {
if log == nil {
log = slog.New(slog.NewTextHandler(io.Discard, nil))