DLAI-数据智能体构建评估笔记-全-
DLAI 数据智能体构建评估笔记(全)
001:课程概述与目标 🎯

在本课程中,我们将学习如何构建一个能够执行网络搜索、连接数据源、收集与分析数据、可视化结果并提供洞察的数据代理。更重要的是,我们将学习如何应用离线评估来迭代改进代理的设计,以及如何实施实时评估,让代理在运行过程中也能动态调整策略。
我们很高兴能与来自Snowflake的AI研究负责人Anupam Datta,以及开发者倡导者Josh Reini共同讲授本课程。Anupam曾是卡内基梅隆大学的教授,也是被Snowflake收购的AI可观测性初创公司TruEra的创始人。Josh此前也在TruEra工作,并持续构建和维护着用于AI追踪与评估的开源库TruLens。
课程核心:评估的重要性 📊
构建评估是开发现代AI代理工作负载的一项关键技能。本课程将展示一些重要的最佳实践。具体而言,我们将从评估代理的答案质量开始。
我们将使用一个评估框架来度量代理的端到端性能,评估其最终答案是否与用户查询相关,以及为分析所检索的数据是否与查询相关。最后,我们还会评估最终答案是否基于所有检索到的数据。
您可以使用这些离线评估指标来改进代理的逻辑、更新其提示词,或者尝试不同的LLM模型。此外,我们还将使用思维链评估来评估代理得出最终答案的过程。
有趣的是,这项技术不仅可用于离线评估,还能在运行时为代理提供反馈,帮助其动态调整数据检索与分析策略。

代理架构:多代理工作流 🤖
您将使用一个多代理工作流来实现您的数据代理。该工作流包含以下组件:
- 规划器:接收用户查询并生成执行计划。
- 计划执行器:指导计划的执行,并根据计划决定应由哪个专业代理进行下一步。
- 专业代理:包括网络研究员、数据研究员、数据可视化器和响应合成器等。
每个代理都会将其执行步骤的输出共享给计划执行器。在每次数据检索步骤之后,计划执行器会反思当前计划,并决定是否需要更新它。
评估维度:目标、计划与行动 🎯
我们将从多个维度评估代理的性能:
- 检索质量:检索到的数据是否相关、准确?
- 计划质量:生成的计划是否与用户请求一致?代理的行动是否与计划对齐,并且执行得高效、合乎逻辑?
在改进代理时,我们将调整其提示词,并实施内联评估。内联评估将在任何检索步骤之后,立即为计划执行器提供关于上下文相关性的实时评分。这将告知计划执行器当前的检索步骤是否缺乏相关性;如果是,计划执行器将要求规划器调整计划。
在完成这些设计改进后,我们将比较改进前后的离线评估指标。无论是离线还是实时评估,都依赖于对代理运行过程的追踪,这也是本课程中将学习的重要内容。
总结与预告 📝
本节课我们一起了解了构建与评估数据代理的课程目标、核心的评估理念以及将要实现的多代理系统架构。我们认识到,评估是迭代改进代理性能的关键,而实时反馈机制能让代理变得更智能、更可靠。

在下一节视频中,我们将深入探讨评估代理的三个核心维度:目标、计划与行动。让我们继续学习。
002:什么是数据代理?🤖

在本节课中,我们将学习什么是数据代理、它们如何工作,以及如何判断一个数据代理是否值得信赖。
概述

数据代理是一种由大型语言模型驱动的自主或半自主系统。它能连接多种数据源,理解自然语言或代码形式的查询,执行数据检索、分析等操作,并最终提供洞察或做出决策。理解其工作原理和评估标准,是构建可靠数据代理应用的基础。
什么是数据代理?📖
上一节我们概述了数据代理,本节中我们来详细看看它的定义。

一个数据代理是由大型语言模型驱动的自主或半自主系统,它能连接到数据源。这些数据源可以是数据库、API、文件、流、传感器、Web API等。它能理解以自然语言或代码表达的查询,并执行诸如查询分解、数据检索、分析或可视化等操作。最终,它基于这些数据提供洞察或做出决策。例如,它可以发送一封包含其工作结果摘要报告的电子邮件。
数据代理的应用场景 🌐
理解了定义后,我们来看看数据代理在现实世界中的应用。
数据代理正开始获得广泛采用。例如,许多人可能非常熟悉ChatGPT的Deep Research系统。根据我们对数据代理的抽象定义,Deep Research可以被视为一个数据代理,它仅利用网络上可用的数据及其内部参数化知识来回答各种主题的开放式问题。
在许多企业环境中,仅依赖网络数据是不够的,还需要利用内部可用的数据。这些数据可以是数据库表中的结构化数据,也可以是文档和其他多媒体格式的非结构化数据。Snowflake Intelligence就是一个覆盖这类更广泛数据源的数据代理示例。
一个具体的工作示例 🔍
现在,我们通过一个具体例子来理解数据代理的工作范围。
以下是一个非常简单的例子,要求数据代理识别金融服务行业的监管变化。在这种情况下,代理可以通过检查网络上可用的数据来发现监管变化的一般趋势。这样的代理可以是一个执行此任务的网络搜索代理。
现在,让我们让查询更详细一些。我们要求数据代理识别当前正经历监管变化的行业中的待处理交易。请注意,这个问题有两个部分。
- 第一部分(识别待处理交易):需要访问内部专有数据,这些数据可能位于企业内部的表格中。一个子代理可以通过使用一个能将自然语言文本转换为SQL的代理来检查或回答这部分问题,在数据库上运行它并检索信息。
- 第二部分(识别经历监管变化的行业):与我们上一张幻灯片上的第一个查询非常相似,它要求获取关于当前正经历监管变化的行业的信息。
在这个上下文中,顶级代理一旦通过使用文本转SQL子代理收集了这些待处理交易,就可以利用网络搜索子代理来找出哪些交易处于当前正经历监管变化的行业中,然后综合调用这两个子代理的结果来生成响应。

现在你可以看到,数据代理开始响应更复杂的查询,这些查询需要使用多个数据源。
最后,让我们进一步扩展这个查询。第一部分直到这里与上一张幻灯片上的查询相同,但现在我们还要求数据代理根据监管变化,帮助为每笔交易重新定位我们的价值主张。
查询的最后一部分,一旦我们从代理那里获得了解决前两个部分的响应,顶级代理就可以使用另一个在企业内部可用文档上进行内部搜索的子代理,来读取与这些交易相关的会议记录,然后为每笔待处理交易综合出一个考虑到相应行业监管变化的价值主张。
至此,数据代理使用了三个不同的子代理来回答一个相当复杂的查询,该查询解决了此任务的所有要素。
数据代理如何工作?⚙️
上一节我们看了一个复杂查询的例子,本节中我们来看看数据代理为实现目标(如回答上一类查询)而工作的顶层概述。


代理将有关联的目标需要完成。对于数据代理,目标可能是响应像上一张幻灯片那样的查询。然后,它会创建一个实现目标的计划,并执行一系列由计划规定的操作,以实现通往最终目标路径上的各种子目标。这里,计划和行动之间可能存在迭代,根据行动的结果,代理可能会更新其计划,然后执行更多操作,并继续这个迭代过程,直到它满意地认为目标已经达成。
如果我们回到正在运行的例子(这也是我们将在后续课程中详细查看的例子),目标是回答这个问题。计划步骤涉及将这个复杂查询分解为三个查询:一个关于识别待处理交易(涉及调用文本转SQL子代理),一个关于理解这些待处理交易处于哪些行业以及这些行业正在经历何种监管变化(使用网络搜索代理),最后使用内部搜索代理来帮助根据变化重新定位每笔交易的价值主张。这就是计划的形式。
计划的第一步涉及查询分解。然后,执行或行动步骤涉及按顺序调用文本转SQL代理、搜索代理和内部搜索代理,最终到达一个点,所有信息都可供代理综合成一个良好的响应以实现其目标。
如何评估数据代理?📊
了解了代理如何工作的结构(包括设定目标、计划和行动以实现这些目标的组合)后,我们现在来看看代理良好工作意味着什么。
值得信赖的代理在执行时,其目标、计划和行动是一致的。换句话说,我们希望代理具有高的GPA,即目标、计划和行动一致性。
让我对此稍作详细说明。我们如何衡量代理的GPA?
第一步是为代理设定目标。在我们的数据代理设置中,这些目标通常涉及最终响应的质量,例如:它是否相关?它是否基于检索结果?以及代理在实现最终查询答案过程中可能被期望实现的子目标,例如检索相关结果。
一旦我们指定了目标,接下来的步骤涉及确保目标与计划、计划与行动以及行动与目标之间有良好的一致性。我们有一套利用LLM评判员来检查代理GPA的评估方法。

