Files

7.7 KiB
Raw Permalink Blame History

title, date, based-on, status
title date based-on status
重构计划 2026-02-26 review-2026-02-26 待执行

重构计划

基于 review-2026-02-26 的审查结果,按优先级分三个阶段执行。


Phase 1 · 数据安全与正确性(Critical)

目标:消除静默数据损坏和误隔离问题。不改变接口,不影响现有功能。

P1-1 修复 blacklist.py — 删除过宽字面量

文件blacklist.py 问题C449% 误隔离率 改动:从 SENSITIVE_LITERAL_MARKERS 删除 'api_key''access_key',这两个已被第 33 行的正则覆盖(要求后跟赋值符号)。 验收:重建索引后 memory_primary 条数应显著增加,memory_secure_audit 条数应下降。

# 删除这两行
'api_key',
'access_key',

P1-2 修复 ingest_vault.py — stale 清理排除 failed_ids

文件ingest_vault.py 第 88 行 问题:C1,embed 失败的文档在下次全量重建时被删除 改动:一行修改

# 修改前
stale_primary_ids = sorted(db_primary_ids - valid_ids)

# 修改后
stale_primary_ids = sorted(db_primary_ids - valid_ids - set(failed_ids))

P1-3 修复 install-hook.sh — 清理 changes 临时文件

