EdgeOne Makers 从入门到精通
附录

§20 会话存储与鉴权:让 Agent 记得住、管得严

20.1 我把对话存哪儿了

2月前我上线第一个 Agent,用户第二轮就"失忆"。§05 讲过平台零配置记忆,但那只是开胃菜。真要做产品,你得会精细管对话——按用户列、分页拉、改某条、甚至压成摘要。

Makers 的 context.store 就是干这个的。它零配置,Node 和 Python 两套 API 完全镜像。

20.2 存储怎么用

agents/ 目录里用 context.storecloud-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 真正"动手"——给它沙箱和工具。