Skip to main content

概述

本指南演示如何使用深度代理构建一个数据分析代理。数据分析任务通常需要规划、代码执行以及处理脚本、报告和图表等制品——这些正是深度代理旨在处理的能力。 我们将构建的代理将:
  1. 接受一个CSV文件进行分析
  2. 执行探索性数据分析并生成可视化
  3. 将结果共享到Slack频道
Slack集成是可选的。该代理可以修改为本地保存制品或通过其他渠道共享结果。

关键概念

本教程涵盖:
  • 用于沙箱代码执行的后端
  • 用于外部集成的自定义工具

设置

安装

安装核心依赖:
pip
pip install deepagents

可选依赖

在本教程中,我们将使用:
pip
pip install slack-sdk
这些服务是可选的,但对于任何生产用途,强烈建议使用沙箱环境。您也可以使用本地shell后端(需注意重要的安全考虑)或直接从后端下载制品。

LangSmith

您使用LangChain构建的许多应用程序将包含多个步骤和多次LLM调用。随着这些应用程序变得越来越复杂,能够检查链或代理内部到底发生了什么变得至关重要。最好的方法是使用LangSmith 在上方链接注册后,请确保设置环境变量以开始记录跟踪:
export LANGSMITH_TRACING="true"
export LANGSMITH_API_KEY="..."
或者,在Python中设置:
import getpass
import os

os.environ["LANGSMITH_TRACING"] = "true"
os.environ["LANGSMITH_API_KEY"] = getpass.getpass()

设置后端

深度代理使用后端在沙箱环境中执行代码。 设置详情请参阅可用提供商
pip install langchain-daytona
from daytona import Daytona

from langchain_daytona import DaytonaSandbox

sandbox = Daytona().create()
backend = DaytonaSandbox(sandbox=sandbox)
验证沙箱是否就绪:
result = backend.execute("echo ready")
print(result)
# ExecuteResponse(output='ready', exit_code=0, ...)

上传示例数据

创建并上传示例销售数据到后端:
import csv
import io

# 创建示例销售数据
data = [
    ["Date", "Product", "Units Sold", "Revenue"],
    ["2025-08-01", "Widget A", 10, 250],
    ["2025-08-02", "Widget B", 5, 125],
    ["2025-08-03", "Widget A", 7, 175],
    ["2025-08-04", "Widget C", 3, 90],
    ["2025-08-05", "Widget B", 8, 200],
]

# 转换为CSV字节
text_buf = io.StringIO()
writer = csv.writer(text_buf)
writer.writerows(data)
csv_bytes = text_buf.getvalue().encode("utf-8")
text_buf.close()

# 上传到后端
backend.upload_files([("/home/daytona/data/sales_data.csv", csv_bytes)])

实现自定义工具

数据分析任务可能会产生制品,如报告或图表。 以下简单的工具使用backend.download_files下载它们,然后使用Slack SDK上传。 我们也可以要求代理列出相关文件路径而不是上传它们,以便相关方可以根据需要单独获取。
from langchain.tools import tool
from slack_sdk import WebClient


slack_token = os.environ["SLACK_USER_TOKEN"]
slack_client = WebClient(token=slack_token)


@tool(parse_docstring=True)
def slack_send_message(text: str, file_path: str | None = None) -> str:
    """发送消息,可选择包含图像等附件。

    Args:
        text: (str) 消息的文本内容
        file_path: (str) 文件系统中附件的文件路径。
    """
    if not file_path:
        slack_client.chat_postMessage(channel=channel, text=text)
    else:
        fp = backend.download_files([file_path])
        slack_client.files_upload_v2(
            channel="C0123456ABC",  # 在此处指定您自己的频道
            content=fp[0].content,
            initial_comment=text,
        )

    return "消息已发送。"
通常,最好避免将凭证和其他机密信息添加到沙箱中。这里我们在沙箱外的工具中管理Slack令牌。

运行代理

让我们实例化一个代理:
from langchain_core.utils.uuid import uuid7

