Files
my-vault/01_Projects/AI-Development/Obsidian Agent/vault-memory/refactor-plan.md
T

274 lines
7.7 KiB
Markdown
Raw Normal View History

2026-02-26 21:10:47 +08:00
---
title: 重构计划
date: 2026-02-26
based-on: "[[review-2026-02-26]]"
status: 待执行
---
# 重构计划
基于 [[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` 条数应下降。
```python
# 删除这两行
'api_key',
'access_key',
```
---
### P1-2 修复 ingest_vault.py — stale 清理排除 failed_ids
**文件**`ingest_vault.py` 第 88 行
**问题**:C1,embed 失败的文档在下次全量重建时被删除
**改动**:一行修改
```python
# 修改前
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 调用,处理完后删除临时文件
```bash
# 修改前
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 后必须重建索引,验收隔离率是否恢复正常:
```bash
# 清空现有数据
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.py``upsert_file()` 函数
**问题**:C2,每次 M 事件都调 APIP95=1.4s 的直接原因
**改动**:在调用 `embed_text` 前查询现有 hash,匹配则跳过
```python
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()` 入口加目录过滤
```python
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(更适合小数据集)
```sql
-- 方案 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,同时修正用法提示
```bash
# 修改前
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(余弦距离,越小越相似)
```python
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.sql``blacklist.py``index_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 失败不再删除已有向量 |