本篇写一下意图识别模块的实现。
用户意图识别,简单说来就是去明确用户到底要解决什么问题。用户输入的问题一定会是千奇百怪,如果不能进行有效的意图识别,就无法将问题路由到对应的子图进行处理,也就无法给出准确的回答。
比如用户询问商品价格,那么通过意图识别就需要将这个问题识别为商品价格查询,然后路由到对应的子图进行处理。 因此,意图识别模块是 Multi-Agent 架构中非常重要的一环。实际在实现过程中,借助 LangGraph 的路由组件,可以非常方便的实现意图识别功能。这个模块直接接收用户输入的原始问题,并分派给对应的子图进行处理。
意图识别模块在 LangGraph 的 Router 组件中实现,实现的关键是提示工程 + 结构化输出方法。
提示工程:
对每一个子路由能处理的问题进行详细的描述,引导大模型给出正确的分类。比如对于这次的电商智能客服场景,提示工程这么设计:
ROUTER_SYSTEM_PROMPT =
"""你是一个电商领域的智能客服。你的工作是帮助用户解决与产品、订单、售后和技术支持相关的问题。
用户会向你提出询问。你的首要任务是对询问类型进行分类。你必须将用户询问的问题分类为以下类型之一
## `general-query`
如果是一般性问题,不需要查询知识库就能回答,请将其分类为此类。
包括但不限于:
- 与商品、订单、售后、技术支持无关的闲聊问题
## `additional-query`
如果你需要更多信息才能帮助用户,请将用户询问分类为此类。例如:
- 用户询问商品但没有提供具体型号或规格
- 用户询问订单状态但没有提供订单号
- 用户描述问题不够具体,无法提供准确帮助
## `graphrag-query`
如果通过查询本地知识库可以回答用户询问,请将其分类为此类。
包括但不限于:
- 商品价格、库存、规格等信息查询
- 订单状态、物流信息查询
- 会员积分、优惠活动查询
- 退换货政策咨询
- 商品使用指导及故障解决方法
## `image-query`
如果用户提供了图片请求提供图片,请将其分类为此类。
## `file-query`
如果用户上传了文件,请将其分类为此类。
"""
结构化输出:
借助 LangChain 的 with_structured_output 方法,将大模型的输出结果转换为具体的结构化数据,并通过 LangGraph 定义图的输出类型,生成结构化的输出结果。
经过这两个处理后,得到的结果只会是我们在提示工程中给出的五种type中的一种,从而可以非常方便的将问题路由到对应的子图进行处理。然后,再交由route_query节点,找到对应的子图,准备进行下一步处理。
def route_query():
if _type == "general-query":
return "respond_to_general_query"
elif _type == "additional-query":
return "get_additional_info"
elif _type == "graphrag-query":
return "create_research_plan"
elif _type == "image-query":
return "create_image_query"
elif _type == "file-query":
return "create_file_query"
这里返回的都是需要自定义的节点和子图。
1、general-query
这是一个图节点,负责处理一般性问题,即与商品、订单、售后、技术支持无关的闲聊问题。
该节点直接根据提示工程即可实现,关键就是需要在提示工程中,对“闲聊、一般性”问题应该如何回复进行详细描述,这就要根据具体的业务需求进行设计。比如对风格、语气、回复策略等进行详细的描述。然后将提示工程作为System Message传递给大模型,将结果储存到LangGraph的全局状态中即可。
2、additional-query
这个图节点负责处理需要更多信息才能回答的问题,比如用户询问商品但没有提供具体型号或规格、用户询问订单状态但没有提供订单号等,这种情况下是没有办法去本地的数据库或者知识库中查询出有效结果的,所以需要引导用户提供更多支撑知识库检索的必要信息。
首先第一个处理组件是安全护栏,核心作用是用来判断用户的问题是否属于服务的范围。比如我们的商品只有智能家居类,但是用户要买服装类,那么这个请求就属于超出服务范围的。因为 general-query中处理的是闲聊问题,所以只要用户提问的问题与电商相关,那么就一定不会进入 general-query 节点。
但是可能存在的问题是:用户即使咨询的是电商问题,但其实和我们的业务是没有关系的。针对这种常见的情况,安全护栏起到的作用就是:先去判断用户的问题是否属于个人服务的范围,如果属于,再继续追问,否则便可以直接回复特定的信息,比如“抱歉,您咨询的问题和我们的业务范围不符, 请您咨询其他平台”等,直接结束掉当前会话。
如何判断用户的问题是不是属于经营范围呢?这就需要根据具体的业务需求进行设计了。但能够确定的是:提示工程是通用的,通过提示词明确的告诉大模型,哪些问题属于经营范围,哪些问题属于超出经营范围的。
description = """
个人电商经营范围:智能家居产品,包括但不限于:
- 智能照明(灯泡、灯带、开关)
- 智能安防(摄像头、门锁、传感器)
- 智能控制(温控器、遥控器、集线器)
- 智能音箱(语音助手、音响)
- 智能厨电(电饭煲、冰箱、洗碗机)
- 智能清洁(扫地机器人、洗衣机)
不包含:服装、鞋类、体育用品、化妆品、食品等非智能家居产品。
"""
其二,除了人工编写提示词,还可以根据业务情况、实现的架构,去找到一些动态的补充信息。比如在这个项目中使用 Neo4j 图数据库去存储商品信息,那 Neo4j Schema 就是一个非常好的动态提示词。因为图数据的 Schema 中的 Node 和 Relationship 、 Property 其实反映的就是存储的
私有数据的结构,它可以一定程度上体现出商品的分类、属性、价格、库存、供应商、物流等信息,以帮助大模型整体去判断用户的问题是否属于个人经营范围。
这里在跑项目时候遇到一个问题,那就是Neo4j 的Schema 能够帮助我们匹配用户问题与知识库结构(比如商品价格、库存等信息的查询模式),但它无法直接告诉我们商品的具体类别内容。
比如当用户询问篮球鞋时,系统可能会根据 Schema 理解用户是在查询商品信息,但无法仅凭Schema 判断篮球鞋是否在经营范围内。解决办法就是人工编写提示工程。两者结合构建出一个
更加完善的判断逻辑。
当然,有判断逻辑还不够,安全护栏作为一个 LangGraph 的节点,还需要一个最终提示,引导大模型在结束安全护栏组件后输出结构化的结果,从而让后续的节点能够根据安全护栏的输出结果执行不同分支的代码 。
剩下的几个明天再写。
这里说一下最近的感想,你要想做出一个非常强大的Agent,首先你自己要知道,假如是你来完成这个任务,你需要做什么?要有一个明确的规则体系,有清晰的定义,哪一步该调用什么样的模型来完成任务,这一步是一个什么样的角色,这些都要在做的时候规划清楚。大模型这个领域需要学习的太多,边干边学吧!
558

被折叠的 条评论
为什么被折叠?



