AI Agent的多轮对话:提升用户体验的关键技巧

在前面的文章中,我们讨论了 AI Agent 的各个核心系统。今天,我想聊聊如何实现一个好用的多轮对话系统。说实话,这个话题我琢磨了很久,因为它直接影响到用户体验。

从一个槽点说起

还记得我最开始做对话系统时的一个典型场景:

用户:帮我查一下订单状态
助手:好的,请提供订单号。
用户:A12345
助手:抱歉,我需要您提供订单号才能查询。
用户:...我刚才不是说了吗?

这种对话体验显然很糟糕。问题出在哪里?主要是 AI Agent 没有正确理解和维护对话上下文。后来我总结了几个关键点:

  1. 要理解用户意图
  2. 要记住上下文信息
  3. 要有状态管理机制
  4. 要能处理各种异常情况

对话系统的设计

经过多次迭代,我设计了一个相对完善的对话系统:

from typing import List, Dict, Any, Optional
from enum import Enum
from datetime import datetime
from pydantic import BaseModel
import asyncioclass DialogueState(Enum):IDLE = "idle"WAITING_INPUT = "waiting_input"PROCESSING = "processing"ERROR = "error"COMPLETED = "completed"class DialogueContext(BaseModel):session_id: struser_id: strcurrent_state: DialogueStatecurrent_intent: Optional[str]slots: Dict[str, Any]history: List[Dict[str, Any]]created_at: datetimeupdated_at: datetimeclass DialogueSystem:def __init__(self,llm,tool_registry,memory_system):self.llm = llmself.tool_registry = tool_registryself.memory_system = memory_systemself.sessions: Dict[str, DialogueContext] = {}async def process_message(self,session_id: str,user_id: str,message: str) -> str:# 1. 获取或创建会话上下文context = self._get_or_create_context(session_id,user_id)try:# 2. 更新状态context.current_state = DialogueState.PROCESSING# 3. 理解用户意图intent = await self._understand_intent(message,context)# 4. 更新上下文context.current_intent = intent.namecontext.slots.update(intent.slots)# 5. 执行对应的处理流程response = await self._handle_intent(intent,context)# 6. 记录对话历史self._update_history(context,message,response)return responseexcept Exception as e:context.current_state = DialogueState.ERRORreturn f"抱歉,处理您的请求时出现错误:{str(e)}"finally:# 保存上下文self._save_context(context)async def _understand_intent(self,message: str,context: DialogueContext) -> Intent:# 结合上下文理解用户意图response = await self.llm.understand_intent(message=message,history=context.history[-5:],  # 最近5轮对话current_intent=context.current_intent,slots=context.slots)return Intent(name=response.intent,confidence=response.confidence,slots=response.slots)async def _handle_intent(self,intent: Intent,context: DialogueContext) -> str:# 检查是否有未填充的必要槽位missing_slots = self._get_missing_slots(intent)if missing_slots:# 返回槽位询问context.current_state = DialogueState.WAITING_INPUTreturn self._generate_slot_question(missing_slots[0])# 所有槽位都已填充,执行操作result = await self._execute_intent(intent,context)context.current_state = DialogueState.COMPLETEDreturn resultdef _get_or_create_context(self,session_id: str,user_id: str) -> DialogueContext:if session_id in self.sessions:return self.sessions[session_id]# 创建新会话context = DialogueContext(session_id=session_id,user_id=user_id,current_state=DialogueState.IDLE,current_intent=None,slots={},history=[],created_at=datetime.now(),updated_at=datetime.now())self.sessions[session_id] = contextreturn context

使用示例:

# 初始化对话系统
dialogue = DialogueSystem(llm=ChatGPT(),tool_registry=tool_registry,memory_system=memory_system
)# 处理用户消息
async def chat():responses = []# 第一轮:查询订单response = await dialogue.process_message(session_id="123",user_id="user_1",message="帮我查一下订单状态")responses.append(response)# 输出:好的,请提供订单号。# 第二轮:提供订单号response = await dialogue.process_message(session_id="123",user_id="user_1",message="A12345")responses.append(response)# 输出:您的订单 A12345 正在配送中,预计明天送达。# 第三轮:追问细节response = await dialogue.process_message(session_id="123",user_id="user_1",message="具体什么时候到?")responses.append(response)# 输出:根据物流信息,预计明天上午10:00-12:00送达。return responses# 运行对话
responses = await chat()
for r in responses:print(r)

关键实现细节

1. 意图理解

