公司动态
从平铺到编排:解决法律Agent多技能集成准确率下降的架构演进
1. 项目缘起当“法律专家”Claude开始“胡说八道”最近在折腾一个挺有意思的项目叫Claude-for-Legal。顾名思义这是基于Anthropic的Claude模型专门为法律领域微调或构建的一个智能体Agent。它的愿景很美好能理解复杂的法律条文、分析案例、起草合同甚至预测案件走向堪称一位不知疲倦的AI法律助理。然而在实际部署和测试中我和团队遇到了一个非常典型且棘手的问题当我们为这个法律Agent平铺即同时启用或集成多个Skill技能或MCPModel Context Protocol模型上下文协议服务器时它的回答准确率会出现显著且难以预测的下降。比如你同时让它使用法律条文检索RAG知识库、案例查询另一个MCP和合同格式检查一个Skill它可能会把不同来源的信息张冠李戴或者在需要精确引用法条时给出一个模糊甚至错误的解释。这可不是小事。在法律场景下准确性就是生命线。一个错误的法条引用或案例解读轻则闹笑话重则可能引发严重的误导。这个问题不解决Claude-for-LLegal就只能停留在玩具阶段无法投入实际应用。所以这篇内容就来深度剖析一下Claude-for-Legal这个项目并聚焦于解决“多Skill/MCP平铺导致准确率下降”这个核心痛点。我们会从问题现象入手拆解其背后的技术原理最后给出经过我们实战验证的解决方案和架构优化思路。无论你是正在开发垂直领域Agent的工程师还是对RAG、MCP、Skill编排感兴趣的研究者相信都能从中获得启发。2. 核心概念拆解Skill、MCP与Agent在法律场景下的协同与冲突要解决问题首先得理解问题中涉及的几个关键角色。它们听起来可能有些抽象但我们可以用律师事务所的团队来类比。Claude-for-Legal (Agent)这就是我们团队的核心“合伙人律师”。它拥有深厚的法律知识基础来自Claude模型的预训练和可能的领域微调负责最终面对客户用户理解问题组织答案并做出判断。它的“大脑”就是大语言模型LLM。Skill技能可以理解为这位合伙律师掌握的“专项技能”或“内部工作流程”。比如合同审阅Skill一套固定的分析模板和检查清单律师拿到合同后会自动套用检查关键条款。法律文书生成Skill根据案件类型和客户信息自动生成起诉状、答辩状等文书的初稿。法律检索Skill内部版一个封装好的函数能按照特定格式查询本地法规数据库。 Skill通常是编写好的、相对固定的代码或提示词Prompt模板直接内嵌在Agent的“工作记忆”或调用流程中。它的特点是响应快、流程固定但灵活性和外部信息获取能力可能有限。MCPModel Context Protocol服务器这就像是律师事务所合作的外部专家团队或专业数据库服务商。Anthropic推出MCP就是为了让Claude等模型能安全、标准化地调用外部工具和数据源。法律条文数据库MCP一个实时更新的全国法律法规库Claude可以通过标准协议向它查询“《民法典》第584条的具体内容是什么”裁判文书网MCP连接至公开的司法案例数据库可以检索类似案例。企业信息查询MCP连接至工商信息数据库核实对方当事人资质。 MCP的核心价值在于标准化接口和动态数据获取。Agent不需要知道数据库的具体实现只需要通过MCP协议发送请求就能拿到最新的、结构化的外部信息。这极大地扩展了Agent的能力边界。RAG检索增强生成知识库这是律师事务所的内部核心知识库包含了历年的典型案例总结、内部办案指引、合伙人笔记等非公开但极具价值的资料。当遇到新案件时律师会先从这个知识库里检索相关历史经验和资料再结合自己的分析形成观点。在技术实现上RAG通常通过向量数据库存储知识片段在用户提问时进行语义检索并将检索到的相关文本作为上下文Context注入给LLM从而生成更准确、更具针对性的回答。那么冲突是如何产生的想象一下这个场景我们的“合伙人律师”Agent同时面对多位“专家”MCP服务器和多项“内部流程”Skill。当客户用户提出一个复杂问题比如“起草一份涉及股权纠纷的借款合同并评估我方风险”时合同起草Skill被触发开始套用借款合同模板。法律检索MCP被调用去查询《公司法》、《合同法》相关条款。案例查询MCP被调用寻找类似股权纠纷的判决。RAG知识库被检索查找所内处理过的类似合同范本和风险点。问题来了所有这些信息——固定的模板、动态的法条、具体的案例、历史的经验——几乎同时涌向Agent。LLM的上下文窗口Context Window是有限的比如200K tokens。信息过载会导致几个问题注意力稀释关键信息如某个特殊法条被淹没在海量上下文中模型无法有效聚焦。指令冲突不同来源的信息可能存在细微差异或表述不同模型需要花费大量“精力”去理解和调和这些冲突而不是专注于生成最佳答案。优先级混乱模型难以判断哪些信息是当前任务最相关的可能导致它过于依赖某个Skill的固定输出而忽略了更重要的MCP实时检索结果或者相反。幻觉加剧在混乱的上下文中模型“捏造”事实即产生幻觉的概率会大大增加因为它试图从相互竞争的信息碎片中拼凑出一个合理的答案。这就是“平铺”架构的弊端简单粗暴地让所有能力同时待命看似强大实则引入了巨大的噪声和决策复杂度最终导致输出质量下降。在法律这种高精度要求的领域这种下降是致命的。3. 问题根因深度剖析为什么平铺架构会“失准”上一节我们看到了现象这一节我们深入到技术层面看看“失准”究竟是如何发生的。这不仅仅是信息太多那么简单而是涉及LLM工作机理、上下文管理、任务调度等多个层面的系统性问题。3.1 上下文污染与“注意力劫持”LLM包括Claude本质上是一个基于概率的序列预测器。它的输出严重依赖于我们提供的输入上下文Prompt Context。当我们把多个Skill的指令、多个MCP返回的原始数据、RAG检索出的多段文档不加处理地全部塞进同一个上下文窗口时就制造了一个高度“污染”的环境。无关噪声干扰对于“评估借款合同风险”这个任务RAG知识库里关于“劳动合同”的片段、MCP返回的“诉讼程序法条”可能都是无关噪声。它们会占据宝贵的token位置并分散模型的注意力。模型需要消耗计算资源去“理解”这些无关信息并努力将它们与当前任务建立可能并不存在的联系这直接损害了核心任务的推理质量。格式与指令冲突不同的Skill和MCP输出格式各异。一个Skill的输出可能是Markdown列表另一个可能是JSON。一个MCP返回纯文本法条另一个返回带注解的案例摘要。模型需要不断切换“解析模式”这增加了认知负荷。更糟糕的是如果两个来源对同一概念有不同表述例如对“不可抗力”的定义略有差异模型就会陷入困惑其输出会变得模糊或自相矛盾。关键信号被淹没假设有一条来自MCP的、非常关键的司法解释但它被埋在了几十条其他法条和案例中间。由于Transformer架构的自注意力机制虽然是全局的但在有限的计算深度内过于分散的信息会导致对关键信号的“注意力权重”被稀释。模型可能“看到”了这条信息但未能给予其应有的重视。3.2 任务调度与编排缺失“平铺”意味着所有组件处于平等、并发的状态缺乏一个智能调度器Orchestrator。这就好比让律所里所有律师和助理同时涌进会议室回答客户一个问题场面必然混乱。缺乏优先级判定系统没有机制判断对于当前用户问题是应该先执行合同起草Skill还是先调用法律检索MCP或者是先进行RAG检索获取背景知识不同的执行顺序会导致模型接收到截然不同的中间状态和信息流最终输出结果天差地别。缺少循环与迭代复杂的法律咨询往往不是一步到位的。它可能是一个多轮对话先厘清基本事实调用事实查询MCP再分析法律适用调用法条MCP最后评估风险结合RAG案例。平铺架构试图一步到位把所有环节的结果一次性扔给模型要求它完成所有推理这超出了当前模型单次处理复杂逻辑链的稳健能力。错误传播与累积如果第一个被调用的Skill或MCP产生了错误或低质量的结果例如检索到了不相关的法条这个错误结果会作为上下文的一部分传递给后续的模型推理步骤。在平铺架构下由于所有信息是同时呈现的模型没有机会在得到错误信息后“重新思考”或“寻求更正”错误会被固化在最终的输出中。3.3 提示词Prompt工程失效精心设计的提示词是引导LLM正确输出的关键。但在平铺架构下我们为单个任务设计的精密提示词会失效。系统指令被淹没我们可能在系统提示词System Prompt中严格定义了Claude-for-Legal的角色“你是一名严谨的中国律师回答必须基于现行法律法规...” 然而当上下文中充满了来自MCP的原始数据、Skill的中间输出时模型对自身角色的“坚守”会被削弱。它可能更像一个“信息汇总者”而不是一个“专业分析者”。少样本示例Few-Shot失效我们通常会在提示词中提供几个高质量的问答示例Few-Shot Learning来教模型如何回答特定类型的问题。但在海量的混乱上下文中这些示例的示范作用会大打折扣。模型难以识别当前问题应该匹配哪一个示例的模式。指令位置敏感性LLM对提示词中指令的位置有一定敏感性通常越靠前、越清晰的指令影响力越大。在平铺模式下用户的查询、Skill的输出、MCP的数据不断追加到上下文末尾最重要的初始指令系统角色、任务要求在相对位置上的“权重”会降低。3.4 评估与反馈环路断裂一个健壮的Agent系统应该有自我评估和修正的能力。但在简单的平铺调用中这一步是缺失的。无结果校验Agent调用了MCP拿到了数据就直接塞进上下文。它没有机制去判断这个数据是否相关、是否准确、是否完整。例如查询“股权质押”相关法条MCP可能返回了10条其中只有3条高度相关。平铺架构会把10条全部送入而一个智能的调度器应该能过滤或标注出那3条核心条款。无置信度评估对于Skill产生的中间结果比如一份自动生成的合同条款Agent无法评估其置信度。这个条款是模板化的稳妥选择还是一个需要重点提示用户审查的风险点平铺架构无法体现这种差异。总结来说平铺架构的本质问题是将复杂的、需要多步推理和决策的认知任务简化成了一个单步的、信息过载的文本补全任务。这违背了LLM当前的能力边界尤其是在法律这种高严谨性领域必然导致准确率崩塌。4. 解决方案从“平铺”到“编排”的架构演进诊断清楚了病因我们就可以对症下药。核心思路是将“平铺”的混乱架构升级为“编排”Orchestration驱动的、有序的、可评估的智能流程。这不仅仅是优化更是一种架构范式的转变。4.1 引入智能调度层Orchestrator这是最关键的一步。我们需要一个独立的“大脑中的大脑”或者说是“律所主任”来指挥Claude-for-Legal这个“合伙人律师”以及它背后的专家团队Skill/MCP。这个调度层可以是一个简单的规则引擎也可以是一个轻量级的LLM例如使用GPT-4或Claude Haiku进行任务规划。它的核心职责是意图识别与任务分解解析用户查询判断其属于哪种法律问题合同、咨询、诉讼、合规等并将复杂问题分解为一系列原子子任务。例如用户问“公司想辞退一名试用期员工怎么操作才合法”调度器解析识别为“劳动法-解除劳动合同”问题。任务分解子任务A检索《劳动合同法》中关于试用期解除的条款调用法条MCP。子任务B检索类似案例看司法实践中的认定标准调用案例MCP。子任务C根据A和B的结果生成一个具体的操作步骤清单和风险提示调用清单生成Skill。子任务D将以上结果整合成一份给HR的简要指引由主Agent完成。执行规划与依赖管理确定子任务的执行顺序。有些任务有依赖关系比如必须先有法条A才能进行案例对比分析B最后才能生成清单C。调度器需要管理这些依赖。工具选择与调用为每个子任务分配合适的Skill或MCP。例如对于“查询上海地区2023年劳动争议案件数量”这样的具体数据查询应该调用统计数据库MCP而不是让主Agent去“思考”或使用通用的法律检索Skill。技术实现参考伪代码思路class LegalAgentOrchestrator: def __init__(self, llm_client, skill_registry, mcp_clients): self.llm llm_client # 用于规划的小模型 self.skills skill_registry self.mcps mcp_clients def plan_and_execute(self, user_query): # 步骤1 意图识别与规划 plan_prompt f 用户问题{user_query} 你是一个法律任务调度专家。请将上述问题分解为一系列具体的子任务并指定每个任务的最佳执行工具SKILL或MCP。 可用的工具类型 - SKILL: 内部固定流程技能如 contract_review, document_generation, risk_checklist。 - MCP: 外部数据源如 law_retrieval, case_search, company_info。 输出格式为JSON列表每个元素包含task_description, tool_type, tool_name。 task_plan self.llm.generate(plan_prompt) # 解析为结构化任务列表 # 步骤2 按顺序执行任务收集结果 context [] for task in task_plan: if task[tool_type] SKILL: result self.skills[task[tool_name]].execute(user_query, context) elif task[tool_type] MCP: result self.mcps[task[tool_name]].query(user_query, context) # 可以对结果进行初步清洗和评估 cleaned_result self._evaluate_and_clean(task, result) context.append({ task: task[task_description], result: cleaned_result }) # 步骤3 将规划过程和所有子任务结果作为高质量上下文交给主Agent生成最终答案 final_prompt self._construct_final_prompt(user_query, context) final_answer self.llm.generate(final_prompt) # 这里可以用更大的主模型如Claude-3.5-Sonnet return final_answer def _evaluate_and_clean(self, task, raw_result): # 简单的评估逻辑例如检查MCP返回是否为空是否包含明显错误关键词 # 更复杂的可以实现一个“验证器”LLM来评分 if not raw_result: return [工具未返回有效结果] # 这里可以添加更多清洗逻辑如提取关键部分、格式化等 return raw_result[:1000] # 限制长度避免上下文过长4.2 实施动态上下文管理我们不能把所有中间结果都原封不动地堆给最终生成答案的主Agent。需要动态地、有选择地构建上下文。摘要与提炼对于MCP返回的大段法律条文或案例调度器可以先用一个小模型或让主模型快速处理对其进行摘要提取核心要件、裁判观点等只将摘要放入最终上下文。这极大地节省了Token并突出了重点。相关性过滤在将子任务结果加入上下文前进行相关性打分。可以计算子任务结果与用户原始问题的语义相似度过滤掉得分过低的内容。这能有效去除噪声。结构化组织不要将不同来源的信息混为一谈。在构建最终Prompt时明确地分块、标注来源。例如【法律依据】 来自法条MCP《劳动合同法》第三十九条劳动者有下列情形之一的用人单位可以解除劳动合同一在试用期间被证明不符合录用条件的... 【类似案例参考】 来自案例MCP(2023)沪01民终1234号判决指出用人单位以“不符合录用条件”解雇试用期员工需承担充分的举证责任... 【内部风险评估清单】 来自清单生成Skill1. 证据固定需有明确的录用条件书面文件... 2. 考核记录试用期考核需客观、有记录...这种结构化的上下文极大地降低了模型的解析难度使其能更精准地定位和引用信息。4.3 设计链式与树状工作流对于复杂问题采用链式Sequential或树状Tree-of-Thought的推理流程取代并行平铺。链式调用这是最基本也最有效的改进。A任务的结果作为B任务的输入。例如用户提问 - 调度器 - 法条检索MCP - 结果摘要 - 案例检索MCP用法条摘要作为查询关键词- 结果摘要 - 主Agent生成最终答案。每一步都基于上一步的精确输出避免了信息混乱。思维树ToT或思维图GoT对于非常复杂、存在多种可能路径的法律问题例如一个案件可能有多个案由可选可以让调度器或主Agent模拟出几种不同的分析路径分支分别调用不同的工具链去验证最后再评估、汇总所有路径的结果选择最优解。这模仿了律师的“多角度思考”过程。4.4 建立验证与回退机制为系统增加“质检”环节。子结果验证在调用某个MCP后可以设计一个简单的验证步骤。例如调用法条MCP后再用一个“法条一致性检查”的轻量级Skill或Prompt判断返回的法条是否与问题主题高度相关。如果相关性太低可以触发重新查询或标记为低置信度信息。最终答案验证在生成最终答案后可以将其中的关键主张如引用的法条号、提到的案例名反向抽取出来再次调用MCP进行事实核查Fact-Checking。如果发现不一致可以触发修正流程。回退策略当某个Skill或MCP调用失败、超时或返回空结果时调度器应有备选方案。例如主法律检索MCP失效时自动回退到备用检索接口或者提示用户“相关外部数据暂时无法获取以下分析仅基于模型内部知识”。4.5 优化提示词工程策略在编排架构下提示词可以设计得更精细、更有层次。分层提示词为调度器、各个Skill、验证器以及最终的主Agent分别设计专属的、高度优化的提示词。每个提示词只专注于一个简单的任务而不是试图用一个庞大的提示词解决所有问题。上下文标记与元数据在传递给模型的上下文中显式地加入元数据标记。例如LAW_SOURCE authorityhigh.../LAW_SOURCECASE_SUMMARY relevance0.8.../CASE_SUMMARY。这可以隐式地引导模型对不同来源和可信度的信息赋予不同的权重。强制格式化输出要求主Agent在最终输出时必须采用“主张-依据-分析”的严格格式并且依据部分必须明确指向上下文中的某个来源块例如“参见【法律依据】第一部分”。这不仅能提高答案的条理性也使得答案更易于事后验证和审计。通过以上五个方面的综合改进我们可以将Claude-for-Legal从一个容易“信息过载”而胡言乱语的“实习生”改造为一个在“律所主任”调度器指挥下有条不紊地协同“专家团队”MCP和“内部流程”Skill最终产出严谨、可靠法律意见的“资深合伙人”。架构的转变带来的是能力质变的可能。