前阵子有个内部项目找上门,需求写得很简单:把公司知识库接进大模型对话,做一个MCP套壳。字面上的工作量不超过三天——一个MCP Server、两个Tool、一条检索接口。可后来事情失控了:这个“套壳”最后长成了一个带文献检索、多模型交叉验证、结论溯源的研究决策系统。标题这句话,是我盯着代码仓库时冒出来的。这篇就聊聊为什么一个薄壳会“长”成系统,哪些需求是伪需求,哪些需求才真正改变了架构走向,顺带把踩过的坑和能直接抄作业的关键配置一起列出来。
1. 从“套壳”到“系统”:不是失控,是“壳”这个词骗了你
1.1 套壳MCP的原始画像:一层薄薄的协议外壳
所谓套壳MCP,本质上就是把一个大模型能力包装成符合MCP协议的“服务”。MCP的全称是Model Context Protocol,模型上下文协议,近两年在AI工具链里普及得很快。它做的事情,你可以理解成一个“USB-C接口”:无论你用什么宿主,只要对方支持MCP,你的服务就能被接进去。这里不需要关心对方的内部实现,也不需要重新写接口文档。
我接到需求时画出来的结构图非常简单:
- 一个MCP Server进程
- 注册两个工具:检索知识库、生成摘要
- 用stdio或HTTP暴露给宿主
核心代码大概一百行以内就能跑起来。当时我给项目取了个很朴素的代号“套壳X”,意思是“薄薄一层,能通气就行”。我甚至没有考虑数据库设计——检索直接走现有接口,摘要直接调模型API。现在回想,这个阶段的“没想到”才是后来一切变化的起点。
1.2 需求的第九个词才是真相
用户最初提需求的时候,原话很短,核心词是“让对话能查到资料”。这听起来像是一个搜索增强功能。但等到真的把检索结果返回给用户之后,对方马上冒出一句话:“能不能告诉我,我从哪份文档得出这个结论?”这个问题一出来,“套壳X”就回不去了。因为“结论”和“来源”是两码事,前者是模型根据检索结果生成的回答,后者需要系统在生成过程中保留引用关系、原文编号、上下文片段。如果你只在表面套了一层MCP壳,模型吐出来的话根本追溯不到具体出处。
紧接着第二个问题也来了:“两篇文档的说法冲突,你帮我评估哪边更可靠。”这一步要从“检索信息”变成“分析信息”,已经需要多来源比对、冲突检测、可信度判断。第三个问题更直白:“以后的研究日报能不能自动按这个格式输出。”这时候,套壳已经不只是一个协议适配器,而是一个要负责任的支持系统。我把这三次追问总结成一句话:用户要的从来不是“接口”,而是“决策支持”。“套壳”只是他描述需求时最顺手的一个词,并不是他真正想要的边界。
1.3 三次清晰的“长成”节点
我后来复盘,把系统的演进过程切成了三个节点:V1无状态问答,提问→检索→生成回答,支持简单链接引用但模型无法对结论负责;V2带状态的分析助手,支持多轮研究目标、子问题拆解、证据暂存,用户可以追问“为什么”;V3研究决策系统,在V2基础上加了证据链校验、冲突检测、默认输出结构化结论、自动回归评测。每一步升级都不是我主动加戏,而是被真实使用场景逼出来的。
V1交付后的第一天,用户试了三个问题,前两个很满意,第三个是“查一下近两年关于模型评估的综述,对比三家的方法”。这时模型一次只能带一个上下文,检索工具返回了一大堆论文摘要,但在单轮对话里,模型没有能力组织跨论文的对比。我需要让它分步骤思考:先查第一组,再查第二组,最后汇总。这个需求直接催生了V2里的状态与子任务调度。V2上线后,用户开始拿它写内部评审意见。评审意味着结论要负责任,因此“来源”必须有,这个需求又催生了V3的证据链。这套演进路径非常有代表性:不是项目计划里写出的架构,而是用户每次多问一句“然后呢”,系统一步步长成了现在的样子。
1.4 为什么挂在MCP协议上,而不是全部自己写
在V2阶段,我一度动过把工具调用全换成自研接口的念头,觉得更自由。但很快发现不对劲:研究决策系统需要的工具太多了。文献检索、数据看板、统计接口、文件读取、验证服务,每个工具都有自己的认证方式、参数规范、返回格式。如果每个都自研接入,等于是给宿主客户端写了一堆强耦合的插件,每加一个工具都要改一遍客户端代码。
MCP的价值恰好是标准化。它规定Tools、Resources、Prompts三个原语,把工具的发现、调用、认证统一起来。我的系统只需要实现MCP Server,任何宿主的MCP Client都能直接发现并调用我的工具。这就好比你把设备的接口都统一成USB-C,插哪个外设都方便。所以,即使“套壳”这个起点很薄,协议本身并不薄。它给了我一个长期不腐化的底座,后续每加一个工具,都只是往MCP Server里多注册一个方法,而不需要动宿主、动客户端、动调度器。
2. 研究决策系统到底“长”成了什么架构
2.1 四个核心模块,分工各有讲究
我最后做出来的系统,可以拆成四个层:
- 工具接入层:一组MCP Server,暴露检索、数据、文件、验证等能力
- 信息组织层:查询改写、结果去重、相关性重排、上下文裁剪
- 推理与论证层:多轮任务调度、证据收集、子问题拆解、冲突检测
- 决策输出层:结构化结论、置信度标注、引用溯源、行动建议
工具接入层的责任是“受控暴露”。注意,这里不是把数据库直接连给模型,而是暴露受白名单约束的检索函数。比如文献检索工具的入参只能是query、top_k、date_range这些,出参固定是论文列表,含标题、摘要、链接。信息组织层解决的是“多工具结果混在一起”的问题。一个复杂提问会触发文献搜索、内部文档搜索、统计数据查询三个工具并行返回。如果直接把几万字塞给模型,效果很差,成本也高。信息组织层会把结果按相关性重排、去重,再用一个抽取模型把最关键的片段拆出来,组成一个紧凑的证据包。
推理与论证层是系统区别于普通QA问答的本质。普通问答是一次性的:问题→上下文→回答。研究决策系统不是,它把问题拆解成子任务、逐项收集证据、暂存中间结论、最终汇总。有点像把一个“大问题”切成几块“小作业”,每块都附上证据,再拼成最终答案。决策输出层则负责把结论做成能被“使用”的东西,而不是一段散文。输出格式是结构化的:结论、支持证据列表、反方证据列表、置信度、未解决风险。这样下游可以拿这个数据结构去生成报告、推送日报,或者归档。
2.2 MCP在架构里的位置
有人会觉得,MCP只是协议,不参与系统设计。但实际恰恰相反,MCP在这个系统里承担了“工具总线”的角色。我用的最小角色划分是:
| 角色 | 在系统中的职责 | 举例 |
|---|---|---|
| MCP Host | 持有会话状态与调度逻辑 | 工作台客户端、命令行工具 |
| MCP Client | 发起工具调用 | 系统内的调度服务 |
| MCP Server | 提供Tools、Resources、Prompts | 文献检索服务、数据查询服务 |
这种划分带来的好处是:系统本身的调度层只需要穿一件“MCP Client”的马甲,就能调用所有工具。消息格式、返回格式、错误处理全部统一,不需要为每个工具单独写适配器。这也解释了为什么“套壳”会长成系统:MCP本身太适合加工具了。加一个检索工具,是一个方法;加一个验证工具,还是一个方法。系统的复杂度不是被协议绑定的,而是被能力放大的。
2.3 一份可以直接照抄的工程目录
我最后整理出了这么一份最小可运行的工程结构,不是完整代码,而是目录与职责对齐:
research-decision/ src/ mcp_servers/ literature.py # 文献检索服务 dataset.py # 数据查询服务 validate.py # 验证与交叉检查服务 orchestrator/ scheduler.py # 子任务调度与状态管理 context.py # 上下文组装与裁剪 reasoner/ planner.py # 研究问题拆解 evidence.py # 证据收集与冲突检测 output.py # 结构化结论生成 eval/ cases.jsonl # 回归评测集 compare.py # 新旧版本对比 config/ tools.json # 工具注册表配置工具注册表里写的是每个工具的MCP描述,这部分非常关键,后面避坑环节我会展开讲。评测目录一开始我是没有的,后来才补上,它对系统稳定性的帮助远远超出预期。
3. 长成系统的过程复盘:每个变化都不是白来的
3.1 从单工具到工具矩阵:为什么单一工具撑不住研究场景
最初的套壳X只有一个检索工具。用户问“某方法在三个数据集上的表现如何”,检索工具返回的是一堆论文列表,模型只能从摘要里猜答案,经常是表面正确、细节错误。单一工具的问题在于,它把“找信息”和“验信息”混在一起了。检索结果本身是二手信息,需要原始数据、统计接口、完整文本来交叉验证。
于是我在V2阶段一口气加了六个工具:文献检索、内部知识库检索、数据集统计接口、全文抽取、数字校验(检查引用数字是否和原文一致)、格式转换。工具矩阵给系统的推理能力带来了质的提升,但也立刻带来了新问题:模型面对一长串工具列表,选错工具、给错参数的频率变高了。这逼着我做两件事:一是给每个工具写非常详细的描述,二是调整工具注册表里的顺序,让高价值工具排前面。把这条经验前置:如果你的MCP工具能被模型准确调用,你的系统已经稳定了一半。描述写得不到位,后面所有优化都白搭。
3.2 从无状态问答到多轮状态机:研究本来就是一连串追问
V1最让我头疼的,是模型记不住研究上下文。用户问“对比A和B两种方法”,模型回答完就算结束了。用户再追问“那A在小样本下有问题吗”,模型已经把A、B的中间结论忘了,只能重新检索。要支持研究决策,核心状态必须持久化。
我设计了一个简单的状态机,状态对象包含四个字段:研究目标,用户本次要解决的问题;已收证据,从各工具拿到的关键结果集合;待验证假设,当前还没有定论的中间判断;结论草稿,已完成的子问题结论。每次用户提问,调度器先加载这个状态,再决定是补充检索还是直接基于已有证据回答。这个设计让“追问”变成系统第一公民,而不是每个问题都被当独立请求处理。状态机的引入是系统从“对话工具”进化成“决策支持工具”的分水岭。
3.3 从“给答案”到“给证据链”:让模型无法甩锅
研究决策系统最大的敌人是“权威感错误”——模型用一套听起来很专业的术语,给了一个完全是编造的结论,用户信了,然后出问题。这个问题靠调prompt是治不了的,必须从输出约束层面解决。
我在V3里做了一件很硬核的事:所有结论对象必须包含evidence_id列表,且每个evidence_id必须对应工具返回的真实内容。模型在生成最终结论前,调度器先把工具返回结果的原文片段传给它,并强制它引用这些片段的编号,而不是自己编。实现上就是要求模型输出遵循一个JSON Schema:
{ "conclusion": "综合现有证据,A方法在小样本场景下更稳定", "confidence": 0.78, "support_evidence": ["lit_001", "data_002"], "risks": ["样本量小于30时置信度显著下降"] }后校验器再检查support_evidence里的ID是否真实存在。如果ID不存在,输出直接标红,提示“结论不可信”。这一层机制让系统在“负责任”这件事上有了底线。
3.4 从手工调prompt到回归评测:系统复杂度必须用数据守住
系统长到V3,prompt、工具描述、调度逻辑都已经很复杂。有一阵子我改了某个工具的排序,第二天才发现之前能正确回答的问题开始胡说八道。问题出在:我改的是工具注册表,影响的却是推理结果,但这种影响不会立刻爆出来。
解决办法是建回归评测集。我准备了大约60个历史典型问题,分四类:事实查询、方法对比、风险分析、报告生成。每次改完配置,自动跑一遍这60个问题,把新旧输出做比对,看核心结论是否保持一致、证据链是否完整。这个动作让系统重新变得“可维护”。它也是判断你的应用是不是真的长成了系统的标准之一:如果改动一个配置你需要担心产生蝴蝶效应,说明复杂度已经超越了手工调试的边界,必须有自动化评测兜底。
4. 实操避坑:真正卡住我的几个坎
4.1 MCP工具注册了,模型就是不调用
这个坑几乎一定会遇到。我第一次写文献检索工具,描述就一行:“搜索文献”。结果模型只在用户明确说“搜索”时才调用,稍微绕一点的提问就无视工具,直接凭训练数据回答。比如用户问“这个方法在小样本上的表现如何”,文献里明明有答案,模型却选择自己编。
原因是工具描述和参数说明没有触发语义联想。后来我把描述展开成了带场景、触发条件、参数示例的完整说明:
工具:文献检索 用途:当用户的问题涉及研究方法、实验结论、模型性能时,必须使用本工具获取最新信息。 触发条件:提问中出现特定方法名、数据集名、研究问题关键词,或用户要求查证、溯源、对比。 参数说明: query:核心关键词,建议把主题词和限定词一起写,例如“小样本学习 模型评估” top_k:返回数量,默认5,研究综述类问题建议10改完之后,调用命中率高了一截。这条经验概括成一句话:工具描述不是文档,是搜索引擎的索引文案,要站在模型理解的视角去写。
4.2 多工具并行返回,上下文直接爆窗
研究问题一做复杂,信息组织层会同时触发三个工具,每个工具返回几百行结果,加起来好几万字。模型上下文窗口再大也扛不住,而且信息太多会稀释关键信号。我的处理方式是“三段式裁剪”:先全局去重,再做相关性重排,最后只取每个结果里最关键的段落,比如摘要前两句话、表格结论行。裁剪后的证据包控制在模型能稳定处理的长度内,大概会让成本下降三到五成,答案质量反而提升。
另一个补充方案是“分块处理”:子任务统一调度,每个子任务只带自己需要的证据,不让全局上下文无限膨胀。这个方案对应的是上一节的状态机设计,两者配合效果最好。建议在MCP工具返回时就限制字段长度,而不是等到组装上下文时再后悔。
4.3 模型自信地胡说,怎么压都压不住
这是研究决策系统最刺痛的部分。我尝试过在prompt里写“请确保事实准确,不要编造来源”,模型偶尔还是会编。后来发现,光靠提示根本不够,必须在模型输出之后加一道“物理校验”。校验手段有两类:一类是结构约束,前面说过的JSON Schema加evidence_id强制引用;另一类是事实验证,把模型引用的每一条evidence重新跑一遍工具,如果结果对不上就拒绝采信。双通道叠加之后,胡说的空间被压到很低。
还有很多细节:温度调太低(小于0.2)会让模型在复杂推理时思维固化,反而不利于发现矛盾;调太高又会开放联想。我最终的权衡是事实性任务用0.3,动态决策类任务用0.4。不要照着某个绝对数值抄,要根据评测集调。
4.4 成本悄悄上涨,账单比代码先失控
研究决策系统热爱检索和验证,但这两件事都是烧钱大户。我在V3初期不关心上下文大小,每个子任务都全量塞证据,结果token消耗翻了好几倍。我现在固定用三招控成本:按难度路由,简单的查询走小模型处理,只有需要深度推理时才上大模型;缓存命中,相同问题或相似证据结果直接复用,不做重复计算;按需降级,非核心步骤比如格式转换、摘要压缩用轻量模型,抽检效果。这套组合打下来,单位问题的平均处理成本大概降了一半,而且答案质量几乎没有下降。
4.5 权限与安全:工具越多,攻击面越大
MCP Server把工具暴露给模型调用的同时,也把一个“网络攻击面”带进来了。潜在风险是恶意输入诱导模型调用危险工具,比如越权读取文件、执行命令、向外部接口发请求。我的做法是:白名单机制,每个MCP Server只注册被允许暴露的方法,宁可少一个工具也不多放一个风险口子;入参校验,所有工具参数都做类型和范围校验,不合法直接拒绝;操作审计,每次工具调用都记录时间、参数、调用方、返回结果,一旦出问题可以精确回溯。这套权限设计并不复杂,但它决定了这个系统能不能真正落地生产。如果只是在实验室里自娱自乐,可以跳过;如果要给业务团队用,安全与权限一定要一开始就划线。
4.6 别写“万能工具”
最后一条个人经验,也是踩过最多坑的地方:不要试图写一个“万能工具”。比如我在V2阶段写过一个“综合查询工具”,想着什么都能查,结果因为描述太泛,模型不知道该传什么参数,经常把query写成一段很长的自然语言,工具内部还得做解析,稳定性极差。后来我把“综合查询”拆成“文献检索”“内部文档查询”“数据统计”三个细粒度工具,每个工具只做一件事,描述清晰,参数明确。模型反而更容易做出正确选择。工具粒度原则很简单:每个工具解决一个问题,宁可数量多一些,也不要做一个什么都做、什么都做不好的怪物。
5. 直接能抄的最小方案
5.1 从零到研究决策系统的四步
如果你也想做相似的东西,我建议按这个顺序推进,每一步都是前一步验证后再走:
先搭最小MCP Server。只注册一个检索工具和一个摘要工具,用一个客户端跑通“对话→调用工具→返回结论”的完整链路。这一步目的就是验证MCP协议通不通,别贪多。
再加信息组织层。给检索结果做去重、重排、裁剪,让模型输入变成一个紧凑的证据包。做完这一步,你可以明显感觉到回答质量和稳定性提升。
然后加证据链约束。强行要求所有结论带evidence_id,输出结构化为JSON。这步是系统从“能聊”变成“能负责”的关键跳跃。
最后建回归评测集。积累至少几十条代表性题目,每次改配置都跑一遍。这里可以安排自动比对流程,不通过的配置不允许上线。
这四步的节奏刚好和系统的四个核心模块对应:先有壳,再有管道,再有机制,最后有护栏。
5.2 关键配置速查表
我整理了一个配置速查表,覆盖我项目里最常用的几项,方便你直接参考:
| 配置项 | 建议值 | 说明与注意点 |
|---|---|---|
| 工具描述长度 | 80-150字 | 太短没有语义触发,太长稀释模型注意力 |
| 检索top_k | 默认5,综述类10 | 太少漏信息,太多挤占上下文 |
| 温度 | 事实性0.2-0.3,决策类0.4 | 低温度压幻觉,但过低会影响复杂推理 |
| 证据包最大长度 | 不超过模型稳定处理长度的70% | 留出输出与推理的token余量 |
| 缓存时长 | 知识库数据1小时,动态数据10分钟 | 太短没用,太长信息过期 |
| 评测集规模 | 至少60条 | 覆盖事实、对比、风险、报告四类 |
这些值是我项目里调出来的,不一定适合你的数据。真正的用法是建好评测集之后,用A/B对比测,谁给你的指标高就用谁。
5.3 建议的技术选型
一个轻量的技术组合是这样的:服务端用Python,配合MCP官方SDK或社区常见的轻量封装来搭建Server;状态管理用带存储的关系型数据库或简单的文件存储都行;工具注册表配置用JSON文件维护,方便管理;评测脚本用Python写,跑在CI或本地定时任务里。这套选型没有特别前沿的东西,但足够稳。它保证了套壳MCP这个起点不会因为选型过于花哨而偏离主线。
6. 几句大实话
做这个项目最大的体会是:软件架构不是设计出来的,是长出来的。如果一开始就想着“我要做一个研究决策系统”,八成会设计出一堆用不上的模块;但从一个薄薄的MCP套壳开始,被真实需求一步步推着走,反而长出了特别贴合实际的结构。
第二句实话是:套壳这个词被人看轻了。套壳不是简单包装,它是在协议边界上做适配。只要是做研究决策这类严肃场景,壳外的协议标准化和壳内的证据链路一样重要。
最后分享一个小技巧,也是踩过几次坑之后最想早知道的一条:当系统长到第五个工具以上,记得先写回归评测集,再改任何配置。这个顺序能帮你省下大量修复“未知回归”的时间。