部署实施指南
技术研究 人工智能 LLM
必需依赖 - Bun = 1.0.0(JavaScript 运行时) - SQLite(macOS 需要 Homebrew 版本以支持扩展) - 磁盘空间 至少 5GB 空闲空间(用于模型下载和数据存储) - 内存 建议 4GB+(运行本地模型需要)
4.1 环境准备
系统要求
qmd 对硬件和软件环境有以下要求:
必需依赖:
- Bun >= 1.0.0(JavaScript 运行时)
- SQLite(macOS 需要 Homebrew 版本以支持扩展)
- 磁盘空间: 至少 5GB 空闲空间(用于模型下载和数据存储)
- 内存: 建议 4GB+(运行本地模型需要)
操作系统支持:
- macOS(推荐,需要
brew install sqlite) - Linux(主流发行版)
- Windows(通过 WSL2)
安装 Bun
# macOS / Linux
curl -fsSL https://bun.sh/install | bash
# 添加到 PATH
echo 'export PATH="$HOME/.bun/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
# 验证安装
bun --version
安装 qmd
# 全局安装 qmd
bun install -g https://github.com/tobi/qmd
# 验证安装
qmd --version
# 确保 PATH 包含 bun 的二进制目录
export PATH="$HOME/.bun/bin:$PATH"
4.2 首次配置
步骤 1: 创建知识库目录
# 创建知识库根目录
mkdir -p ~/agent-knowledge/{projects,meetings,standards,snippets,research}
# 创建示例文档
cat > ~/agent-knowledge/standards/coding-standards.md << 'EOF'
# 代码规范
## Python 规范
- 使用 PEP 8 风格
- 类型注解必需
- 异步代码使用 async/await
## 错误处理
- 使用 try/except/finally
- 记录所有异常到日志
- 不向用户暴露内部错误
EOF
步骤 2: 配置 qmd 集合
# 添加项目文档集合
qmd collection add ~/agent-knowledge/projects --name projects
qmd context add qmd://projects "项目文档、架构设计和技术规范"
# 添加会议纪要集合
qmd collection add ~/agent-knowledge/meetings --name meetings
qmd context add qmd://meetings "团队会议、决策记录和行动项"
# 添加规范集合
qmd collection add ~/agent-knowledge/standards --name standards
qmd context add qmd://standards "编码规范、API 设计规范和安全指南"
# 添加代码片段集合
qmd collection add ~/agent-knowledge/snippets --name snippets
qmd context add qmd://snippets "可复用代码片段和最佳实践示例"
# 查看配置状态
qmd status
步骤 3: 生成向量嵌入
首次运行会下载约 2GB 的模型文件(自动从 HuggingFace 下载):
# 生成所有文档的向量嵌入
qmd embed
# 如果后续有新增文档,重新嵌入
qmd embed -f
# 查看模型缓存位置
ls -la ~/.cache/qmd/models/
模型文件说明:
embeddinggemma-300M-Q8_0.gguf(~300MB): 用于生成向量嵌入qwen3-reranker-0.6b-q8_0.gguf(~640MB): 用于结果重排序qmd-query-expansion-1.7B-q4_k_m.gguf(~1.1GB): 用于查询扩展
4.3 Agent 机器人集成代码示例
Python 集成示例(使用 subprocess)
import subprocess
import json
from typing import List, Dict, Optional
class QMDClient:
"""qmd CLI 客户端封装"""
def __init__(self, collection: Optional[str] = None):
self.collection = collection
def search(self, query: str, n: int = 5) -> List[Dict]:
"""执行 BM25 关键词搜索"""
cmd = ["qmd", "search", query, "--json", "-n", str(n)]
if self.collection:
cmd.extend(["-c", self.collection])
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
raise RuntimeError(f"qmd search failed: {result.stderr}")
return json.loads(result.stdout)
def vsearch(self, query: str, n: int = 5) -> List[Dict]:
"""执行向量语义搜索"""
cmd = ["qmd", "vsearch", query, "--json", "-n", str(n)]
if self.collection:
cmd.extend(["-c", self.collection])
result = subprocess.run(cmd, capture_output=True, text=True)
return json.loads(result.stdout)
def query(self, query: str, n: int = 5, min_score: float = 0.3) -> List[Dict]:
"""执行混合搜索(推荐)"""
cmd = [
"qmd", "query", query,
"--json", "-n", str(n),
"--min-score", str(min_score)
]
if self.collection:
cmd.extend(["-c", self.collection])
result = subprocess.run(cmd, capture_output=True, text=True)
return json.loads(result.stdout)
def get_document(self, path: str, full_content: bool = True) -> str:
"""获取完整文档内容"""
cmd = ["qmd", "get", path]
if full_content:
cmd.append("--full")
result = subprocess.run(cmd, capture_output=True, text=True)
return result.stdout
def get_context_for_llm(self, query: str, max_tokens: int = 4000) -> str:
"""获取适合 LLM 上下文的检索结果"""
results = self.query(query, n=10, min_score=0.4)
context_parts = []
current_tokens = 0
for result in results:
doc_path = result.get("path", "")
doc_content = self.get_document(doc_path, full_content=False)
# 简单估算 tokens(实际应使用 tokenizer)
estimated_tokens = len(doc_content) / 4
if current_tokens + estimated_tokens > max_tokens:
break
context_parts.append(f"## {doc_path}\n{doc_content}\n")
current_tokens += estimated_tokens
return "\n".join(context_parts)
# 在 Agent 中使用示例
class AgentWithQMD:
def __init__(self):
self.qmd = QMDClient()
async def process_request(self, user_message: str) -> str:
# 判断是否需要知识检索
if self._needs_knowledge_retrieval(user_message):
# 从 qmd 获取相关上下文
context = self.qmd.get_context_for_llm(user_message)
# 构建精简的提示词
prompt = f"""
基于以下相关文档回答问题:
{context}
用户问题:{user_message}
请提供准确、简洁的回答。
"""
else:
prompt = user_message
# 调用 LLM(token 消耗大幅减少)
response = await self.call_llm(prompt)
return response
def _needs_knowledge_retrieval(self, message: str) -> bool:
"""判断是否需要知识检索"""
keywords = ["规范", "标准", "文档", "之前", "上次", "会议", "项目"]
return any(kw in message.lower() for kw in keywords)
Node.js/TypeScript 集成示例
import { exec } from 'child_process';
import { promisify } from 'util';
const execAsync = promisify(exec);
interface QMDResult {
path: string;
docid: string;
title: string;
score: number;
snippet: string;
}
class QMDClient {
constructor(private collection?: string) {}
async search(query: string, n: number = 5): Promise<QMDResult[]> {
let cmd = `qmd search "${query}" --json -n ${n}`;
if (this.collection) {
cmd += ` -c ${this.collection}`;
}
const { stdout } = await execAsync(cmd);
return JSON.parse(stdout);
}
async query(query: string, n: number = 5, minScore: number = 0.3): Promise<QMDResult[]> {
let cmd = `qmd query "${query}" --json -n ${n} --min-score ${minScore}`;
if (this.collection) {
cmd += ` -c ${this.collection}`;
}
const { stdout } = await execAsync(cmd);
return JSON.parse(stdout);
}
async getContextForLLM(query: string, maxTokens: number = 4000): Promise<string> {
const results = await this.query(query, 10, 0.4);
const contextParts: string[] = [];
let currentTokens = 0;
for (const result of results) {
const { stdout: content } = await execAsync(
`qmd get "${result.path}" --full`
);
const estimatedTokens = content.length / 4;
if (currentTokens + estimatedTokens > maxTokens) {
break;
}
contextParts.push(`## ${result.path}\n${content}\n`);
currentTokens += estimatedTokens;
}
return contextParts.join('\n');
}
}
export default QMDClient;
4.4 MCP 服务器配置(生产环境推荐)
Claude Code 配置
编辑 ~/.claude/settings.json:
{
"mcpServers": {
"qmd": {
"command": "qmd",
"args": ["mcp"]
}
}
}
自定义 Agent MCP 客户端配置
如果你的 Agent 使用 Python,可以使用 mcp 库:
# requirements.txt
# mcp>=1.0.0
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def use_qmd_mcp():
# 配置 MCP 服务器参数
server_params = StdioServerParameters(
command="qmd",
args=["mcp"],
env=None
)
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
# 初始化连接
await session.initialize()
# 列出可用工具
tools = await session.list_tools()
print(f"Available tools: {[tool.name for tool in tools.tools]}")
# 调用 qmd_query 工具
result = await session.call_tool(
"qmd_query",
arguments={
"query": "user authentication best practices",
"n": 5,
"min_score": 0.3
}
)
return result
4.5 维护与监控
定期维护任务
#!/bin/bash
# qmd-maintenance.sh
# 1. 检查索引状态
echo "=== QMD Status ==="
qmd status
# 2. 更新索引(拉取最新文档并重新索引)
echo "=== Updating Index ==="
qmd update
# 3. 重新生成向量嵌入
echo "=== Regenerating Embeddings ==="
qmd embed
# 4. 清理缓存
echo "=== Cleaning Up ==="
qmd cleanup
echo "Maintenance completed!"
添加到 crontab(每天凌晨 3 点执行)
0 3 * * * /path/to/qmd-maintenance.sh >> /var/log/qmd-maintenance.log 2>&1
监控指标
建议监控以下指标:
| 指标 | 说明 | 告警阈值 |
|---|---|---|
| 索引大小 | ~/.cache/qmd/index.sqlite 文件大小 | > 1GB |
| 查询响应时间 | 平均 query 执行时间 | > 2s |
| Token 节省率 | (原始 tokens - 检索后 tokens) / 原始 tokens | < 80% |
| 检索命中率 | 返回结果数 / 查询次数 | < 50% |
4.6 故障排除
常见问题
问题 1: 模型下载失败
# 检查网络连接
ping huggingface.co
# 手动下载模型到 ~/.cache/qmd/models/
# 或使用镜像
export HF_ENDPOINT=https://hf-mirror.com
qmd embed
问题 2: 权限错误
# 确保目录权限正确
chmod -R 755 ~/.cache/qmd
chmod -R 755 ~/agent-knowledge
问题 3: SQLite 扩展不支持
# macOS 用户需要安装 Homebrew 的 SQLite
brew install sqlite
export PATH="/opt/homebrew/opt/sqlite/bin:$PATH"
问题 4: 搜索结果为空
# 检查集合配置
qmd collection list
# 确认文档已正确索引
qmd ls <collection-name>
# 重新索引
qmd update
qmd embed -f
4.7 需要额外下载的模型说明
qmd 会自动下载以下模型到 ~/.cache/qmd/models/:
| 模型 | 大小 | 用途 | 下载来源 |
|---|---|---|---|
| embeddinggemma-300M-Q8_0.gguf | ~300MB | 生成向量嵌入 | HuggingFace |
| qwen3-reranker-0.6b-q8_0.gguf | ~640MB | 结果重排序 | HuggingFace |
| qmd-query-expansion-1.7B-q4_k_m.gguf | ~1.1GB | 查询扩展 | HuggingFace |
首次使用需要: 网络连接以下载模型(约 2GB 数据) 后续使用: 完全离线运行,无需网络
如果无法访问 HuggingFace,可以:
- 使用镜像站点(如 hf-mirror.com)
- 手动下载模型文件并放置到正确位置
- 使用预配置好的 Docker 镜像
4.8 Docker 部署方案(可选)
如果需要容器化部署,可以使用社区提供的 Docker 方案:
FROM oven/bun:1
RUN bun install -g https://github.com/tobi/qmd
# 预下载模型(可选,避免运行时下载)
# COPY models/ /root/.cache/qmd/models/
VOLUME ["/app/knowledge", "/root/.cache/qmd"]
CMD ["qmd", "mcp"]
# 构建镜像
docker build -t qmd-server .
# 运行容器
docker run -d \
--name qmd \
-v ~/agent-knowledge:/app/knowledge \
-v qmd-cache:/root/.cache/qmd \
qmd-server
参考资料
- QMD GitHub - Installation - 官方安装指南
- QMD GitHub - Usage - 详细使用文档
- Bun 官方文档 - Bun 运行时文档
- MCP 官方文档 - Model Context Protocol 规范