trading skills: 文档同步 (单币上限规则/平仓盈亏字段/静默规则/推送模板)
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
# 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
|
||||
```
|
||||
Reference in New Issue
Block a user