from langgraph.checkpoint.memory import InMemorySaver
from deepagents import create_deep_agent


checkpointer = InMemorySaver()

agent = create_deep_agent(
    model="google_genai:gemini-3.1-pro-preview",
    tools=[slack_send_message],
    backend=backend,
    checkpointer=checkpointer,
)

thread_id = str(uuid7())
config={"configurable": {"thread_id": thread_id}}
我们包括: 现在让我们调用我们的代理。
input_message = {
    "role": "user",
    "content": (
        "分析当前目录下的 ./data/sales_data.csv 并生成一个漂亮的图表。"
        "完成后,使用工具将您的分析和图表发送到Slack。"
    ),
}
for step in agent.stream(
    {"messages": [input_message]},
    config,
    stream_mode="updates",
):
    for _, update in step.items():
        if update and (messages := update.get("messages")) and isinstance(messages, list):
            for message in messages:
                message.pretty_print()
================================== AI 消息 ==================================

[{'text': "我将帮助您分析销售数据并创建一个漂亮的图表,然后将结果发送到Slack。让我先探索数据。", 'type': 'text'}, {'id': 'toolu_01LRot5h6WkhdpDQ1SG6EQGQ', 'input': {'file_path': './data/sales_data.csv'}, 'name': 'read_file', 'type': 'tool_use'}]
工具调用:
  read_file (toolu_01LRot5h6WkhdpDQ1SG6EQGQ)
 调用 ID: toolu_01LRot5h6WkhdpDQ1SG6EQGQ
  参数:
    file_path: ./data/sales_data.csv
================================= 工具消息 =================================
名称: read_file

错误:未找到文件 '/data/sales_data.csv'
================================== AI 消息 ==================================

[{'text': '让我先检查当前目录结构:', 'type': 'text'}, {'id': 'toolu_018DSeF8Dx714Dh2M5vJ1JuK', 'input': {'command': 'pwd'}, 'name': 'execute', 'type': 'tool_use'}, {'id': 'toolu_01As782aRsVB3mJpKPKNwg9c', 'input': {'path': '/'}, 'name': 'ls', 'type': 'tool_use'}]
工具调用:
  execute (toolu_018DSeF8Dx714Dh2M5vJ1JuK)
 调用 ID: toolu_018DSeF8Dx714Dh2M5vJ1JuK
  参数:
    command: pwd
  ls (toolu_01As782aRsVB3mJpKPKNwg9c)
 调用 ID: toolu_01As782aRsVB3mJpKPKNwg9c
  参数:
    path: /
================================= 工具消息 =================================
名称: execute

/home/daytona
[命令成功,退出码为 0]
================================= 工具消息 =================================
名称: ls

['/bin', '/boot', '/dev', '/etc', '/home', '/lib', '/lib64', '/media', '/mnt', '/opt', '/proc', '/root', '/run', '/sbin', '/srv', '/sys', '/tmp', '/usr', '/var', '/.dockerenv']
================================== AI 消息 ==================================

[{'id': 'toolu_01Sv4ut1P2XFsjrzVmQbJLpr', 'input': {'path': '/home/daytona'}, 'name': 'ls', 'type': 'tool_use'}]
工具调用:
  ls (toolu_01Sv4ut1P2XFsjrzVmQbJLpr)
 调用 ID: toolu_01Sv4ut1P2XFsjrzVmQbJLpr
  参数:
    path: /home/daytona
================================= 工具消息 =================================
名称: ls

['/home/daytona/.bash_logout', '/home/daytona/.bashrc', '/home/daytona/.face', '/home/daytona/.face.icon', '/home/daytona/.profile', '/home/daytona/.daytona', '/home/daytona/data', '/home/daytona/.zshrc']
================================== AI 消息 ==================================

[{'text': "很好!我看到有一个 data 目录。让我检查其内容:", 'type': 'text'}, {'id': 'toolu_01WN6V9RZjGGoDXzuWdEgB1z', 'input': {'path': '/home/daytona/data'}, 'name': 'ls', 'type': 'tool_use'}]
工具调用:
  ls (toolu_01WN6V9RZjGGoDXzuWdEgB1z)
 调用 ID: toolu_01WN6V9RZjGGoDXzuWdEgB1z
  参数:
    path: /home/daytona/data