以下是四种关键的评估方法:
- 计划质量:位于目标与计划的接口处。它检查代理的计划是否是实现其目标的良好计划。计划不必是静态的,随着代理获得新信息,它可能会随时间演变,但重新计划的步骤也需要根据代理可访问的信息和观察结果进行充分论证。
- 计划遵循度:位于计划与行动的接口处。它检查代理的行动是否符合或遵循其计划。换句话说,代理是否真的按照它计划的方式行动?偏离计划可能表明存在故障模式,这对开发者在构建数据代理时意识到并解决问题是有帮助的。
- 执行效率:位于目标与行动的接口处(即维恩图的这一部分)。它检查代理所采取的执行路径(它执行的操作序列)是否是实现目标的最有效路径。它可以帮助识别代理规划过程中的冗余,这是我们在开发数据代理的迭代过程中需要改进的地方。
- 逻辑一致性:位于我们所有三个GPA支柱的交集处。它检查计划与目标之间的不一致性(例如,计划是否包含或建议了与实现目标或子目标不一致的步骤),以及计划与重新计划步骤之间或计划与行动步骤之间的不一致性。所有这些都可能是不正确的来源,也是改进数据代理的机会。
课程路线图 🗺️
有了这个简要介绍,让我为课程的其余部分制定课程计划。
- 第二课:我们将构建一个使用网络搜索子代理作为构建计划的数据代理。
- 第三课:我们将扩展该数据代理,增加文本转SQL以及内部非结构化数据搜索的能力。因此,第二课和第三课的结合将帮助你在笔记本中构建一个完整的代理,该代理能够回答我们在本课早些时候看到的那类示例查询。你将了解到从头开始构建这样一个代理所涉及的不同步骤。我们将使用Laro开源框架来构建这些代理,但通用概念也适用于其他构建代理的框架。
- 第四课:我们将为数据代理添加追踪、检测和追踪支持,并为与该代理相关的目标设置评估。为此,我们将使用TruLens,这是一个用于追踪和评估代理及LLM应用的开源库。你将具体看到我们如何利用与OpenTelemetry兼容的追踪,以及在TruLens内可以为数据代理指定的特定类型目标。
- 第五课:我们将扩展评估以衡量代理的GPA。在这里,我们将为计划质量、计划遵循度、逻辑一致性和执行效率添加评估,这些评估将建立在第四课先前创建的评估指标之上。在第五课中,我们还将展示代理可能如何失败,这些评估能捕捉到哪些类型的故障模式,这将为开发者引入改进措施提供基础。
- 第六课:我们将研究由第五课的评估所启发的特定机制,以提高代理的GPA。
- 最后:我们将总结课程,并提供一些关键要点。
总结

本节课中,我们一起学习了数据代理的基本概念。我们了解到数据代理是一个能连接多种数据源、处理复杂查询的智能系统。其核心工作流程包括设定目标、制定计划和执行行动。为了确保其可靠性和效率,我们引入了GPA(目标-计划-行动)一致性框架,并通过计划质量、计划遵循度、执行效率和逻辑一致性四个维度来评估代理的表现。从下一课开始,我们将动手实践,逐步构建并完善我们自己的数据代理。
003:构建多智能体工作流 🏗️

在本节课中,我们将使用 LangGraph 将数据代理实现为一个多智能体工作流。这个数据代理将能够进行网络研究以回答用户查询,然后可视化研究结果或综合生成结果摘要。
概述 📋

