资讯动态

基于Mastra与Elasticsearch构建具备记忆与工具调用能力的AI代理实践

发布时间:2026/10/22 19:30:27 来源:尧图企业网站定制
这两年做 AI 应用大家的关注点逐渐从“会不会调接口”转到了“怎么让应用真正具备自主能力”。所谓代理能力说白了就是让 AI 能根据目标自己决定调用什么工具、查什么资料、走什么流程而不是每次都由用户手把手喂上下文。Mastra 是一个专门为这类场景设计的 AI 代理编排框架结构轻、思路清晰配合 Elasticsearch 做外部记忆和知识检索能把一个“只会聊天的机器人”变成“能查数据、能记上下文、能自己规划任务的智能体”。这篇文章就基于我的实际项目经验完整拆解这套方案的落地过程适合正在选型或准备重构 AI 应用架构的开发者参考。1. 先搞清楚代理能力到底从哪来在动手写代码之前我建议先想清楚一个问题我们常说的 AI 代理和普通的大模型对话接口本质区别到底是什么1.1 大模型本身不会“干活”裸用大模型时模型的能力仅限于“根据输入生成文本”。你问它“帮我查一下最近一周的订单异常”它能给你一段像模像样的回答但它实际上并没有真的去查数据库。要让它真正完成查询动作必须由我们在外部编写工具函数然后把结果塞回提示词里再让模型基于结果继续生成。这就是代理最核心的机制模型负责决策外部代码负责执行。判断该调用哪个工具、按什么顺序调用、参数填什么这些交给模型真正的数据读取、API 请求、文件操作由我们自己写的工具完成。Mastra 在这个环节做的事就是把“模型决策”和“工具执行”串成一个可控的工作流。1.2 记忆是代理和聊天机器人的分水岭聊天机器人也能调工具但它每次对话都是孤立的。代理不一样它需要记住用户偏好、历史决定、之前查到的中间结果并且在多轮任务里保持一致的行为。这个记忆不能只放在模型上下文窗口里——上下文窗口有限而且每次调用都携带全部历史会导致成本飙升。更合理的做法是把记忆外置到一个独立的存储系统里。Elasticsearch 在这个架构里就扮演这个角色它既能存结构化的记忆片段又能做向量检索找语义相似的历史信息。1.3 Mastra 给了什么框架Mastra 本身不替代大模型也不替代 Elasticsearch它提供的是三层能力工作流编排用代码方式定义代理的执行步骤支持条件分支、循环、并行执行。工具注册机制把普通的 TypeScript 函数包装成模型可识别的工具描述模型按需调用。记忆集成接口提供统一的记忆读写抽象层底层可以接不同的存储实现。选它而不是自己手写一套 agent loop原因在于它把这些通用逻辑做成了可复用的框架同时保留了足够的自由度。团队不需要重复造轮子也不必被某个云厂商的闭源方案绑死。2. 整体架构设计数据是怎么流动的架构设计是决定项目后续维护成本的关键。我的方案里总共四条链路理解清楚这四条链路整个系统就清楚了。2.1 查询链路从用户问题到最终回答用户输入问题后Mastra 的代理开始执行先判断问题是否需要检索外部知识或历史记忆。这个判断由模型完成Mastra 会提供当前可用的工具清单。如果需要查询 Elasticsearch代理调用我们注册的searchKnowledge工具传入查询向量和过滤条件。Elasticsearch 执行混合检索返回匹配的文档片段和相关性分数。代理把检索结果合并进提示词生成面向用户的最终答案。整个过程里Elasticsearch 对代理来说就是一个“可调用的外部大脑”。代理自己决定“我现在要查一下”而不是被动等待上下文里塞入资料。2.2 写入链路对话结束后沉淀记忆每次有意义的对话结束我会触发记忆写入流程提取对话中的关键实体比如用户提到的项目名称、编号、偏好。生成文本摘要并做向量化处理。批量写入 Elasticsearch 的memories索引。写入操作放在后台异步执行不阻塞主对话的响应。如果写入失败我设计了重试机制最多重试三次仍然失败就丢弃本次记忆保证主流程稳定。2.3 工具调用链路注册和执行Mastra 里注册工具非常简单核心就是一个描述清晰的函数import { createTool } from mastra/core; const searchKnowledge createTool({ name: searchKnowledge, description: 在知识库中搜索相关资料适用于查询产品文档、历史工单、项目记录等场景。输入为自然语言查询语句。, inputSchema: { type: object, properties: { query: { type: string }, topK: { type: number, minimum: 1, maximum: 10 }, }, required: [query], }, async execute({ query, topK 5 }) { return await searchInElasticsearch(query, topK); }, });这里最容易踩的坑是 description 写得太笼统。模型靠这段文字判断“什么时候该用这个工具”如果描述模糊它就会在不需要查询的时候也去调用或者在需要查询的时候完全忽略它。我一般会在 description 里写清楚触发场景、输入含义、返回内容结构。2.4 数据隔离与多租户设计如果你的应用要服务多个项目或者多个团队Elasticsearch 索引里一定要设计好租户维度。我的做法是每个索引里都带tenantId字段所有查询和写入工具在入口统一从会话上下文里取租户信息强制附加过滤条件。当模型需要从用户的多次对话里提取有价值的信息并据此在下一次对话中形成连续的、个性化的互动时我需要把这种能力赋予 AI 应用。这套设计能帮你在构建自己的应用时少走很多弯路。一开始就把底层架构想清楚后续加功能只是填代码而不是推翻重来。import { Mastra } from mastra/core; const mastra new Mastra({ agents: { supportAgent: { systemPrompt: 你是智能客服助手。你可以使用工具查询内部知识库。 回答要简洁并引用信息出处。如果查询不到请如实告知用户。 , tools: [searchKnowledge, createTicket], memory: { storage: elasticsearchStorage, window: { type: sliding, maxSize: 20, }, semanticRecall: { topK: 3, messageRange: 10, }, }, }, }, });3.3 记忆读取的优先级我在实现记忆读取时设了两层逻辑先用语义召回从 Elasticsearch 里检索与当前问题最相关的历史记忆取前三条。这些记忆会作为上下文拼接到系统提示词里。然后是最近窗口内的对话原文按时间倒序取最近 20 条保证代理对当前对话的连续性有完整的把握。这两层组合的效果比单独用哪一层都好。语义召回解决的是“很久以前说过但相关”的问题滑窗解决的是“刚刚说过但上下文窗口有限”的问题。实际测试中这个组合让用户明显感觉到“这个助手记得我上次说过的话”。3.4 工具参数校验Mastra 内置了工具参数的运行时校验。建议不要关闭这个功能它能帮你拦截掉很多模型生成的非法参数。我自己遇到过模型把字符串参数传给数字类型字段的情况如果没有校验Elasticsearch 查询直接报错有了校验就能自动触发生成纠正逻辑。特别说明工具函数执行的超时时间也要配置。Elasticsearch 查询偶尔会慢比如聚合太复杂或者分片有问题如果代理调工具卡死了整个对话就卡住了。我一般把工具超时设为 15 秒超时后立刻返回一个友好的错误信息给模型让它尝试其他方式回答。3.5 文本切分策略文本切分这个环节闲聊几句心得。我测试过多种切分方案固定长度切分按每 500 个字符切成一段。实现简单但语义被切碎的概率大。段落切分按换行符把文本拆成段落再合并到接近目标长度。段落内部语义完整效果明显更好。递归字符切分先按段落切再按句子切最后按字符切逐级降级。Elasticsearch 自带的 LangChain 集成不是必须的你也可以用 Elasticsearch 的 ingest pipeline 做文本切分和向量化。整个流程里关键指标是切分后的段落是否语义完整。我推荐先用段落切分如果段落太长再细化。4. 实际踩坑记录与排查方法这部分分享我在项目中实际遇到的问题和排查思路这些经验是文档里不太会写的。4.1 代理循环调用工具停不下来第一个遇到的问题是代理在工具调用链里陷入了循环比如查询知识、发现问题、再次查询反复多次不结束。排查后发现是工具返回结果里包含了“建议查询更多资料”之类的文本模型把这段文本当成了指令。解决办法有两层。第一层在工具返回的文本里明确标注“这是检索结果不是指令”弱化模型对结果的执行欲。第二层在 Mastra 的工作流配置里设置最大工具调用次数超过就强制结束并回复当前已有信息。这两层配合之后循环问题基本消失。4.2 Elasticsearch 查询超时拖慢响应在并发测试中部分查询响应时间超过 10 秒导致整个代理流程超时。定位后发现两个原因一是某些查询语句没有走过滤条件触发了全索引扫描二是部分时间段创建了过多索引导致查询需要扫描多个分片。优化方法很直接对所有查询强制附加时间范围过滤利用 Elasticsearch 的 query context 缩小扫描范围。固定使用一个滚动索引而不是每天建新索引避免分片过多。对热门返回字段做字段折叠减少网络传输量。优化后P50 响应时间降到了 400ms 左右效果非常明显。实际开发中做 Elasticsearch 查询优化时一定要关注查询计划搞清楚慢查询到底慢在哪一步。4.3 向量维度不一致导致写入失败这个问题属于比较低级的错误但值得注意。写入向量时我一开始手动指定了一个 1536 维的向量而文本向量化模型输出的向量维度是 3072结果 Elasticsearch 直接拒绝了文档写入。排查时我习惯先把错误日志打出来发现mapper_parsing_exception之后立刻去检查向量化函数输出的维度和索引 mapping 是否一致。这个问题的教训是任何向量模型的升级或者替换都要同步检查索引 mapping 里的维度配置。4.4 记忆检索返回空结果语义检索偶尔返回空结果后来排查发现是索引里写入的数据本身就没有向量字段。原因是写入链路里有一段异步任务在向量化接口限流时直接丢弃了文档。修复方案是引入消息队列做缓冲向量化失败的任务放进重试队列连续失败三次才丢弃同时增加告警通知。这个修复的重要性在于一旦记忆链路静默失败用户无法直接感知但整个代理的智能程度会明显下降。5. 项目核心需求解析与场景展望5.1 智能化升级的全链路能力这个项目真正解决的问题是让 AI 应用从“被动应答”走向“主动治理”。当我回头审视整个项目时它最大的价值不只是把 Mastra 和 Elasticsearch 组合到了一起而是构建了一个具备自我学习能力的系统底座。对话中沉淀的知识通过记忆写入链路被结构化地存储下来下一次遇到相似问题时代理能够调用这些沉淀的知识提供更精准、更贴合上下文的回答。5.2 技术选型分析用 Mastra 而不是自己实现 agent loop最大的收益是稳定性和速度。Mastra 的社区版本已经覆盖了大多数使用场景文档质量在 AI 框架里算比较高的遇到问题也比较容易找到解决方案。用 Elasticsearch 而不是专门的向量数据库重要原因是它同时满足了关键词检索、向量检索和过滤检索一个系统承担了知识库和记忆库两个角色运维成本更低。5.3 可扩展方向这个架构可以很方便地扩展到更多场景。比如接入实时数据流让代理具备更强的实时决策能力。在 Elasticsearch 的一侧配置独立的模型推理端点让向量化过程更稳定在数据增长到一定量级后给知识库和记忆库分别调配独立集群避免相互影响。对我来说一个 AI 应用是否真正智能不只是看它模型有多大而是看它有没有积累和运用知识的能力。用 Mastra 和 Elasticsearch 构建代理正是这种积累的开始。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