当前位置: 首页 > article >正文

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 的安全机制。如果你对对话系统的设计有什么想法,欢迎在评论区交流。

相关文章:

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

在前面的文章中&#xff0c;我们讨论了 AI Agent 的各个核心系统。今天&#xff0c;我想聊聊如何实现一个好用的多轮对话系统。说实话&#xff0c;这个话题我琢磨了很久&#xff0c;因为它直接影响到用户体验。 从一个槽点说起 还记得我最开始做对话系统时的一个典型场景&…...

分布式光纤应变监测是一种高精度、分布式的监测技术

一、土木工程领域 桥梁结构健康监测 主跨应变监测&#xff1a;在大跨度桥梁的主跨部分&#xff0c;如悬索桥的主缆、斜拉桥的斜拉索和主梁&#xff0c;分布式光纤应变传感器可以沿着这些关键结构部件进行铺设。通过实时监测应变情况&#xff0c;能够精确捕捉到车辆荷载、风荷…...

机器学习10-解读CNN代码Pytorch版

机器学习10-解读CNN代码Pytorch版 我个人是Java程序员&#xff0c;关于Python代码的使用过程中的相关代码事项&#xff0c;在此进行记录 文章目录 机器学习10-解读CNN代码Pytorch版1-核心逻辑脉络2-参考网址3-解读CNN代码Pytorch版本1-MNIST数据集读取2-CNN网络的定义1-无注释版…...

面向程序员的Lean 4教程(2) - 数组和列表

面向程序员的Lean 4教程(2) - 数组和列表 上一节我们介绍了Lean4的基本语法&#xff0c;因为大部分程序员都有一定的编程基础&#xff0c;所以并没有介绍过细。这一节我们介绍Lean4中的线性表结构&#xff1a;数组和列表&#xff0c;顺带复习一下上一节的流程控制等内容。 数…...

【C++高并发服务器WebServer】-7:共享内存

本文目录 一、共享内存1.1 shmget函数1.2 shmat1.3 shmdt1.4 shmctl1.5 ftok1.6 共享内存和内存映射的关联1.7 小demo 二、共享内存操作命令 一、共享内存 共享内存允许两个或者多个进程共享物理内存的同一块区域&#xff08;通常被称为段&#xff09;。由于一个共享内存段会称…...

【力扣Hot 100】链表1

1. 相交链表 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交**&#xff1a;** !https://assets.leetcode-cn.com/aliyun-lc-upload/uplo…...

稀土抗菌剂:提升产品质量,保障公共健康

随着全球对抗菌技术需求的不断增长&#xff0c;传统的抗菌剂逐渐暴露出其局限性&#xff0c;包括耐药性、环境污染及副作用等问题。在此背景下&#xff0c;稀土抗菌剂作为一种新兴的抗菌材料&#xff0c;凭借其卓越的抗菌性能、环保特性以及应用多样性&#xff0c;正在成为各行…...

机器学习11-学习路径推荐

机器学习11-学习路径推荐 本文希望摒除AI学习商业宣传要素&#xff0c;推荐一条极简的AI学习路线&#xff01;推荐内容均为在线免费内容&#xff0c;如果有条件可以咨询专业的培训机构&#xff01; 文章目录 机器学习11-学习路径推荐[toc] 1-AI培训路线第一阶段 Python-人工智能…...

postgresql根据主键ID字段分批删除表数据

生产环境针对大表的处理相对比较麻烦。 方案1、直接truncate&#xff0c;可能会遇到系统卡主的情况&#xff0c;因为truncate的过程中会对表进行加锁&#xff0c;会导致数据不能正常的写入 方案2、创建一个同结构的表结构&#xff0c;rename旧表&#xff0c;不停业务rename表担…...

《边界感知的分而治之方法:基于扩散模型的无监督阴影去除解决方案》学习笔记

paper&#xff1a;Boundary-Aware Divide and Conquer: A Diffusion-Based Solution for Unsupervised Shadow Removal 目录 摘要 1、介绍 2、相关工作 2.1 阴影去除 2.2 去噪扩散概率模型&#xff08;Denoising Diffusion Probabilistic Models, DDPM&#xff09; 3、方…...

