每一步都合理,但结果是错的——企业AI落地的真实困境
我见过一次很典型的失败。 一个团队做了一个采购助手,让采购员用自然语言提交补货需求。背后接了公司的库存系统,用Function Calling让模型决定调哪个接口。测试阶段跑了几十个case,结果看起来都对,上线了。 上线三天之后,仓库那边发现有一批货的补货数量不对,系统建议补200件,实际应该补20件。去查原因,发现模型调用库存查询接口的时候,把 最后那批货多备了180件,压了将近两个月才消化掉。 这个错误有意思的地方不在于"模型推断错了",而在于它推断的方式在逻辑上完全说得通—— 好消息是,这个失败案例并不是随机出错,而是一种系统性的偏差。 大语言模型的推理方式,本质上是在做"最合理的猜测"。训练数据给了它大量的语言规律,让它知道什么词经常和什么词一起出现,什么样的上下文通常对应什么样的答案。这在自然语言处理上表现出色,因为自然语言里的规律和通用知识高度重叠。 但企业系统不是自然语言,是私有的业务约定。 这是第一类错误:字段语义的私有性。字段名给了模型一个方向,但没有给它足够的约束。 第二类错误更隐蔽。同一个目标,可能有多个接口能"部分实现",但只有一个是业务上正确的路径。比如查询客户的历史订单,系统里可能同时存在 第三类错误发生在多步调用的时候。AI在执行复杂任务时需要把多个接口的结果组合起来。比如先查库存,再查销售记录,再生成补货方案。每一步的错误都会传递给下一步,而且可能被放大。查库存用了错误的字段,算出来的当前库存偏高,传给下一步之后补货数量就系统性偏低,最后生成的方案在数字上看起来合理,但整体方向是错的。 这三类错误有一个共同的根源:模型不知道你的系统是什么,只能靠猜。这导致了模型没有一个结构化的、关于"这个系统是什么"的认知。 不是文档,不是示例,是结构化的认知——这个系统里有哪些概念,这些概念之间是什么关系,每个概念可以做哪些操作,这些操作的规则和约束是什么。 人类新员工入职的时候,有一个非正式的学习过程,就是在完成这种认知的建立。他不是把API文档背下来,而是在工作中逐渐理解:哦,我们说的"客户"在系统里对应两张表,一张是基本信息,一张是交易记录;销售用的那个查询接口只返回有效合同,你如果想看退款得去另一个地方查。这种认知是结构化的、关系化的,不是文本片段的堆积。 这个认知,有一个名字,叫本体(Ontology)。 本体这个词听起来有点哲学,实际上在信息科学里的含义很具体:对一个特定领域中的概念以及概念之间关系的形式化描述。你的业务系统里有哪些"名词"(实体),这些名词有哪些"属性",可以对它们做哪些"动作",它们之间是什么关系——把这些东西结构化地描述出来,就是这个系统的本体。 这不是一个新概念。OWL(Web Ontology Language)从2004年就是W3C标准,帕兰蒂尔(Palantir)在它的AIP平台里把本体作为核心概念来组织企业数据,已经跑了很多年。只是在大语言模型之前,本体主要是给人看的,是知识管理和语义检索的工具。现在它多了一个用途:给AI看,作为AI理解和操作企业系统的认知基础。 为了让AI能够弄明白你的业务系统,一个完整的业务系统本体除了需要描述实体(如物品表)和属性(如库存、库存上限),还需要描述实体之间的关系(库存记录属于哪个仓库,关联哪个SKU),接口的业务语义(这个接口适合给谁用,返回的是什么口径的数据),操作的前置条件和约束,等等。 怎么把这些东西系统性地建立起来,以及建立之后AI怎么用它,是接下来几篇要讲的内容。 很多人在看完全文后会有一个问题:把API文档喂给模型,让它去查,就像RAG那样,不行吗?实际上,这个思路对了一半。 RAG(检索增强生成)的工作方式是把文档切成片段,存进向量库,用户提问的时候检索相关片段,塞进上下文让模型参考。它在知识问答类场景里表现不错,因为"用户的问题"和"文档里的答案"在语义上通常是对应的——你问差旅报销标准,系统能找到写着报销标准的那一段。但"调用哪个接口、传哪些参数、怎么理解返回值"不是这种结构。 原因在于切片。RAG的基本假设是:完成这个查询所需的信息,尽量在一个或少数几个相邻的切片里。但一个业务操作的完整知识是分散的:接口定义在API文档里,字段含义在数据字典里,业务规则在需求文档里,接口之间的依赖关系可能只在老员工脑子里。把这些内容切片之后,检索时很难保证把相关的几块都拿回来,拿回来的顺序也未必正确。 2023年Langchain发布过一份内部评估报告(后来收录在他们的文档里),测试了RAG在代码库问答上的表现。当问题需要跨越多个文件、多个函数的关联理解时,准确率从单文件场景的约75%下降到不足40%。企业系统里的接口调用问题,在结构上非常接近这种"跨文件关联理解"的场景,结果大概率也会不及预期。库存上限 列当成了库存列。这两个字段放在同一张物品表里,名字里都有库存,返回的JSON里挨着,模型自己"推断"了一个错误的对应关系。库存上限确实和库存有关,确实在库存表里,确实是一个数字字段。如果你不知道这个系统的业务含义,你也很可能这么理解。为什么"每一步都合理,但结果是错的"
库存上限在A公司的系统里是仓库容量上限,在B公司的系统里可能是触发补货的阈值,在C公司甚至可能是历史最高库存的记录值。这些差异写在哪里?写在内部Wiki、需求文档、老员工的脑子里,不在任何公开训练数据里。模型只能用它"觉得最合理"的解释,而这个解释在特定业务场景下可能是错的。/ServerCommand/GetOrders?customer_id=xxx和/ServerCommand/GetCustomerOrders?orders_id=xxx两个接口,前者用在售后服务/客户订单查询页面,是给客服用的,能看到所有订单包括退款记录;后者用在销售管理/工作台和销售管理/订单查询页面是给销售用的,只显示有效订单。模型看到两个接口都能返回"客户的订单",没有额外信息的情况下,它随机选一个,或者选名字看起来更通用的那个。结果是调用路径对了,数据错了,而且错误不会触发任何异常。也就是说,接口正常返回,数据格式正确,只是业务口径不对。番外:RAG能解决这个问题吗
扩展文章