class IntentRecognizer:def __init__(self, llm):self.llm = llmasync def recognize(self,message: str,context: Dict[str, Any]) -> Intent:# 1. 准备提示词prompt = self._prepare_prompt(message,context)# 2. 调用 LLMresponse = await self.llm.generate(prompt)# 3. 解析结果intent = self._parse_response(response)# 4. 验证意图self._validate_intent(intent)return intentdef _prepare_prompt(self,message: str,context: Dict[str, Any]) -> str:return f"""请分析以下对话内容,识别用户意图:历史对话:{self._format_history(context.get('history', []))}当前状态:- 意图:{context.get('current_intent')}- 已知信息:{json.dumps(context.get('slots', {}), indent=2)}用户消息:{message}请返回:1. 意图名称2. 置信度3. 识别出的槽位信息"""

2. 状态管理

class StateManager:def __init__(self):self.state_handlers = {DialogueState.IDLE: self._handle_idle,DialogueState.WAITING_INPUT: self._handle_waiting,DialogueState.PROCESSING: self._handle_processing,DialogueState.ERROR: self._handle_error,DialogueState.COMPLETED: self._handle_completed}async def handle_state(self,context: DialogueContext,message: str) -> str:# 获取当前状态的处理器handler = self.state_handlers.get(context.current_state)if not handler:raise ValueError(f"未知状态:{context.current_state}")# 执行状态处理return await handler(context, message)async def _handle_waiting(self,context: DialogueContext,message: str) -> str:# 检查是否填充了等待的槽位slot_name = context.waiting_for_slotif self._is_valid_slot_value(slot_name,message):# 更新槽位context.slots[slot_name] = message# 继续处理return await self._continue_processing(context)else:# 重新询问return f"抱歉,这似乎不是有效的{slot_name},请重新输入。"

3. 上下文管理

class ContextManager:def __init__(self, memory_system):self.memory = memory_systemself.max_history = 10async def update_context(self,context: DialogueContext,message: str,response: str):# 1. 更新对话历史context.history.append({"role": "user","content": message,"timestamp": datetime.now()})context.history.append({"role": "assistant","content": response,"timestamp": datetime.now()})# 2. 限制历史长度if len(context.history) > self.max_history * 2:# 保存旧对话到长期记忆old_messages = context.history[:-self.max_history * 2]await self._save_to_memory(context.session_id,old_messages)# 保留最近的对话context.history = context.history[-self.max_history * 2:]# 3. 更新时间戳context.updated_at = datetime.now()async def _save_to_memory(self,session_id: str,messages: List[Dict]):# 将对话保存到长期记忆await self.memory.remember(content=self._format_messages(messages),metadata={"type": "dialogue","session_id": session_id,"timestamp": datetime.now()})

优化技巧

在实践中,我总结了一些提升用户体验的技巧:

1. 主动确认

class ConfirmationManager:def __init__(self, threshold: float = 0.8):self.threshold = thresholddef need_confirm(self,intent: Intent,context: DialogueContext) -> bool:# 检查是否需要确认if intent.confidence < self.threshold:return Trueif self._is_critical_operation(intent):return Truereturn Falsedef generate_confirmation(self,intent: Intent,context: DialogueContext) -> str:return f"""请确认您是否要{intent.description}?- 操作:{intent.name}- 参数:{json.dumps(intent.slots, indent=2)}回复"是"或"否"。"""

2. 错误恢复

class ErrorRecovery:async def recover(self,error: Exception,context: DialogueContext) -> str:# 分析错误analysis = await self._analyze_error(error)if analysis.can_retry:# 自动重试return await self._retry_operation(context)elif analysis.need_clarification:# 请求用户澄清return self._generate_clarification_question(analysis)else:# 友好的错误提示return self._generate_error_message(analysis)

3. 上下文压缩

class ContextCompressor:def compress_history(self,history: List[Dict],max_tokens: int) -> List[Dict]:# 1. 计算当前token数current_tokens = self._count_tokens(history)if current_tokens <= max_tokens:return history# 2. 提取关键信息key_messages = self._extract_key_messages(history)# 3. 压缩对话compressed = self._compress_messages(key_messages,max_tokens)return compresseddef _extract_key_messages(self,history: List[Dict]) -> List[Dict]:# 提取重要的对话轮次key_turns = []for i, msg in enumerate(history):if self._is_key_message(msg, history, i):key_turns.append(msg)return key_turns

实践心得

