任务 ID: task-embed-docker-e5c170  |  文件: task.md  |  最后修改: 2026-02-27 23:27:39

Task task-embed-docker-e5c170 — Embed Server Docker 代码库开发

文件路径

原始需求

开发一个极简的 embedding 服务 Docker 代码库,部署在 RS1000(AMD EPYC Zen4),供 LE-B 上的 OpenClaw 调用。


架构决策(已确定,不需重新讨论)

LE-B(OpenClaw 主机)              RS1000(embedding 服务器)
├── OpenClaw Gateway               ├── embed-server(Docker)
├── Chromium(62GB RAM)           │    └── Qwen3-Embedding-0.6B
└── memory_search ──────────────→  └── POST /v1/embeddings (+15ms RTT)

关键设计决策:
- embed server 只做向量生成,不做检索(检索由 OpenClaw 内置 sqlite-vec + BM25 hybrid 完成)
- 接口格式:OpenAI embeddings API 兼容(OpenClaw 原生支持)
- 模型从宿主机外部挂载(方便以后换更大的 Qwen3 模型)
- 镜像发布到线上 registry(ghcr.io 或 Docker Hub),方便迁移
- 监听 0.0.0.0:8765(需要从 LE-B 跨机访问,不能只绑 127.0.0.1)


技术规格(银狼实测确认)

运行环境(RS1000):
- CPU:AMD EPYC 9634,4 vCPU 虚拟机
- 支持:AVX-512 + AVX-512 BF16 + VNNI
- 内存:7.8GB 总计,可用约 5GB

最优推理配置(实测):

torch==2.7.0+cpu(纯 CPU 构建,非 CUDA build)
zentorch==5.1.0
sentence-transformers
OMP_NUM_THREADS=4
batch_size=8(建索引用)

推理代码(关键部分):

import torch
import zentorch  # noqa: F401 — 注册 zentorch backend
torch.set_num_threads(4)
from sentence_transformers import SentenceTransformer

model = SentenceTransformer(os.environ.get("MODEL_PATH", "/models/Qwen3-Embedding-0.6B"))
model[0].auto_model = torch.compile(
    model[0].auto_model,
    backend=zentorch.zentorch_compiler_noinductor  # 关键:绕过 inductor,走 ZENDNN/BLIS
)
# 启动时预热,吃掉 ~8s 编译时间,之后 healthcheck 才返回 ok
model.encode(["warmup"], normalize_embeddings=True)

为什么这样写(避坑):
- torch.compile(backend="inductor") → AVX-512 codegen bug,崩溃
- torch.compile(backend="zentorch") → 底层仍走 inductor,同样崩溃
- zentorch.optimize(model) → Qwen3 动态控制流无法 FX trace,报错
- ✅ 只有 backend=zentorch.zentorch_compiler_noinductor 有效
- PyTorch 必须用 2.7.0+cpu(CUDA build 在 CPU 模式有初始化开销,慢 2x)
- BF16 不推荐:单条短文本场景 cast overhead 抵消收益,反而慢 21%

性能基准:
- 单条 encode P50:40ms,P95:43ms
- 批量吞吐:25.5 条/秒(batch=8)
- 首次 zentorch 编译:~8s(一次性)


接口规格

必须实现(OpenClaw 调用格式):

POST /v1/embeddings
Content-Type: application/json

{
  "model": "Qwen3-Embedding-0.6B",  // 会原样传入,建议忽略,用挂载的模型
  "input": ["text1", "text2", ...]   // 字符串数组,单条查询也是数组
}

响应:
{
  "data": [
    {"embedding": [0.123, -0.456, ...]},  // float 数组,dim=1024
    {"embedding": [0.789, 0.012, ...]}
  ]
}

辅助端点:

GET /health
响应:{"status": "ok", "model_ready": true}
// 注意:模型预热完成前返回 {"status": "loading", "model_ready": false}

OpenClaw 配置(供参考,不需要实现):

agents.defaults.memorySearch: {
  "provider": "openai",
  "model": "Qwen3-Embedding-0.6B",
  "remote": {
    "baseUrl": "http://RS1000_IP:8765/v1",
    "apiKey": ""
  }
}

代码库结构

embed-server/
├── Dockerfile
├── docker-compose.yml
├── .dockerignore
├── server.py              # FastAPI 主程序
├── requirements.txt
└── README.md              # 部署说明

