Agentic AI:从概念到落地实践
在刚刚落幕的Open Data Science Conference AI Builders Summit 2025上,Agentic AI( agency intelligence)和AI Agent(智能代理)成为了数据领域最炙手可热的话题。作为一名数据从业者,你是否也经常被各种架构、框架和技术方案的信息淹没?
面对如此繁杂的信息海洋,与其盲目追逐热点,不如从基础开始,真正理解这些技术的核心逻辑。这也是本文及配套实践的初衷。
Agentic AI 简明解析
在学术界,智能体(agent)
的概念由来已久。简单来说,一个智能体就是一个系统------当它被赋予目标和工具后,会利用这些工具去实现目标。
而Agentic AI
,则是将大语言模型(LLMs)应用于智能体系统中的最新实践。具体来说,就是用LLMs来决定如何达成目标、规划工具的使用顺序,并处理各种意外状态。
这种结合了生成式AI 和大语言模型的方案,相比传统的规则引擎或机器学习方法,展现出两大独特优势:
1. 内嵌知识(Embedded knowledge)
现代大型语言模型经过海量数据训练,内置了丰富的知识库。这使得它们能够用最少的指令处理多种复杂场景。
2. 结构化输出(Structured responses)
LLMs可以被训练生成结构化的输出格式。这种特性极大简化了与智能体系统的集成,因为智能体本质上就是在处理这些结构化的信息流。
接下来,我们将通过一个数据分析师AI代理的实践案例,带你一步步了解Agentic AI的核心原理和落地应用。
我们的AI数据专家:基于Teradata Vantage的智能问答系统
在本次实践中,我们将打造一个专注于解答业务问题的AI代理。这个智能助手具备以下特点:
- 目标:根据数据库中的信息回答用户提出的商业相关问题
- 核心工具:内置了可以直接执行SQL语句的函数,能够从Teradata Vantage数据库中获取数据
开发所需资源
为了构建这个AI代理,我们需要以下资源:
-
Teradata VantageCloud开发环境(含Jupyter Notebook集成)
(免费获取地址:ClearScape Analytics Experience)
这是一个强大的数据开发平台,特别适合处理复杂的业务数据分析场景。
-
大语言模型(LLM)的API访问权限
在本次示例中我们使用OpenAI API,但你也可以根据需求选择其他服务(如Ollama、Google PaLM等)。只需调整对应的API调用方式即可。
开发环境搭建指南
-
登录ClearScape Analytics Experience 打开浏览器访问平台地址,输入你的账号信息完成登录。建议使用常用邮箱作为用户名,方便后续找回密码。
-
创建开发环境
登录后进入控制台,点击"Create Environment"按钮开始配置你的开发环境。
为新环境命名并设置访问密码(记住这个密码,后续会用于数据库连接)。
-
启动Jupyter Notebook
环境创建完成后,点击"Run demos"按钮启动交互式开发环境。
进入Jupyter Notebook界面后,你就可以开始编写代码和测试模型了。
-
准备开发 workspace
在Jupyter环境中找到"Use Cases"文件夹,在其中新建一个以项目名称命名的文件夹。这里我们将存放所有的代码和相关配置文件。
通过以上步骤,你已经完成了AI代理的基础环境搭建。接下来就可以开始编写具体的业务逻辑代码了!这个过程类似于训练一个数据专家,让它能够理解你的数据库结构并根据需求返回准确的答案。
- 打开你创建的文件夹,点击菜单栏中的"File"选项,选择"Open from URL",然后在弹出的对话框中粘贴以下链接:
- raw.githubusercontent.com/Teradata/si...
加载示例笔记本到ClearScape Analytics Experience
- 这将把项目中的笔记本加载到你的环境中。
- 在你创建的文件夹中,新建或加载一个名为
configs.json
的文件,并使用以下结构(记得将"your-api-key-here"
替换为你的实际LLM API密钥):
json
{"llm-api-key": "your-api-key-here"}
- 依赖项注意事项
- 如果你在ClearScape Analytics Experience上运行该项目,上述内容即为所有必需的先决条件,笔记本可以直接运行。
- 如果你使用的是不同的LLM提供商,则需要安装相应的SDK,并根据需要调整笔记本中的代码。
构建数据分析师AI代理
项目设置:
在本节中,我们将安装并导入所需的库,将LLM服务API密钥加载到内存中,创建数据库连接,并加载示例数据。
第一步是安装我们选择的LLM API提供商的SDK:
diff
!pip install openai
数据分析新姿势:用ClearScape Analytics让数据库连接更简单!
你是否还在为复杂的数据库配置烦恼?今天让我们一起探索如何在ClearScape Analytics环境中快速上手数据分析。
项目运行体验大升级
在ClearScape Analytics环境下运行项目,你会发现数据库已经贴心地集成在了笔记本环境中。这意味着我们告别了繁琐的安装和配置步骤,直接进入数据分析的核心环节!
创建数据库连接变得异常简单,就像使用瑞士军刀一样方便。代码如下:
python
%run -i ../startup.ipynb
eng = create_context(host='host.docker.internal', username='demo_user', password=password)
print(eng)
只需要提供对应的环境密码(在前期准备中已经设置好),就能轻松建立连接。
数据加载的秘密武器
数据库连接成功后,就可以运行data_loading_queries
来创建所需的表格了。这一步骤就像是为数据分析准备好弹药库,确保后续分析顺利进行。
智能Agent的终极配置指南
在ClearScape Analytics中,我们通过定义系统的提示词来设定数据分析师助手的行为规范。这个系统提示就像是一个任务清单,指导AI完成从理解需求到执行查询再到呈现结果的完整流程。
来看看具体的配置步骤:
- 目标设定:明确助手作为零售公司数据分析师的角色
- 行动指南:详细说明如何根据用户问题构建SQL查询
- 数据库权限:提供可用数据库、表和字段的目录信息
- 代码规范:要求生成标准的Teradata SQL语法
系统提示示例:
python
system_prompt = f"""You are a data analyst for a retail company working with a Teradata system.
1. Users send you business questions in plain English, and you provide answers to those questions based on the data in the provided databases.
2. To generate answers, you must construct an SQL statement to query the Teradata database. The catalog of databases, tables, and columns available for querying is provided in the following JSON structure: {query_teradata_dictionary(databases)}.
- The SQL query must be written as a single line of text without carriage returns or line breaks.
- Ensure that the query adheres to Teradata's SQL dialect and does not include unsupported keywords such as `LIMIT`.
- Joins across tables should be used when necessary to fulfill the user's request.
3. Execute the SQL query in Teradata.
4. Present the query results to the user in plain English."""
为什么选择ClearScape Analytics?
- 开箱即用:数据库环境已经为你准备就绪
- 智能提示:系统提示帮你简化复杂操作
- 高效执行:直接生成并运行标准SQL语句
通过这些配置,你将拥有一位"聪明的数据分析师助手",它能理解业务问题、构建查询、执行分析,并以自然语言呈现结果。是不是感觉数据分析从未如此简单?
让我们一起来看看如何用Python玩转Teradata数据库查询吧!🚀
首先,我们需要一位"数据目录快递员"来帮助我们获取所需的数据信息。这个任务就交给了query_teradata_dictionary()
函数。
python
databases = ["teddy_retailers"]
def query_teradata_dictionary(databases_of_interest):
query = f''' SELECT DatabaseName, TableName, ColumnName, ColumnFormat, ColumnType FROM DBC.ColumnsV WHERE DatabaseName IN ('{', '.join(databases_of_interest)}') '''
table_dictionary = DataFrame.from_query(query)
return json.dumps(table_dictionary.to_pandas().to_json())
这个函数就像一位神通广大的数据库查询专家,它会从Teradata系统的"信息中心"DBC数据库中提取我们需要的信息。DBC数据库就像是一个数据目录的信息宝库,里面存放着所有数据库、表和字段的相关信息。
接下来,我们看看如何让我们的LLM(大语言模型)知道它可以使用的工具。在这里,我们只需要为它提供一个瑞士军刀般的函数------用来执行任意的SQL查询:
python
def query_teradata_database(sql_statement):
query_statement = sql_statement.split('ORDER BY',1)[0]
query_result = DataFrame.from_query(query_statement)
return json.dumps(query_result.to_pandas().to_json())
这个工具就像是一把万能钥匙,可以打开Teradata数据库的任意数据之门。只需要提供合法的SQL语句,它就能帮我们获取所需的数据。
通过这种方式,我们可以让LLM更专注于数据分析和结果解释,而不用再去关心具体的数据库查询细节。是不是很酷?
在处理查询结果返回给Agent时,默认使用JSON结构是一个非常高效的选择。这时候,Teradata DataFrames就成了我们的好帮手------它能让我们轻松处理SQL查询,但这里有个小秘密:不能用ORDER BY语句哦!
这个限制可能让刚开始接触的同学感到困惑,毕竟LLM(大语言模型)并不知道这个"潜规则"。我们需要通过一些巧妙的提示和测试来让它生成符合要求的SQL语句。
说到提示(Prompts),这里有个重要的小技巧:规则比提示更靠谱。在与LLM打交道时,如果能把需求转化成明确的规则,效果会事半功倍。就像代码开头那个不起眼的小字符串操作,就是我们用来"驯服"模型的小小秘密武器。
工具(Tools)是通过它们对应的签名(Signatures)让LLM了解的。这些签名可以手动创建,也可以用一些辅助函数自动生成------相比手动定义,后者显然更高效、出错也少得多。
让我们一起看看这个工具函数的实现原理吧!它就像一位专业的数据架构师,帮我们把Python函数转换成标准的JSON Schema格式。
python
def function_to_schema(func) -> dict:
# 创建一个类型映射表,将Python类型转换为JSON Schema支持的类型
type_map = {
str: "string", # 字符串类型
int: "integer", # 整数类型
float: "number", # 数字类型
bool: "boolean", # 布尔类型
list: "array", # 数组类型
dict: "object", # 对象类型
type(None): "null" # null类型
}
try:
# 获取函数的签名信息
signature = inspect.signature(func)
except ValueError as e:
raise ValueError(
f"Failed to get signature for function {func.__name__}: {str(e)}"
)
parameters = {}
for param in signature.parameters.values():
try:
# 尝试获取参数的类型注解
param_type = type_map.get(param.annotation, "string")
except KeyError as e:
raise KeyError(
f"Unknown type annotation {param.annotation} for parameter {param.name}: {str(e)}"
)
parameters[param.name] = {"type": param_type}
# 确定哪些参数是必须的(没有默认值)
required = [
param.name
for param in signature.parameters.values()
if param.default == inspect._empty
]
return {
"type": "function",
"function": {
"name": func.__name__,
"description": (func.__doc__ or "").strip(),
"parameters": {
"type": "object",
"properties": parameters,
"required": required,
},
},
}
这个工具函数就像一位专业的数据架构师,帮我们把Python函数转换成标准的JSON Schema格式。它会自动分析函数的参数类型,并生成对应的Schema结构。
在实际应用中,我们可以这样使用:
python
tools = [query_teradata_database] # 定义我们的工具列表
tool_schemas = [function_to_schema(tool) for tool in tools] # 自动生成每个工具的Schema
通过这种方式,我们就能为每个AI大语言模型工具生成统一的数据结构描述,让它们能够更好地协同工作。这在当前流行的工具化开发中非常实用,特别是在处理复杂数据流和API接口设计时,可以显著提升代码的可维护性和扩展性。
这个方法不仅简化了类型转换的工作,还为我们未来的功能扩展提供了良好的基础架构。
- 创建函数名称到实际对象的映射关系:为了让大语言模型返回的字符串形式的函数名能够被我们的代码正确调用,我们需要建立一个工具映射。这个映射会把每个工具的名称(字符串)和对应的函数对象关联起来。
python
tools_map = {tool.__name__: tool for tool in tools}
- 工具调用执行器:这是一个帮助函数,它接收函数名(字符串)和参数,并通过我们刚刚创建的工具映射来调用相应的函数。这个过程有点像在玩乐高积木,我们需要先找到正确的积木(函数),然后按照说明书(参数)正确拼接起来。
python
def execute_tool_call(tool_call, tools_map):
name = tool_call.function.name
args = json.loads(tool_call.function.arguments)
print(f"Assistant: {name}({args})")
# 调用对应的函数并传入参数
return tools_map[name](**args)
- 运行时函数:这是整个流程的核心部分。这个函数接收系统提示和用户消息,并通过一个循环与大语言模型进行交互。每次迭代都会向LLM发送请求,获取新的响应,并将其添加到消息链中。
python
def run_full_turn(system_message, messages):
while True:
print(f"just logging messages {messages}")
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "system", "content": system_message}] + messages,
tools=tool_schemas or None,
seed = 2
)message = response.choices[0].messagemessages.append(message)
这个过程有点像在和一个智能助手聊天,每次你发送一条消息(蓝色方块),它都会根据上下文回复一条新的消息(绿色方块)。循环会一直进行下去,直到达到某种终止条件。
让我们来一步步拆解这个过程:
-
外层循环的第一次迭代:
- 消息链中只有用户的初始查询:
[{'role': 'user', 'content': 'what is the month with the greatest amount of orders'}]
- 消息链中只有用户的初始查询:
-
LLM处理后返回消息:
python
message=ChatCompletionMessage(
content=None,
refusal=None,
role='assistant',
audio=None,
function_call=None,
tool_calls=[
ChatCompletionMessageToolCall(
id='call_OokTYjelsTCfERs9pTfUwA9h',
function=Function(
arguments='{"sql_statement":"SELECT EXTRACT(MONTH FROM order_date) AS order_month, COUNT(order_id) AS order_count FROM teddy_retailers.source_orders GROUP BY order_month ORDER BY order_count DESC"}',
name='query_teradata_database'
),
type='function'
)
]
)
- 更新后的消息链:
python
[
{'role': 'user', 'content': 'what is the month with the greatest amount of orders'},
ChatCompletionMessage(
content=None,
refusal=None,
role='assistant',
audio=None,
function_call=None,
tool_calls=[
ChatCompletionMessageToolCall(
id='call_OokTYjelsTCfERs9pTfUwA9h',
function=Function(
arguments='{"sql_statement":"SELECT EXTRACT(MONTH FROM order_date) AS order_month, COUNT(order_id) AS order_count FROM teddy_retailers.source_orders GROUP BY order_month ORDER BY order_count DESC"}',
name='query_teradata_database'
),
type='function'
)
]
)
]
在这个过程中,我们可以看到:
- 外层循环负责总体协调
- 内部循环处理具体的工具调用请求
- 每次迭代都会将新的结果添加到消息链中
- 这种设计让LLM能够逐步完成复杂的任务
通过这种分步执行的方式,模型可以有效地调用各种工具(在这个例子中是数据库查询),最终得到准确的结果。
让我们深入理解这个关键的机制:消息链路是LLM了解工具及其执行结果的重要上下文信息载体。每个tool_call
都有一个唯一的id来标识操作。
python
ChatCompletionMessageToolCall(id='call_OokTYjelsTCfERs9pTfUwA9h'...)
这个代理系统通过内部的for循环逻辑,执行工具调用并将其结果追加到消息链路中。在本次迭代中,我们成功获取了数据库查询的结果:
json
[{'role': 'user', 'content': 'what is the month with the greatest amount of orders'}, ChatCompletionMessage(content=None, refusal=None, role='assistant', audio=None,function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_OokTYjelsTCfERs9pTfUwA9h', function=Function(arguments='{"sql_statement":"SELECT EXTRACT(MONTH FROM order_date) AS order_month, COUNT(order_id) AS order_count FROM teddy_retailers.source_orders GROUP BY order_month ORDER BY order_count DESC"}', name='query_teradata_database'), type='function')]), {'role': 'tool', 'tool_call_id': 'call_OokTYjelsTCfERs9pTfUwA9h', 'content': '"{\\"order_month\\":{\\"0\\":11,\\"1\\":8,\\"2\\":12,\\"3\\":7,\\"4\\":10,\\"5\\":9},\\"order_count\\":{\\"0\\":607,\\"1\\":810,\\"2\\":721,\\"3\\":623,\\"4\\":780,\\"5\\":902}}"'}]
可以看到,这个结果清晰地展示了订单量最大的月份是第5个月(索引为5),对应的订单数量为902。
热门问题:哪个月的订单量最大?一文搞懂数据分析思路!
问题背景
我们需要找出 Teddy Retailers 数据库中订单量最大的月份。具体来说,就是从 source_orders
表中提取每个订单的日期,并统计每个月的订单数量。
数据查询方案
为了实现这个目标,我们可以使用以下 SQL 查询语句:
sql
SELECT
EXTRACT(MONTH FROM order_date) AS order_month,
COUNT(order_id) AS order_count
FROM
teddy_retailers.source_orders
GROUP BY
order_month
ORDER BY
order_count DESC;
这个查询的作用是:
EXTRACT(MONTH FROM order_date)
:提取订单日期的月份COUNT(order_id)
:统计每个月份的订单数量GROUP BY order_month
:按月份分组ORDER BY order_count DESC
:按订单量从高到低排序
数据分析结果
运行上述查询后,我们得到了以下数据:
json
{
"order_month": {
"0": 11,
"1": 8,
"2": 12,
"3": 7,
"4": 10,
"5": 9
},
"order_count": {
"0": 607,
"1": 810,
"2": 721,
"3": 623,
"4": 780,
"5": 902
}
}
从数据中可以看出:
- 月份对应关系 :这里的数字代表月份,例如
11
对应的是 11月。 - 订单量排名 :September(9月)以 902单 的成绩拔得头筹!
结论
经过分析可以得出结论:September(9月)是订单量最大的月份。
这个案例展示了如何通过简单的 SQL 查询快速定位业务中的热门问题。在实际应用中,类似的问题可以帮助我们优化库存、调整营销策略等。数据分析真的是"卷王之王"啊!
结语:Agentic AI的无限可能
别误会,AI代理不是单纯的聊天机器人!它们就像是你的"智能管家",默默在背后处理各种复杂任务。这次项目展示的核心就是这个"编排"过程------让不同工具和规则协同工作,完成特定目标。
说白了,LLM只是提供思路,真正干活的是我们的代理系统。它会根据LLM的建议调用合适的工具,就像是你指挥团队成员一起完成工作任务一样。
虽然像OpenAI这样的大厂提供了强大的API支持(包括工具调用、消息链路和响应记录等功能),但如果你有自己的私有化部署,可能就得靠自己搭建这套编排系统了。这时候,完善的错误处理机制就显得尤为重要,毕竟谁都不想看到"程序崩溃"吧?
这个项目只是一个起点,未来还有无限可能!你可以尝试:
- 加入实时监控功能,让代理更稳定
- 增加多语言支持,让它能听懂更多指令
- 或者甚至赋予它一定的学习能力,让它越用越聪明
快来动手试试吧!如果你有任何疑问或者改进建议,欢迎在评论区和我们交流。一起探索Agentic AI的无限可能!