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);
});