纲要
- 智能体评估的挑战
- 非确定性输出的测试困境
- 传统测试方法的局限性
- 模拟用户评估方法
- 核心思想: AI 对 AI 的双智能体交互
- 系统架构与工作流程
- 完整可运行示例
- 环境准备与依赖配置
- 被评估智能体 (客服机器人) 的实现
- 模拟用户智能体的实现
LangGraph对话编排引擎的构建- 执行与观察
- 评估结果分析与优化策略
- API 速览
- 参考文档
智能体评估的挑战
在将基于大语言模型的对话智能体部署至生产环境之前,必须进行系统性的质量验证。然而,LLM 输出的非确定性特征使得传统的单元测试方法难以有效覆盖对话场景。针对客服、销售助手等对话类智能体,一种高效的评估方案是利用智能体模拟用户,通过构建自动化的 AI 对 AI 交互闭环,在可控条件下观察目标智能体的行为表现。
此方法的核心优势在于能够以较低成本快速覆盖大量边缘场景,尤其适用于识别智能体在情绪化交互、政策边界说明及多轮对话策略等方面的潜在缺陷。
模拟用户评估方法
评估系统由三个核心逻辑组件构成:
- 目标智能体 (Target Agent): 待测试的客服机器人实例,包含其系统提示词、业务逻辑与约束条件。
- 模拟用户 (Simulated User): 由独立 LLM 实例驱动的角色扮演者,被注入特定的用户画像、历史背景和交互目标 (如提出不合理退款诉求并逐步升级情绪)。
- 编排引擎 (Orchestration Engine): 基于
LangGraph构建的对话流程控制器,负责交替调用两个智能体,管理对话状态,并根据轮次或终止标记决定是否结束交互。
模拟用户会依据预设的人设指令 (如 “五年前购买机票,要求全额退款,且态度逐渐愤怒”) 生成回复;目标智能体则按既定客服角色回应。开发者通过分析完整的对话日志,可以从合规性、情绪识别能力、问题解决效率等维度评估智能体的综合性能。
完整可运行示例
以下实现创建了一个航空公司客服智能体,并为其配置了一个带有不合理退款诉求且情绪易怒的模拟用户,自动执行 10 轮对话测试。
环境准备
项目依赖 langgraph、langchain-openai 以及环境管理库。
# 安装核心依赖
pip install langgraph langchain-openai python-dotenv
确保在项目根目录下创建 .env 文件,配置 OPENAI_API_KEY 环境变量。
项目文件 eval_agent.py
以下是完整的评估脚本实现,包含状态定义、双智能体节点及图编排逻辑。
import os
from typing import TypedDict, List, Literal
from dotenv import load_dotenv
from langgraph.graph import StateGraph, END
from langgraph.checkpoint.memory import MemorySaver
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, AIMessage, BaseMessage, SystemMessage
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
# 加载环境变量
load_dotenv()
# ---------- 初始化模型 ----------
# 适用版本: langchain-openai >= 0.1.0, langgraph >= 0.0.20
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.7)
# ---------- 状态定义 ----------
class EvalState(TypedDict):
messages: List[BaseMessage] # 对话历史
round_count: int # 当前客服回复次数
# ---------- 目标智能体 (客服) ----------
AGENT_SYSTEM_PROMPT = """你是一名航空公司的资深客服人员。你的行为准则如下:
- 始终保持礼貌、专业且富有同理心的语气。
- 对于超出政策范围的诉求,需清晰解释相关规定并提供替代方案。
- 严禁承认自身为 AI 或机器人,始终以人类客服身份回应。
- 在处理退款请求时,严格遵循航司条款,不做出无依据的承诺。
"""
def agent_node(state: EvalState) -> dict:
"""被评估的客服智能体节点。
接收当前对话状态,调用 LLM 生成客服回复,并更新轮次计数。
"""
# 构建消息列表: 系统提示词 + 完整历史
messages = [SystemMessage(content=AGENT_SYSTEM_PROMPT)] + state["messages"]
response = llm.invoke(messages)
return {
"messages": state["messages"] + [response],
"round_count": state["round_count"] + 1
}
# ---------- 模拟用户 ----------
SIM_USER_NAME = "Tommy"
SIM_USER_INSTRUCTIONS = """你的核心诉求是为五年前飞往阿拉斯加的航班申请全额退款。
你的行为特征如下:
- 对退款被拒表现出逐渐加剧的不耐烦和愤怒情绪。
- 在对话中坚持主张自身权利,试图施压客服。
- 除非诉求得到满足或你判断对话已无意义,否则不应主动结束。
- 若决定终止对话,请在回复中仅包含单词 "FINISHED"。
"""
def simulated_user_node(state: EvalState) -> dict:
"""模拟用户节点。
基于最近的对话历史和预设人设,生成符合角色设定的用户回复。
"""
# 限制上下文长度,仅取最近 6 条消息以控制 Token 消耗
recent_history = state["messages"][-6:] if len(state["messages"]) > 6 else state["messages"]
prompt = ChatPromptTemplate.from_messages([
("system", f"""你是航空公司的一名客户,正在与客服人员沟通。
你的名字是 {SIM_USER_NAME}。
{SIM_USER_INSTRUCTIONS}
重要约束:
- 请以真实人类用户的语气回复,输出内容应自然且口语化。
- 严禁输出任何关于自身 AI 身份、分析过程或角色说明的元文本。
- 如想结束对话,请严格只回复单词 'FINISHED'。"""),
MessagesPlaceholder(variable_name="history"),
("human", "请根据以上对话历史,生成你作为用户的下一条回复内容:")
])
chain = prompt | llm
response = chain.invoke({"history": recent_history})
user_reply = HumanMessage(content=response.content)
return {"messages": state["messages"] + [user_reply]}
# ---------- 路由条件 ----------
def should_continue(state: EvalState) -> Literal["agent", "end"]:
"""判断对话是否继续的路由函数。
终止条件:
1. 用户最新回复包含结束标记 "FINISHED"。
2. 客服回复次数 (轮次) 达到预设上限 (10次)。
"""
last_message = state["messages"][-1]
if isinstance(last_message, HumanMessage) and "FINISHED" in last_message.content.strip().upper():
return "end"
if state["round_count"] >= 10:
return "end"
return "agent"
# ---------- 构建状态图 ----------
# 1. 初始化图构建器
builder = StateGraph(EvalState)
# 2. 添加节点
builder.add_node("agent", agent_node) # 客服节点
builder.add_node("user", simulated_user_node) # 用户节点
# 3. 定义边与流程
builder.set_entry_point("agent") # 入口: 客服先发言
builder.add_edge("agent", "user") # 客服发言后,流转至用户节点
# 用户节点后,执行条件路由
builder.add_conditional_edges("user", should_continue, {
"agent": "agent", # 若未终止,继续客服节点
"end": END # 若满足终止条件,结束
})
# 4. 编译图并设置内存检查点 (用于状态持久化)
memory = MemorySaver()
app = builder.compile(checkpointer=memory)
# ---------- 执行评估 ----------
if __name__ == "__main__":
# 配置会话 ID 以启用检查点
config = {"configurable": {"thread_id": "eval-session-001"}}
# 初始化状态: 以一条用户主动问候作为触发
initial_state = {
"messages": [HumanMessage(content="你好,我想咨询退票事宜。")],
"round_count": 0
}
print("========== 智能体评估对话开始 ==========")
# 执行图计算
final_state = app.invoke(initial_state, config)
# 格式化输出对话日志
print("\n--- 完整对话记录 ---")
for idx, msg in enumerate(final_state["messages"]):
role = "用户" if isinstance(msg, HumanMessage) else "客服"
# 过滤掉系统消息 (若存在)
if not isinstance(msg, SystemMessage):
print(f"[{idx+1}] {role}: {msg.content}")
print(f"\n========== 对话结束,共执行 {final_state['round_count']} 轮客服回复 ==========")
执行与观察
在终端执行 python eval_agent.py,系统将自动输出完整的对话日志。模拟用户会基于设定的“愤怒退款者”人设逐步施压,而客服智能体则需在政策限制与用户体验之间寻求平衡。通过观察目标智能体的回复策略,可评估其是否存在以下问题:
- 做出不符合政策的无依据承诺。
- 在用户情绪升级时无法有效安抚或陷入机械性重复。
- 未能清晰传达替代解决方案。
评估结果分析与优化策略
运行示例脚本后,典型的对话流程可能呈现如下模式:
- 客服: 您好,很高兴为您服务。请问有什么可以帮您?
- 用户: 我是 Tommy,五年前乘坐你们航班去阿拉斯加,那趟体验很糟糕,我要全额退款!
- 客服: 非常抱歉给您带来了不佳体验。不过该行程已超出我们的退款政策时效,我可以协助您查询是否有里程补偿或其他优惠方案。
- 用户: 我等了五年你就给我这个答复?我要求立刻退款,否则我会投诉到底!
- …
通过调整模拟用户的 SIM_USER_INSTRUCTIONS 变量 (例如修改诉求类型、情绪波动模式或语言风格),可以高效构建覆盖各类边缘场景的测试集。若评估发现智能体在特定压力条件下表现欠佳,可针对性地优化其系统提示词、增加护栏指令或引入更精细的上下文检索增强生成策略。
API 速览
本示例主要涉及以下核心 API:
langgraph.graph.StateGraph: 用于构建基于状态的多智能体工作流。add_node(name, function): 注册节点及其处理函数。set_entry_point(name): 指定图计算的起始节点。add_edge(from, to): 定义确定性的状态转移边。add_conditional_edges(source, router, path_map): 添加条件分支路由。compile(checkpointer): 编译图并附加状态持久化机制。
langgraph.checkpoint.memory.MemorySaver: 基于内存的检查点保存器,用于维护会话状态。langchain_core.messages: 定义了标准消息类型 (HumanMessage,AIMessage,SystemMessage)。langchain_openai.ChatOpenAI: OpenAI 聊天模型的标准接口封装。
参考文档
总结
本文系统阐述了利用模拟用户智能体对对话式 AI 进行自动化评估的方法论。通过 LangGraph 编排引擎,开发者能够精确控制 AI 对 AI 的交互流程,实现低成本的对话质量验证。该方案不仅适用于客服场景,还可扩展至销售助理、技术支持、心理咨询模拟等多个领域。
核心技术要点包括: 状态图驱动的多智能体协作、基于检查点的对话记忆管理、以及条件路由控制的动态终止逻辑。本文提供的代码示例可直接作为评估框架的基础模板,帮助团队建立持续集成与质量保障流水线。
376



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