================================= 工具消息 =================================
名称: ls

['/home/daytona/data/sales_data.csv']
================================== AI 消息 ==================================

[{'text': '完美!现在让我读取销售数据:', 'type': 'text'}, {'id': 'toolu_01RFmneg7mua1G5csH3rH2Zq', 'input': {'file_path': '/home/daytona/data/sales_data.csv'}, 'name': 'read_file', 'type': 'tool_use'}]
工具调用:
  read_file (toolu_01RFmneg7mua1G5csH3rH2Zq)
 调用 ID: toolu_01RFmneg7mua1G5csH3rH2Zq
  参数:
    file_path: /home/daytona/data/sales_data.csv
================================= 工具消息 =================================
名称: read_file

     1	Date,Product,Units Sold,Revenue
     2	2025-08-01,Widget A,10,250
     3	2025-08-02,Widget B,5,125
     4	2025-08-03,Widget A,7,175
     5	2025-08-04,Widget C,3,90
     6	2025-08-05,Widget B,8,200
================================== AI 消息 ==================================

[{'text': "太好了!我看到了销售数据。现在我将创建一个Python脚本来分析数据并生成一个漂亮的图表。让我创建一个包含可视化的综合分析:", 'type': 'text'}, {'id': 'toolu_01JcBjEXgPpCPuaGxn9ALwMg', 'input': {'file_path': '/home/daytona/analyze_sales.py', 'content': 'import pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom datetime import datetime\n\n# 设置美观图表的样式\nsns.set_style("whitegrid")\nplt.rcParams[\'figure.figsize\'] = (14, 10)\nplt.rcParams[\'font.size\'] = 11\n\n# 读取数据\ndf = pd.read_csv(\'/home/daytona/data/sales_data.csv\')\ndf[\'Date\'] = pd.to_datetime(df[\'Date\'])\n\n# 分析\nprint("=" * 60)\nprint("销售数据分析报告")\nprint("=" * 60)\nprint("\\n1. 数据概览")\nprint("-" * 60)\nprint(f"日期范围: {df[\'Date\'].min().strftime(\'%Y-%m-%d\')} 到 {df[\'Date\'].max().strftime(\'%Y-%m-%d\')}")\nprint(f"总记录数: {len(df)}")\nprint(f"产品: {\', \'.join(df[\'Product\'].unique())}")\n\nprint("\\n2. 汇总统计")\nprint("-" * 60)\nprint(f"总收入: ${df[\'Revenue\'].sum():,.2f}")\nprint(f"总销售量: {df[\'Units Sold\'].sum()}")\nprint(f"平均每日收入: ${df[\'Revenue\'].mean():.2f}")\nprint(f"平均每笔销售量: {df[\'Units Sold\'].mean():.2f}")\n\nprint("\\n3. 产品表现")\nprint("-" * 60)\nproduct_stats = df.groupby(\'Product\').agg({\n    \'Revenue\': [\'sum\', \'mean\'],\n    \'Units Sold\': [\'sum\', \'mean\']\n}).round(2)\nprint(product_stats)\n\nprint("\\n4. 表现最佳的一天")\nprint("-" * 60)\nbest_day = df.loc[df[\'Revenue\'].idxmax()]\nprint(f"日期: {best_day[\'Date\'].strftime(\'%Y-%m-%d\')}")\nprint(f"产品: {best_day[\'Product\']}")\nprint(f"收入: ${best_day[\'Revenue\']:.2f}")\nprint(f"销售量: {best_day[\'Units Sold\']}")\n\n# 创建可视化\nfig = plt.figure(figsize=(16, 12))\nfig.suptitle(\'销售数据分析仪表板\', fontsize=20, fontweight=\'bold\', y=0.995)\n\n# 1. 按日期的收入\nax1 = plt.subplot(2, 3, 1)\ncolors = sns.color_palette("husl", len(df))\nbars = ax1.bar(df[\'Date\'].dt.strftime(\'%m-%d\'), df[\'Revenue\'], color=colors, edgecolor=\'black\', linewidth=1.5)\nax1.set_title(\'每日收入\', fontsize=14, fontweight=\'bold\', pad=10)\nax1.set_xlabel(\'日期\', fontsize=12, fontweight=\'bold\')\nax1.set_ylabel(\'收入 ($)\', fontsize=12, fontweight=\'bold\')\nax1.grid(axis=\'y\', alpha=0.3)\nfor bar in bars:\n    height = bar.get_height()\n    ax1.text(bar.get_x() + bar.get_width()/2., height,\n            f\'${int(height)}\',\n            ha=\'center\', va=\'bottom\', fontsize=9, fontweight=\'bold\')\nplt.setp(ax1.xaxis.get_majorticklabels(), rotation=45)\n\n# 2. 按日期的销售量\nax2 = plt.subplot(2, 3, 2)\nbars = ax2.bar(df[\'Date\'].dt.strftime(\'%m-%d\'), df[\'Units Sold\'], color=colors, edgecolor=\'black\', linewidth=1.5)\nax2.set_title(\'每日销售量\', fontsize=14, fontweight=\'bold\', pad=10)\nax2.set_xlabel(\'日期\', fontsize=12, fontweight=\'bold\')\nax2.set_ylabel(\'数量\', fontsize=12, fontweight=\'bold\')\nax2.grid(axis=\'y\', alpha=0.3)\nfor bar in bars:\n    height = bar.get_height()\n    ax2.text(bar.get_x() + bar.get_width()/2., height,\n            f\'{int(height)}\',\n            ha=\'center\', va=\'bottom\', fontsize=9, fontweight=\'bold\')\nplt.setp(ax2.xaxis.get_majorticklabels(), rotation=45)\n\n# 3. 按产品的收入(饼图)\nax3 = plt.subplot(2, 3, 3)\nproduct_revenue = df.groupby(\'Product\')[\'Revenue\'].sum()\ncolors_pie = sns.color_palette("Set2", len(product_revenue))\nwedges, texts, autotexts = ax3.pie(product_revenue, labels=product_revenue.index, autopct=\'%1.1f%%\',\n                                     colors=colors_pie, startangle=90, textprops={\'fontsize\': 11, \'fontweight\': \'bold\'},\n                                     explode=[0.05] * len(product_revenue))\nax3.set_title(\'按产品的收入分布\', fontsize=14, fontweight=\'bold\', pad=10)\nfor autotext in autotexts:\n    autotext.set_color(\'white\')\n    autotext.set_fontsize(12)\n\n# 4. 按产品的总收入(条形图)\nax4 = plt.subplot(2, 3, 4)\nproduct_revenue_sorted = product_revenue.sort_values(ascending=False)\ncolors_product = sns.color_palette("coolwarm", len(product_revenue_sorted))\nbars = ax4.barh(product_revenue_sorted.index, product_revenue_sorted.values, color=colors_product, edgecolor=\'black\', linewidth=1.5)\nax4.set_title(\'按产品的总收入\', fontsize=14, fontweight=\'bold\', pad=10)\nax4.set_xlabel(\'收入 ($)\', fontsize=12, fontweight=\'bold\')\nax4.set_ylabel(\'产品\', fontsize=12, fontweight=\'bold\')\nax4.grid(axis=\'x\', alpha=0.3)\nfor i, (product, revenue) in enumerate(product_revenue_sorted.items()):\n    ax4.text(revenue, i, f\' ${int(revenue)}\', va=\'center\', fontsize=10, fontweight=\'bold\')\n\n# 5. 按产品的总销售量\nax5 = plt.subplot(2, 3, 5)\nproduct_units = df.groupby(\'Product\')[\'Units Sold\'].sum().sort_values(ascending=False)\ncolors_units = sns.color_palette("viridis", len(product_units))\nbars = ax5.barh(product_units.index, product_units.values, color=colors_units, edgecolor=\'black\', linewidth=1.5)\nax5.set_title(\'按产品的总销售量\', fontsize=14, fontweight=\'bold\', pad=10)\nax5.set_xlabel(\'数量\', fontsize=12, fontweight=\'bold\')\nax5.set_ylabel(\'产品\', fontsize=12, fontweight=\'bold\')\nax5.grid(axis=\'x\', alpha=0.3)\nfor i, (product, units) in enumerate(product_units.items()):\n    ax5.text(units, i, f\' {int(units)}\', va=\'center\', fontsize=10, fontweight=\'bold\')\n\n# 6. 产品分布\nax6 = plt.subplot(2, 3, 6)\nproduct_counts = df[\'Product\'].value_counts()\ncolors_count = sns.color_palette("muted", len(product_counts))\nwedges, texts, autotexts = ax6.pie(product_counts, labels=product_counts.index, autopct=\'%1.1f%%\',\n                                     colors=colors_count, startangle=45, textprops={\'fontsize\': 11, \'fontweight\': \'bold\'},\n                                     explode=[0.05] * len(product_counts))\nax6.set_title(\'按产品的销售交易\', fontsize=14, fontweight=\'bold\', pad=10)\nfor autotext in autotexts:\n    autotext.set_color(\'white\')\n    autotext.set_fontsize(12)\n\nplt.tight_layout()\nplt.savefig(\'/home/daytona/sales_analysis_plot.png\', dpi=300, bbox_inches=\'tight\', facecolor=\'white\')\nprint("\\n" + "=" * 60)\nprint("图表已保存为: sales_analysis_plot.png")\nprint("=" * 60)\n'}, 'name': 'write_file', 'type': 'tool_use'}]
工具调用:
  write_file (toolu_01JcBjEXgPpCPuaGxn9ALwMg)
 调用 ID: toolu_01JcBjEXgPpCPuaGxn9ALwMg
  参数:
    file_path: /home/daytona/analyze_sales.py
    content: import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from datetime import datetime