我们将构建一个分层结构的数据代理。首先,一个规划器会接收用户查询并将其分解为子目标。然后,执行器将使用子智能体来执行计划中的每一步。本示例中的子智能体将包括网络研究员、图表生成器、图表摘要器和综合器。
构建智能体状态 🧠
首先,我们需要加载环境变量并初始化智能体状态。智能体状态为智能体提供记忆功能。
# 初始化自定义状态类,继承自 LangGraph 的 MessageState
class CustomState(MessageState):
user_query: str
enabled_agents: List[str]
current_step: int
agent_query: str
last_reason: str
replan_attempts: int
通过继承 MessageState,我们自动获得一个 messages 键,用于跟踪不同智能体之间的对话历史。此外,状态还包括用户查询、可用智能体列表、当前步骤、子智能体查询、上次选择子智能体的原因以及重新规划次数的跟踪信息。
创建规划器 📝
上一节我们介绍了智能体状态,本节中我们来看看如何创建规划器。规划器负责将用户查询分解为可执行的步骤。
首先,我们查看规划器的提示词。提示词指示规划器将用户请求分解为编号步骤,并将查询分解为子查询。每个子查询应尽可能小,以便由单个数据源处理。
以下是规划器提示词的核心部分:
你是一个多智能体系统中的规划器。请将用户请求分解为编号步骤,并将查询分解为子查询。子查询应尽可能小,每个子查询对应一个数据源。
可用智能体列表:[{enabled_agents}]
请以以下JSON格式输出计划:{"steps": [{"agent": "agent_name", "action": "action_description"}, ...]}
接下来,我们在笔记本中构建规划器节点。该节点接收状态,调用推理大语言模型(如 GPT-3.5-turbo)并生成计划,然后路由到执行器。
def planner_node(state: CustomState):
# 构建提示词,填充用户查询等信息
prompt = build_plan_prompt(state.user_query, state.enabled_agents)
# 调用大语言模型
lm_reply = reasoning_llm.invoke(prompt)
# 解析并验证返回的JSON计划
plan = validate_and_extract_plan(lm_reply.content)
# 更新状态
new_state = {
"plan": plan,
"replan_flag": False,
# ... 其他状态更新
}
# 返回命令,指示下一步前往执行器
return Command(update=new_state, goto="executor")
创建执行器 ⚙️
规划器制定了计划,本节中我们来看看执行器如何根据计划选择并调用具体的子智能体。
执行器的提示词指导它决定当前计划是否需要修订、下一步运行哪个智能体、为什么选择该智能体,以及为所选智能体编写确切的查询问题。
以下是执行器决策的关键逻辑:
- 判断是否需要重新规划:根据当前进展和从子智能体(如网络研究员)获得的新信息。
- 选择下一个智能体:如果进展合理,则移动到计划中的下一个步骤对应的智能体;否则,执行当前步骤指定的智能体。
- 构建查询:为选定的智能体编写清晰、独立、可由该智能体回答的英文指令。
执行器节点代码如下:
def executor_node(state: CustomState):
# 构建执行器提示词
prompt = build_executor_prompt(state.plan, state.current_step, state.messages, state.enabled_agents)
# 调用大语言模型
lm_reply = reasoning_llm.invoke(prompt)
# 解析决策结果
decision = parse_executor_decision(lm_reply.content) # 包含 replan, goto, reason, query
if decision.replan and state.replan_attempts < MAX_REPLANS:
# 触发重新规划,前往规划器
new_state = { "replan_flag": True, "replan_attempts": state.replan_attempts + 1 }
return Command(update=new_state, goto="planner")
else:
# 执行计划,前往下一个子智能体
next_agent = get_agent_from_plan(state.plan, state.current_step)
new_state = { "current_step": state.current_step + 1, "agent_query": decision.query }
return Command(update=new_state, goto=next_agent)
创建子智能体 🤖
执行器负责调度,本节中我们来看看具体完成任务的各个子智能体是如何构建的。我们将创建四个子智能体:网络研究员、图表生成器、图表摘要器和综合器。
网络研究员 🔍
网络研究员是一个 ReAct 风格智能体,它绑定了一个网络搜索工具(如 Tavily Search),用于从互联网获取信息。
from langchain_community.tools import TavilySearchResults
from langgraph.prebuilt import create_react_agent
# 创建搜索工具
search_tool = TavilySearchResults(max_results=5)
# 创建 ReAct 智能体
web_researcher = create_react_agent(
llm=ChatOpenAI(model="gpt-4"),
tools=[search_tool],
prompt="你是一名研究员,只能使用提供的搜索工具进行研究。找到所有需要的信息后,请结束输出。"
)
def web_researcher_node(state: CustomState):
# 从状态中获取子查询
query = state.agent_query
# 调用网络研究员智能体
response = web_researcher.invoke({"messages": [HumanMessage(content=query)]})
# 更新对话历史
new_messages = state.messages + [response.messages[-1]]
new_state = {"messages": new_messages}
# 返回执行器,决定下一步
return Command(update=new_state, goto="executor")
图表生成器 📊
图表生成器也是一个 ReAct 智能体,但它绑定的是一个 Python REPL 工具,允许大语言模型执行 Python 代码来生成图表(例如使用 matplotlib)。
from langchain_community.utilities import PythonREPL
repl_tool = PythonREPL()
chart_generator = create_react_agent(
llm=ChatOpenAI(model="gpt-4"),
tools=[repl_tool],
prompt="你负责生成图表。请先打印图表,将其保存到当前工作目录,然后告知图表摘要器文件路径和主要见解摘要。"
)
def chart_generator_node(state: CustomState):
# 调用图表生成器,使用之前网络研究员收集的数据
response = chart_generator.invoke({"messages": state.messages})
# 更新状态并路由到图表摘要器
new_state = {"messages": state.messages + [response.messages[-1]]}
return Command(update=new_state, goto="chart_summarizer")
图表摘要器 🖼️
图表摘要器接收生成的图表图像,并提供一个简洁的文本描述。
chart_summarizer = create_react_agent(
llm=ChatOpenAI(model="gpt-4"),
tools=[], # 无需工具,直接描述图像
prompt="你负责为保存在本地路径的图表生成独立、简洁的摘要说明。"
)
def chart_summarizer_node(state: CustomState):
response = chart_summarizer.invoke({"messages": state.messages})
new_state = {"messages": state.messages + [response.messages[-1]]}
# 摘要完成后,前往综合器或直接结束
return Command(update=new_state, goto="synthesizer")
综合器 ✨
当代理任务不需要生成图表,只需文本响应时,由综合器负责。它汇总所有子智能体收集的信息,生成最终答案。
def synthesizer_node(state: CustomState):
# 1. 从对话历史中提取关键信息(来自研究员、图表生成器、图表摘要器的消息)
relevant_messages = extract_messages_from_agents(state.messages, ['web_researcher', 'chart_generator', 'chart_summarizer'])
# 2. 获取原始用户问题
user_question = state.user_query
# 3. 构建综合提示词
synthesis_instructions = """
请基于以下内容回答问题。可以进行轻量级的比较或推理,但不要捏造任何内容中没有支持的事实。
请提供简洁的回应,完整回答问题。以直接答案开头,如果需要请包含引用,保持输出清晰。
"""
prompt = f"用户问题:{user_question}\n\n{synthesis_instructions}\n\n相关内容:{relevant_messages}"
# 4. 调用大语言模型生成最终答案
lm_reply = chat_llm.invoke(prompt)
final_answer = lm_reply.content
# 5. 更新状态并结束流程
new_state = {
"messages": state.messages + [HumanMessage(content=final_answer)],
"final_answer": final_answer
}
return Command(update=new_state, goto="__end__")
组装工作流图 🧩
所有节点构建完成后,现在我们可以将它们组装成一个完整的工作流图。
from langgraph.graph import StateGraph, END
# 使用自定义状态初始化图
workflow = StateGraph(CustomState)
# 添加所有节点
workflow.add_node("planner", planner_node)
workflow.add_node("executor", executor_node)
workflow.add_node("web_researcher", web_researcher_node)
workflow.add_node("chart_generator", chart_generator_node)
workflow.add_node("chart_summarizer", chart_summarizer_node)
workflow.add_node("synthesizer", synthesizer_node)
# 设置边的连接关系(根据每个节点的`goto`返回值动态路由,这里设置起始点)
workflow.set_entry_point("planner")
# 编译图
graph = workflow.compile()
运行智能体 🚀
现在,我们的多智能体工作流已经构建完成,让我们通过两个示例查询来测试它。
示例查询 1:绘制美国前五大银行当前市值图表
我们期望代理执行以下流程:网络研究员搜索数据 -> 图表生成器创建图表 -> 图表摘要器提供文本描述。
initial_state = {
"user_query": "Chart the current market capitalization of the top five banks in the US.",
"messages": [HumanMessage(content="Chart the current market capitalization of the top five banks in the US.")],
"enabled_agents": ["web_researcher", "chart_generator", "chart_summarizer", "synthesizer"],
"current_step": 0,
"replan_attempts": 0
}
result = graph.invoke(initial_state)
print(result["final_answer"])
代理将生成一个图表,并附上摘要文本(例如:“图表显示了美国市值前五的银行,其中摩根大通领先...”)。
示例查询 2:识别美国金融服务业的最新监管变化
这个查询不需要图表,因此流程将是:网络研究员搜索信息 -> 综合器生成文本答案。
initial_state = {
"user_query": "Identify recent regulatory changes for the financial services industry in the US.",
"messages": [HumanMessage(content="Identify recent regulatory changes for the financial services industry in the US.")],
"enabled_agents": ["web_researcher", "synthesizer"], # 不需要图表相关智能体
"current_step": 0,
"replan_attempts": 0
}
result = graph.invoke(initial_state)
print(result["final_answer"])
代理将提供一份详细的文本回答,总结关键监管变化,并可能引用信息来源。
总结 🎯
本节课中,我们一起学习了如何使用 LangGraph 构建一个分层多智能体工作流。我们创建了:
- 规划器:将复杂用户查询分解为逐步计划。
- 执行器:动态选择并调用子智能体执行计划,并能处理重新规划。
- 子智能体:
- 网络研究员:利用搜索工具获取外部数据。
- 图表生成器:利用代码执行工具创建数据可视化。
- 图表摘要器:描述生成的图表。
- 综合器:汇总所有信息,生成最终文本答案。

通过将任务分解并由专门智能体处理,我们构建了一个能够进行网络研究、数据可视化和信息综合的强大数据代理。在下一节课中,我们将扩展此代理的能力,使其能够利用 Text-to-SQL 和文档搜索等技术对内部数据进行推理。
004:扩展数据代理能力


