Files
Hermes-Skills/okx-auto-position/references/qdrant-memory-provider-integration-2026-08-13.md

164 lines
5.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Qdrant Memory Provider 接入 + 时间衰减 (2026-08-13)
## 接入状态
| # | 项 | 状态 |
|---|---|---|
| 1 | `config.yaml` 启用 `memory.provider: qdrant` | ✅ |
| 2 | `hermes memory status` 显示 Provider: qdrant | ✅ |
| 3 | 容器 `hermes-qdrant` 运行中 (7+ 天) | ✅ |
| 4 | 向量库 263 points | ✅ |
| 5 | 同步 cron `e9278f724fc8` 每小时运行 | ✅ |
| 6 | 新 session 自动 prefetch top-3 → system prompt | ✅ |
| 7 | `qdrant_recall` 工具暴露 | ✅ |
| 8 | **时间衰减** (indexed_at + recency_half_life_days) | ✅ 2026-08-13 新增 |
## 架构
```
~/.hermes/hermes-agent/
plugins/memory/qdrant/__init__.py ← QdrantProvider (prefetch + recall)
agent/agent_init.py line 1360 ← load_memory_provider() 自动加载
~/.hermes/config.yaml ← memory.provider: qdrant
~/.hermes/qdrant/
index_memory.py ← 写入侧: chunk_text() 加 indexed_at
sync_memory.sh ← cron wrapper: MEMORY.md/USER.md/git history
```
## ⚠️ qdrant_client 1.18 API 破坏性变更 (2026-08-13 实测)
**问题**: `qdrant_client 1.18` 移除了 `client.search()` 方法,改用 `client.query_points()`。
**错误信息**:
```
AttributeError: 'QdrantClient' object has no attribute 'search'
```
**修复** (`plugins/memory/qdrant/__init__.py` `_query()`):
```python
# 旧 (qdrant_client < 1.18):
results = self._client.search(
collection_name=self._collection,
query_vector=query_vector,
limit=top_k,
score_threshold=min_score,
with_payload=True,
)
# 新 (qdrant_client 1.18+):
from qdrant_client.models import ExactCountQuery, ScrollQuery
results = self._client.query_points(
collection_name=self._collection,
query=ExactCountQuery(**{"exact": False}),
query_vector=query_vector,
limit=top_k,
score_threshold=min_score,
with_payload=True,
).points
```
**验证**: `python3 -c "from qdrant_client import QdrantClient; print(hasattr(QdrantClient, 'search'), hasattr(QdrantClient, 'query_points'))"` → `False True`
**Server 版本**: qdrant server 1.13, client 1.18, minor 差 5 → 有警告但功能正常:
```
Qdrant client version 1.18.0 is incompatible with server version 1.13.0.
Major versions should match and minor version difference must not exceed 1.
```
## 时间衰减机制 (2026-08-13 新增)
### 写入侧: `~/.hermes/qdrant/index_memory.py`
`chunk_text()` 每个 chunk 加 `indexed_at` 字段:
```python
indexed_at = datetime.now(timezone.utc).isoformat()
# 每个 chunk payload: {text, source, file, indexed_at, fp}
```
### 查询侧: `plugins/memory/qdrant/__init__.py`
`prefetch()` 里 `_recency_boost()` 对结果重排:
```python
def _recency_boost(self, results, half_life_days=60):
"""按 indexed_at 时间衰减, 新笔记优先"""
for r in results:
age_days = (now - indexed_at).days
decay = 0.5 ** (age_days / half_life_days) # 60天前半衰
r['score'] = r['score'] * decay
# 按 combined_score 降序重排 → 截 top_k
```
### 衰减效果 (实测)
| 笔记 | 原始 score | 年龄 | 衰减后 |
|---|---|---|---|
| 2026-08-13 新笔记 (今天, indexed_at 有) | 0.65 | 0 天 ×1.0 | **0.65** |
| 2026-08-04 旧笔记 (无 indexed_at) | 0.65 | ~1 年 | **0.19** (×0.3) |
结果: 旧笔记自动沉底,新笔记冲到 top。
### 配置项 (`config.yaml`)
```yaml
memory:
provider: qdrant
qdrant:
host: 127.0.0.1
port: 6333
collection: hermes_memory
embed_model: BAAI/bge-small-zh-v1.5
top_k: 3
min_score: 0.3
enabled: true
recency_half_life_days: 60 # 新增: 半衰期(天)
overfetch: 4 # 新增: 多召回倍数(给重排留余地)
```
## BOM + 截断 UTF-8 兼容 (2026-08-13)
**问题**: `MEMORY.md` 有 UTF-8 BOM (0xEF 0xBB 0xBF),且末尾截断(最后字节 0xBB 不完整)。
**报错**:
```
UnicodeDecodeError: 'utf-8' codec can't decode bytes in position 1758-1759: unexpected end of data
```
**修复** (`~/.hermes/qdrant/index_memory.py` `index_file()`):
```python
try:
text = file_path.read_text(encoding='utf-8-sig', errors='ignore')
except Exception:
text = file_path.read_text(encoding='utf-8', errors='ignore')
```
## git 提交
| 仓库 | 提交 | 内容 |
|---|---|---|
| `~/.hermes/qdrant/` (新 init) | `ae090fe` | index_memory.py (indexed_at + UTF-8 容错) + sync_memory.sh + docker-compose.yml |
| `~/.hermes/hermes-agent/` (fork) | `e969ec498d` | plugins/memory/qdrant/__init__.py (时间衰减 + overfetch + query_points 修复) |
## 验证命令
```bash
# 1. provider 状态
hermes memory status
# 2. prefetch 召回测试 (新 session)
hermes chat -q "lottery Qi Nq 期号到底用哪个"
# 3. 衰减验证 (Python)
cd ~/.hermes/hermes-agent && PYTHONPATH=. venv/bin/python -c "
from plugins.memory.qdrant import QdrantProvider
p = QdrantProvider({'host':'127.0.0.1','port':6333,'collection':'hermes_memory','top_k':3,'min_score':0.2,'recency_half_life_days':60,'overfetch':6})
p.initialize('test')
print(p.prefetch('六合 cron Nq 期号', session_id='test'))
"
# 4. is_lock_expired 验证 (锁不过期)
cd ~/.hermes/skills/trading/okx-auto-position/scripts
python3 -c "from signal_tracker import is_lock_expired; print(is_lock_expired(None), is_lock_expired({}))"
# 输出: False False
```