详细要求

Dockerfile

docker-compose.yml

server.py

requirements.txt

列出所有依赖及版本(torch 单独注释说明安装方式,不能通过 requirements.txt 直接安装 cpu-only 版)

README.md

包含:
1. 快速部署步骤(3步:clone → 放模型 → docker compose up)
2. 模型下载命令(huggingface-cli)
3. 换更大模型的方法(改 MODEL_PATH)
4. OpenClaw 配置示例
5. 测试命令(curl 测试 /health 和 /v1/embeddings)
6. 性能参数说明(RS1000 实测数据)


执行链

coder → reviewer → 爱衣质检


各 Agent 职责

Ai.Dev(session key: agent:coder:main

任务:按上述规格实现完整代码库,输出到 /root/.openclaw/workspace-coder/tasks/task-embed-docker-e5c170/

开始时:
1. 发工作日志:
bash /root/.openclaw/workspace/scripts/log-to-channel.sh coder receive "Embed Server Docker 开发" task-embed-docker-e5c170

完成后:
1. 将实现说明和关键决策追加到 session.md(/root/.openclaw/workspace/tasks/task-embed-docker-e5c170/session.md
2. 发工作日志:
bash /root/.openclaw/workspace/scripts/log-to-channel.sh coder handoff "Embed Server Docker 开发" reviewer task-embed-docker-e5c170
3. sessions_send 通知 reviewer(agent:reviewer:main必须传 timeoutSeconds=0,禁止省略):
task_id=task-embed-docker-e5c170 task=/root/.openclaw/workspace/tasks/task-embed-docker-e5c170/task.md code_path=/root/.openclaw/workspace-coder/tasks/task-embed-docker-e5c170/


Ai.Rev(session key: agent:reviewer:main

任务:审查 coder 产出的代码库,重点检查:
1. zentorch_compiler_noinductor 使用方式是否正确(参考 task.md 避坑说明)
2. /v1/embeddings 响应格式是否符合 OpenClaw 期望
3. 预热逻辑是否在 healthcheck 前完成
4. Dockerfile layer 顺序是否合理(torch 单独一层)
5. docker-compose 内存限制、healthcheck 配置是否合理
6. README 部署步骤是否清晰可执行

开始时:
1. 发工作日志:
bash /root/.openclaw/workspace/scripts/log-to-channel.sh reviewer receive "Embed Server Docker 开发" task-embed-docker-e5c170

完成后:
1. 将审查报告追加到 session.md
2. 发工作日志:
bash /root/.openclaw/workspace/scripts/log-to-channel.sh reviewer handoff "Embed Server Docker 开发" main task-embed-docker-e5c170
3. sessions_send 通知爱衣(agent:main:main必须传 timeoutSeconds=0,禁止省略):
task_id=task-embed-docker-e5c170 task=/root/.openclaw/workspace/tasks/task-embed-docker-e5c170/task.md


⚠️ 爱衣质检 SOP

Step 1:评估工作区大小

du -sb /root/.openclaw/workspace/tasks/task-embed-docker-e5c170/

Step 2:读取 session.md

wc -l /root/.openclaw/workspace/tasks/task-embed-docker-e5c170/session.md

Step 3:质检要点

Step 4:输出结论

通过
1. 将代码库复制到正式路径:
bash cp -r /root/.openclaw/workspace-coder/tasks/task-embed-docker-e5c170/ \ /root/.openclaw/workspace/projects/embed-server/
2. 发工作日志:
bash /root/.openclaw/workspace/scripts/log-to-channel.sh main done "Embed Server Docker 开发" task-embed-docker-e5c170
3. message 主人(telegram, 92763607),附代码库路径和关键内容摘要

不通过(rejectCount == 0)
1. 在 session.md 末尾追加 rejectCount=1
2. sessions_send 给 coder 说明问题,重新开发
3. 发工作日志:
bash /root/.openclaw/workspace/scripts/log-to-channel.sh main retry "Embed Server Docker 开发" "coder → reviewer → main" coder 1 task-embed-docker-e5c170

rejectCount >= 1
1. 发工作日志:
bash /root/.openclaw/workspace/scripts/log-to-channel.sh main fail "Embed Server Docker 开发" task-embed-docker-e5c170
2. message 主人请裁决