20.1 我把对话存哪儿了
2月前我上线第一个 Agent,用户第二轮就"失忆"。§05 讲过平台零配置记忆,但那只是开胃菜。真要做产品,你得会精细管对话——按用户列、分页拉、改某条、甚至压成摘要。
Makers 的 context.store 就是干这个的。它零配置,Node 和 Python 两套 API 完全镜像。
20.2 存储怎么用
agents/ 目录里用 context.store,cloud-functions/ 里用 context.agent.store,指向同一份数据。
最常用的是追加和读取:
// 追加一条用户消息
await context.store.appendMessage({
conversationId: context.conversation_id,
role: "user",
content: context.request.body.message,
userId: context.request.body.userId, // 关联用户,便于按用户列对话
});
// 拉历史,直接拼成 OpenAI 格式喂给模型
const messages = await context.store.getMessages({
conversationId: context.conversation_id,
limit: 50,
});
const reply = await openai.chat.completions.create({
model: "deepseek/deepseek-v4-flash",
messages: context.store.toOpenAIInput(messages),
});
预期结果:多轮上下文自动承接,还能按 userId 归拢。
| 方法 | 干什么 |
|---|---|
appendMessage |
追加一条消息,对话不存在自动建 |
getMessages |
拉消息,支持游标分页 |
listConversations |
按用户列对话,倒序 |
updateConversation |
更新对话元信息(标题、标签) |
deleteConversation |
删整个对话,不可恢复 |
| 标叔的结论 | 通用 API 够接管 90% 场景 |
toOpenAIInput那行。Store 帮你把内部消息转成任意框架格式,Claude / OpenAI / LangGraph 都能直接吃。
20.3 长对话要压缩
getMessages 单次最多 100 条。对话长了怎么办?保留最近 N 条原文,早期压成一段摘要。
平台不替你调 LLM 生成摘要,只提供一个 metadata JSON 容器。约定两个 key:summary(摘要文本)、summarizedUntil(已摘要到哪条)。你用便宜模型生成摘要,写回即可。
async function summarize(previousSummary, newMessages) {
const prompt = previousSummary
? `合并已有摘要和新增对话成一段新摘要,控制在 500 字内:\n${previousSummary}\n${newMessages}`
: `总结这段对话成摘要,控制在 500 字内:\n${newMessages}`;
const { text } = await generateText({
model: openai("gpt-4o-mini"), // 便宜模型压摘要,省钱
prompt,
});
return text.trim();
}
预期结果:上万条消息的对话,模型每次只看最近 N 条 + 一段摘要,又快又省。
20.4 没鉴权,任何人都能调你 Agent
上个月我一个没加登录的 Agent,半夜被刷了几万次调用。查账单我心都凉了。
Agent 接口若没登录,任何人都能直接请求 /agents/*,白嫖你的 LLM 和工具额度。Makers 的解法是中间件 + Cloud Functions 签发 JWT:
cloud-functions/auth/*负责登录注册、签发 JWT;middleware.js在边缘节点拦未登录请求,失败直接 401;- Agent 入口再
requireAuth(context)验一次。
// middleware.js:命中受保护路径先验 JWT
export const config = {
matcher: ["/chat/:path*", "/agents/:path*"],
};
export async function middleware(context) {
const token = readCookie(context.request.headers, "jwt_token");
if (!token) return unauthorized("no auth cookie");
await verifyJwt(token, context.env.JWT_SECRET); // 失败直接 401
return context.next();
}
预期结果:未登录请求在边缘就被挡掉,省下 Agent Runtime、Sandbox、LLM 的成本。
| 层 | 作用 | 标叔的结论 |
|---|---|---|
| 中间件 | 边缘早拒 | 省钱第一道 |
| Agent 侧 | 再验一次 | 安全兜底 |
| HttpOnly Cookie | token 不落前端 JS | 防 XSS 偷 |
公开 Agent 前,先加这一层。我那次被刷,就是漏了中间件。早拒比事后查账单便宜一万倍。
记忆精细了,入口锁住了。下一章,让 Agent 真正"动手"——给它沙箱和工具。