在实现和优化对话系统的过程中,我总结了几点经验:

  1. 以用户体验为中心

    • 理解用户真实意图
    • 保持对话的连贯性
    • 给出清晰的反馈
  2. 要有容错机制

    • 优雅处理异常
    • 支持意图澄清
    • 允许用户更正
  3. 注意性能优化

    • 合理管理上下文
    • 及时清理无用信息
    • 异步处理耗时操作

写在最后

一个好的对话系统应该像一个专业的客服,既要理解用户需求,又要高效地解决问题。它不仅要"能听懂",还要"会说话"。

在下一篇文章中,我会讲解如何实现 AI Agent 的安全机制。如果你对对话系统的设计有什么想法,欢迎在评论区交流。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/68121.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue router路由复用及刷新问题研究

路由复用问题 当路由匹配路径未发生变化时&#xff0c;只是相关的参数发生了变化&#xff0c;路由跳转时&#xff0c;会发现虽然地址栏中的地址更新到了新的链接&#xff0c;但是页面渲染并未触发响应路由组件的created,mounted等钩子函数&#xff0c;也就意味着组件并没有被重…

Android各个版本存储权限适配

一、Android6.0-9.0 1、动态权限申请&#xff1a; private static String[] arrPermissions {android.Manifest.permission.READ_EXTERNAL_STORAGE, android.Manifest.permission.WRITE_EXTERNAL_STORAGE,android.Manifest.permission.ACCESS_FINE_LOCATION,android.Manifest.…

房租管理系统的智能化应用助推租赁行业高效运营与决策优化

内容概要 在现代租赁行业中&#xff0c;房租管理系统的智能化应用正在逐步成为一个不可或缺的工具。通过整合最新技术&#xff0c;这些系统为租赁管理的各个方面提供了极大的便利和效率提升。从房源管理到合同签署再到财务监控&#xff0c;智能化功能能够帮助运营者在繁琐的事…

数据结构初阶之队列的介绍与队列的实现

一、概念与结构 概念&#xff1a;只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表&#xff0c;队列具有先进先出 FIFO (First In First Out) 的特点。 入队列&#xff1a;进行插入操作的一端称为队尾 出队列&#xff1a;进行删除操作的一端称为…

GTO 门级可关断晶闸管,全控性器件

介绍 门级可关断晶闸管是一种通过门极来控制器件导通和关断的电力半导体器件。 结构特点 - 四层半导体结构&#xff1a;与普通晶闸管相似&#xff0c;GTO也是由PNPN四层半导体构成&#xff0c;外部引出三个电极&#xff0c;分别是阳极&#xff08;A&#xff09;、阴极&#x…

FlinkSql使用中rank/dense_rank函数报错空指针

问题描述 在flink1.16(甚至以前的版本)中&#xff0c;使用rank()或者dense_rank()进行排序时&#xff0c;某些场景会导致报错空指针NPE(NullPointerError) 报错内容如下 该报错没有行号/错误位置&#xff0c;无法排查 现状 目前已经确认为bug&#xff0c;根据github上的PR日…

序列标注:从传统到现代,NLP中的标签预测技术全解析

引言 序列标注任务是自然语言处理&#xff08;NLP&#xff09;中的核心任务之一&#xff0c;广泛应用于信息抽取、文本分类、机器翻译等领域。随着深度学习技术的快速发展&#xff0c;序列标注任务的性能得到了显著提升。本文将从基础概念入手&#xff0c;逐步深入探讨序列标注…

速通Docker === Docker Compose

目录 Docker Compose 简介 Docker Compose 常用命令 使用 Docker Compose 启动 WordPress 普通启动方式&#xff08;使用 Docker 命令&#xff09; 使用 Docker Compose 启动 Docker Compose 的特性 Docker Compose 简介 Docker Compose 是一个用于定义和运行多容器 Dock…

ESP32服务器和PC客户端的Wi-Fi通信

ESP32客户端-服务器Wi-Fi通信 本指南将向您展示如何设置ESP32板作为服务端&#xff0c;PC作为客户端&#xff0c;通过HTTP通信&#xff0c;以通过Wi-Fi&#xff08;无需路由器或互联网连接&#xff09;交换数据。简而言之&#xff0c;您将学习如何使用HTTP请求将一个板的数据发…

为什么IDEA提示不推荐@Autowired❓️如果使用@Resource呢❓️

