十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

正交化Agent设计:解耦通用智能与业务深度

正交化Agent设计:解耦通用智能与业务深度 三个月前我接手了一个“改造Agent”的活儿公司现有的客服Agent在通用对话上还算机灵但一碰到具体业务流程就露馅另一个团队做的业务自动化脚本倒是把退款、改地址、催发货做得明明白白可稍微换一种问法就死机。两边都想融合结果每次融合都变成一场灾难——通用模型把业务规则当“闲聊”给带偏了业务代码又不断干扰模型的自由发挥。这不是个别现象几乎每个做Agent落地的团队都会撞上这堵墙通用智能和业务深度到底该怎么放进同一个系统里这篇文章想跟你聊一个我自己验证有效的设计思路把Agent的通用推理能力和业务领域知识看成两个“正交”的维度在架构上彻底解耦。你不需要是算法专家只要会看代码、有基本的系统设计概念就能理解下面这套做法。我会用一个真实项目订单售后场景从零搭一个“正交化Agent”讲清楚每一步为什么这么做并把我踩过的坑和排查经验全部整理出来。如果你正在做Agent开发或者正准备启动一个Agent项目这篇东西应该能帮你少走很多弯路。1. 当“通用智能”撞上“业务深度”一场每天都在发生的架构拉扯1.1 我踩过的两个极端要么是“什么都懂”的聊天机器人要么是“只会一件事”的死板流程先说说我见过最多的两种失败形态。第一种大家把Agent当作一个“万能大脑”。一个大模型把所有业务知识、行业术语、公司流程全塞进System Prompt里然后期待它既懂常识又懂业务。试过的人都知道结果它在闲聊测试里表现完美一旦用户说出“我上个月买的那双鞋左脚的鞋带孔有毛刺想换货但我原包装盒扔了”它就不知道怎么结合售后规则来判断能不能换。不是模型能力不行而是通用推理链被大量业务细节给稀释了模型分不清哪些是“常识推理”哪些是“强制规则”。第二种大家把Agent当作一个“定时脚本”。用传统编程把业务流程写死每一步都由代码控制模型只负责填几个槽位。这种系统的好处是稳定但代价是用户不能有一点偏离脚本同一个意思换个说法就识别不了跨场景的组合请求更是完全无能为力。这个月是这个流程下个月业务变了你改的不是一个参数而是一整条代码逻辑链。我之所以一直强调这是“架构问题”而不是“模型问题”是因为底层模型已经很强了问题是它被架错了位置。你让一个擅长逻辑推理的大脑去背一万条业务规则它当然会累你让一条精确的业务流水线去应对开放对话它当然会僵。1.2 为什么说“正交”是这个问题的最优解像正交编码器一样互不干扰又精确联动“正交”这个词在不同地方有不同含义。在数学里正交矩阵的特点是行向量之间彼此独立、内积为零在信号处理里正交IQ锁相解调靠两路相位差90度的本振信号把微弱的目标信号从强干扰里稳稳捞出来在工业控制里正交编码器用A/B两路相位差90度的脉冲判断旋转方向和位置我在一个电机项目里用过这玩意儿A相和B相哪怕有一根线接反你都得调半天。这些应用都有一个共同点两个维度互不污染但又配合得极其精确。Agent开发里的“正交”我把它定义为通用智能负责“怎么想”业务深度负责“知道该做什么”。两者不是竞争关系而是两个正交的坐标轴。通用智能管推理、分解任务、理解语义、生成回复业务深度管领域规则、流程约束、数据格式、校验逻辑。通用层不需要知道“七天无理由退货要满足什么条件”业务层也不需要知道“用户这句话的意图有哪三种可能性”。关键在于这两个轴必须在架构上各自独立而不是混在一个层级里。如果业务规则散落在提示词、Agent逻辑、外部工具三处那系统改一次业务就要动一次通用层这就破坏了正交性。反过来如果通用推理能力放进业务代码里硬编码那换一个底层模型就得重写整个业务层。真正稳定的Agent架构应该让通用层像一个“操作系统”业务层像运行在它上面的“应用”。你想升级模型、换推理策略业务App不用动你想调整退款规则、新增物流查询功能通用内核也不用动。这个思想听起来简单实际操作起来坑很多后面我会一步步展开。2. 正交化Agent设计的核心拆解让通用推理与领域知识各归其位2.1 内核只做三件事感知、决策、行动先忘掉“别人家的Agent框架”回到本质。一个Agent内核无论外面包装得多玄乎核心循环就是三件事感知Perception、决策Planning/Decision、行动Action。感知负责把外部的输入用户消息、系统事件、文件内容统一成内部可理解的格式。决策负责判断“现在要解决什么问题、需要哪些信息、分几步做”。行动负责执行具体操作调用工具、查数据库、调用子Agent、生成回复。正交化的第一原则是这三件事必须保持纯粹不能因为业务不同而改变。不管你在做客服Agent还是运维Agent感知应该用同一套语义理解模块决策应该用同一套任务分解策略行动应该用同一个工具调用协议。“业务深度”不能长在内核里否则这个内核就退化成专用脚本了。我在一个项目里试过把“订单状态判断”直接写进决策逻辑结果是这个Agent只能做客服。后来想扩展一个“工单风险预警”功能决策层全乱套了因为业务逻辑和推理逻辑已经缠成一团。正确做法是决策层只负责“我决定调用一个技能”至于这个技能里是不是查订单表那是业务层的事。2.2 业务深度不塞进Prompt而是外挂成“技能”和“工具”既然业务深度不能进内核那它应该放在哪我的实践答案是放在Agent外面的“技能层”通过标准接口被内核调用。要理解这个设计可以先想清楚“Skill”和“Agent”到底有什么区别。一个Agent是一个可以独立感知-决策-行动的完整智能体一个Skill则是一个能力包它知道“某个具体领域怎么做”。Agent可以拥有多个Skill然后根据任务动态挑选合适的Skill来执行。这就像你是一个项目经理Agent手下有法务专员、财务专员、技术专员Skill需要处理什么问题就调动哪类专员。你要是把法务细则全背在自己脑子里其他项目就没法干了。具体的实现上我会把“业务深度”拆成两个载体Skill偏重“流程判断”的业务能力。比如“售后退款Skill”知道退款申请应该按什么条件审批知道不同支付渠道的到账时限能够输出“可以退款”“需要人工复核”等结论。Tool偏重“操作”的能力。比如“查订单Tool”接收一个订单号就返回订单状态“发短信Tool”接收手机号和内容就完成发送。Agent内核通过统一的接口调用Skill和Tool但不知道它们内部是怎么实现的。这样带来的一个直接好处是你可以用不同的底层模型来跑同一个逻辑技能今天用本地部署的Hermes Agent明天切到云端模型Skill接口不变业务代码一行都不用改。如果你的项目比较小不一定要区分“技能”和“工具”。但一旦业务逻辑复杂到需要多步判断强烈建议分开否则工具注册表会变得臃肿意图识别也会跟着混乱。2.3 记忆与上下文管理的隔离设计很多Agent跑着跑着就“人格分裂”原因大多出在上下文管理上。你让通用模型记住“用户是VIP、当前订单编号、退款状态”又让它记住“公司文化、客服话术、语气规范”。这些信息全部拼在一个Context里模型确实都能看到但它分不清优先级结果就是关键时刻该用的业务上下文被无关信息冲淡或者该保持稳定的通用人格被业务异常数据带偏。正交化的第三板斧是把“记忆”也分层通用对话记忆记录用户偏好、话题历史、交互风格服务于通用推理。业务状态记忆记录当前业务流转到哪一步、已有字段值、待办动作服务于业务深度。领域知识库静态的规则文档、FAQ、政策条款需要时按需检索而不是全程挂在上下文里。我在项目里会用带命名空间的Memory对象比如memory.set(“business.order_no”, “A12345”)和memory.set(“user.lang”, “zh-CN”)分开存。查询时业务逻辑只能读业务命名空间通用推理只能读通用命名空间。这样看起来有点麻烦但能避免百分之八九十的上下文污染问题。隔离做好了你还可以更进一步给记忆加读写审计。哪个模块写了什么哪一段上下文被哪次工具调用修改过到时候出了问题一看日志就知道是谁干的。3. 从零搭一个“正交式”Agent完整实操与代码走读3.1 项目结构设计与依赖选择下面我带你从零写一个最小可运行的正交化Agent场景是“订单售后”。整个项目我们不依赖重量级Agent框架只用Python标准库加一个可选的OpenAI SDK接口目的是把正交化机制显式展示出来。如果你愿意也可以把这个结构映射到LangChain、Dify这类框架上思路是通用的。先看目录结构ortho_agent/ ├── agent/ │ ├── core.py # Agent内核只做感知/决策/行动 │ ├── memory.py # 分层记忆管理 │ └── tool.py # 工具与技能注册表 ├── skills/ │ ├── refund_skill.py # 售后退款判断技能 │ └── shipping_skill.py # 物流查询技能 ├── tools/ │ ├── order_tool.py # 查订单工具 │ └── notify_tool.py # 发通知工具 ├── runtime/ │ ├── openai_llm.py # 通用模型适配器 │ └── main.py # 启动入口 └── tests/ └── test_refund_flow.py这个结构其实就是把第2节说的“内核/技能/工具/记忆”落到了硬盘上。你注意看agent目录下没有业务代码skills和tools目录下没有模型调用。这就是正交的物理表达谁依赖谁一眼就能看出来。依赖选择方面我的建议是如果项目以试错和学习为主先用一个轻量的LLM封装层自己定义好接口等想清楚边界了再迁移到成熟框架。直接上重框架有一个风险——框架本身的抽象会“引导”你把业务逻辑写错位置。3.2 核心代码实现Agent内核、Skill接口、工具注册表我们先把正交化的“边界”用代码定义出来。第一步是定义Skill和Tool的接口。# agent/tool.py from abc import ABC, abstractmethod class BaseTool(ABC): 工具执行一个原子操作不包含业务决策逻辑 name: str description: str abstractmethod def run(self, **kwargs) - dict: ... class BaseSkill(ABC): 技能封装一个业务领域内的判断/流程逻辑 name: str description: str input_schema: dict {} abstractmethod def run(self, agent: Agent, user_input: str, context: dict) - dict: ...为什么要有两个不同抽象Tool是“没有脑子”的执行器输入什么参数就返回什么结果Skill是有“业务判断能力”的逻辑包它需要读取上下文、调用若干Tool、按规则做决策。我把Skill放在“业务流程层”Tool放在“基础设施层”。接下来是Agent内核。它只做标准循环感知输入、规划动作、调用Skill、返回输出。# agent/core.py from typing import List, Dict, Any from .memory import AgentMemory from .tool import BaseTool, BaseSkill class Agent: def __init__(self, llm_backend, memory: AgentMemory, skills: List[BaseSkill], tools: List[BaseTool]): self.llm llm_backend # 通用推理后端 self.memory memory self.skills {s.name: s for s in skills} self.tools {t.name: t for t in tools} def perceive(self, message: str) - Dict[str, Any]: 感知解析用户输入得到意图与关键实体 return self.llm.perceive(message, self.memory.read(user)) def decide(self, perceive_result: Dict[str, Any]) - str: 决策根据感知结果决定调用哪个技能 prompt f 你是一个通用任务调度器。用户输入已经被解析为如下意图和实体 {perceive_result} 在你的知识库中你有以下可用技能 {list(self.skills.keys())} 请只输出你要使用的技能名称不要输出任何解释。 如果所有技能都不匹配输出 unknown。 return self.llm.complete(prompt).strip() def act(self, skill_name: str, user_input: str) - str: 行动执行技能并把结果封装成自然语言回复 if skill_name unknown or skill_name not in self.skills: return 抱歉我现在还处理不了这个问题。 skill self.skills[skill_name] business_ctx self.memory.read(business) result skill.run(self, user_input, business_ctx) self.memory.write(business, result.get(state, {})) reply self.llm.say(f把下面的业务结果转成友好回复{result}) return reply def handle_message(self, message: str) - str: self.memory.write(user, {last_message: message}) perceived self.perceive(message) skill_name self.decide(perceived) return self.act(skill_name, message)这里有个重要设计Agent的decide是通用的它只依据“感知结果技能列表”做匹配不关心技能内部逻辑。如果你新增一个“物流查询技能”只需要在初始化时把它加进skills列表其他代码不用改。这就是“业务深度外挂”的落地。3.3 接入真实业务场景以订单售后为例现在我们来写一个具体的RefundSkill。它的职责是判断一笔订单能否退款并把判断依据记录下来。它会调用两个Tool一个查订单一个发送通知。# skills/refund_skill.py from agent.tool import BaseSkill class RefundSkill(BaseSkill): name refund description 处理售后退款申请判断是否符合退款条件 def run(self, agent, user_input, business_ctx): # 1. 让通用模型从用户输入中抽取订单号 order_no agent.llm.extract(user_input, order_no) # 2. 调用通用工具查询订单 order_data agent.tools[query_order].run(order_noorder_no) # 3. 业务判断这部分是领域知识写死也不怕 reasons [] if order_data[status] ! delivered: reasons.append(订单未送达不能直接退款) if order_data[refund_count] 2: reasons.append(该订单退款次数过多需要人工审核) if reasons: decision rejected summary ;.join(reasons) else: decision approved summary 符合退款条件 agent.tools[notify].run(phoneorder_data[phone], msg您的退款申请已通过) # 4. 返回结构化结果 return { decision: decision, summary: summary, order_no: order_no, state: {last_order: order_no, refund_status: decision} }这个Skill里面有一个问题需要专门说明为什么订单查询本身没有并到Skill里如果你把query_order的逻辑也塞进这个SkillSkill就会变得“胖”将来别的技能想查订单还得重新实现。把它拆成Tool之后物流Skill、发票Skill都能复用同一个查询入口。有人会担心这里用agent.llm.extract算不算“通用智能混进了业务层”。我的看法是抽取实体是通用能力不属于业务规则。只要Skill里不出现“如果是VIP客户且购买超过一个月才能退款”这种硬编码的业务策略这个Skill就仍然是“业务逻辑外壳”里面调用的还都是通用工具。3.4 运行效果与参数调优写完代码我们跑一个例子。假设底层模型用的是本地部署的一个通用对话模型温度设成0.2。控制台会是这样用户: 我上个月买的鞋有质量问题想退掉。订单号是 A20240601。 感知结果: {intent: refund_request, entities: {order_no: A20240601}} 决策结果: refund 技能执行: order_data{status: delivered, refund_count: 1} 业务判断: approved, 符合退款条件 通知发送: 您的退款申请已通过 Agent回复: 您的退款申请已经通过了我们会尽快为您处理。这里我建议新手把大模型的温度参数调低一点0.1~0.3尤其在decide这个环节。意图识别和技能路由属于“低随机性任务”温度太高容易选错Skill。相反在最后“生成友好回复”的环节可以适当把温度调回0.7左右让话术更自然。一个Agent里不同环节用不同参数这本身就是正交化思想在参数配置上的体现。如果你发现某个业务场景下模型经常“走神”换用更专注的小模型往往比加更多提示词更有效。我试过用一个7B模型做意图识别速度和成本都好看而且因为任务单一正确率比用大模型不低。4. 从“能跑”到“稳定”Agent开发中的常见问题与排查技巧实录4.1 agent execution terminated due to error先查工具再查上下文做Agent开发的人对这句话都熟agent execution terminated due to error看起来像是一句笼统的系统报错实际在正交化架构里它背后通常就是三种原因之一。第一是工具调用出了异常。比如Tool接收的order_no是None说明实体抽取环节失败了。这时候先不要去看模型而是看感知层有没有把订单号正确抽出来。我会在代码里给每个Tool调用包一层try...except并在异常信息里带上入参比如query_order received kwargs{...}。光是这一条就能省下一半的排查时间。第二是上下文里的业务状态过期。最常见的是用户先问了一单又突然问“那这单呢”模型可能把上一单的订单号又复用了一遍。正交化架构里业务记忆应该显式地隔离和清理。我建议在Skill结束时把无关的state字段删掉只保留本单业务流转必需的状态。很多Agent“跑飞了”不是模型傻而是上一个业务的状态没清干净。第三是推理链路太长导致模型失去焦点。如果你发现同一个错误反复出现试着把Skill拆小。一个Skill里如果既要判断退款、又要计算补偿、又要写工单那错误率必然高。拆成三个Skill让内核逐次调用每个Skill只做一件事稳定性会明显上升。4.2 输出漂移与“幻觉”的抑制正交化调试三板斧业务Agent最怕的还不是报错而是“一本正经地胡说八道”。比如用户问“我的快递到哪了”物流Skill明明只查到一个未发货状态模型的友好回复却是“您的包裹已达配送站”。这就是典型的输出漂移。遇到幻觉我有一套三板斧全部围绕正交化原则展开。第一板斧检查业务结果是否结构化、是否与回复隔离。上面的例子中Agent调用shipping_skill返回的是一个字典里面status pending模型却编造了“已达配送站”说明回复生成环节没有“守住底线”。正确做法是给LLM一个明确的指令比如“基于给定数据回复不允许添加未提及的事实”并且在提示词里把业务结果以JSON格式单独放一段不让模型自由发散。第二板斧缩小技能内部的自由空间。如果某个业务场景对准确性要求极高就不要让模型写自然语言结论而是让它输出受控枚举值。比如RefundSkill的decision字段可以预先定义为approved/rejected/manual_review三选一而不是让模型自由填。业务规则越严留给通用模型的自由度就要越小这就是“业务深度”对“通用智能”的正交约束。第三板斧做好回归测试。每改一次业务规则就把历史会话回放一遍重点看之前能正确处理的案例有没有被改坏。我习惯在tests目录里塞一批“黄金用例”用断言检查Agent返回里的业务结论字段。虽然不能完全自动化但能拦下大部分回归问题。4.3 框架与编排选型harness、skill、agent到底怎么分工市面上关于“Agent框架”“Agent编排”“Harness与Agent的区别”的讨论很多很多人被术语绕晕。我按自己踩坑后的理解用大白话给你捋一遍。Agent一个能独立做决策的智能体它“知道”自己可以调用哪些Skill和Tool并能根据任务选择路径。SkillAgent可复用的能力单元封装“某个领域的一整套做事方法”。Harness可以理解成“Agent的运行容器/执行环境”负责编排多个Agent或者Agent与外围系统的交互比如超时控制、权限校验、中间结果缓存都是Harness的职责。正交化设计里的一个分支标准是如果一段逻辑关心“怎么调用模型”那就属于Harness或内核如果它关心“业务上该不该这么做”那就属于Skill。把这两者混在一起写后面维护会非常痛苦。选型上我的建议是早期项目不要迷信“全家桶”框架。你自己用几十行代码实现一个极简内核之后对“Agent、Skill、Tool”这三者的边界会理解得特别清晰。然后再去用别人现成的框架你才能判断它的抽象是帮你还是坑你。反过来如果项目已经成熟直接用社区的编排框架可以少造轮子但一定要确保业务技能是作为独立模块接进去的而不是写进框架的配置里。5. 正交平衡不是“五五开”如何根据业务场景动态调整5.1 什么时候通用多、业务少探索型场景很多人一听“正交平衡”下意识认为就是通用智能占50%、业务深度占50%这是误解。两个维度可以独立伸缩它们的权重应该跟着场景走。像“行业知识问答”“竞品调研分析”这类探索型场景用户希望你放开思路、提供多元视角业务规则只起“边界”作用。这种场景里我在架构上会更偏重通用智能Agent的决策链更长允许它自由组合多个SkillSkill内部也只放最粗粒度的限制比如“不要回答超出公司经营范围的业务”。如果这时候你塞入大量流程约束用户会觉得这个Agent“死板”。5.2 什么时候业务深、通用少执行型场景反过来像“退款审批”“工单自动关闭”“风险交易拦截”这类执行型场景系统价值在于确定性而不在于发散。我的做法是弱化自由推理Agent内核只做低层级意图识别真正的动作完全由业务Skill主导通用模型只负责理解少量歧义表达。在这种场景里我甚至会让技能返回时携带一个strict_mode字段把回复模板固定住模型只负责往里填用户可感知的话术。你说这还算Agent吗算的因为Task分解和Tool调用仍然是动态的但它更像一个“戴着镣铐的Agent”镣铐正是业务深度对通用智能的合法约束。正交化的好处就在这里你不用为了“通用Agent”的愿景牺牲业务效率也不用以“业务脚本”为代价放弃智能交互。你可以按场景自由调节两个维度的强度而核心架构不需要推倒重来。5.3 用“正交解耦”反过来指导团队分工与代码组织最后说一点团队层面的体会。正交化不仅是一个技术架构它还能直接影响团队协作方式。在我现在的项目里负责通用能力的同学只维护agent内核和LLM适配层不碰任何业务代码负责业务的同学只维护skills和tools不需要关心模型从哪个端点来、上下文窗口多大。两边通过接口文档对齐效率比之前“大家一起改同一个prompt”高太多了。如果你在一个人全栈做Agent也建议在代码层面把目录分清楚别让自己在同一个文件里既改模型调用又改退款规则。人的思维也是有上下文的频繁切换会成倍增加出错概率。把组件拆开就是给未来的自己减少“精神切换成本”。文化上说团队里要有一个共识业务规则的修改不需要“模型升级”来承载模型能力的升级也不应该打断业务节奏。当你的组员不再说“这个需求要等模型优化后才能接”的时候属于你的正交化Agent就基本成了。最后再分享一个小技巧。做Agent开发时我给自己定的规矩是每写一个Skill前先写它的接口定义和一组测试用例再动手写实现。这样能强迫你想清楚“这个技能的输入输出是什么、边界在哪”而不是想当然地先把业务逻辑堆上去。坚持半年之后你会发现“通用智能”和“业务深度”不再是对抗关系它们成了一个系统里两个干净利落的坐标轴。希望这套思路也能帮你在Agent开发路上少一些拉扯多一些掌控感。
返回列表