
1. 从只会聊天到能干活企业AI落地的真实断层在哪里过去两年几乎每家公司都在内部试过把大模型接进工作流。最常见的做法是搭一个对话框员工输入问题模型给出回答。用起来挺新鲜但用不了多久大家就发现一个问题它除了聊天什么也干不了。你问它帮我查一下上个月的报销单状态它只能告诉你我无法访问你的系统你让它把这份合同里的关键条款整理成表格它倒是能整理但整理完你还得手动复制到文档里再手动发给相关同事。整个流程下来省下的时间还不如自己动手快。这就是当前企业AI落地最真实的断层模型有理解能力但没有执行能力。它能听懂你说什么但它碰不到你的业务系统、读不了你的数据库、发不了你的邮件、改不了你的工单。它像一个知识渊博但被绑住手脚的顾问只能动嘴不能动手。我所在的团队从去年开始就在琢磨这件事。我们试过几种方案第一种是给模型挂一堆API让它自己决定调哪个。结果发现模型经常选错工具或者把参数传得乱七八糟稳定性极差。第二种是写死流程比如查报销就走A接口发通知就走B接口模型只负责填参数。这种方案倒是稳定但每加一个新场景就要改一次代码扩展成本太高。第三种是让模型生成代码然后执行代码去操作业务系统。这个思路听起来很美好但安全风险太大万一模型生成了一段删除数据的代码后果不堪设想。折腾了大半年我们最终沉淀出一套相对靠谱的架构。核心思路是把企业里的每一个操作都封装成技能模型只负责理解意图和编排技能实际执行交给确定性的代码。这样既保留了模型的灵活性又保证了执行的安全性和稳定性。这套东西我们内部叫它企业工作伙伴后来决定把它开源出来让更多团队少走弯路。这篇文章不讲空泛的概念只讲我们踩过的坑、做过的取舍、以及最终跑通的方案。如果你正在负责公司内部的AI落地或者你是一个开发者想做一个能真正干活的AI助手下面的内容应该能帮你省下至少三个月的试错时间。2. 为什么给模型挂API这条路走不通2.1 模型选工具的随机性远超你的想象刚开始做的时候我们想得很简单把公司内部的API都整理出来写成工具描述让模型自己选。比如用户说帮我查一下张三的工单模型应该选查询工单这个工具参数填张三。听起来很合理对吧实际跑起来完全不是这么回事。我们当时挂了大概二十个工具包括查工单、查报销、查考勤、发通知、建群、改权限等等。测试的时候发现模型选错的概率大概在15%左右。最离谱的一次用户说帮我看看张三最近有没有提交报销模型选了查询考勤工具参数填了张三。你问它为什么选错它给出的理由是报销和考勤都是员工行为记录我判断查询考勤也能间接反映报销情况。这个逻辑你不能说它完全错但结果就是错的。更麻烦的是参数填充。有些工具的参数字段名很相似比如员工姓名和申请人姓名模型经常搞混。还有些工具需要传日期范围模型有时候传上个月有时候传2024-05-01到2024-05-31格式不统一后端解析经常报错。2.2 工具数量超过一定阈值后准确率断崖式下跌我们做过一组对比测试结果如下工具数量意图识别准确率参数填充准确率端到端成功率5个96%94%91%10个92%89%83%20个85%78%67%40个72%61%45%这张表的数据很能说明问题。当工具数量从5个增加到40个时端到端成功率从91%暴跌到45%。也就是说超过一半的请求会失败。这个失败率在企业场景里是完全不可接受的因为员工用两次发现不好用就再也不会用了。为什么会这样核心原因是模型的注意力被稀释了。工具描述越多模型在选工具时需要考虑的维度就越多相似工具之间的干扰就越严重。而且很多工具的描述文本本身就存在语义重叠模型很难区分。2.3 纯API方案缺少上下文记忆和多步编排还有一个更深层的问题企业里的很多任务不是单步操作而是多步编排。比如帮我把这个月的部门报销汇总一下发给财务并抄送给我。这个任务拆开来看至少需要四步查询报销数据、按部门汇总、生成报表、发送邮件。每一步都依赖上一步的输出而且中间可能需要人工确认。纯API方案只能处理单步调用模型调完一个工具拿到结果后要么直接返回给用户要么再调下一个工具。但模型在多轮调用中很容易忘记之前的上下文或者把中间结果搞丢。我们测试过让模型连续调三个工具完成一个任务成功率不到30%。所以给模型挂API这条路在工具数量少、任务简单的场景下可以凑合用但一旦要覆盖企业里的真实工作流就会立刻崩掉。这不是模型能力的问题而是架构设计的问题。3. 我们最终采用的架构技能层 编排层 执行层3.1 三层架构的分工逻辑踩了上面那些坑之后我们重新设计了一套架构。核心思路是把理解和执行彻底分开中间加一层确定性的编排逻辑。整个架构分三层第一层是技能层。我们把企业里的每一个原子操作都封装成一个技能每个技能有明确的输入输出定义、参数校验规则、以及执行逻辑。技能层不依赖模型它是纯代码实现的可以单独测试、单独部署。比如查询员工信息是一个技能发送邮件是一个技能创建工单是一个技能。第二层是编排层。这一层负责理解用户意图然后把意图拆解成技能调用序列。编排层是模型和规则引擎的结合体模型负责理解自然语言规则引擎负责校验和补全。比如用户说帮我查一下张三的工单模型识别出意图是查询工单实体是张三然后规则引擎会检查查询工单这个技能是否存在、参数是否完整、当前用户是否有权限调用。第三层是执行层。这一层就是实际调用技能的地方它按照编排层给出的序列依次执行处理异常和重试最后把结果汇总返回给用户。执行层完全不涉及模型它是确定性的代码逻辑。这三层分开之后每一层都可以独立优化。技能层可以不断增加新技能而不影响其他层编排层可以换模型、调提示词而不影响执行执行层可以加监控、加重试而不影响理解。整个系统的可维护性大幅提升。3.2 技能封装的具体规范技能封装看起来简单实际上有很多细节要注意。我们内部定了一套规范每个技能必须包含以下字段{ skill_id: query_ticket, skill_name: 查询工单, description: 根据员工姓名或工单编号查询工单状态和详情, parameters: [ { name: employee_name, type: string, required: false, description: 员工姓名与工单编号二选一 }, { name: ticket_id, type: string, required: false, description: 工单编号与员工姓名二选一 } ], permissions: [ticket_read], handler: handlers.ticket.query_ticket }这里有几个关键点。第一description必须写得非常精确不能有歧义。我们之前吃过亏有个技能的描述写的是查询员工相关信息结果模型把它当成了万能查询工具什么请求都往这里塞。后来改成根据员工姓名查询工单状态和详情准确率立刻上来了。第二参数设计要尽量扁平化避免嵌套结构。模型对嵌套参数的处理能力很弱如果参数是一个对象数组模型经常填错。我们现在的做法是如果确实需要复杂参数就拆成多个技能或者用字符串拼接的方式传参后端再解析。第三权限字段是必须的。企业场景下不同角色的员工能调用的技能是不一样的。比如普通员工不能调用修改薪资技能部门主管不能调用查看全公司考勤技能。权限校验放在编排层做模型不需要知道权限细节它只负责理解意图。3.3 编排层的意图识别与槽位填充编排层的核心任务是把自然语言转成结构化的技能调用请求。我们用的是意图识别 槽位填充的经典范式但做了一些针对企业场景的优化。意图识别方面我们没有让模型直接输出技能ID而是让它输出一个意图标签然后通过映射表找到对应的技能。这样做的好处是意图标签可以用更自然的语言描述模型更容易理解。比如用户说我想看看我的报销走到哪了模型输出意图标签查询报销进度映射表里查询报销进度对应技能query_reimbursement_status。槽位填充方面我们用了模型抽取 规则校验的双保险。模型先从用户输入里抽取参数然后规则引擎检查参数是否完整、格式是否正确。如果参数缺失规则引擎会生成一个追问话术让模型去问用户。比如用户说帮我查一下工单模型抽取出意图查询工单但没抽到员工姓名或工单编号规则引擎就会让模型追问请问您要查谁的工单或者提供工单编号这套机制跑下来意图识别准确率能到95%以上槽位填充准确率能到90%左右。剩下的10%主要是用户表达太模糊比如帮我处理一下那个事情这种只能靠追问来补全。4. 多步任务编排让AI真正完成一个完整流程4.1 任务拆解的策略与边界单步技能调用只能解决简单问题企业里真正有价值的是多步任务。比如帮我统计上个月部门的加班时长生成报表发给HR并抄送部门主管。这个任务需要四步查询加班数据、按部门汇总、生成报表文件、发送邮件。每一步都是一个独立的技能但它们之间有依赖关系。我们的做法是在编排层加一个任务规划器。用户输入一个复杂请求后规划器先把它拆解成技能调用序列然后逐个执行。拆解的策略有两种一种是模型直接生成调用序列另一种是预定义模板匹配。我们两种都用简单任务用模型生成复杂任务用模板匹配。为什么复杂任务要用模板因为模型在生成多步序列时经常会出现逻辑错误。比如它可能先生成报表再查询数据或者漏掉某个必要步骤。模板匹配虽然不够灵活但胜在稳定。我们内部维护了一个任务模板库每个模板定义了技能调用顺序、参数传递规则、异常处理逻辑。当用户请求匹配到某个模板时直接按模板执行模型只负责填充参数。4.2 步骤间的数据传递与状态管理多步任务最麻烦的地方是数据传递。第一步的输出往往是第二步的输入如果传递过程中丢了数据整个任务就断了。我们一开始用模型的上下文来传递数据结果发现模型经常忘记中间结果或者把数据搞混。后来我们改成用显式的任务上下文来管理数据。每个任务有一个上下文对象每一步执行完后把输出写入上下文下一步从上下文里读取。上下文是一个结构化的JSON对象不依赖模型的记忆。比如{ task_id: task_20240520_001, steps: [ { step: 1, skill: query_overtime, input: {department: 研发部, month: 2024-04}, output: {total_hours: 320, details: [...]} }, { step: 2, skill: generate_report, input: {data: $steps[0].output, format: xlsx}, output: {file_path: /reports/overtime_202404.xlsx} } ] }注意$steps[0].output这个引用语法它表示第二步的输入来自第一步的输出。执行层会解析这个引用把实际数据填进去。这样做的好处是数据传递完全由代码控制不依赖模型的记忆稳定性大幅提升。4.3 异常处理与人工确认节点多步任务执行过程中难免会遇到异常。比如查询数据时发现没有权限或者生成报表时文件写入失败。我们的处理策略是能自动重试的自动重试不能自动处理的转人工确认。自动重试主要针对网络超时、临时性错误。我们给每个技能调用设置了重试次数和退避策略比如第一次失败后等1秒重试第二次失败后等3秒重试第三次还失败就放弃。人工确认主要针对两种情况一是权限不足需要用户申请权限或换人操作二是关键操作比如发送邮件、修改数据需要用户确认后才能执行。我们在编排层加了确认节点的概念当任务执行到确认节点时系统会暂停给用户发一条确认消息用户确认后才继续执行。这个设计在实际使用中非常受欢迎。员工反馈说以前用其他AI工具最怕它自作主张把邮件发出去了现在有了确认节点心里踏实多了。5. 开源过程中踩过的坑与最终取舍5.1 模型选型不是越大越好开源的时候很多人问我们用的什么模型。我们试过好几种包括一些参数很大的模型和一些轻量级模型。结论可能有点反直觉在企业工作伙伴这个场景下模型的大小不是决定性因素提示词工程和技能设计才是。我们做过对比测试用同一个技能库和同一套提示词分别接不同规模的模型端到端成功率差距在5%以内。大模型在理解复杂意图时确实略好一些但在标准化的企业场景下用户表达通常比较规范轻量级模型完全够用。而且轻量级模型响应更快、成本更低更适合企业内部高频使用。最终我们开源的版本支持多种模型接入用户可以根据自己的场景选择。如果任务简单、并发高用轻量级模型如果任务复杂、对准确率要求极高用大模型。架构上做了抽象换模型只需要改配置不需要改代码。5.2 技能粒度太细和太粗都难受技能粒度是我们纠结最久的问题。粒度太细比如把查询工单拆成查询工单基本信息和查询工单处理记录模型需要调两次编排复杂度上升。粒度太粗比如把查询工单和修改工单合并成一个工单管理技能模型又容易搞混。我们最后定的原则是一个技能只做一件事但这件事的边界要清晰。具体来说如果两个操作经常一起出现而且共享大部分参数就合并成一个技能如果两个操作逻辑独立只是领域相同就分开。比如查询工单和修改工单虽然都是工单操作但一个读一个写权限要求也不同就分成两个技能。另外我们给每个技能加了别名机制。同一个技能可以有多个别名模型可以用任意别名调用。比如查询工单的别名包括查工单看工单工单状态查询等。这样模型不需要记住精确的技能名用自然语言描述也能匹配到。5.3 权限控制模型不该知道的事就别让它知道企业场景下权限控制是红线。我们一开始的设计是把权限信息也写进技能描述里让模型自己判断。比如技能描述里写仅限HR角色调用模型在选技能时会考虑当前用户的角色。结果发现模型经常忽略权限限制或者判断错误。后来我们改成权限校验完全在编排层做模型不接触权限信息。模型只负责理解意图和选技能选完之后编排层检查当前用户是否有权限调用这个技能。如果没有直接返回您没有权限执行此操作不执行技能。这样做的好处是权限逻辑是确定性的代码不会出错模型也不需要处理它不擅长的权限判断。这个设计还有一个额外好处技能描述可以写得更简洁不需要包含权限信息减少了模型的认知负担意图识别准确率反而提升了。6. 实际跑起来之后哪些设计最值钱6.1 技能注册中心让扩展变得像搭积木我们内部最满意的设计是技能注册中心。所有技能都注册在一个中心化的地方新增技能只需要写一个配置文件和一个处理函数不需要改编排层和执行层的代码。编排层启动时从注册中心拉取技能列表自动生成提示词和映射表。这个设计让扩展变得极其简单。我们有个同事花了一个下午就接了五个新技能包括查询会议室预订申请门禁权限提交IT工单等。他只需要写技能定义和处理逻辑剩下的交给框架。这种低门槛的扩展方式让整个系统的技能数量在三个月内从20个增长到80多个覆盖了公司内部大部分高频操作。6.2 对话日志回放排查问题的最佳工具企业场景下出问题是常态。用户说它没理解我的意思你得能复现当时的情况。我们做了一个对话日志回放功能把每次交互的完整链路都记录下来用户输入、模型输出、意图识别结果、槽位填充结果、技能调用序列、每步的输入输出、最终返回结果。这个功能在排查问题时极其有用。有一次用户反馈查报销一直失败我们回放日志发现模型把报销识别成了报考勤因为两个词的拼音很像模型在语音输入场景下搞混了。后来我们在意图识别里加了拼音纠错问题就解决了。如果没有日志回放这种问题根本没法定位。所以我的建议是从第一天就把日志做全不要等出问题了再补。6.3 灰度发布与A/B测试让优化有数据支撑我们每次调整提示词或技能定义都会先在小范围灰度发布观察一周的数据再全量。灰度期间我们会对比新旧版本的端到端成功率、平均响应时间、用户满意度等指标。只有新版本在各项指标上都不差于旧版本才会全量。这个流程看起来麻烦但实际省了很多事。有一次我们改了一个技能的描述灰度期间发现准确率反而下降了3%赶紧回滚。如果没有灰度直接全量可能就要面对大量用户投诉了。A/B测试我们也做主要针对提示词的不同写法。比如同一个意图用查询工单和获取工单信息两种描述哪个准确率高这种问题只能靠数据回答拍脑袋是不行的。7. 如果你也想做一个我的几点实在建议第一不要一上来就追求大而全。我们最开始想做一个能处理所有企业事务的万能助手结果什么都做不好。后来收缩到只做高频的十几个场景反而用起来了。先跑通一个场景让用户感受到价值再逐步扩展。第二技能设计比模型选型重要十倍。我见过太多团队花大量时间对比模型却不愿意花时间打磨技能描述和参数设计。实际上在企业场景下技能设计的质量直接决定了系统的上限。一个好的技能设计用轻量级模型也能跑出很好的效果一个糟糕的技能设计用再大的模型也救不回来。第三权限和审计从第一天就要做。企业场景和个人场景最大的区别就是权限。个人助手可以随便调工具企业助手必须严格控制。而且审计日志不仅是排查问题的工具也是合规的要求。哪些人、在什么时间、调用了什么技能、传了什么参数、返回了什么结果这些都要记录。第四人工确认节点不是累赘是信任的基石。很多团队觉得人工确认影响效率想全部自动化。但在企业场景下用户对AI的信任是一点点建立的。有了确认节点用户才敢用用多了发现AI靠谱才会逐渐放开权限。这是一个渐进的过程急不得。第五开源不是终点是起点。我们把代码开源出来不是因为它完美了而是希望更多人一起把它做得更好。企业AI落地是一个巨大的工程没有哪个团队能独自解决所有问题。开源之后我们收到了很多有价值的反馈和贡献有些是我们自己完全没想到的场景和用法。这种社区的力量比闭门造车强太多了。最后说一个我个人的体会做企业AI最难的从来不是技术而是理解业务。模型、架构、工具都是手段真正重要的是搞清楚企业里的工作流是怎么跑的、痛点在哪里、哪些环节适合AI介入、哪些环节必须人工把关。把这些想清楚了技术方案自然就出来了。反过来如果业务没想清楚技术再先进也是白搭。