前言 在使用 Spring 框架时&#xff0c;依赖注入&#xff08;DI&#xff09;是一个非常重要的概念。通过注解&#xff0c;我们可以方便地将类的实例注入到其他类中&#xff0c;提升开发效率。Autowired又是被大家最为熟知的方式&#xff0c;但很多开发者在使用 IntelliJ IDEA …

如何用数据编织、数据虚拟化与SQL-on-Hadoop打造实时、可扩展兼容的数据仓库?

在大数据技术迅猛发展的背景下&#xff0c;许多人认为传统数据仓库已过时。然而&#xff0c;这种观点忽略了数据仓库的核心价值&#xff1a;统一的数据视图、强大的业务逻辑支撑以及丰富的数据分析能力。在企业数据架构转型中&#xff0c;数据仓库不仅未被淘汰&#xff0c;反而…

DuckDB:Golang操作DuckDB实战案例

DuckDB是一个嵌入式SQL数据库引擎。它与众所周知的SQLite非常相似&#xff0c;但它是为olap风格的工作负载设计的。DuckDB支持各种数据类型和SQL特性。凭借其在以内存为中心的环境中处理高速分析的能力&#xff0c;它迅速受到数据科学家和分析师的欢迎。在这篇博文中&#xff0…

day1代码练习

输出3-100以内的完美数&#xff0c;(完美数&#xff1a;因子和(因子不包含自身)数本身) #include <stdio.h>// 判断一个数是否为完美数的函数 int panduan(int n) {if (n < 2) {return 0; // 小于2的数不可能是完美数}int sum 1; // 因子和初始化为1&#xff08;因…

dify大模型应用开发平台搭建

原文地址&#xff1a;dify大模型应用开发平台搭建 – 无敌牛 欢迎参观我的技术分享网站&#xff1a;无敌牛 – 技术/著作/典籍/分享等 之前分享了一个私有化部署开源大模型的方法&#xff0c;具体参看往期文章&#xff1a;私有化部署开源AI模型 – 无敌牛 今天搭建一个大模型…

Spring Boot 邂逅Netty:构建高性能网络应用的奇妙之旅

一、引言 在当今数字化时代&#xff0c;构建高效、可靠的网络应用是开发者面临的重要挑战。Spring Boot 作为一款强大的 Java 开发框架&#xff0c;以其快速开发、简洁配置和丰富的生态支持&#xff0c;深受广大开发者喜爱。而 Netty 作为高性能、异步的网络通信框架&#xff…

Spring--SpringMVC使用(接收和响应数据、RESTFul风格设计、其他扩展)

SpringMVC使用 二.SpringMVC接收数据2.1访问路径设置2.2接收参数1.param和json2.param接收数据3 路径 参数接收4.json参数接收 2.3接收cookie数据2.4接收请求头数据2.5原生api获取2.6共享域对象 三.SringMVC响应数据3.1返回json数据ResponseBodyRestController 3.2返回静态资源…

Unity在WebGL中拍照和录视频

原工程地址https://github.com/eangulee/UnityWebGLRecoder Unity版本2018.3.6f1&#xff0c;有点年久失修了 https://github.com/xue-fei/Unity.WebGLRecorder 修改jslib适配了Unity2021 效果图 录制的视频 Unity在WebGL中拍照和录视频

数据结构——AVL树的实现

Hello&#xff0c;大家好&#xff0c;这一篇博客我们来讲解一下数据结构中的AVL树这一部分的内容&#xff0c;AVL树属于是数据结构的一部分&#xff0c;顾名思义&#xff0c;AVL树是一棵特殊的搜索二叉树&#xff0c;我们接下来要讲的这篇博客是建立在了解搜索二叉树这个知识点…

【25美赛A题-F题全题目解析】2025年美国大学生数学建模竞赛(MCM/ICM)解题思路|完整代码论文集合

我是Tina表姐&#xff0c;毕业于中国人民大学&#xff0c;对数学建模的热爱让我在这一领域深耕多年。我的建模思路已经帮助了百余位学习者和参赛者在数学建模的道路上取得了显著的进步和成就。现在&#xff0c;我将这份宝贵的经验和知识凝练成一份全面的解题思路与代码论文集合…

jenkins-k8s pod方式动态生成slave节点

一. 简述&#xff1a; 使用 Jenkins 和 Kubernetes (k8s) 动态生成 Slave 节点是一种高效且灵活的方式来管理 CI/CD 流水线。通过这种方式&#xff0c;Jenkins 可以根据需要在 Kubernetes 集群中创建和销毁 Pod 来执行任务&#xff0c;从而充分利用集群资源并实现更好的隔离性…