# 设置美观图表的样式
sns.set_style("whitegrid")
plt.rcParams['figure.figsize'] = (14, 10)
plt.rcParams['font.size'] = 11

# 读取数据
df = pd.read_csv('/home/daytona/data/sales_data.csv')
df['Date'] = pd.to_datetime(df['Date'])

# 分析
print("=" * 60)
print("销售数据分析报告")
print("=" * 60)
print("\n1. 数据概览")
print("-" * 60)
print(f"日期范围: {df['Date'].min().strftime('%Y-%m-%d')} 到 {df['Date'].max().strftime('%Y-%m-%d')}")
print(f"总记录数: {len(df)}")
print(f"产品: {', '.join(df['Product'].unique())}")

print("\n2. 汇总统计")
print("-" * 60)
print(f"总收入: ${df['Revenue'].sum():,.2f}")
print(f"总销售量: {df['Units Sold'].sum()}")
print(f"平均每日收入: ${df['Revenue'].mean():.2f}")
print(f"平均每笔销售量: {df['Units Sold'].mean():.2f}")

print("\n3. 产品表现")
print("-" * 60)
product_stats = df.groupby('Product').agg({
    'Revenue': ['sum', 'mean'],
    'Units Sold': ['sum', 'mean']
}).round(2)
print(product_stats)

