Memai
Guides

最佳实践

构建高效的记忆驱动应用的建议

最佳实践

遵循这些最佳实践,使用 Memai 构建高效的应用。

记忆内容

编写清晰、描述性的内容

记忆内容应该清晰且自包含,以便更好地进行语义搜索:

// 好的:清晰且描述性
await createMemory({
  content: "用户张三偏好深色模式,使用 VS Code 作为主要 IDE",
  userId: "user_123"
});

// 避免:模糊或依赖上下文
await createMemory({
  content: "他喜欢深色模式",
  userId: "user_123"
});

包含相关上下文

添加有助于检索的上下文:

await createMemory({
  content: "在 1 月 15 日的入职电话中,用户提到他们需要与 Slack 和 GitHub 集成来支持开发工作流程",
  userId: "user_123",
  tags: ["入职", "集成"]
});

使用标签进行组织

标签有助于过滤和组织记忆:

await createMemory({
  content: "用户最喜欢的编程语言是 Python",
  userId: "user_123",
  tags: ["偏好", "编程", "个人资料"]
});

// 稍后,按标签过滤
await searchMemories({
  query: "编程偏好",
  userId: "user_123",
  tags: ["偏好"]  // 只搜索带有偏好标签的记忆
});

搜索优化

编写自然查询

Memai 使用语义搜索,因此自然语言查询效果最好:

// 好的:自然语言
await searchMemories({
  query: "用户的沟通偏好是什么?"
});

// 效果较差:关键词风格
await searchMemories({
  query: "沟通 偏好 用户"
});

设置适当的阈值

使用 threshold 参数过滤低相关性结果:

// 高阈值用于精确匹配
await searchMemories({
  query: "用户的邮箱是什么?",
  threshold: 0.8,  // 只返回高度相关的结果
  limit: 3
});

// 低阈值用于更广泛的搜索
await searchMemories({
  query: "告诉我用户的背景",
  threshold: 0.5,  // 包含部分相关的结果
  limit: 10
});

适当限定搜索范围

始终将搜索范围限定到相关的用户/代理:

// 好的:限定到特定用户
await searchMemories({
  query: "项目截止日期",
  userId: "user_123"
});

// 好的:限定到特定代理
await searchMemories({
  query: "对话上下文",
  agentId: "support_bot"
});

实体与关系管理

提取有意义的实体

为重要的、可重用的概念创建实体:

// 好的:值得追踪的重要实体
await createEntity({
  name: "科技公司",
  type: "ORGANIZATION",
  description: "用户的雇主,一家财富 500 强科技公司"
});

// 避免:临时或不重要的实体
await createEntity({
  name: "今天",
  type: "OTHER"  // 太笼统
});

使用一致的关系类型

为您的应用建立关系类型词汇表:

// 定义您的关系词汇表
const RELATIONSHIP_TYPES = {
  WORKS_FOR: 'WORKS_FOR',
  MANAGES: 'MANAGES',
  KNOWS: 'KNOWS',
  INTERESTED_IN: 'INTERESTED_IN',
  LOCATED_IN: 'LOCATED_IN'
};

// 一致使用
await createRelation({
  sourceId: personEntity.id,
  targetId: companyEntity.id,
  type: RELATIONSHIP_TYPES.WORKS_FOR
});

错误处理

优雅地处理错误

始终实现适当的错误处理:

async function searchUserMemories(userId, query) {
  try {
    const results = await searchMemories({ userId, query });
    return results;
  } catch (error) {
    if (error.code === 'QUOTA_EXCEEDED') {
      // 处理配额超限
      logWarning('配额超限,使用缓存结果');
      return getCachedResults(userId, query);
    }

    if (error.code === 'RATE_LIMIT_EXCEEDED') {
      // 实现退避重试
      await sleep(error.retryAfter * 1000);
      return searchUserMemories(userId, query);
    }

    // 记录并重新抛出未知错误
    logError('记忆搜索失败', error);
    throw error;
  }
}

发送前验证输入

在发起 API 调用前验证数据:

function validateMemoryContent(content) {
  if (!content || typeof content !== 'string') {
    throw new Error('内容必须是非空字符串');
  }

  if (content.length > 10000) {
    throw new Error('内容超过 10,000 字符的最大长度');
  }

  return content.trim();
}

async function createMemorySafe(data) {
  const content = validateMemoryContent(data.content);
  return createMemory({ ...data, content });
}

性能

实现缓存

缓存频繁访问的数据:

class MemoryCache {
  constructor(ttlMs = 5 * 60 * 1000) {
    this.cache = new Map();
    this.ttlMs = ttlMs;
  }

  get(key) {
    const entry = this.cache.get(key);
    if (!entry) return null;

    if (Date.now() > entry.expiresAt) {
      this.cache.delete(key);
      return null;
    }

    return entry.value;
  }

  set(key, value) {
    this.cache.set(key, {
      value,
      expiresAt: Date.now() + this.ttlMs
    });
  }
}

const memoryCache = new MemoryCache();

async function getMemory(id) {
  const cached = memoryCache.get(id);
  if (cached) return cached;

  const memory = await fetchMemory(id);
  memoryCache.set(id, memory);
  return memory;
}

使用分页

列出记忆时,使用分页避免加载过多数据:

async function* getAllMemories(userId) {
  let page = 1;
  let hasMore = true;

  while (hasMore) {
    const response = await listMemories({
      userId,
      page,
      limit: 100
    });

    for (const memory of response.items) {
      yield memory;
    }

    hasMore = page < response.pagination.totalPages;
    page++;
  }
}

// 使用
for await (const memory of getAllMemories('user_123')) {
  processMemory(memory);
}

安全

永远不要暴露 API 密钥

将 API 密钥保留在服务器端:

// 好的:服务器端 API 调用
// server.js
app.post('/api/memories/search', async (req, res) => {
  const results = await memaiClient.searchMemories({
    query: req.body.query,
    userId: req.user.id
  });
  res.json(results);
});

// 不好的:客户端暴露 API 密钥
// client.js(不要这样做)
fetch('https://api.memai.dev/v1/memories/search', {
  headers: {
    'Authorization': 'Bearer mm_exposed_key'  // 永远不要这样做
  }
});

验证用户访问

始终验证用户只能访问自己的数据:

app.get('/api/memories/:id', async (req, res) => {
  const memory = await memaiClient.getMemory(req.params.id);

  // 验证所有权
  if (memory.userId !== req.user.id) {
    return res.status(403).json({ error: '访问被拒绝' });
  }

  res.json(memory);
});