
1. 从能跑通到敢上线这套应用到底在解决什么问题大模型应用最尴尬的阶段不是跑不通而是能跑通但不敢给别人用。我自己就经历过这个阶段本地写个脚本把文档塞进向量库接上模型 API问几句答得还挺像样。可一旦要交给同事用、要接入公司内部系统、要面对真实用户问题就全冒出来了——谁在问问了什么模型调了哪些工具花了多少钱有没有把不该给的数据给出去出了事能不能追溯这套基于 RAG、记忆、API 和 MCP 构建带鉴权审计的应用实践核心要解决的就是这个从 Demo 到可用系统之间的鸿沟。它不是一个单点技术而是把四块能力拼成一个闭环RAG 负责知道什么让模型能基于私有知识回答记忆负责记得什么让多轮对话和跨会话的上下文不丢失API 负责能做什么把模型能力暴露成标准接口供外部调用MCP 负责怎么接工具用统一协议把外部工具、数据源接进来。而鉴权与审计是贯穿这四者的那根线决定了这套东西能不能真正落地到有合规要求的场景里。适合读这篇的人有三类一是已经跑通过 RAG Demo、想往生产环境推的开发者二是正在做企业内部 AI 助手、需要对接权限体系的工程师三是想搞清楚 MCP 到底在架构里扮演什么角色、和传统 API 调用有什么区别的技术负责人。我会尽量把每一步的为什么这么设计讲清楚而不是只丢一段能跑的代码——因为这套东西的坑基本都藏在设计决策里不在语法里。先说一个我踩过的真实教训。早期我做的 RAG 应用检索和生成是裸奔的用户输入直接进检索检索结果直接拼进 prompt模型输出直接返回。上线第一天就有人问帮我总结一下张三的绩效记录而张三的数据恰好在那批文档里。没有鉴权层检索根本不知道这个用户能不能看这份文档。后来我加了一层检索前的权限过滤把用户身份和文档的 ACL 做交集问题才解决。这件事让我彻底明白RAG 的检索环节不是纯技术问题它天然带着权限语义。这也是为什么这套实践要把鉴权和审计放在和 RAG 同等重要的位置。2. RAG 检索链路里那些决定成败的细节2.1 分块策略不是越小越好也不是越大越准RAG 最容易被低估的环节就是分块。很多人上来就用固定 512 token 切切完发现检索出来的片段要么缺上下文、要么混进无关内容。我的经验是分块粒度要跟着文档结构走而不是跟着 token 数走。技术文档、合同、说明书这类有明确层级的按标题层级切一个二级标题下的内容作为一个 chunk超长再二次切分对话记录、日志这类时序性强的按会话或时间窗口切表格类内容单独处理别硬塞进文本 chunk否则行列关系全丢。我实测下来带 10% 到 20% 重叠的语义分块比纯固定长度分块的检索命中率能高出不少代价是存储和检索成本略增。这里有个反直觉的点chunk 不是越小检索越准。chunk 太小单个片段信息量不足模型拿到也答不好chunk 太大检索时容易把无关内容一起带进来稀释了关键信息。我一般会做一轮离线评估准备 50 到 100 个真实问题人工标注正确答案所在的 chunk然后跑检索看命中率hit rate。命中率低于 80% 就回去调分块别急着调模型。2.2 检索增强不只是向量检索混合检索才是常态纯向量检索有个硬伤对精确匹配不敏感。用户问错误码 401 怎么处理向量检索可能给你返回一堆讲认证失败的段落但就是漏掉那条明确写着401的记录。所以生产环境我基本都用混合检索向量检索负责语义召回关键词检索BM25 之类负责精确召回两路结果做融合排序。融合排序常用 RRFReciprocal Rank Fusion逻辑很简单对每个文档把它在两路结果里的排名取倒数相加得分高的排前面。这个算法不需要调参鲁棒性好我一般作为默认方案。如果业务对排序质量要求高再上 cross-encoder 做精排但那个延迟和成本都上去了得权衡。提示混合检索的权重不是固定的。技术问答类场景关键词权重要高一些开放闲聊类场景向量权重要高一些。建议把权重做成配置项别写死在代码里。2.3 检索结果的权限过滤必须在检索阶段做不能事后补前面提到的权限问题正确做法是在检索阶段就把用户无权访问的文档排除掉而不是检索完再过滤。原因有两个一是事后过滤会导致返回结果数量不稳定用户可能只拿到 1 条甚至 0 条二是事后过滤意味着无权文档已经进入了检索计算存在信息泄露的侧信道风险。具体实现上我给每个 chunk 打上权限标签比如部门、密级、项目组检索时把用户身份转成对应的过滤条件在向量库的 metadata filter 里直接过滤。主流向量库Milvus、Qdrant、pgvector 等都支持 metadata 过滤用起来不复杂。关键是权限标签的维护要和源文档的权限体系同步文档权限变了chunk 标签也得跟着更新这块最好做成定时同步任务。3. 记忆系统短期靠窗口长期靠检索别混为一谈3.1 短期记忆和长期记忆是两套机制很多人把记忆当成一个东西其实它至少分两层。短期记忆是当前会话的上下文靠对话历史窗口维护超出窗口就截断或摘要压缩。长期记忆是跨会话的、需要持久化的信息比如用户的偏好、历史决策、重要事实靠向量库或结构化存储维护需要时检索出来注入 prompt。这两层的技术选型完全不同。短期记忆的核心是窗口管理策略是简单滑动窗口还是做摘要压缩还是关键信息抽取。我一般用滑动窗口 定期摘要的组合最近 N 轮保留原文更早的对话压缩成一段摘要。这样既控制了 token 消耗又不至于把早期重要信息全丢掉。长期记忆的核心是写入和召回策略。不是什么信息都值得记我一般只记三类用户明确表达的偏好、影响后续决策的事实、需要跨会话延续的任务状态。写入时打上时间戳和来源标记召回时结合相关性和时效性排序。3.2 记忆的时效性score 加时间半衰期热词里有个说法叫记忆score时间半衰期这个思路很实用。长期记忆如果只按语义相关性排序会出现三年前的偏好和昨天的偏好同等权重的情况这显然不合理。我的做法是在相关性得分上叠加一个时间衰减因子final_score relevance_score * exp(-lambda * days_since_created)lambda 控制衰减速度业务变化快的场景比如电商偏好lambda 大一些衰减快业务稳定的场景比如个人基本信息lambda 小一些。这样既保证了相关记忆能被召回又让新鲜记忆有更高优先级。实测下来这个简单的衰减机制对多轮任务型对话的体验提升很明显。3.3 记忆的隔离多用户场景下最容易出事的地方单用户场景下记忆怎么写都行多用户场景下记忆隔离是红线。我见过有系统把用户 A 的对话记忆写进了全局记忆池结果用户 B 提问时召回了 A 的信息这是严重的数据泄露。正确做法是每条记忆都绑定 owner_id召回时强制带上 owner 过滤条件和 RAG 的权限过滤是同一个逻辑。另外会话级记忆和用户级记忆要分开存会话级记忆只在当前会话有效会话结束可以清理用户级记忆跨会话持久化但必须严格按用户隔离。这两者的存储位置和生命周期都不一样别图省事塞一个表里。4. API 层设计鉴权、限流、审计一个都不能少4.1 鉴权不是加个 token 就完事API 层的鉴权很多人理解成请求头里带个 key 就行。但真实场景下鉴权至少要做三件事身份认证你是谁、权限校验你能调什么、配额管理你能调多少次。身份认证用标准的 API Key 或 JWT 都行关键是 key 要能关联到具体用户或应用不能是一把全局万能钥匙。我见过有系统所有客户端共用一把 key出了问题根本查不到是谁调的。权限校验要细化到接口级和资源级接口级控制能不能调这个 API资源级控制能访问哪些数据和 RAG 的权限过滤打通。配额管理则是防止单个用户把额度跑满影响其他人。这里有个常见坑401 和 403 要分清楚。401 是你没认证key 无效或缺失403 是你认证了但没权限。热词里那个unexpected status 401 unauthorized: incorrect api key provided就是典型的 401说明 key 本身有问题不是权限问题。排查时先确认 key 是否正确、是否过期、是否带对了前缀再去查权限配置。4.2 审计日志记什么、怎么记、存多久审计是这套系统能上生产的关键。我设计的审计日志至少包含这些字段字段说明用途request_id请求唯一标识全链路追踪user_id调用者身份责任归属timestamp请求时间时序分析endpoint调用的接口行为分析input_hash输入摘要隐私保护下的可追溯retrieved_docs召回的文档 ID 列表检索审计tools_called调用的工具及参数工具审计token_usagetoken 消耗成本核算response_status响应状态异常监控注意input_hash这里我用的是哈希而不是原文因为用户输入可能含敏感信息全量存原文有合规风险。但哈希要加盐防止被彩虹表反查。retrieved_docs和tools_called是审计的重点出了数据泄露或误操作靠这两个字段定位问题。审计日志的存储我一般用冷热分离最近 30 天放热存储ES 之类供实时查询更早的归档到对象存储需要时再捞。保留期限看合规要求一般不少于 6 个月。4.3 限流与降级别让一个用户拖垮整个系统限流我一般做三层用户级单用户 QPS 上限、应用级单应用总量上限、全局级系统总上限。用户级防止单用户滥用应用级防止单个接入方拖垮系统全局级是最后一道保险。降级策略也要提前设计。模型 API 超时或报错时是返回缓存结果、返回兜底话术、还是直接报错我的做法是检索结果可以缓存相同问题短时间内直接返回缓存模型调用失败时返回明确的错误提示而不是假装成功返回空内容。热词里那个api error: 400 this models maximum context length is 1048576 tokens就是典型的上下文超限这种要在调用前做 token 预估超了就截断或摘要别等 API 报错。5. MCP 接入统一协议背后的工程价值5.1 MCP 到底解决了什么问题MCPModel Context Protocol本质上是给模型和外部工具之间定了一套标准接口。在没有 MCP 之前每接一个工具就要写一套适配代码这个工具用 REST那个用 gRPC另一个是本地函数模型侧要针对每种调用方式做适配。工具一多适配代码就成了维护噩梦。MCP 的价值在于把工具怎么调这件事标准化了。工具方按 MCP 协议暴露能力工具列表、参数 schema、调用接口模型侧按统一方式发现和调用工具。新增工具时模型侧几乎不用改代码只要工具符合协议就能接进来。热词里提到的playwright mcp、chrome devtools mcp、browser use mcp都是这个思路的产物——把浏览器操作、调试能力封装成标准工具供模型调用。5.2 MCP 和传统 API 调用的区别很多人问 MCP 和直接调 API 有什么区别。我的理解是API 是给人用的接口MCP 是给模型用的接口。传统 API 的文档是写给人看的参数含义、调用顺序、错误处理都需要人来理解。MCP 的工具描述是结构化的模型能直接读懂参数 schema 和工具用途自主决定调不调、怎么调。这个区别在工程上很关键。传统 API 集成你得写代码把 API 包装成模型能用的函数MCP 集成工具方自己按协议暴露模型侧自动发现。前者是每个工具都要写胶水代码后者是协议统一即插即用。当然 MCP 也不是银弹工具的质量、权限控制、调用审计这些还是得自己做。5.3 MCP 接入的鉴权与审计怎么落地MCP 工具调用同样要走鉴权和审计。我的做法是MCP 工具调用统一经过 API 层的鉴权网关工具本身不直接暴露给模型而是通过网关代理。网关负责校验调用者身份、检查工具权限、记录调用日志。这样审计链路是完整的不会出现模型调了工具但日志里查不到的情况。工具权限要细化到工具级和参数级。工具级控制能不能调这个工具参数级控制能传什么参数。比如一个查询订单的工具参数级权限可以限制只能查自己名下的订单。这块和 RAG 的资源级权限是同一个思路权限控制要下沉到数据层不能只在接口层拦。注意MCP 工具调用往往涉及外部系统操作发邮件、改数据、调第三方这类操作必须做二次确认或审批不能让模型自主决定就执行。我的做法是对写操作类工具加一道确认环节读操作类工具可以直接执行。6. 把四块拼起来一次完整请求的链路拆解6.1 从请求进来到响应出去把 RAG、记忆、API、MCP 拼成一个系统后一次完整请求的链路大致是这样的请求进入 API 网关校验 API Key识别用户身份检查接口权限和配额。加载记忆根据 user_id 和 session_id 拉取短期记忆对话历史和长期记忆用户偏好、历史事实。RAG 检索把用户问题做向量化结合关键词做混合检索检索时带上用户权限过滤拿到有权访问的相关文档。组装 prompt把系统提示、长期记忆、检索结果、短期对话历史按优先级拼进上下文注意控制总 token 数。模型调用与工具决策模型判断是否需要调工具需要则通过 MCP 网关调用工具结果回填后继续生成。写回记忆把本轮对话写入短期记忆把值得长期保留的信息写入长期记忆。记录审计日志把整个链路的输入、检索、工具调用、输出、token 消耗全部落库。返回响应把模型输出返回给调用方。这条链路里第 3 步和第 5 步是鉴权审计的重点第 4 步是效果的关键第 6 步是体验的关键。任何一步偷懒系统都会在某个场景下出问题。6.2 上下文窗口管理别让 token 超限毁了体验热词里那个maximum context length is 1048576 tokens的错误本质是上下文管理没做好。我的做法是在组装 prompt 前做 token 预算先算系统提示、长期记忆、检索结果、对话历史各占多少超出预算就按优先级裁剪。优先级一般是系统提示 当前问题 检索结果 近期对话 长期记忆 早期对话。裁剪策略上检索结果可以按相关性得分截断对话历史可以做摘要压缩长期记忆可以只保留最相关的几条。关键是别等 API 报错才处理要在调用前就预估好。不同模型的上下文窗口不一样这个预算逻辑要跟着模型配置走别写死。6.3 错误处理401、400、超时分别怎么应对生产环境里 API 报错是常态关键是怎么应对。我整理了几类常见错误和处理方式错误类型典型表现处理方式401 认证失败incorrect api key检查 key 配置不重试直接告警400 参数错误context length exceeded裁剪上下文后重试一次429 限流rate limit exceeded指数退避重试超过阈值降级5xx 服务端错误internal error重试 2 到 3 次仍失败则降级超时timeout缩短上下文或换更快的模型重试这里的原则是认证类错误不重试重试也没用key 就是错的参数类错误修正后重试限流和服务端错误退避重试超时类错误降级处理。重试一定要有上限和退避否则会把下游打垮。7. 上线前必须过的几道关7.1 权限测试用越权用例打自己上线前我一定会做一轮越权测试用 A 用户的身份去访问 B 用户的数据看系统能不能拦住。测试用例要覆盖 RAG 检索、记忆召回、MCP 工具调用三条路径因为这三条路径都可能泄露数据。我见过有系统 RAG 权限做得好但记忆召回没做隔离结果还是泄露了。测试方法上可以准备两套测试数据分别属于两个测试用户然后用交叉身份去查看返回结果里有没有对方的数据。这个测试要自动化每次权限逻辑改动都跑一遍防止回归。7.2 审计完整性每条请求都能追溯审计完整性的验证方法是随机抽一批请求看能不能从审计日志里还原出完整的处理链路。包括用户是谁、问了什么、检索了哪些文档、调了哪些工具、返回了什么、花了多少 token。如果某个环节日志缺失那这个环节就是审计盲区。我一般会写个脚本定期抽样检查审计日志的完整性发现缺失就告警。另外审计日志本身也要做防篡改至少是只追加不修改最好加上哈希链或签名防止事后被改。7.3 成本监控token 消耗要能按用户拆成本是很容易被忽视的一环。我见过系统上线一个月账单出来才发现某个用户消耗了 80% 的额度。所以token 消耗必须按用户、按接口、按模型维度统计并且设置预算告警。超过阈值就告警严重超支就自动限流。成本优化的手段有几个检索结果做去重和截断减少无效上下文简单问题用小模型复杂问题才用大模型缓存高频问题的答案长期记忆只保留真正有用的信息。这些手段叠加起来成本能降不少。8. 几个我踩过的坑和对应的解法第一个坑是检索结果和记忆内容打架。有次用户先说了我偏好简洁回答这条进了长期记忆但检索出来的文档里有一段详细说明模型最后给了个又臭又长的回答。原因是 prompt 组装时没处理好优先级记忆和检索结果平级了。解法是在 prompt 里明确标注各部分来源和优先级让模型知道该听谁的。第二个坑是MCP 工具调用超时拖垮整个请求。有次接了个外部工具那个工具偶尔会卡住导致整个请求超时。解法是给工具调用设独立超时超时就跳过这个工具让模型基于已有信息回答而不是整个请求失败。第三个坑是审计日志写得太粗。早期我只记了请求和响应没记检索和工具调用结果出了次数据泄露根本查不到是哪个环节漏的。后来把检索文档 ID、工具调用参数全记上才定位到是权限过滤的一个边界条件没覆盖。教训是审计日志的粒度要能支撑问题定位不能只记结果不记过程。第四个坑是记忆写入太随意。有次模型把用户的玩笑话当成了真实偏好写进长期记忆后续对话一直被这个错误偏好影响。解法是长期记忆写入加一道判断要么让模型显式标记这是需要记住的信息要么用规则过滤掉明显是闲聊的内容。别让模型自主决定什么该记它判断不准。9. 关于扩展方向的一点个人看法这套架构跑通之后扩展方向其实挺多的。往深了做可以把 RAG 升级成 GraphRAG 或本体 RAG用知识图谱增强检索的推理能力适合关系复杂的领域。往宽了做可以把 MCP 工具生态接得更丰富让模型能操作更多外部系统。往稳了做可以把审计和权限做成独立的中间件复用到其他 AI 应用上。但我的建议是别一上来就追求大而全。先把 RAG 的检索质量、记忆的隔离、API 的鉴权审计这三块做扎实MCP 工具按需接入。我见过太多项目工具接了一堆结果基础检索都不准用户问啥都答不好。基础能力决定下限扩展能力决定上限下限没守住上限再高也没用。另外这套东西的迭代节奏要跟着真实反馈走。上线后收集用户的实际问题看哪些答得好、哪些答得差针对性优化检索和 prompt。别闭门造车调参数真实场景的分布和你想的往往不一样。我自己就是靠上线后头两周的真实问题把检索命中率从 70% 出头调到了 85% 以上靠的全是真实 case 的反馈不是实验室里的评估集。