概述
在本节课中,我们将扩展数据代理的能力,使其能够从企业结构化和非结构化数据中检索上下文信息,以回答用户的查询。我们将通过添加一个新的子代理——Cortex Researcher Agent来实现这一目标,该代理将访问存储在Snowflake中的数据。
回顾上节课的架构
在上一节课中,我们构建了一个包含规划器(Planner)和执行器(Executor)的数据代理架构。网络研究员(Web Researcher)负责收集数据,然后我们通过图表生成器、图表总结器和综合器来响应用户。
引入新的数据检索代理
在本节中,我们将扩展数据代理的能力,使其能够检索数据。我们将通过添加Cortex Researcher Agent来实现这一点。
Cortex Researcher Agent将访问Snowflake,使用文档搜索工具检索非结构化数据,并使用“文本转SQL”工具检索结构化数据。该代理将配备两个与我们之前创建的任何代理都不同的工具。通过将此代理添加到我们的架构中,我们现在可以跨所有数据进行推理,提出需要结合内部和外部数据源信息的深度复杂问题。
现在,让我们开始构建。
设置环境与连接数据
我们首先在notebook中加载环境。环境变量包含访问LLM和进行网络搜索的权限,同时我们也可以连接Snowflake以从中检索数据。
在Snowflake中,我们已为您预加载了数据库中的数据,以便您理解结构化数据的外观以及如何检索此类数据,同时还加载了文档,以便您理解如何检索非结构化数据。此外,我们还创建了一个位于非结构化数据之上的搜索服务。
现在,让我们更仔细地查看这些数据。
连接Snowflake并查看结构化数据
我们将导入snowpark会话,这是我们使用刚刚提到的凭据连接到Snowflake的方式。导入后,我们可以直接用它执行SQL。
我们将首先使用一个已为您创建好的指定仓库,名为sales_intelligence_warehouse。您可以将其视为Snowflake用于执行查询的计算资源。
接着,我们将执行第二个查询,从sales_metrics表中选择前五行数据。重要的是,我们会添加.collect()来实际执行SQL。
执行SQL后,我们实际上看到了可以从该表获取的所有信息的详细信息。我们看到诸如交易ID、客户名称、交易价值、日期、是否赢得交易,甚至与交易相关的销售代表和产品线等信息。这就是我们的数据代理将访问的结构化数据。
查看非结构化数据
我们拥有的非结构化数据是会议记录。这些会议记录也存储在一个表中,因此我们可以再次使用snowpark会话进行查询。它们存储在sales_con表中,与sales_metrics表位于相同的模式中。
现在,我们可以查看一组会议记录的示例。在这个例子中,我们可以看到与Techcorp的会议是一次发现电话,围绕集成、时间线和复杂性进行了详细讨论。他们提出了许多技术问题,并提到了第二季度的预算分配。总结是,这是一次积极的接触,有明确的后续步骤。这是我们的数据代理将用来帮助回答问题的会议记录示例。
创建Cortex Researcher Agent
现在我们已经查看了数据,可以开始创建我们的Cortex代理了。
导入必要的库
我们将从导入一些需要的库开始:
- 从
snowflake导入Session和Route,用于连接Snowflake中已为我们预建的工具。 - 从
langchain和langgraph导入一些相同的组件。 - 从
Snowflake Cortex导入AgentRunRequest,这是我们用来运行代理、访问工具并从Snowflake检索非结构化和结构化数据的方式。
了解代理可用的工具
Cortex代理可以访问的第一个工具是cortex_analyst。这是一个“文本转SQL”工具,它接收用户的查询,然后编写SQL来回答该问题。该服务依赖于一个语义模型,即数据实际含义的描述,包括列和表的含义、值的含义,以及查询此数据的人员常用的任何注释。所有这些信息都包含在语义模型文件中。我们已经为您创建了这个文件并加载到了Snowflake中,我们将在此处指定该文件的确切位置。
第二个工具是search_service。这是一个预建的检索器,位于Snowflake中那些我们刚刚查看过的非结构化会议记录之上。这是一个混合检索器,结合了语义搜索和关键词搜索以及重新排序,以从这些文档中返回相关的片段。就像语义模型文件一样,我们已经在Snowflake中为您创建了这个服务,并在此处简单地指向它。它位于sales_intelligence数据库的data模式中,服务名称为sales_conversation_search。
初始化并创建Cortex代理
现在我们已经定义了使用工具所需的关键资源,可以开始初始化和创建我们的Cortex代理了。
我们的Cortex代理将接受一个单一参数,即我们将在基于Pydantic的模型中定义的查询。
然后,我们创建Cortex代理的工具类。我们将提供工具的名称和描述(它应使用销售对话和指标来回答问题),并给出参数的模式,即我们刚刚创建的空间模型。
我们将使用snowpark会话初始化我们的Cortex代理工具,在该会话之上创建一个路由,然后访问位于该路由内的Cortex代理服务。
构建代理请求并定义执行方法
为我们的Cortex代理工具提供了必要的Snowflake连接后,我们可以开始构建请求。在AgentRunRequest中,包含了调用Cortex代理所需的所有信息。Cortex代理是一个无状态服务,可以访问数据工具,例如我们刚刚为搜索和“文本转SQL”创建的工具。
我们首先提供我们希望此代理使用的模型,告诉它使用claude-3-5-sonnet。然后,我们提供一个工具列表:analyst和search。接着,我们为每个工具提供执行和操作所需的工具资源。analyst将获取我们的语义模型文件,这是“文本转SQL”实际理解数据含义的方式。然后,search工具将获取Cortex搜索服务的名称,并提供一个我们希望返回的最大结果数量。最后,我们向代理提供一组消息,这里只是一条包含用户查询的单一消息。
构建请求后,我们定义一个辅助函数来消费流。然后,我们创建run方法。run方法将接收查询并开始执行,从我们的代理返回结果。run方法的第一步是构建请求(调用我们刚刚创建的函数),然后使用代理服务并实际运行我们刚刚构建的请求。
代理服务将返回一个事件流,如果我们需要访问结构化数据,此流将包含待执行的SQL;如果访问非结构化数据,它将返回一个响应以及一些引用。
为了消费这个流,我们需要提取所有这些数据。让我们编写一个快速的辅助函数来实现这一点,并将其放在run方法上方。consume_stream将接受流,然后返回文本、SQL和引用。对于非结构化数据,它将只返回文本(即响应)和引用;对于结构化数据,我们将获得文本(即重写的查询)以及待执行的SQL。
定义了该辅助函数后,一旦运行了代理服务,我们就可以实际执行SQL。我们将使用刚刚创建的方法消费流。然后,如果确实获得了SQL(在需要访问结构化数据的情况下),我们希望执行该SQL。为此,我们将首先使用仓库sales_intelligence_warehouse(就像我们在探索数据时那样),然后使用会话执行从代理服务生成的SQL。
包装代理并测试
为了初始化这个工具,我们将使用我们的snowpark会话实例化它。
我们将围绕这个工具创建一个ReAct代理,就像我们在上一课中所做的那样。这里的ReAct代理将再次使用GPT-4,并将其绑定到我们的工具CortexAgentTool.do_run。然后,我们给它一个最终提示,告诉它是研究员,将使用我们的Cortex研究员工具进行研究。一旦找到必要的信息,它将结束其输出,不再尝试采取进一步行动。
现在让我们测试这个代理。
首先,我们尝试一个访问结构化数据的问题:“我们的前三大客户交易是什么?”在后台,这个代理将把查询重写为真正可回答且可翻译成SQL的内容,然后执行该SQL以返回结果。现在,我们可以看到基于结构化数据的前三大客户交易。
接下来,尝试一个非结构化的例子。我们再次调用Cortex代理,但询问“健康科技销售流程的下一步是什么?”这需要来自那些非结构化会议记录的信息。在这里,我们得到了一个详细的答案,告诉我们销售流程中的三个后续步骤:进行广泛的技术深度探讨、提供文档并安排后续会议。这很棒。
将新代理集成到图中
现在我们已经创建了子代理,只需要用一个LangGraph节点包装它,这样它就准备好添加到我们的图中了。
为此,我们将从状态中提取代理查询,然后使用该代理查询调用我们的Cortex代理。我们将其添加到消息历史记录中,然后使用command转到执行器。这与我们在网络搜索中所做的类似,即返回执行器。
现在我们已经创建了额外的Cortex代理研究节点,我们准备将其添加到图中。我们在辅助方法中定义了所有其他节点,因此可以重用它们,并且它们的定义与之前相同。然后,我们将像之前一样实例化我们的状态图,并添加所有相同的节点,包括这个额外的节点——Cortex研究员。这是我们对该单元所做的唯一更改。
再次确认我们构建了预期的架构,我们可以绘制Mermaid图,并看到Cortex研究员现在已包含在我们的图架构中。
测试集成后的图
现在我们已经创建了图,让我们来测试一下。