print("\n4. 表现最佳的一天")
print("-" * 60)
best_day = df.loc[df['Revenue'].idxmax()]
print(f"日期: {best_day['Date'].strftime('%Y-%m-%d')}")
print(f"产品: {best_day['Product']}")
print(f"收入: ${best_day['Revenue']:.2f}")
print(f"销售量: {best_day['Units Sold']}")

# 创建可视化
fig = plt.figure(figsize=(16, 12))
fig.suptitle('销售数据分析仪表板', fontsize=20, fontweight='bold', y=0.995)

# 1. 按日期的收入
ax1 = plt.subplot(2, 3, 1)
colors = sns.color_palette("husl", len(df))
bars = ax1.bar(df['Date'].dt.strftime('%m-%d'), df['Revenue'], color=colors, edgecolor='black', linewidth=1.5)
ax1.set_title('每日收入', fontsize=14, fontweight='bold', pad=10)
ax1.set_xlabel('日期', fontsize=12, fontweight='bold')
ax1.set_ylabel('收入 ($)', fontsize=12, fontweight='bold')
ax1.grid(axis='y', alpha=0.3)
for bar in bars:
    height = bar.get_height()
    ax1.text(bar.get_x() + bar.get_width()/2., height,
            f'${int(height)}',
            ha='center', va='bottom', fontsize=9, fontweight='bold')