java后端之事务管理

Transactional注解&#xff1a;作用于业务层的方法、类、接口上&#xff0c;将当前方法交给spring进行事务管理&#xff0c;执行前开启事务&#xff0c;成功执行则提交事务&#xff0c;执行异常回滚事务 spring事务管理日志&#xff1a; 默认情况下&#xff0c;只有出现Runti…...

深入浅出 SQLSugar:快速掌握高效 .NET ORM 框架

SQLSugar 是一个高效、易用的 .NET ORM 框架&#xff0c;支持多种数据库&#xff08;如 SQL Server、MySQL、PostgreSQL 等&#xff09;。它提供了丰富的功能&#xff0c;包括 CRUD 操作、事务管理、动态表名、多表联查等&#xff0c;开发者可以通过简单的链式操作实现复杂的数…...

数据结构——概念与时间空间复杂度

目录 前言 一相关概念 1什么是数据结构 2什么是算法 二算法效率 1如何衡量算法效率的好坏 2算法的复杂度 三时间复杂度 1时间复杂度表示 2计算时间复杂度 2.1题一 2.2题二 2.3题三 2.4题四 2.5题五 2.6题六 2.7题七 2.8题八 四空间复杂度 1题一 2题二 3…...

浅谈在AI时代GIS的发展方向和建议

在AI时代&#xff0c;GIS&#xff08;地理信息系统&#xff09;的发展正经历着深刻的变革&#xff0c;随着人工智能技术的进步&#xff0c;GIS不再仅仅是传统的地图和空间数据处理工具&#xff0c;而是向更加智能化、自动化、精准化的方向发展。作为一名GIS开发工程师&#xff…...

牛客周赛 Round 78 A-C

A.时间表查询&#xff01; 链接&#xff1a;https://ac.nowcoder.com/acm/contest/100671/A 来源&#xff1a;牛客网 题目描述 今天是2025年1月25日&#xff0c;今年的六场牛客寒假算法基础集训营中&#xff0c;前两场比赛已经依次于 20250121、20250123 举行&#xff1b;而…...

Java I/O 流介绍

Java学习资料 Java学习资料 Java学习资料 一、引言 在 Java 编程中&#xff0c;I/O&#xff08;Input/Output&#xff09;流是处理输入和输出操作的核心机制。它允许程序与外部设备&#xff08;如文件、网络连接、键盘、显示器等&#xff09;进行数据交互。通过使用 I/O 流&…...

linux 内核学习方向以及职位

### 学习路径 1. 基础阶段&#xff1a; - C语言高级特性 - 指针和内存管理 - 数据结构实现 - 位操作 - 多线程编程 - Linux系统编程 - 文件I/O操作 - 进程管理 - 信号处理 - IPC机制 - Socket编程 - 必备知识 - 操作系统原理 - 计算机体系结构 - …...

HTML-新浪新闻-实现标题-样式1

用css进行样式控制 css引入方式&#xff1a; --行内样式&#xff1a;写在标签的style属性中&#xff08;不推荐&#xff09; --内嵌样式&#xff1a;写在style标签中&#xff08;可以写在页面任何位置&#xff0c;但通常约定写在head标签中&#xff09; --外联样式&#xf…...

【电磁兼容】CE 传导骚扰

一。是什么&#xff1f; 传导骚扰是指电气或电子设备产生的不需要的电磁能量&#xff0c;这些能量通过导线或其他金属路径传播到其他设备或者系统中。这种类型的干扰通常发生在同一电源线路连接的不同装置之间&#xff0c;或者是共享相同布线系统的组件间。 传导骚扰可以分为两…...

能说说MyBatis的工作原理吗?

大家好&#xff0c;我是锋哥。今天分享关于【Redis为什么这么快?】面试题。希望对大家有帮助&#xff1b; 能说说MyBatis的工作原理吗&#xff1f; MyBatis 是一款流行的持久层框架&#xff0c;它通过简化数据库操作&#xff0c;帮助开发者更高效地与数据库进行交互。MyBatis…...

詳細講一下RN(React Native)中的列表組件FlatList和SectionList

