MCP 面试高频考点:把知识库 RAG 检索封装为 Tool,Docker 与结构化输出怎么配合不踩坑
面试场景
这是一场面向后端/AI 应用工程师的技术面试,岗位要求候选人不仅理解 MCP 协议,还能把 RAG 检索能力做成可被 AI 应用安全调用的服务。面试官从"如何封装一个检索 Tool"切入,逐步追问协议边界、结构化输出、容器部署、异常处理、安全与可观测性。
面试官:我们有一套内部知识库,已经完成文档解析、切块、索引和召回链路,现在希望通过 MCP 暴露给 AI Host 使用。你会优先把它封装成 Tool、Resource 还是 Prompt?为什么?
候选人 :结论是优先封装成 Tool,但要严格控制它的语义为"只读检索",不能把检索结果夹带写操作。
原因有三点。第一,RAG 检索本质上是"模型根据用户问题决定是否查询外部知识"的动作,属于模型可控调用的操作,和 Tool 的定位一致:模型可以发现并自动调用,再把结果用于后续回答资料3。第二,Resource 更适合由 URI 标识的可读取上下文,例如某篇固定文档、某个配置对象;而检索是"输入查询词,返回动态匹配片段",不是稳定 URI 能直接表达的静态资源资料1。第三,Prompt 适合用户显式选择的模板化工作流,例如"总结知识库并生成周报",但通用知识检索不是固定模板,不应强绑定到某条 Prompt资料1。
不过我会在 Tool 描述里明确写清:它只返回检索片段和来源元数据,不直接生成最终答案;调用方仍需做引用、去重和安全过滤。这样可以避免 Host 或模型把检索 Tool 误当成"带副作用的业务操作"。
面试官:好,那这个 Tool 的输入输出你怎么设计?尤其是你提到结构化输出,为什么在 MCP 场景里它很重要?
候选人 :结论是 输入用严格 JSON Schema,输出也用稳定结构化对象,不要只返回一大段自然语言拼接文本。
输入上,我会至少包含这些字段:query(必填,检索语句)、top_k(可选,返回条数)、filters(可选,按知识库、文档类型、时间范围、权限范围过滤)、need_rerank(可选,是否启用重排)。Schema 不是为了"提示模型",而是让 MCP Server 在反序列化阶段就能拦截明显非法请求;但我也知道,schema 只是结构约束,不能代替服务端业务校验和授权资料1。
输出上,我不会只返回拼接后的长字符串,而是返回结构化结果,例如: - query_rewrite:实际用于召回的查询,便于排错; - results:数组,每个元素包含 chunk_id、title、source_uri、score、snippet、updated_at; - total_matched:命中数量; - warnings:例如"结果被权限过滤""查询词过短可能影响召回"。
这样设计的原因是:RAG 链路本身就要求保留来源元数据,才能支持引用和排错资料1。如果只返回一段文本,模型虽然也能读,但客户端很难做"显示来源""按权限隐藏""结果截断""失败重试"这些工程动作。Azure AI Search 的 RAG 设计里也强调 retrieve action 作为应用或 agent 可调用的对象,结果需要带上执行元数据,才能支持排序、答案合成和后续编排资料2。
面试官:追问一下:模型传入的参数不可信,你会做哪些校验?如果检索超时、索引为空、或者命中了不该返回的文档怎么办?
候选人 :结论是 把模型输入和检索文档都视为不可信数据,在 Server 侧做分层校验,不能因为请求来自 AI Host 就默认可信资料1。
我会分四层处理:
- 结构与长度校验 :限制
query最大长度,禁止异常控制字符;top_k设置上下界,避免模型一次要求拉取过多片段造成上下文膨胀;filters只允许白名单字段,防止传入任意查询条件。 - 权限校验:Tool 调用必须带调用者身份上下文,服务端按用户、租户、知识库范围做授权,而不是只判断"是否登录"资料1。如果某个 chunk 属于用户无权访问的空间,即使向量相似度高,也要在召回后或索引侧过滤掉。
- 检索异常处理 :超时、索引不可用、重排服务失败时,不返回伪造结果,而是返回结构化错误,例如
error_code、retryable、message。是否重试、重试几次、超时多久,需要根据业务 SLA 和压测结果确定,不能拍脑袋写死。 - 结果安全处理:检索到的文档内容本身也是不可信数据,其中的指令不能覆盖系统规则资料1。因此返回给模型时,要把片段放在明确的"检索内容"字段中,必要时做截断和脱敏;凭据、内部路径、访问令牌绝不能出现在日志、Tool 返回值或模型上下文里资料1。
如果索引为空或召回结果为空,我会返回空 results 而不是编造答案,同时给出 warnings,让模型决定是换词检索、提示用户补充条件,还是明确回答"知识库中未找到相关信息"。
面试官:再往工程化走一步。这个 MCP Server 你准备怎么部署?Docker 和传输方式怎么选?为什么?
候选人 :结论是 分场景选传输,Docker 负责一致的运行环境和依赖隔离,不要把容器化和协议传输混为一谈。
如果是开发者本地使用,例如桌面 AI 应用在本机启动 MCP Server,我会优先用 stdio 传输。因为 stdio 适合 Host 启动子进程的场景,部署简单,不需要额外开放端口资料1。这时 Docker 的作用是把 Python 运行时、检索依赖、模型客户端、配置文件打包成固定镜像,避免"本地能跑、线上缺库"的问题。需要特别注意:stdio 模式下协议消息走标准输出,调试日志必须写到标准错误,否则日志内容会被当成 JSON-RPC 消息,直接破坏通信资料1。这是非常容易踩坑的细节。
如果是团队共享或远程部署,例如多个 Host 访问同一套企业知识库,我会选择 Streamable HTTP。因为它更适合远程服务,但必须补齐认证、授权、会话管理、限流和超时这些能力资料1。这时 Docker 通常配合反向代理、密钥管理和健康检查使用:容器只暴露服务端口,不把敏感配置写进镜像层;日志输出到标准错误或统一采集通道;审计日志记录谁在什么时间调用了哪个 Tool、关键资源范围和结果状态,并对敏感字段脱敏资料1。
我不会一开始就上复杂集群,而是先把接口边界、权限模型、结构化错误和可观测性做稳,再根据调用量决定是否拆分检索服务和 MCP 层。
面试官:你刚才提到可观测性,如果这个 Tool 上线后被模型频繁调用,或者召回质量不好,你怎么排查?
候选人 :结论是围绕"调用行为 "和"检索质量"两条线做观测,而不是只看服务是否 200。
调用行为侧,我会记录:Tool 名称、调用时间、调用方标识、脱敏后的查询、过滤条件、返回条数、耗时、错误码、是否触发限流、是否命中权限拒绝。这些审计字段是安全要求的一部分资料1。
检索质量侧,我会记录:实际召回使用的 query、是否触发重排、每个结果的分数区间、来源分布、空结果率、用户后续是否追问"没找到"。因为 RAG 切块大小会影响语义完整性和上下文占用,结果又必须保留来源元数据,所以排查时需要能回溯到具体 chunk 和文档版本资料1。
一个关键取舍是:不要把完整检索片段和用户敏感信息全部打进日志,否则既增加泄露风险,也会让日志成本失控。我的做法是记录 chunk_id、score、source_uri 和必要的摘要,正文只在调试链路中按需查看。
面试官:最后一个问题:如果让你给出一个最小可落地版本,接口上你会怎么定义?注意不要写你不确定的 SDK 细节。
候选人:我会采用版本无关的接口设计,先定义 Tool 契约,再用官方 SDK 实现。
Tool 名称建议语义清晰,例如 search_knowledge_base。描述中说明:"根据查询词检索内部知识库,返回相关文本片段及来源;只读,不执行写入操作。"
输入 schema 核心字段: - query: string:用户问题或检索语句; - top_k?: integer:返回结果数量; - filters?: object:知识库、文档类型、时间范围、权限标签等过滤条件; - need_rerank?: boolean:是否进行语义重排。
输出对象核心字段: - results: array:每项包含 chunk_id、title、source_uri、snippet、score、metadata; - warnings: array:空结果、权限裁剪、降级提示; - error?: object:失败时返回结构化错误码和是否可重试。
伪代码层面,Server 的处理流程是: 1. 解析 Tool 调用参数; 2. 做参数白名单校验和调用者授权; 3. 构造检索请求,必要时做查询改写; 4. 调用向量/关键词检索,可选重排; 5. 按权限过滤结果,裁剪片段长度; 6. 返回结构化 JSON,并写审计日志; 7. 异常时返回明确错误,不泄露内部堆栈和凭据。
如果使用 Python 技术栈,应选择官方维护的 MCP Python SDK 来实现 server、tool 注册和传输适配,它支持 stdio、Streamable HTTP、SSE 等标准传输资料4。具体 API 以所选 SDK 版本文档为准,避免跨版本猜测方法名。
面试官点评
考察点: - 是否理解 MCP 中 Tool、Resource、Prompt 的语义边界; - 是否知道 RAG 封装为 MCP Tool 的适用场景与风险; - 是否能设计稳定的结构化输入输出,而不是只拼自然语言; - 是否掌握 stdio 与 Streamable HTTP 的部署差异,以及 Docker 的真实作用; - 是否具备安全边界意识:输入不可信、文档不可信、授权不能省、日志要脱敏; - 是否考虑异常、空结果、可观测性和工程取舍。
合格回答: - 能明确说明检索应封装为 Tool,并解释为什么不是 Resource 或 Prompt; - 能设计包含来源元数据的结构化返回,说明其对引用、排错和客户端处理的价值; - 能指出 schema 不等于安全,必须在服务端做授权和输入校验; - 能说清 stdio 适合本地子进程、HTTP 适合远程服务,并知道日志输出位置对 stdio 的影响; - 能给出空结果、超时、权限不足等情况的处理思路。
加分项: - 能主动提到"检索文档也是不可信数据",避免提示词注入覆盖系统规则; - 能区分"模型触发检索"和"Host 前置检索"两种 RAG 接入方式,并说明前者灵活但需控制调用次数和权限资料1; - 能说明审计日志需要记录调用主体、Tool、资源范围和结果状态,同时脱敏敏感字段; - 能在不臆造 SDK 细节的前提下给出稳定接口契约,体现工程落地能力。
总结
把知识库 RAG 检索封装为 MCP Tool,核心不是"把搜索接口包一层协议",而是在 MCP 的模型可控调用模型下,重新处理好能力边界、结构化契约、安全校验、部署传输和可观测性。Docker 解决的是运行环境一致性和部署隔离,不替代传输选型;结构化输出解决的是模型消费、客户端展示和排错问题,不替代检索质量优化。最容易踩坑的细节,就是 stdio 模式下把调试日志打到标准输出,导致 JSON-RPC 通信被污染;以及误以为参数 schema 或"来自 AI 应用"就代表可信,最终在权限和输入校验上出问题。真正可上线的 MCP RAG Tool,一定是"只读语义清晰、返回可追溯、错误可诊断、权限可收敛"的服务。