第一个问题,我们期望使用结构化数据。我们将询问“前三大客户交易是什么?”,并要求它为每个交易绘制图表。我们期望这能使用Cortex代理中的结构化数据检索工具,同时也使用图表生成和图表总结功能。看起来我们已经收到了代理的响应。代理生成了图表,清晰地显示了前三大客户交易,Fast Track领先,其次是SecureBank和Health Tech Solutions。它还提供了一个文本响应,准确描述了数据和图表中的情况。
现在,让我们尝试一些更难的问题。第二个问题有点棘手。我们首先要求识别我们待处理的交易,研究它们是否可能经历任何监管变化,然后使用每个客户的会议记录,询问并提供考虑到这些监管变化的价值主张。这很像课程开始时Andrew展示的例子。
在这个例子中,我们提出了一个棘手的问题,需要访问关于交易的结构化数据、关于这些公司所在行业可能发生的监管变化的网络数据,以及会议记录访问权限以了解我们在每笔交易中的价值主张。在这里,我们收到了代理对此问题的响应。代理在推理这个复杂请求时未能成功,它提供了一些关于如何完成任务的信息,但无法提供我们所需的关键细节。在下一课中,我们将能够了解更多这里实际出错的地方。
第三个例子,我们询问了不同会议记录中的一个共同主题。这需要访问非结构化数据,综合器能够为我们提供一个主题:会议记录侧重于使销售工作与客户需求保持一致。

总结
在本节课中,我们一起学习了如何扩展数据代理的能力,通过添加Cortex Researcher Agent使其能够从Snowflake检索结构化和非结构化数据。我们查看了可用的数据,创建并配置了新的代理及其工具,将其集成为LangGraph中的一个节点,并测试了集成后的多代理工作流。在下一课中,您将使用追踪来真正理解代理执行时底层发生的情况,并开始使用您的首次评估,通过答案相关性、上下文相关性和忠实度来衡量代理的目标完成情况。
005:观察代理性能 📊

在本节课中,我们将学习如何追踪数据代理的执行步骤,并使用三个核心指标来评估其响应是否准确。我们将介绍RAG三元组的概念,并将其应用于数据代理的评估中。

概述
你已经准备好了一个可以运行的数据代理。接下来,我们需要追踪它为实现目标所采取的步骤,并评估其响应是否准确地回答了用户的查询。为此,我们将使用三个指标:上下文相关性、答案相关性和事实依据性,以此来评估目标完成情况。
现在,让我们开始编写代码。
RAG三元组概念介绍
上一节我们介绍了评估的必要性,本节中我们来看看一个核心的评估框架:RAG三元组。
虽然RAG三元组最初是为RAG系统引入的,但它同样非常适用于数据代理。因为数据代理仍然包含相同的检索(或研究)步骤以及综合步骤,以实现其核心目标。
RAG三元组的工作原理如下:
- 首先评估任何检索到的上下文的相关性。
- 然后评估响应是否由该上下文支持。
- 最后,检查响应是否与用户的查询相关。
现在,让我们看看如何将这些概念应用于数据代理。
将RAG三元组应用于数据代理架构
当我们查看数据代理的架构时,可以清晰地看到在黄色方框中,上下文来自何处。我们有从Cortex研究器和网络研究器检索到的上下文。这些就是我们需要用来评判响应的上下文。
我们需要确保语言模型的响应或综合步骤完全基于研究步骤中检索到的这些上下文(事实依据性指标)。在答案相关性指标中,我们仍在检查响应是否与查询相关。这是将输入到数据代理的用户查询与最终完成并返回给用户的响应进行比较。最后是上下文相关性。在这里,我们测量每个代理的子查询(发送给各个子代理的查询)以及从每个子代理检索到的上下文。我们将单独测量每个代理研究的上下文相关性,然后对所有不同研究器的结果进行聚合。
收集追踪信息
为了计算这些评估,我们需要从追踪中收集关键信息。追踪是我们跟踪代理为实现用户查询和目标所采取的每一步的方式。
以下是一个追踪的示例。在这个例子中,追踪从一个研究节点开始,进行了一些图表绘制和一些额外的研究。特别是对于数据代理,我们需要仔细关注检索步骤。这些步骤包含了我们将用于评估RAG三元组的关键数据,包括事实依据性和上下文相关性等指标。
在这里,我们将使用基于OpenTelemetry构建的追踪。OpenTelemetry是一个与语言无关的分布式追踪系统,它允许我们捕获代理为实现目标所采取的每一步。这些步骤也称为跨度,是代理正在执行的工作单元。这些跨度包括规划、路由、检索、工具使用和生成等步骤。我们将特别关注这些检索类型的跨度,因为它们包含了我们需要运行评估(如上下文相关性和事实依据性)的关键数据,这也是本节课的重点。
定义评估指标
现在,让我们进入笔记本,开始定义这些指标。
我们在笔记本中要做的第一件事是导入环境。和之前一样,这包含了连接Snowflake的关键凭证,以及调用语言模型和进行网络搜索的密钥。在这个笔记本中新增的是,我们将通过这里的环境变量启用TruLens的追踪。
现在,我们可以开始定义我们的指标了。这里使用的指标依赖于“语言模型作为评判者”。我们将使用GPT-4作为这个评判任务的语言模型。为了调用这个语言模型评判者,我们将使用TruLens。具体来说,我们将使用TruLens中的OpenAI提供程序。
现在我们可以定义事实依据性指标。
为了开始定义我们的第一个反馈函数,我们首先导入Feedback类。Feedback类是我们用来构建反馈函数或评估器的工具。然后我们导入Select。Select是我们用来从追踪中关注特定跨度的方法,这包括检索到的上下文和查询文本等。
事实依据性函数接受两个关键输入:代理的最终答案(输出)以及从任何研究步骤中检索到的上下文。检索到的上下文将通过Select.RecordCalls语句来识别。然后,最终答案将通过Select.RecordOutput来识别。Select.RecordCalls和Select.RecordOutput是我们将跨度中的关键数据传递到反馈函数中的方式。
稍后在笔记本中,我们将探讨如何实际为你的代理添加追踪。这就是我们将指标与追踪连接起来的方法。现在,我们先从定义反馈函数开始。
定义答案相关性
既然我们已经定义了事实依据性,接下来可以继续定义答案相关性。答案相关性衡量数据代理产生的最终答案与原始用户查询的相关性。
我们将再次使用相同的语言模型评判者,并通过思维链推理来指定反馈函数。这意味着语言模型评判者将在给出分数之前进行推理。
答案相关性的两个输入是查询和最终答案(输出)。这些同样使用Select.RecordInput和Select.RecordOutput来指定。
定义上下文相关性
我们将定义的最后一个指标是上下文相关性。上下文相关性将接受两个输入来计算每个上下文块的相关性。第一个是查询文本,这里指的是发送给每个独立代理的各个子问题。第二个输入是检索到的上下文,即每个子代理产生的研究结果。
构建这个反馈函数后,我们还会选择对其进行聚合。这意味着我们将对不同研究步骤计算出的每个上下文相关性指标取平均值。
创建TruLens会话
定义好指标后,我们可以创建TruLens会话,以便开始记录追踪和评估。我们将导入Tru会话,它将保存我们与日志数据库的连接。我们还将导入DefaultDBConnector,这是我们用来指定连接到特定数据库的方式。这里我们连接到一个位于模块中的SQLite文件。该数据库将在我们运行代理时存储追踪和评估。
我们要做的最后一件事是通过实例化Tru会话并重置数据库来创建日志会话,以清除之前运行中的任何旧追踪或评估。
为代理添加追踪
现在,我们可以开始使用TruLens为我们的代理添加追踪。我们将自动跟踪和记录整个代理的信息,从规划到执行器再到工具调用。我们还将添加额外的自定义检测,以便专门标注我们关心的检索步骤中的信息,并将输入插入到我们的评估器中。
为此,我们将从之前的课程中导入许多熟悉的方法,包括Cortex代理、状态以及来自LangGraph和类型标注的各种类型。这里新增的是从TruLens导入instrument装饰器。这就是我们添加自定义检测的方式。
完成这一步后,我们将复制之前已经创建的相同Cortex代理研究节点,并准备为其添加检测。
添加检测只需在这个方法上方添加一个@instrument装饰器。我们将在上面创建一个新行,然后添加我们的检测。
我们要做的第一件事是将跨度类型设置为retrieval。这表明Cortex代理的研究节点正在执行检索任务,添加该跨度类型会为该跨度标注“检索”类型。然后,我们将指定我们关心的确切属性。
提醒一下,我们需要从研究节点获取的关键信息是查询文本(发送给该子代理的确切子问题)和检索到的上下文(代理产生的研究结果)。我们可以使用函数的返回值和任何参数或关键字参数来访问这些信息。Ret代表函数的返回值,Args代表函数的输入参数,在这里是state。
我们先讨论查询文本。我们将通过查看函数的第一个参数(索引0)来获取查询文本,在这里是state。然后,我们获取agent_query,它是state中的一个键。因此,综合起来,我们将查询文本设置为在研究节点被使用时,state中的agent_query。
然后我们可以讨论检索到的上下文。检索到的上下文从函数的返回值中提取。这里我们想关注消息,特别是该研究节点被调用时产生的最后一条消息,并从中提取内容。因此,综合起来,我们将检索到的上下文跨度属性分配给研究节点产生的最后一条消息,并通过这种方式获取其内容。
通过这种检测,我们将拥有开始评估代理所需的所有数据。
对于网络搜索节点,我们希望应用相同的过程。我们将采用现有的节点,并通过在其上方添加instrument装饰器来添加检测。我们将设置与之前相同的跨度类型retrieval,并设置完全相同的跨度属性query_text和retrieved_context。
重建图并注册代理
现在我们已经为研究节点添加了额外的检测,我们可以用这个新增的检测来重建图。
我们将再次从LangGraph导入StateGraph和START。我们将从辅助函数中导入在之前课程中创建的每个节点。此外,我们还将添加我们刚刚用新检测重新创建的新的网络研究节点和Cortex代理研究节点。
重建图之后,我们可以将代理注册到TruLens。为此,我们将使用TruLens中一个特定于LangGraph的包装器,称为TruGraph。TruGraph为LangGraph的节点和任务API提供了自动检测,因此我们可以捕获图中每个节点的输入和输出,以及每个节点的名称。通过这种方式,即使没有我们之前添加的额外检测,我们也能理解代理的整个追踪。而额外检测带给我们的好处是,现在我们有了明确的标签来标识哪些步骤是检索步骤,并且我们还处理了进出这些节点的数据,以专门提取传入的代理查询和传出的检索文本。如果没有这种额外的处理,这些信息将被埋藏在复杂的数据结构中,评估起来会困难得多。
现在,让我们注册我们的代理。除了添加检测,注册代理还允许我们跟踪关键元数据,如应用程序名称和应用程序版本。这使我们能够区分不同的版本或应用程序,以便在做出更改时比较性能。我们还将指定我们希望与此代理关联的评估列表,这里我们将使用之前在笔记本中定义的RAG三元组。
运行代理并查看结果
现在,我们可以再次开始使用我们的代理,但这一次,我们将记录它的每一个动作。在这里,我们将输入三个连续的查询,开始询问代理并使用其新技能。同时,我们也将通过追踪和评估来测量其性能。
在第一个问题中,我们再次询问:“我们的前三大客户交易是什么?”并要求它绘制结果图表。在第二个问题中,我们试图识别我们的待处理交易,研究监管变化,并为每个交易考虑一个新的价值主张。在最后一个查询中,我们将要求它识别我们最大的客户交易,从会议记录中查找相关的重要主题,然后为每个主题查找新闻文章。
我们将所有三个查询提交给我们的代理,并等待它响应。
第一个代理已经响应。它给了我们一个漂亮的图表,以图表形式显示了按价值排名的前三大客户交易。然而,图表摘要再次未能给出一个漂亮的文本答案。
现在,我们的第二个代理已经响应。它成功地识别了一些公司,并研究了它们的价值主张以及会议记录的重点内容。但据我所知,它未能将其过滤到仅包含待处理交易。
当我们探索TruLens仪表板时,将能够更具体地了解该查询到底出了什么问题。
现在,代理已经响应了第三个查询。代理无法访问关于最大客户交易的信息,因为它在访问Snowflake时遇到了问题。
查看仪表板进行诊断