1. FlatList 基礎使用 import React from react; import { View, Text, FlatList, StyleSheet } from react-native;export const SimpleListDemo: React.FC () > {// 1. 準備數據const data [{ id: 1, title: 項目 1 },{ id: 2, title: 項目 2 },{ id: 3, title: 項目 3…...

LabVIEW进行可靠性测试时有哪些常见的问题

在进行LabVIEW开发和测试时&#xff0c;尤其是用于可靠性测试&#xff0c;可能会遇到一些常见的问题。以下是一些常见问题及其解决方法&#xff1a; 1. 数据采集卡与硬件兼容性问题 问题描述&#xff1a;某些数据采集卡&#xff08;DAQ&#xff09;与硬件设备的兼容性问题可能…...

MFC程序设计(四)窗口创建机制

钩子函数 钩子属于win32技术&#xff0c;具有优先勾取消息的权利&#xff1a;当一个消息产生时&#xff0c;钩子勾取消息进行处理&#xff0c;然后消息才送回程序 接下来以一个勾取窗口创建消息的钩子为例进行讲解 钩子类型有键盘钩子&#xff0c;鼠标钩子&#xff0c;WH_CBT…...

【JavaEE进阶】Spring留言板实现

目录 &#x1f38d;预期结果 &#x1f340;前端代码 &#x1f384;约定前后端交互接口 &#x1f6a9;需求分析 &#x1f6a9;接口定义 &#x1f333;实现服务器端代码 &#x1f6a9;lombok介绍 &#x1f6a9;代码实现 &#x1f334;运行测试 &#x1f384;前端代码实…...

Unity开发一个单人FPS游戏的教程总结

这个系列的前几篇文章介绍了如何从头开始用Unity开发一个FPS游戏&#xff0c;感兴趣的朋友可以回顾一下。这个系列的文章如下&#xff1a; Unity开发一个FPS游戏_unity 模仿开发fps 游戏-CSDN博客 Unity开发一个FPS游戏之二_unity 模仿开发fps 游戏-CSDN博客 Unity开发一个F…...

论文速读|Is Cosine-Similarity of Embeddings Really About Similarity?WWW24

论文地址&#xff1a; https://arxiv.org/abs/2403.05440 https://dl.acm.org/doi/abs/10.1145/3589335.3651526 bib引用&#xff1a; inproceedings{Steck_2024, series{WWW ’24},title{Is Cosine-Similarity of Embeddings Really About Similarity?},url{http://dx.doi.o…...

71.在 Vue 3 中使用 OpenLayers 实现按住 Shift 拖拽、旋转和缩放效果

前言 在前端开发中&#xff0c;地图功能是一个常见的需求。OpenLayers 是一个强大的开源地图库&#xff0c;支持多种地图源和交互操作。本文将介绍如何在 Vue 3 中集成 OpenLayers&#xff0c;并实现按住 Shift 键拖拽、旋转和缩放地图的效果。 实现效果 按住 Shift 键&#…...

PyQt6医疗多模态大语言模型(MLLM)实用系统框架构建初探(上.文章部分)

一、引言 1.1 研究背景与意义 在数字化时代,医疗行业正经历着深刻的变革,智能化技术的应用为其带来了前所未有的发展机遇。随着医疗数据的指数级增长,传统的医疗诊断和治疗方式逐渐难以满足现代医疗的需求。据统计,全球医疗数据量预计每年以 48% 的速度增长,到 2025 年将…...

250125-package

1. 定义 包就是文件夹&#xff0c;作用是在大型项目中&#xff0c;避免不同人的编写的java文件出现同名进而导致报错&#xff1b;想象一个场景&#xff0c;在一个根目录中&#xff0c;每一个人都有自己的一个java文件夹&#xff0c;他可以将自己编写的文件放在该文件夹里&…...

左右互博02-unidbg主动调用外层so函数

unidbg 代码 ` package com.koohairev.demo;import com.github.unidbg.AndroidEmulator; import com.github.unidbg.LibraryResolver; import com.github.unidbg.Module; import com.github.unidbg.Symbol; import com.github.unidbg.arm.backend.DynarmicFactory; import com.…...