plt.setp(ax1.xaxis.get_majorticklabels(), rotation=45)

# 2. 按日期的销售量
ax2 = plt.subplot(2, 3, 2)
bars = ax2.bar(df['Date'].dt.strftime('%m-%d'), df['Units Sold'], color=colors, edgecolor='black', linewidth=1.5)
ax2.set_title('每日销售量', fontsize=14, fontweight='bold', pad=10)
ax2.set_xlabel('日期', fontsize=12, fontweight='bold')
ax2.set_ylabel('数量', fontsize=12, fontweight='bold')
ax2.grid(axis='y', alpha=0.3)
for bar in bars:
    height = bar.get_height()
    ax2.text(bar.get_x() + bar.get_width()/2., height,
            f'{int(height)}',
            ha='center', va='bottom', fontsize=9, fontweight='bold')
plt.setp(ax2.xaxis.get_majorticklabels(), rotation=45)

# 3. 按产品的收入(饼图)
ax3 = plt.subplot(2, 3, 3)
product_revenue = df.groupby('Product')['Revenue'].sum()
colors_pie = sns.color_palette("Set2", len(product_revenue))
wedges, texts, autotexts = ax3.pie(product_revenue, labels=product_revenue.index, autopct='%1.1f%%',
                                     colors=colors_pie, startangle=90, textprops={'fontsize': 11, 'fontweight': 'bold'},
                                     explode=[0.05] * len(product_revenue))
ax3.set_title('按产品的收入分布', fontsize=14, fontweight='bold', pad=10)
for autotext in autotexts:
    autotext.set_color('white')
    autotext.set_fontsize(12)

# 4. 按产品的总收入(条形图)
ax4 = plt.subplot(2, 3, 4)
product_revenue_sorted = product_revenue.sort_values(ascending=False)
colors_product = sns.color_palette("coolwarm", len(product_revenue_sorted))
bars = ax4.barh(product_revenue_sorted.index, product_revenue_sorted.values, color=colors_product, edgecolor='black', linewidth=1.5)
ax4.set_title('按产品的总收入', fontsize=14, fontweight='bold', pad=10)
ax4.set_xlabel('收入 ($)', fontsize=12, fontweight='bold')
ax4.set_ylabel('产品', fontsize=12, fontweight='bold')
ax4.grid(axis='x', alpha=0.3)
for i, (product, revenue) in enumerate(product_revenue_sorted.items()):
    ax4.text(revenue, i, f' ${int(revenue)}', va='center', fontsize=10, fontweight='bold')

# 5. 按产品的总销售量
ax5 = plt.subplot(2, 3, 5)
product_units = df.groupby('Product')['Units Sold'].sum().sort_values(ascending=False)
colors_units = sns.color_palette("viridis", len(product_units))
bars = ax5.barh(product_units.index, product_units.values, color=colors_units, edgecolor='black', linewidth=1.5)
ax5.set_title('按产品的总销售量', fontsize=14, fontweight='bold', pad=10)
ax5.set_xlabel('数量', fontsize=12, fontweight='bold')
ax5.set_ylabel('产品', fontsize=12, fontweight='bold')
ax5.grid(axis='x', alpha=0.3)
for i, (product, units) in enumerate(product_units.items()):
    ax5.text(units, i, f' {int(units)}', va='center', fontsize=10, fontweight='bold')

