zqf b1f93588f1 初始化:记忆系统源代码上传(已脱敏)
- 排除 .env / *.bak / 内部运维文档(README_INTERNAL.html)
- config.py 默认密码已替换为占位符 CHANGE_ME_*
- init_db.sql 移除生产数据库用户 GRANT 段
- README.html 数据库用户名已脱敏
- 保留:源码 + 公网 API 文档 + 建表 SQL(无授权语句)
2026-08-03 04:53:48 +08:00

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.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>多智能体记忆系统 — 内部运维文档</title>
<style>
:root { --bg: #0d1117; --card: #161b22; --border: #30363d; --text: #c9d1d9; --heading: #58a6ff; --accent: #3fb950; --warn: #d29922; --code-bg: #0d1117; }
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: var(--bg); color: var(--text); line-height: 1.7; padding: 2rem; max-width: 960px; margin: 0 auto; }
h1 { color: var(--heading); margin-bottom: 0.5rem; font-size: 1.8rem; }
h2 { color: var(--heading); margin: 2.5rem 0 0.5rem; border-bottom: 1px solid var(--border); padding-bottom: 0.3rem; font-size: 1.3rem; }
h3 { color: var(--accent); margin: 1.5rem 0 0.3rem; font-size: 1.1rem; }
.meta { color: #8b949e; margin-bottom: 2rem; font-size: 0.9rem; }
table { width: 100%; border-collapse: collapse; margin: 1rem 0; }
th, td { text-align: left; padding: 0.5rem 0.75rem; border: 1px solid var(--border); }
th { background: var(--card); color: var(--heading); font-weight: 600; }
td { background: var(--card); }
code { font-family: 'Fira Code', monospace; background: var(--code-bg); border: 1px solid var(--border); padding: 0.15rem 0.4rem; border-radius: 4px; font-size: 0.85rem; color: var(--accent); }
pre { background: var(--code-bg); border: 1px solid var(--border); border-radius: 6px; padding: 1rem; overflow-x: auto; margin: 0.75rem 0; font-size: 0.85rem; line-height: 1.5; }
pre code { border: none; padding: 0; background: none; color: var(--text); }
.method { display: inline-block; padding: 0.15rem 0.5rem; border-radius: 4px; font-weight: bold; font-size: 0.8rem; margin-right: 0.5rem; }
.get { background: #1f6feb33; color: #58a6ff; }
.post { background: #3fb95033; color: #3fb950; }
.put { background: #d2992233; color: #d29922; }
.delete { background: #f8514933; color: #f85149; }
.endpoint { font-family: monospace; color: var(--text); }
.warn-box { background: #d2992222; border: 1px solid var(--warn); border-radius: 6px; padding: 1rem; margin: 1rem 0; }
.ok-box { background: #3fb95022; border: 1px solid var(--accent); border-radius: 6px; padding: 1rem; margin: 1rem 0; }
.danger-box { background: #f8514922; border: 1px solid #f85149; border-radius: 6px; padding: 1rem; margin: 1rem 0; }
ul, ol { margin: 0.5rem 0 0.5rem 1.5rem; }
li { margin: 0.25rem 0; }
a { color: var(--heading); }
.tag { display: inline-block; padding: 0.1rem 0.4rem; border-radius: 3px; font-size: 0.75rem; margin-left: 0.3rem; }
.tag-new { background: #3fb95033; color: #3fb950; }
.tag-auth { background: #d2992233; color: #d29922; }
</style>
</head>
<body>

<h1>🧠 多智能体记忆系统 — 内部运维文档</h1>
<p class="meta">
  服务器: 43.156.57.141 (新加坡腾讯云) &nbsp;|&nbsp;
  域名: <a href="https://memory.lsz.name">memory.lsz.name</a> &nbsp;|&nbsp;
  版本: 3.0.2 &nbsp;|&nbsp;
  最后更新: 2026-06-22
</p>

<div class="ok-box">
  <strong>✅ 系统状态:</strong> 所有组件运行正常<br>
  MySQL 8.0.46 · Redis 6.0.16 · BGE-small-zh-v1.5 (512维) · Gunicorn + Flask · Nginx + SSL (Let's Encrypt)<br>
  2 个团队 · 4 个 Agent · 32+ 个人记忆 · 4 条团队记忆 · v3.0.2 性能优化(LLM thinking mode 禁用)
</div>

<h2>🏗️ 架构概览</h2>
<table>
<tr><th>组件</th><th>技术</th><th>端口</th><th>说明</th></tr>
<tr><td>API 服务</td><td>Flask + Gunicorn</td><td>8081 (local)</td><td>HTTP JSON API</td></tr>
<tr><td>Nginx</td><td>nginx 1.18</td><td>443 (SSL)</td><td>反向代理 + SSL 终止</td></tr>
<tr><td>持久存储</td><td>MySQL 8.0.46</td><td>3306</td><td>记忆数据 + 向量 BLOB</td></tr>
<tr><td>缓存层</td><td>Redis 6.0.16</td><td>6379</td><td>工作记忆 (TTL 24h)</td></tr>
<tr><td>Embedding</td><td>BGE-small-zh-v1.5</td><td>8080 (local)</td><td>512维语义向量</td></tr>
</table>

<h2>🔐 API 认证 <span class="tag tag-auth">HMAC-SHA256</span></h2>
<p>除 <code>GET /health</code> 和 <code>/admin/*</code> 外,所有 API 端点均需以下 HTTP 头:</p>
<table>
<tr><th>Header</th><th>说明</th></tr>
<tr><td><code>X-API-Key</code></td><td>API Key(以 <code>msk_</code> 前缀开头)</td></tr>
<tr><td><code>X-Timestamp</code></td><td>Unix 时间戳(秒,5分钟内有效)</td></tr>
<tr><td><code>X-Signature</code></td><td><code>HMAC-SHA256(secret, timestamp + request_body)</code></td></tr>
</table>

<h3>认证流程</h3>
<ol>
  <li>获取 API Key 和 Secret(通过 <code>/admin/api-keys</code> 端点,仅限 localhost</li>
  <li>构造请求体(JSON),取当前 Unix 时间戳</li>
  <li>计算签名:<code>signature = HMAC-SHA256(secret, str(timestamp) + json_body)</code></li>
  <li>在请求头中附加三个认证字段</li>
</ol>

<h3>Python 认证示例</h3>
<pre><code>import time, hmac, hashlib, json, requests

API_KEY = "msk_xxx..."
SECRET = "xxx..."
BASE = "https://memory.lsz.name"

def api(method, path, body=None):
    ts = str(time.time())
    body_bytes = json.dumps(body).encode() if body else b""
    sig = hmac.new(
        SECRET.encode(), ts.encode() + body_bytes, hashlib.sha256
    ).hexdigest()
    headers = {
        "Content-Type": "application/json",
        "X-API-Key": API_KEY,
        "X-Timestamp": ts,
        "X-Signature": sig,
    }
    resp = requests.request(method, BASE + path, data=body_bytes, headers=headers)
    return resp.json()

# 使用示例
result = api("POST", "/memories/personal/search", {
    "agent_id": "yihui",
    "query": "策略回测",
    "limit": 5
})</code></pre>

<div class="warn-box">
  <strong>⚠️ 注意:</strong> Secret 仅在创建时显示一次,不会再次展示。请妥善保管。<br>
  Admin 端点仅限 <code>127.0.0.1</code> 访问,外部请求会被拒绝(403)。
</div>

<h2>👥 多团队隔离</h2>
<p>所有记忆数据通过 <code>team_id</code> 字段实现硬隔离。查询时强制 <code>WHERE team_id = ?</code>。每个团队的 API Key 绑定固定的 <code>team_id</code>,跨团队数据完全不可见。</p>

<h3>当前团队</h3>
<table>
<tr><th>Team ID</th><th>名称</th><th>Agent 数</th><th>API Key</th></tr>
<tr><td><code>hermes1</code></td><td>策略研究部</td><td>2 (弈回, 云策)</td><td><code>msk_a192...85</code></td></tr>
<tr><td><code>wangcheng</code></td><td>王成</td><td>2 (Hermes Agent, OpenClaw Main)</td><td><code>msk_6804...18</code></td></tr>
</table>

<h2>📋 记忆类型</h2>
<table>
<tr><th>类型</th><th>存储</th><th>生命周期</th><th>说明</th></tr>
<tr><td>个人长期记忆</td><td>MySQL</td><td>持久化</td><td>每个 Agent 独立的持久记忆,支持语义检索</td></tr>
<tr><td>个人工作记忆</td><td>Redis List</td><td>TTL 24h</td><td>短期上下文缓存</td></tr>
<tr><td>团队共享记忆</td><td>MySQL</td><td>持久化</td><td>同团队内所有 Agent 可访问的知识库</td></tr>
</table>

<h2>📡 API 端点</h2>

<h3>公开端点(无需认证)</h3>
<table>
<tr><th>方法</th><th>路径</th><th>说明</th></tr>
<tr><td><span class="method get">GET</span></td><td class="endpoint">/health</td><td>健康检查</td></tr>
</table>

<h3>团队管理</h3>
<table>
<tr><th>方法</th><th>路径</th><th>说明</th></tr>
<tr><td><span class="method post">POST</span></td><td class="endpoint">/teams</td><td>创建团队</td></tr>
<tr><td><span class="method get">GET</span></td><td class="endpoint">/teams/{team_id}</td><td>获取团队信息</td></tr>
<tr><td><span class="method delete">DELETE</span></td><td class="endpoint">/teams/{team_id}</td><td>删除团队(级联删除所有关联数据)</td></tr>
</table>

<h3>Agent 管理</h3>
<table>
<tr><th>方法</th><th>路径</th><th>说明</th></tr>
<tr><td><span class="method post">POST</span></td><td class="endpoint">/agents</td><td>创建 Agent</td></tr>
<tr><td><span class="method get">GET</span></td><td class="endpoint">/agents/{agent_id}</td><td>获取 Agent 信息</td></tr>
</table>

<h3>个人记忆</h3>
<table>
<tr><th>方法</th><th>路径</th><th>说明</th></tr>
<tr><td><span class="method post">POST</span></td><td class="endpoint">/memories/personal</td><td>添加个人记忆(自动生成 embedding</td></tr>
<tr><td><span class="method post">POST</span></td><td class="endpoint">/memories/personal/search</td><td>语义检索个人记忆(团队隔离)</td></tr>
<tr><td><span class="method get">GET</span></td><td class="endpoint">/memories/personal/recent/{agent_id}</td><td>获取最近个人记忆</td></tr>
<tr><td><span class="method get">GET</span></td><td class="endpoint">/memories/personal/{memory_id}</td><td>获取单条个人记忆</td></tr>
<tr><td><span class="method put">PUT</span></td><td class="endpoint">/memories/personal/{memory_id}</td><td>更新个人记忆</td></tr>
<tr><td><span class="method delete">DELETE</span></td><td class="endpoint">/memories/personal/{memory_id}</td><td>删除个人记忆</td></tr>
</table>

<h3>工作记忆</h3>
<table>
<tr><th>方法</th><th>路径</th><th>说明</th></tr>
<tr><td><span class="method post">POST</span></td><td class="endpoint">/memories/working</td><td>添加工作记忆(RedisTTL 24h</td></tr>
<tr><td><span class="method get">GET</span></td><td class="endpoint">/memories/working/{agent_id}</td><td>获取工作记忆</td></tr>
<tr><td><span class="method delete">DELETE</span></td><td class="endpoint">/memories/working/{agent_id}</td><td>清空工作记忆</td></tr>
</table>

<h3>团队记忆</h3>
<table>
<tr><th>方法</th><th>路径</th><th>说明</th></tr>
<tr><td><span class="method post">POST</span></td><td class="endpoint">/memories/team</td><td>添加团队共享记忆</td></tr>
<tr><td><span class="method post">POST</span></td><td class="endpoint">/memories/team/search</td><td>语义检索团队记忆</td></tr>
<tr><td><span class="method get">GET</span></td><td class="endpoint">/memories/team/recent/{team_id}</td><td>获取最近团队记忆</td></tr>
<tr><td><span class="method put">PUT</span></td><td class="endpoint">/memories/team/{memory_id}</td><td>更新团队记忆</td></tr>
<tr><td><span class="method delete">DELETE</span></td><td class="endpoint">/memories/team/{memory_id}</td><td>删除团队记忆</td></tr>
</table>

<h3>生命周期管理</h3>
<table>
<tr><th>方法</th><th>路径</th><th>说明</th></tr>
<tr><td><span class="method post">POST</span></td><td class="endpoint">/lifecycle/compress</td><td>压缩工作记忆到长期记忆</td></tr>
<tr><td><span class="method post">POST</span></td><td class="endpoint">/lifecycle/cleanup</td><td>清理过期/低重要性记忆</td></tr>
<tr><td><span class="method post">POST</span></td><td class="endpoint">/lifecycle/extract-facts</td><td>原子事实提取(LLM, 5-15s</td></tr>
<tr><td><span class="method post">POST</span></td><td class="endpoint">/lifecycle/aggregate-scenes</td><td>场景聚合(LLM, 5-15s</td></tr>
<tr><td><span class="method post">POST</span></td><td class="endpoint">/lifecycle/generate-persona</td><td>画像生成(LLM, 5-15s</td></tr>
<tr><td><span class="method post">POST</span></td><td class="endpoint">/lifecycle/auto-config</td><td>Pipeline 自动化配置</td></tr>
<tr><td><span class="method get">GET</span></td><td class="endpoint">/lifecycle/scenarios/{agent_id}</td><td>获取场景块</td></tr>
<tr><td><span class="method get">GET</span></td><td class="endpoint">/lifecycle/persona/{agent_id}</td><td>获取用户画像</td></tr>
</table>

<h3>统计</h3>
<table>
<tr><th>方法</th><th>路径</th><th>说明</th></tr>
<tr><td><span class="method get">GET</span></td><td class="endpoint">/stats?team_id=xxx</td><td>获取系统统计信息</td></tr>
</table>

<h3>Admin 端点 <span class="tag tag-new">仅 localhost</span></h3>
<table>
<tr><th>方法</th><th>路径</th><th>说明</th></tr>
<tr><td><span class="method post">POST</span></td><td class="endpoint">/admin/api-keys</td><td>创建 API Key(需 team_id, name</td></tr>
<tr><td><span class="method get">GET</span></td><td class="endpoint">/admin/api-keys</td><td>列出所有 API Key</td></tr>
<tr><td><span class="method delete">DELETE</span></td><td class="endpoint">/admin/api-keys/{key_id}</td><td>禁用 API Key</td></tr>
</table>

<h2>⚡ 性能指标(2026-06-08 验证)</h2>
<table>
<tr><th>指标</th><th>实测值</th><th>目标</th><th>状态</th></tr>
<tr><td>写入延迟 P50</td><td>14.4ms</td><td>—</td><td>✅</td></tr>
<tr><td>写入延迟 P99</td><td>16.3ms</td><td>&lt; 100ms</td><td>✅ PASS</td></tr>
<tr><td>检索延迟 P50</td><td>17.9ms</td><td>—</td><td>✅</td></tr>
<tr><td>检索延迟 P99</td><td>27.5ms</td><td>&lt; 50ms</td><td>✅ PASS</td></tr>
<tr><td>进程 RSS</td><td>51 MB</td><td>&lt; 1.5 GiB</td><td>✅ PASS</td></tr>
<tr><td>团队隔离</td><td>100%</td><td>100%</td><td>✅ PASS</td></tr>
</table>

<h2>🗄️ 数据库表结构</h2>
<table>
<tr><th>表名</th><th>说明</th></tr>
<tr><td><code>teams</code></td><td>团队信息(id, name, description, config JSON</td></tr>
<tr><td><code>agents</code></td><td>Agent 信息(id, team_id FK, name, role</td></tr>
<tr><td><code>personal_memories</code></td><td>个人长期记忆(agent_id, team_id, content, embedding BLOB, importance, metadata JSON</td></tr>
<tr><td><code>team_memories</code></td><td>团队共享记忆(team_id, content, embedding BLOB, importance, category, metadata JSON</td></tr>
<tr><td><code>api_keys</code></td><td>API 认证密钥(team_id, api_key, secret, is_active</td></tr>
<tr><td><code>pipeline_config</code></td><td>Pipeline 自动化配置(compress_every_n, cleanup_idle_days, warmup 等)</td></tr>
<tr><td><code>memory_scenarios</code></td><td>场景块(team_id, agent_id, name, summary, memory_ids JSON</td></tr>
<tr><td><code>user_personas</code></td><td>用户画像(team_id, agent_id, preferences/habits/expertise JSON, summary</td></tr>
</table>

<h2>🔧 运维操作</h2>

<h3>服务管理</h3>
<pre><code># 查看服务状态
sudo systemctl status memory-system

# 重启服务
sudo systemctl restart memory-system

# 查看日志
sudo journalctl -u memory-system -f --no-pager -n 100

# Nginx 配置测试
sudo nginx -t

# 重载 Nginx
sudo systemctl reload nginx</code></pre>

<h3>项目路径</h3>
<pre><code>/opt/memory-system/              # 项目根目录
/opt/memory-system/venv/         # Python 虚拟环境
/opt/memory-system/config.py     # 配置文件
/opt/memory-system/app.py        # Flask 入口 + JSON 序列化
/opt/memory-system/api/routes.py # API 路由
/opt/memory-system/auth/api_auth.py  # HMAC 认证中间件
/opt/memory-system/storage/      # MySQL + Redis + 向量检索
/opt/memory-system/lifecycle/    # 记忆生命周期管理
/opt/memory-system/tests/        # 测试
/opt/memory-system/docs/         # 公网文档</code></pre>

<h3>关键配置</h3>
<pre><code># config.py
MYSQL_HOST = "127.0.0.1"
MYSQL_PORT = 3306
MYSQL_USER = "your_db_user"
MYSQL_DATABASE = "memory_system"
MYSQL_UNIX_SOCKET = "/var/run/mysqld/mysqld.sock"
REDIS_HOST = "127.0.0.1"
REDIS_PORT = 6379
EMBEDDING_SERVICE_URL = "http://127.0.0.1:8080"
EMBEDDING_DIM = 512
API_PORT = 8081
WORKING_MEMORY_TTL = 86400  # 24小时</code></pre>

<h3>创建新 API KeySSH 到服务器)</h3>
<pre><code># 通过 localhost 访问 admin 端点
curl -X POST http://127.0.0.1:8081/admin/api-keys \
  -H 'Content-Type: application/json' \
  -d '{"team_id": "your_team_id", "name": "Your Key Name"}'

# 响应中会包含 api_key 和 secret(仅显示一次)</code></pre>

<h3>SSL 证书</h3>
<pre><code># 自动续期已配置,手动测试:
sudo certbot renew --dry-run

# 证书路径:
/etc/letsencrypt/live/memory.lsz.name/fullchain.pem
/etc/letsencrypt/live/memory.lsz.name/privkey.pem</code></pre>

<h2>🔒 安全说明</h2>
<ul>
  <li>Redis 已禁用 FLUSHALL/FLUSHDB/DEBUG/SHUTDOWN 命令</li>
  <li>Redis CONFIG/KEYS 命令已重命名(CONFIG_b9f7e2d4, KEYS_a3c8f1e6</li>
  <li>MySQL 使用 unix socket 连接</li>
  <li>Nginx 强制 HTTPS (Let's Encrypt)</li>
  <li>API 仅监听 127.0.0.1,通过 Nginx 暴露公网</li>
  <li>Admin 端点仅限 localhost 访问</li>
  <li>API 使用 HMAC-SHA256 签名认证,时间戳窗口 5 分钟</li>
</ul>

<h2>🧪 测试验证</h2>
<pre><code># 运行测试
cd /opt/memory-system
./venv/bin/python tests/test_memory.py</code>
<p>测试覆盖: 创建团队/Agent → 写入记忆 → 语义检索 → 跨团队隔离 → 工作记忆 → 性能基准</p>

<div class="ok-box">
<strong>✅ 2026-06-08 验证结果:</strong><br>
14/14 项测试全部通过,包括:<br>
• 健康检查 · 统计端点 · 团队信息查询<br>
• 个人记忆语义检索 · 团队记忆语义检索<br>
• 写入+搜索+更新+删除全流程<br>
• 跨团队隔离验证(wangcheng 看不到 hermes1 数据)<br>
• 工作记忆 CRUD · 认证拒绝测试
</div>


<h2>📋 变更日志</h2>

<h3>v3.0.2 (2026-06-22) — 性能优化</h3>
<div class="ok-box">
<strong>核心修复</strong>:禁用 DeepSeek thinking mode<br>
根因:DeepSeek v4-flash 默认开启思考模式,每次 LLM 调用输出 3000-7500 字符中文推理链,token 预算被吃光<br>
修复:llm_client.py 新增 thinking 参数透传;llm_parse.py 所有 JSON 生成任务添加 thinking={"type": "disabled"}<br>
<strong>实测提速</strong>aggregate-scenes 7.9x, generate-persona 4.9x, extract-facts 2.0s
</div>

<h3>v3.0.1-bugfix (2026-06-22) — Bug 修复</h3>
<div class="ok-box">
• BGE 长文本双保险截断(服务端 512 tokens + 客户端 2000 字符)<br>
• 空 query fallback 到 get_recent<br>
• embedding 失败返回友好错误,不暴露内部 URL<br>
• GET /memories/personal/{id} 405 → 补上 GET 路由<br>
• aggregate-scenes JSON 解析失败 → thinking mode 禁用
</div>

<h3>v3.0.0 (2026-06-08) — 生命周期管理</h3>
<div class="ok-box">
• Pipeline 自动化引擎(自动压缩、清理、暖机)<br>
• 原子事实提取(/lifecycle/extract-facts<br>
• 场景聚合(/lifecycle/aggregate-scenes<br>
• 用户画像生成(/lifecycle/generate-persona
</div>

</body>
</html>
S
Description
智能记忆系统 (Memory System) - 团队/Agent 长期记忆、场景聚合、画像生成
Readme
119 KiB
Languages
Python 100%