文件install-hook.sh(以及 vault 里已安装的 .git/hooks/post-commit 问题:C3,每次 commit 留下临时文件 改动:用子 shell 包装 nohup 调用,处理完后删除临时文件

# 修改前
nohup uv run --project "$vault_dir/.scripts/memory" python \
  "$vault_dir/.scripts/memory/incremental_ingest.py" \
  --changes-file "$changes_file" >> "$log_file" 2>&1 &

# 修改后
nohup bash -c "uv run --project '$vault_dir/.scripts/memory' python \
  '$vault_dir/.scripts/memory/incremental_ingest.py' \
  --changes-file '$changes_file' >> '$log_file' 2>&1; \
  rm -f '$changes_file'" &

修改后需重新运行 install-hook.sh 更新已安装的 hook。


P1-4 重建全量索引验收

完成 P1-1 后必须重建索引,验收隔离率是否恢复正常:

# 清空现有数据
psql $PG_DSN -c "TRUNCATE memory_primary, memory_secure_audit;"

# 重建
uv run python ingest_vault.py

# 检查结果
psql $PG_DSN -c "SELECT count(*) FROM memory_primary;"
psql $PG_DSN -c "SELECT count(*) FROM memory_secure_audit;"

预期:memory_primary 应接近 81+memory_secure_audit 应大幅下降。


Phase 2 · 性能与一致性(Important

目标:修复 P95 延迟超标问题,统一两个 ingest 脚本的行为。

P2-1 增量索引加入 hash 检查

文件incremental_ingest.pyupsert_file() 函数 问题:C2,每次 M 事件都调 APIP95=1.4s 的直接原因 改动:在调用 embed_text 前查询现有 hash,匹配则跳过

def upsert_file(cur, rel_path: str) -> None:
    # ... 现有的 excluded / pruned 检查 ...

    text = abs_path.read_text(encoding='utf-8', errors='ignore')
    new_hash = sha256_text(text)

    # 新增:hash 未变则跳过 embed
    cur.execute('SELECT content_hash FROM memory_primary WHERE id=%s', (rel_path,))
    row = cur.fetchone()
    if row and row[0] == new_hash:
        print(f'[SKIPPED] {rel_path}')
        return

    # 继续 embed ...

预期效果:纯格式调整或无关文件的 commit 不再触发 API 调用,P95 应降至网络延迟本身(~0.3-0.5s)。


P2-2 统一两个 ingest 脚本的目录范围

文件incremental_ingest.py 问题:I2,增量索引处理任意目录,全量索引只处理 PRIMARY_DIRS 改动:在 upsert_file() 入口加目录过滤

PRIMARY_DIRS = {'01_Projects', '02_Areas'}  # 与 ingest_vault.py 保持一致

def upsert_file(cur, rel_path: str) -> None:
    # 新增:只处理 PRIMARY_DIRS 内的文件
    top_dir = Path(rel_path).parts[0] if Path(rel_path).parts else ''
    if top_dir not in PRIMARY_DIRS:
        print(f'[SKIPPED-OUT-OF-SCOPE] {rel_path}')
        return
    # ... 其余逻辑不变 ...

P2-3 修复 ivfflat index lists 参数

文件schema.sql 问题I1lists=100 对 81 行数据无效 改动:重建索引时使用动态 lists 值,或改用 HNSW(更适合小数据集)

-- 方案 A:删除旧索引,改用 HNSW(推荐,无需调参)
DROP INDEX IF EXISTS memory_primary_embedding_idx;
CREATE INDEX memory_primary_embedding_idx
ON memory_primary USING hnsw (embedding vector_cosine_ops);

-- 方案 B:保留 ivfflat,修正 lists
DROP INDEX IF EXISTS memory_primary_embedding_idx;
CREATE INDEX memory_primary_embedding_idx
ON memory_primary USING ivfflat (embedding vector_cosine_ops) WITH (lists = 1);

推荐方案 A:HNSW 在小数据集上性能更好,且不需要手动维护 lists 参数。


P2-4 修复 agent-with-memory.sh 的 VAULT_DIR 推导

文件agent-with-memory.sh 问题:I4,路径推导假设脚本在特定目录层级 改动:从 .env.memory 读取 VAULT_DIR,同时修正用法提示

# 修改前
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
VAULT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"

# 修改后
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ENV_FILE="$SCRIPT_DIR/.env.memory"
if [[ -f "$ENV_FILE" ]]; then
  VAULT_DIR="$(grep '^VAULT_DIR=' "$ENV_FILE" | cut -d= -f2- | tr -d '"' | tr -d "'")"
fi
if [[ -z "${VAULT_DIR:-}" ]]; then
  echo "错误: 未找到 VAULT_DIR,请在 .env.memory 中配置" >&2
  exit 1
fi

P2-5 查询加入相似度阈值

文件query_pgvector.py 问题:I5,无关查询仍返回 top_k 结果污染上下文 改动:加 --threshold 参数,默认 0.5(余弦距离,越小越相似)

def query(text: str, top_k: int = 5, max_chars: int = 2500, threshold: float = 0.5) -> str:
    # ...
    cur.execute(
        '''
        SELECT source, content
        FROM memory_primary
        WHERE embedding <=> %s::vector < %s
        ORDER BY embedding <=> %s::vector
        LIMIT %s
        ''',
        (vector, threshold, vector, top_k),
    )

阈值 0.5 为初始值,需根据实际 Recall@5 评测结果调整。


Phase 3 · 可观测性与运营(Nice to have

目标:让系统状态可见,减少静默失败。

P3-1 audit 表记录触发原因

文件schema.sqlblacklist.pyindex_common.py 问题I6,无法区分隔离原因 改动is_excluded 返回触发原因字符串而非布尔值,audit 表 risk 列存具体规则名


P3-2 hook 失败时写入可见日志

文件install-hook.sh 问题I7,所有错误静默 改动:在 .memory-sync.log 里写入带时间戳的错误行,并在 hook 末尾检查日志最后一行是否为错误


P3-3 添加 health-check 脚本

新增 health-check.sh,检查:

  • PostgreSQL 是否可连接
  • memory_primary 条数是否 > 0
  • 最近一次 updated_at 是否在 24h 内
  • .env.memory 是否存在且关键字段非空

P3-4 日志轮转

文件install-hook.sh 问题O1.memory-sync.log 无限增长 改动:hook 里加简单的大小检查,超过 5MB 时截断旧内容


执行顺序

Phase 1(必须先做)
  P1-1 → P1-2 → P1-3 → P1-4(验收)

Phase 2P1 完成后)
  P2-1 → P2-2 → P2-3 → P2-4 → P2-5

Phase 3(可选,按需)
  P3-1 → P3-2 → P3-3 → P3-4

预期收益

问题 修复后预期
49% 误隔离 memory_primary 恢复到 ~140+ 文档
P95=1.4s hash 命中时降至 <0.1s,实际 embed 时降至 ~0.8sHNSW 加速)
临时文件积累 每次 commit 后自动清理
数据静默丢失 embed 失败不再删除已有向量