# 6. 产品分布
ax6 = plt.subplot(2, 3, 6)
product_counts = df['Product'].value_counts()
colors_count = sns.color_palette("muted", len(product_counts))
wedges, texts, autotexts = ax6.pie(product_counts, labels=product_counts.index, autopct='%1.1f%%',
                                     colors=colors_count, startangle=45, textprops={'fontsize': 11, 'fontweight': 'bold'},
                                     explode=[0.05] * len(product_counts))
ax6.set_title('按产品的销售交易', fontsize=14, fontweight='bold', pad=10)
for autotext in autotexts:
    autotext.set_color('white')
    autotext.set_fontsize(12)

plt.tight_layout()
plt.savefig('/home/daytona/sales_analysis_plot.png', dpi=300, bbox_inches='tight', facecolor='white')
print("\n" + "=" * 60)
print("图表已保存为: sales_analysis_plot.png")
print("=" * 60)
================================= 工具消息 =================================
名称: write_file

已更新文件 /home/daytona/analyze_sales.py
================================== AI 消息 ==================================

[{'text': '现在让我运行分析脚本:', 'type': 'text'}, {'id': 'toolu_015xkZr21TuzCTEKGzSqgnbp', 'input': {'command': 'cd /home/daytona && python analyze_sales.py'}, 'name': 'execute', 'type': 'tool_use'}]
工具调用:
  execute (toolu_015xkZr21TuzCTEKGzSqgnbp)
 调用 ID: toolu_015xkZr21TuzCTEKGzSqgnbp
  参数:
    command: cd /home/daytona && python analyze_sales.py
================================= 工具消息 =================================
名称: execute

============================================================
销售数据分析报告
============================================================

1. 数据概览
------------------------------------------------------------
日期范围: 2025-08-01 到 2025-08-05
总记录数: 5
产品: Widget A, Widget B, Widget C

2. 汇总统计
------------------------------------------------------------
总收入: $840.00
总销售量: 33
平均每日收入: $168.00
平均每笔销售量: 6.60

3. 产品表现
------------------------------------------------------------
         Revenue        Units Sold
             sum   mean        sum mean
Product
Widget A     425  212.5         17  8.5
Widget B     325  162.5         13  6.5
Widget C      90   90.0          3  3.0

4. 表现最佳的一天
------------------------------------------------------------
日期: 2025-08-01
产品: Widget A
收入: $250.00
销售量: 10

============================================================
图表已保存为: sales_analysis_plot.png
============================================================
[命令成功,退出码为 0]
================================== AI 消息 ==================================

[{'text': '完美!分析已完成,图表已生成。现在让我将分析和图表都发送到Slack:', 'type': 'text'}, {'id': 'toolu_01Qust5a8DECKPZuU9pBBvkz', 'input': {'text': '📊 **销售数据分析报告** 📊\n\n**1. 数据概览**\n• 日期范围: 2025年8月1-5日 (5天)\n• 分析的产品: Widget A, Widget B, Widget C\n\n**2. 关键指标**\n💰 总收入: $840.00\n📦 总销售量: 33 件\n📈 平均每日收入: $168.00\n📊 平均每笔销售量: 6.60\n\n**3. 产品表现**\n🏆 Widget A (表现最佳)\n   - 总收入: $425 (占总收入的50.6%)\n   - 总销售量: 17 件 (平均每笔交易8.5件)\n\n🥈 Widget B\n   - 总收入: $325 (占总收入的38.7%)\n   - 总销售量: 13 件 (平均每笔交易6.5件)\n\n🥉 Widget C\n   - 总收入: $90 (占总收入的10.7%)\n   - 总销售量: 3 件 (平均每笔交易3.0件)\n\n**4. 表现最佳的一天**\n🌟 2025年8月1日\n   - 产品: Widget A\n   - 收入: $250.00\n   - 销售量: 10 件\n\n**关键洞察:**\n✅ Widget A 是明显的领导者,创造了超过50%的总收入\n✅ Widget B 表现强劲,销售稳定\n⚠️ Widget C 有改进潜力 - 销售量和收入最低\n📊 每日收入范围在 $90 到 $250 之间\n\n请参阅附件的可视化仪表板以获取详细图表!📈', 'file_path': '/home/daytona/sales_analysis_plot.png'}, 'name': 'slack_send_message', 'type': 'tool_use'}]
工具调用:
  slack_send_message (toolu_01Qust5a8DECKPZuU9pBBvkz)
 调用 ID: toolu_01Qust5a8DECKPZuU9pBBvkz
  参数:
    text: 📊 **销售数据分析报告** 📊