现在,让我们进入仪表板,以便准确诊断这些不同查询出了什么问题。
为了启动仪表板,我们将从TruLens导入run_dashboard。我们还将指定一个特定的端口来运行它,并运行run_dashboard来启动仪表板。
仪表板将读取我们在本笔记本运行期间写入数据库的追踪和评估数据。我们将能够探索追踪,查看它们如何与评估相关联,并真正深入地理解到底出了什么问题,识别代理的不同故障模式。
运行run_dashboard后,我们会看到两个链接。请点击第二个,因为它是在深度学习环境中运行的。如果你在本地运行,可以使用这个本地主机链接。
现在我们已经打开了TruLens仪表板,可以开始检查我们的代理性能了。
为了扩大视野,我们可以通过选择左上角的双箭头来最小化左侧窗格。在排行榜中,我们可以看到按应用程序版本汇总的不同评估指标。这里我们开始时只有一个应用程序版本,即我们在笔记本中设置的“L4_base”。我们看到我们的应用程序(或代理)的答案相关性得分和事实依据性得分都很低。
让我们点击版本本身以进行更多探索。为此,我们将选择要检查的版本旁边的复选框,然后选择“检查记录”。
现在我们可以看到提交给该特定版本代理的每个查询。让我们逐一检查每个查询。
当我们询问前三大客户交易是什么并绘制每个交易的价值时,我们看到了图表的输出,但它实际上并没有总结图表。我们可以检查评估指标,看看语言模型评判者是如何以编程方式识别故障模式的。
查看答案相关性,我们看到答案相关性得分为0。响应与用户查询完全不相关,因为它没有提供文本答案。我们也可以在这里的语言模型评判者解释中看到这一点。我们还可以检查其他指标,如上下文相关性,并看到代理所做的研究都没有返回回答查询所需的相关结果。
现在,让我们看一下第二次执行。这里,与其关注冗长的输出,不如先看看评估分数。这次我们看到答案相关性得分很高。虽然响应是相关的,但它实际上并没有基于研究结果。我们看到了事实依据性得分低的问题。语言模型所做的许多陈述实际上并没有得到研究结果的支持,而是语言模型做出的推断。造成这种情况的原因是研究步骤检索到的一些上下文并不相关。

在这里,我们也可以查看追踪的细节,以便真正理解代理采取的不同步骤。当我们向下滚动到追踪时,追踪一开始是完全展开的,因此我们可以通过点击树形结构中的箭头来最小化,以获得更清晰的视图。
完成这一步后,我们可以清楚地看到代理所采取的路径。代理从规划器开始,然后移动到执行器,接着使用了Cortex代理的研究节点。然后在这里进行了重新规划,进行了第二次网络研究、第二次Cortex代理研究,最后移动到综合器。如果我们想进一步探索,实际上可以通过点击节点本身并向下滚动来检查节点的输入和输出。


你也可以检查第三个查询的细节。根据代理的性能,你可能会看到不同的评估指标数值,如果代理在你运行时选择了不同的行动方式。
总结

在本节课中,我们一起学习了如何为数据代理添加追踪和评估。我们介绍了RAG三元组(上下文相关性、答案相关性、事实依据性)的概念,并利用TruLens框架实现了对代理每一步执行过程的监控和量化评估。通过分析仪表板中的追踪数据和评估分数,我们能够诊断代理在回答不同查询时出现的具体问题,例如未能提供文本摘要、回答缺乏事实依据或检索到不相关的上下文。这为我们后续优化代理的行为和性能提供了重要的数据支持。
006:衡量代理的GPA 🎯
在本节课中,我们将学习如何评估你的数据代理的“目标-计划-行动”是否协调一致。我们将介绍四种具体的评估方法,并通过代码示例展示如何实施这些评估,以帮助你识别代理的强项和改进空间。
概述
上一节我们介绍了数据代理的基本概念。本节中,我们将深入探讨如何衡量代理的“目标-计划-行动”协调性。我们将学习四种评估方法:计划质量、计划遵循度、执行效率和逻辑一致性。每种评估都将通过一个简单的示例来说明其工作原理和能发现的典型问题。
1. 计划质量评估
计划质量评估衡量代理制定的计划是否能有效达成目标。它关注目标与计划之间的交集。
以下是定义和运行计划质量评估函数的步骤:
# 定义计划质量评估函数
plan_quality_feedback = Feedback(
plan_quality,
name="Plan Quality"
).on_input_output()
# 设置评估模型
plan_quality_feedback.provider = OpenAI(model="gpt-4-1106-preview")
# 运行评估
plan_quality_result = plan_quality_feedback.run(
goal=user_query,
plan=agent_plan,
trace=full_execution_trace
)
评估结果示例:
- 得分:0.66(满分1.0)
- 发现的问题:
- 选择标准模糊(例如,“过去12个月的所有销售线索”缺乏紧迫性约束)。
- 优先级排序方法薄弱(例如,仅选择“最大的20个”而忽略了线索评分、阶段紧迫性或截止日期)。
- 可操作性不足(未指定具体的下一步行动或负责人)。
- 输出不具体(单一的表格,缺少与目标关联的必要字段)。
通过改进计划(例如,增加紧迫性过滤条件、多维度排序、指定具体行动项),我们可以将计划质量得分提升至1.0。
2. 计划遵循度评估
计划遵循度评估衡量代理实际执行的动作是否遵循了它自己制定的计划。
以下是定义和运行计划遵循度评估函数的步骤:
# 定义计划遵循度评估函数
plan_adherence_feedback = Feedback(
plan_adherence,
name="Plan Adherence"
).on_input_output()
# 设置评估模型
plan_adherence_feedback.provider = OpenAI(model="gpt-4-1106-preview")
# 运行评估
adherence_result = plan_adherence_feedback.run(
plan=agent_plan,
trace=full_execution_trace
)
评估结果示例:
- 得分:0.0(当动作严重偏离计划时)
- 发现的问题:
- 多个计划步骤被省略。
- 步骤执行顺序错乱或被未计划的动作替代。
- 未对计划变更或新动作做出解释或记录。
当代理的动作严格遵循计划时,计划遵循度得分可以达到1.0。
3. 执行效率评估
执行效率评估衡量代理的执行轨迹是否高效,即是否为达成目标的最优路径。它会标记冗余或不必要的步骤。
以下是定义和运行执行效率评估函数的步骤:
# 定义执行效率评估函数
exec_efficiency_feedback = Feedback(
execution_efficiency,
name="Execution Efficiency"
).on_input_output()
# 设置评估模型
exec_efficiency_feedback.provider = OpenAI(model="gpt-4-1106-preview")
# 运行评估
efficiency_result = exec_efficiency_feedback.run(trace=full_execution_trace)
评估结果示例:
- 得分:0.66
- 发现的问题:
- 存在冗余的数据检索(多次应用相同的过滤器)。
- 不必要的输出格式重复(例如,同时导出为XLSX和CSV格式)。
4. 逻辑一致性评估
逻辑一致性评估在目标、计划和行动的交集中寻找矛盾。它检查代理在推理、规划步骤或执行步骤中是否存在逻辑不一致。
以下是定义和运行逻辑一致性评估函数的步骤:
# 定义逻辑一致性评估函数
logical_consistency_feedback = Feedback(
logical_consistency,
name="Logical Consistency"
).on_input_output()
# 设置评估模型
logical_consistency_feedback.provider = OpenAI(model="gpt-4-1106-preview")
# 运行评估
consistency_result = logical_consistency_feedback.run(trace=full_execution_trace)
评估结果示例:
- 得分:0.33
- 发现的问题:
- 步骤间数据矛盾(例如,步骤1报告96条线索,步骤2报告13条,且无解释)。
- 使用的排名逻辑(如“最小参与度”)理由不充分。
- 决策依据(如“活跃的下一步行动”)未作说明。
5. 在完整数据代理上运行评估
现在,我们将这四种GPA评估应用到我们在第4课构建的完整数据代理上,并查看其在真实查询中的表现。
以下是注册代理并添加GPA评估的代码:
# 构建代理图
app = create_data_agent_graph()

# 使用TruLens注册代理,并添加评估项
tru_app = Tru(
app,
app_id='Data Agent',
feedbacks=[
# RAG三元评估(目标完成度)
answer_relevance,
context_relevance,
groundedness,
# GPA评估
plan_quality_feedback,
plan_adherence_feedback,
exec_efficiency_feedback,
logical_consistency_feedback
]
)
# 运行代理并记录评估结果
for query in test_queries:
with tru_app as recording:
response = app.invoke(query)
评估结果分析:
通过TruLens仪表板,我们可以查看代理在多个查询上的平均表现:
- 计划质量和逻辑一致性通常表现良好。
- 计划遵循度和执行效率有较大的改进空间。
- RAG三元评估(答案相关性、上下文相关性、真实性)的得分也有提升余地。
我们可以深入查看单个查询的记录,分析LLM评估者给出的具体反馈,例如,找出计划在哪个步骤被偏离,或者执行中出现了哪些冗余操作。
总结



本节课中,我们一起学习了如何衡量数据代理的“目标-计划-行动”协调性。我们介绍了四种核心评估方法:
- 计划质量:评估计划本身对目标的达成度。
- 计划遵循度:评估执行动作对计划的遵循程度。
- 执行效率:评估执行轨迹是否高效、无冗余。
- 逻辑一致性:评估整个推理和执行过程中是否存在逻辑矛盾。


通过代码示例,我们看到了如何定义这些评估函数,并将其应用于代理的执行轨迹上。最后,我们在一个完整的数据代理上运行了这些评估,并通过仪表板分析了结果,识别了代理的优势和待改进的领域。在下一课中,我们将学习系统性的技术来提升代理的GPA表现,以解决本节中发现的问题。
007:提升代理的GPA 🚀

在本节课中,我们将学习如何诊断并提升数据代理的性能。具体来说,我们将聚焦于如何通过调整提示词、引入内联评估等方法来改善代理的“目标-计划-行动”一致性,从而提升其GPA。
上一节我们介绍了如何识别代理性能中的明确失败模式。本节中,我们来看看如何通过一些常见方法来提升代理的GPA。
提升代理性能的常见方法
以下是几种提升代理性能的常见策略:

- 调整提示词:例如,可以修改规划提示词,使其包含明确的子目标、前置条件和后置条件。这样,代理的每一步计划都会非常明确地指出需要完成什么。
- 公式/代码示例:
更新后的规划提示 = 基础提示 + “请为每一步添加:前置条件、后置条件、目标描述”
- 公式/代码示例:
- 引入内联评估:内联评估提供实时反馈,让代理能够理解其执行效果。例如,在执行研究步骤后,代理执行器可以获得一个评估分数和解释,以判断研究是否遗漏了关键细节,然后利用这些信息来选择下一步行动。
- 优化检索器或尝试不同模型:通过调整检索器参数或更换基础模型来提升性能。
- 通过离线评估验证改进:任何改进都应在独立的离线评估中进行验证,以确保其有效性。
现在我们已经了解了提升代理GPA的一般流程,接下来让我们回到Notebook中,将这些方法付诸实践。
在Notebook中实施针对性改进
在上节课中,我们分析了代理的具体表现,并识别出一个关键失败节点:计划遵循度不足。代理的行动没有遵循规划器制定的计划。
在本Notebook中,我们将逐步演示如何进行针对性修改以提升代理的GPA。
第一步:设置环境与启用追踪
和之前一样,我们首先导入环境并开启TruLens追踪。
# 导入环境并开启TruLens追踪
import os
from trulens_eval import Tru
tru = Tru()
tru.run_dashboard() # 启用仪表盘
第二步:实施第一个针对性更改——内联评估
为了添加内联评估,我们需要导入一些必要的组件。
# 导入相关组件
from trulens_eval import Feedback, TruLlama
from trulens_eval.tru_custom_app import instrument
from typing import Dict, List
# 从helper文件中导入我们之前设置好的代理、状态和评估函数
from helper import cortex_agent, State, context_relevance_feedback
接下来,我们将导入一个新的关键组件:内联评估装饰器。这里我们使用一个LangGraph特定的装饰器,因为它需要与LangGraph的状态进行交互,但这个概念是通用的。
核心概念:内联评估是一种在代理执行某个步骤或行动后立即进行评估,并将反馈结果存入代理记忆的方法。这样,代理就能利用这些反馈来改进其后续行动,并在必要时重新规划。
添加内联评估非常简单,只需在已插桩的节点上添加一个装饰器。我们从Cortex代理的研究节点开始。
# 对Cortex代理的研究节点添加内联评估
@instrument
def cortex_research_node(state: State):
# ... 原有的研究节点逻辑 ...
return {"research_results": results}
# 添加内联评估装饰器
@TruLlama.inline_eval(feedback=context_relevance_feedback)
@instrument
def cortex_research_node_with_eval(state: State):
# 这是同一个研究节点,现在被装饰了
return cortex_research_node(state)
这个内联评估装饰器接受一个我们想要对研究节点运行的反馈函数(评估器)。评估将在研究节点执行后立即运行,并针对反馈函数定义中指定的跨度属性进行计算。评估结果(包括分数和解释)将被添加回代理的状态中(通过更新状态中的消息),供代理后续使用。
我们对网络搜索节点也进行同样的操作。
# 对网络搜索节点添加内联评估(过程完全相同)
@TruLlama.inline_eval(feedback=context_relevance_feedback)
@instrument
def web_search_node_with_eval(state: State):
# 原有的网络搜索节点逻辑
return web_search_results
第三步:实施第二个针对性更改——更新规划提示词
我们想要更新规划提示词,具体是指示规划语言模型为代理计划中的每一步添加明确的前置条件、后置条件和目标描述。增加这些明确的细节有助于执行器理解每一步的目标,从而改善工具调用和代理决策。
我们通过修补计划提示词来实现这一更改。
# 更新规划提示词模板
updated_plan_prompt_template = """
... 原有的提示词内容 ...
请为计划中的每一步生成以下内容:
- 步骤名称: {step_name}
- 行动: {action}
- **前置条件**: 执行此步骤前必须满足的条件。
- **后置条件**: 此步骤成功执行后预期达到的状态。
- **目标描述**: 此步骤旨在实现的具体目标。
...
"""
# 在运行时,这个模板将被填充并用于规划节点
我们所做的是扩展了给规划语言模型的输出模板。除了步骤名称和行动,我们还要求它包含明确的前置条件、后置条件和目标描述。
第四步:重建图并注册新版本代理
现在我们已经完成了两项针对性更改,接下来用新增的内联评估和更新后的计划来重建图。
# 使用更新后的节点重建图
graph = create_agent_graph(
cortex_researcher=cortex_research_node_with_eval,
web_researcher=web_search_node_with_eval,
# ... 从helper导入其他节点 ...
)
# 连接到日志数据库(该数据库已包含上一节课的结果)
tru.connect_database("path_to_database.db")
连接到数据库后,我们需要注册新版本的代理。这允许我们将新版本的结果与旧版本进行直接比较。
# 注册新版本代理
app_version = "v2_inline_eval_updated_plan" # 描述性版本名,说明所做的更改
tru.register_app(
name="data_agent_app",
version=app_version,
app=graph
)
第五步:重新测试代理并分析结果

使用相同的查询集重新运行代理。
test_queries = ["查询1", "查询2", "查询3"]
for query in test_queries:
result = graph.run(query)
print(result)

运行完成后,我们可以打开TruLens仪表盘来深入分析代理性能。
在仪表盘中比较与分析
在仪表盘的排行榜视图中,我们可以比较不同代理版本的综合指标。我们可以看到:
- 答案相关性有所提升。
- 上下文相关性保持不变。
- 事实依据性有显著改善。
- 计划遵循度得到提升。
- 执行效率和逻辑一致性略有下降(这是为了更高目标完成度所做的权衡)。
最有趣的是直接比较同一记录在不同代理版本下的表现。我们可以并排查看两个版本的追踪轨迹。
比较追踪轨迹:在改进后的版本(右侧)中,追踪轨迹更长,包含了更多的网络搜索和Cortex研究调用。这些额外的研究步骤是由内联评估所识别出的信息缺口触发的,并且代理的行动也在与计划中列出的子目标进行比对。

比较评估指标:以“计划遵循度”为例。
- 基础版本(左):得分很低(例如0分),因为代理省略了原始计划中的多个步骤。
- 改进版本(右):得分为1(满分)。评估解释显示:“计划中的每一步都被执行并完成,特别是重新计划中的每一步。任何因外部数据访问限制而产生的偏差都得到了明确说明。没有步骤被跳过或忽略,所有偏差都有合理解释。计划被尽可能紧密地遵循。”


通过同时观察代理的追踪轨迹和比较评估指标,我们可以清晰地看到在迭代和修改过程中代理性能的差异。
改进版本中增加的内联评估正在帮助引导代理成功完成目标。我们在这里做了一个权衡:牺牲了一些执行效率,以换取更高的目标完成度。我们明确地选择基于评估结果进行额外研究,这甚至可以直接在追踪轨迹中展开研究节点看到评估的发生。
总结
本节课中我们一起学习了如何通过系统性的方法来提升数据代理的GPA。我们主要探讨了两个核心的改进策略:
- 引入内联评估:为关键节点(如研究、搜索)添加实时反馈机制,使代理能动态调整其行动。
- 优化规划提示词:通过要求规划器为每一步定义明确的前置条件、后置条件和目标,来增强“目标-计划-行动”的一致性。
我们通过Notebook实践了如何实施这些更改,并使用TruLens仪表盘对新旧版本进行了可视化比较,从而验证了改进措施的有效性。实践表明,本课程所探讨的围绕评估、追踪和谨慎迭代的技术,确实有助于使代理变得更加可靠。

恭喜你完成了本课程的学习!
008:总结 🎯
在本节课中,我们将对构建与评估数据代理的整个课程内容进行回顾与总结。

恭喜你完成了这门课程。你已经设计并评估了一个数据代理,这个代理能够创建计划、执行计划,并能根据代理自身的状态更新来调整计划。
上一节我们介绍了如何衡量代理的行动,本节中我们来看看整个学习过程的最终成果。
你使用RAG三元组(RAG Triad)来评估了代理的目标达成情况。最重要的是,你测量并提升了代理的GPA(目标-计划-行动一致性,Goal, Plan and Action alignment)。
以下是本课程的核心成就列表:
- 设计与构建:你成功设计并构建了一个功能完整的数据代理。
- 评估与测量:你运用RAG三元组框架对代理性能进行了系统评估。
- 优化与对齐:你通过测量和改善GPA,确保了代理的目标、计划与行动高度一致。
做得非常出色。我迫不及待想看到你接下来会构建出什么。
本节课中我们一起学习了构建与评估数据代理的完整流程,从设计、执行到基于状态的动态调整,并掌握了使用RAG Triad进行评估以及优化GPA(目标-计划-行动一致性)的关键方法。恭喜你掌握了这些核心技能,期待你将它们应用于未来的项目中。

浙公网安备 33010602011771号