**1. 数据概览**
• 日期范围: 2025年8月1-5日 (5天)
• 分析的产品: Widget A, Widget B, Widget C

**2. 关键指标**
💰 总收入: $840.00
📦 总销售量: 33 件
📈 平均每日收入: $168.00
📊 平均每笔销售量: 6.60

**3. 产品表现**
🏆 Widget A (表现最佳)
   - 总收入: $425 (占总收入的50.6%)
   - 总销售量: 17 件 (平均每笔交易8.5件)

🥈 Widget B
   - 总收入: $325 (占总收入的38.7%)
   - 总销售量: 13 件 (平均每笔交易6.5件)

🥉 Widget C
   - 总收入: $90 (占总收入的10.7%)
   - 总销售量: 3 件 (平均每笔交易3.0件)

**4. 表现最佳的一天**
🌟 2025年8月1日
   - 产品: Widget A
   - 收入: $250.00
   - 销售量: 10 件

**关键洞察:**
✅ Widget A 是明显的领导者,创造了超过50%的总收入
✅ Widget B 表现强劲,销售稳定
⚠️ Widget C 有改进潜力 - 销售量和收入最低
📊 每日收入范围在 $90 到 $250 之间

请参阅附件的可视化仪表板以获取详细图表!📈
    file_path: /home/daytona/sales_analysis_plot.png
================================= 工具消息 =================================
名称: slack_send_message

消息已发送。
================================== AI 消息 ==================================

太好了!我已成功完成分析并将所有内容发送到Slack。以下是我所做的:

## 总结

**分析完成:**
- 分析了2025年8月1-5日的销售数据
- 处理了3种产品(Widget A、B和C)的数据
- 生成了全面的统计信息和洞察

**主要发现:**
1. **总收入:** 5天内总计$840
2. **最佳产品:** Widget A(收入$425,占总收入的50.6%)
3. **最佳日期:** 8月1日,收入$250
4. **总销售量:** 33件

**创建的可视化:**
一个漂亮的6面板仪表板,包含:
- 每日收入趋势
- 每日销售量
- 按产品的收入分布(饼图)
- 按产品的总收入(水平条形图)
- 按产品的总销售量(水平条形图)
- 销售交易分布(饼图)

**已交付到Slack:**
✅ 包含关键指标和洞察的全面分析报告
✅ 高分辨率可视化仪表板(保存为PNG)

分析显示Widget A是表现最佳的产品,而Widget C则有改进空间。所有结果已发送到您的Slack工作区!
查看此执行的完整 LangSmith 跟踪

结果

代理成功分析数据,并将包含可视化的综合报告共享到Slack:
Slack中的销售分析结果
您可以直接从后端下载制品,无需使用外部工具: python backend.download_files(list_of_filepaths)
有关完成后如何清理沙箱,请参阅提供商指南

后续步骤

既然您已经构建了一个数据分析代理,请探索这些资源以扩展其功能:
  • 后端:了解深度代理后端系统
  • 沙箱:查看用于沙箱代码执行的后端,包括安全考虑和高级配置
  • 自定义:了解如何使用不同的模型、工具、提示和规划策略自定义您的代理
  • CLI:尝试深度代理CLI作为终端编码代理,以在本地协助数据分析和其他代理任务
  • 技能:为您的代理配备用于常见工作流的可重用技能
  • Human in the Loop:为数据分析工作流中的关键操作添加交互式审批步骤