> ## Documentation Index
> Fetch the complete documentation index at: https://langchain-zh.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# 构建数据分析智能体

> 构建一个能够分析数据文件、生成可视化图表并分享结果的智能体

## 概述

本指南演示如何使用[深度智能体](/oss/python/deepagents)构建一个数据分析智能体。数据分析任务通常需要规划、代码执行以及处理脚本、报告和图表等工件——这些都是深度智能体设计用来处理的能力。

我们将构建的智能体将能够：

1. 接收用于分析的 CSV 文件
2. 执行探索性数据分析并生成可视化图表
3. 将结果分享到 Slack 频道

<Tip>
  Slack 集成是可选的。该智能体可以修改为将工件保存在本地或通过其他渠道分享结果。
</Tip>

### 关键概念

本教程涵盖：

* 用于沙盒化代码执行的[后端](/oss/python/deepagents/backends)
* 用于外部集成的自定义[工具](/oss/python/langchain/tools)

## 环境配置

### 安装

安装核心依赖：

```bash pip theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
pip install deepagents
```

### 可选依赖

在本教程中，我们将使用：

* 用于分享结果的 [Slack Python SDK](https://docs.slack.dev/tools/python-slack-sdk/)（[令牌设置](https://docs.slack.dev/authentication/tokens/)）
* 用于代码执行的[沙盒](/oss/python/deepagents/sandboxes)环境。详细设置请参见[可用提供商](/oss/python/deepagents/sandboxes#available-providers)

```bash pip theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
pip install slack-sdk
```

<Note>
  这些服务是可选的，但对于任何生产环境，强烈建议使用沙盒环境。你也可以使用本地 shell 后端（注意重要的[安全考虑](/oss/python/deepagents/backends#localshellbackend-local-shell)）或直接从后端下载工件。
</Note>

### LangSmith

你用 LangChain 构建的许多应用程序将包含多个步骤和多次 LLM 调用。随着这些应用程序变得越来越复杂，能够检查链或智能体内部究竟发生了什么变得至关重要。最好的方法是使用[LangSmith](https://smith.langchain.com)。

在上述链接注册后，请确保设置环境变量以开始记录追踪：

```shell theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
export LANGSMITH_TRACING="true"
export LANGSMITH_API_KEY="..."
```

或者在 Python 中设置：

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import getpass
import os

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

## 设置后端

深度智能体使用[后端](/oss/python/deepagents/backends)在沙盒化环境中执行代码。

详细设置请参见[可用提供商](/oss/python/deepagents/sandboxes#available-providers)。

<Tabs>
  <Tab title="AgentCore">
    <CodeGroup>
      ```bash pip theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      pip install langchain-agentcore-codeinterpreter
      ```

      ```bash uv theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      uv add langchain-agentcore-codeinterpreter
      ```
    </CodeGroup>

    ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    from bedrock_agentcore.tools.code_interpreter_client import CodeInterpreter
    from langchain_agentcore_codeinterpreter import AgentCoreSandbox

    interpreter = CodeInterpreter(region="us-west-2")
    interpreter.start()
    backend = AgentCoreSandbox(interpreter=interpreter)
    ```
  </Tab>

  <Tab title="Daytona">
    <CodeGroup>
      ```bash pip theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      pip install langchain-daytona
      ```

      ```bash uv theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      uv add langchain-daytona
      ```
    </CodeGroup>

    ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    from daytona import Daytona

    from langchain_daytona import DaytonaSandbox

    sandbox = Daytona().create()
    backend = DaytonaSandbox(sandbox=sandbox)
    ```

    验证沙盒已就绪：

    ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    result = backend.execute("echo ready")
    print(result)
    # ExecuteResponse(output='ready', exit_code=0, ...)
    ```
  </Tab>

  <Tab title="Modal">
    ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    import modal

    from langchain_modal import ModalSandbox

    app = modal.App.lookup("your-app")
    modal_sandbox = modal.Sandbox.create(app=app)
    backend = ModalSandbox(sandbox=modal_sandbox)
    ```
  </Tab>

  <Tab title="Runloop">
    <CodeGroup>
      ```bash pip theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      pip install langchain-runloop
      ```

      ```bash uv theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      uv add langchain-runloop
      ```
    </CodeGroup>

    ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    from runloop_api_client import RunloopSDK

    from langchain_runloop import RunloopSandbox

    api_key = "..."
    client = RunloopSDK(bearer_token=api_key)

    devbox = client.devbox.create()
    backend = RunloopSandbox(devbox=devbox)
    ```
  </Tab>

  <Tab title="本地 shell">
    <Warning>
      此后端提供不受限制的文件系统和 shell 访问权限。仅在受控环境中用于开发和测试。更多详情请参见[安全考虑](/oss/python/deepagents/backends#localshellbackend-local-shell)。
    </Warning>

    ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    from deepagents.backends import LocalShellBackend

    backend = LocalShellBackend(root_dir=".", env={"PATH": "/usr/bin:/bin"})
    ```
  </Tab>
</Tabs>

### 上传示例数据

创建示例销售数据并上传到后端：

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
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)])
```

## 实现自定义工具

数据分析任务可能会产生报告或图表等工件。
下面这个简单的[工具](/oss/python/langchain/tools)使用 `backend.download_files` 下载这些工件，然后使用 Slack SDK 上传它们。
我们也可以让智能体列出相关文件路径而不是直接上传，这样相关方可以根据需要单独获取这些文件。

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
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 "消息已发送。"
```

<Note>
  通常建议避免将凭证和其他机密信息放入沙盒。这里我们在沙盒外部的工具中管理 Slack 令牌。
</Note>

## 运行代理

让我们实例化一个代理：

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import uuid

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


checkpointer = InMemorySaver()

agent = create_deep_agent(
    model="anthropic:claude-sonnet-4-5",
    tools=[slack_send_message],
    backend=backend,
    checkpointer=checkpointer,
)

thread_id = str(uuid.uuid4())
config={"configurable": {"thread_id": thread_id}}
```

我们包含：

* [模型](/oss/python/deepagents/customization#model)的选择
* 我们的自定义[工具](/oss/python/deepagents/customization#tools)
* [后端](/oss/python/deepagents/backends)
* 支持多轮对话的[检查点存储器](/oss/python/langchain/short-term-memory)

现在让我们调用我们的代理。

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
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()
```

```结果 可展开 theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
================================== 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# Set style for beautiful plots\nsns.set_style("whitegrid")\nplt.rcParams[\'figure.figsize\'] = (14, 10)\nplt.rcParams[\'font.size\'] = 11\n\n# Read the data\ndf = pd.read_csv(\'/home/daytona/data/sales_data.csv\')\ndf[\'Date\'] = pd.to_datetime(df[\'Date\'])\n\n# Analysis\nprint("=" * 60)\nprint("SALES DATA ANALYSIS REPORT")\nprint("=" * 60)\nprint("\\n1. DATA OVERVIEW")\nprint("-" * 60)\nprint(f"Date Range: {df[\'Date\'].min().strftime(\'%Y-%m-%d\')} to {df[\'Date\'].max().strftime(\'%Y-%m-%d\')}")\nprint(f"Total Records: {len(df)}")\nprint(f"Products: {\', \'.join(df[\'Product\'].unique())}")\n\nprint("\\n2. SUMMARY STATISTICS")\nprint("-" * 60)\nprint(f"Total Revenue: ${df[\'Revenue\'].sum():,.2f}")\nprint(f"Total Units Sold: {df[\'Units Sold\'].sum()}")\nprint(f"Average Daily Revenue: ${df[\'Revenue\'].mean():.2f}")\nprint(f"Average Units per Sale: {df[\'Units Sold\'].mean():.2f}")\n\nprint("\\n3. PRODUCT PERFORMANCE")\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. BEST PERFORMING DAY")\nprint("-" * 60)\nbest_day = df.loc[df[\'Revenue\'].idxmax()]\nprint(f"Date: {best_day[\'Date\'].strftime(\'%Y-%m-%d\')}")\nprint(f"Product: {best_day[\'Product\']}")\nprint(f"Revenue: ${best_day[\'Revenue\']:.2f}")\nprint(f"Units Sold: {best_day[\'Units Sold\']}")\n\n# Create visualizations\nfig = plt.figure(figsize=(16, 12))\nfig.suptitle(\'Sales Data Analysis Dashboard\', fontsize=20, fontweight=\'bold\', y=0.995)\n\n# 1. Revenue by Date\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(\'Daily Revenue\', fontsize=14, fontweight=\'bold\', pad=10)\nax1.set_xlabel(\'Date\', fontsize=12, fontweight=\'bold\')\nax1.set_ylabel(\'Revenue ($)\', 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. Units Sold by Date\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(\'Daily Units Sold\', fontsize=14, fontweight=\'bold\', pad=10)\nax2.set_xlabel(\'Date\', fontsize=12, fontweight=\'bold\')\nax2.set_ylabel(\'Units\', 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. Revenue by Product (Pie Chart)\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(\'Revenue Distribution by Product\', fontsize=14, fontweight=\'bold\', pad=10)\nfor autotext in autotexts:\n    autotext.set_color(\'white\')\n    autotext.set_fontsize(12)\n\n# 4. Total Revenue by Product (Bar Chart)\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(\'Total Revenue by Product\', fontsize=14, fontweight=\'bold\', pad=10)\nax4.set_xlabel(\'Revenue ($)\', fontsize=12, fontweight=\'bold\')\nax4.set_ylabel(\'Product\', 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. Total Units Sold by Product\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(\'Total Units Sold by Product\', fontsize=14, fontweight=\'bold\', pad=10)\nax5.set_xlabel(\'Units\', fontsize=12, fontweight=\'bold\')\nax5.set_ylabel(\'Product\', 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. Product Distribution\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(\'Sales Transactions by Product\', 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("Plot saved as: 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 追踪](https://smith.langchain.com/public/ac2443a8-16ad-4e00-8f02-afde6a737534/r)。

## 结果

代理成功分析数据并将包含可视化的综合报告分享到 Slack：

<Frame caption="代理生成的分析报告和可视化仪表板已发送到 Slack">
  <img src="https://mintcdn.com/hhh-8c10bf0c/1xJTbE4Z922F2hsr/images/data_analysis_slack_response.png?fit=max&auto=format&n=1xJTbE4Z922F2hsr&q=85&s=7be9df108ea5d8912a82f70d91aaca85" alt="Slack 中的销售分析结果" width="514" height="961" data-path="images/data_analysis_slack_response.png" />
</Frame>

<Tip>
  您可以直接从后端下载工件，无需使用外部工具：

  ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  backend.download_files(list_of_filepaths)
  ```
</Tip>

<Note>
  请参阅[提供商指南](/oss/python/deepagents/sandboxes#available-providers)，了解完成后如何清理沙箱。
</Note>

## 后续步骤

现在您已经构建了一个数据分析代理，请探索以下资源以扩展其能力：

* [后端](/oss/python/deepagents/backends)：了解 Deep Agents 后端系统
* [沙箱](/oss/python/deepagents/sandboxes)：查看用于沙箱化代码执行的后端，包括安全考虑因素和高级配置
* [定制化](/oss/python/deepagents/customization)：了解如何使用不同的模型、工具、提示和规划策略来定制您的代理
* [CLI](/oss/python/deepagents/cli/overview)：尝试使用 Deep Agents CLI 作为终端编码代理，在本地协助进行数据分析和其他代理任务
* [技能](/oss/python/deepagents/skills)：为您的代理配备可重用的技能，以处理常见工作流
* [人机协作](/oss/python/deepagents/human-in-the-loop)：为数据分析工作流中的关键操作添加交互式审批步骤

***

<div className="source-links">
  <Callout icon="edit">
    [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/i18n\zh-CN\oss\deepagents\data-analysis.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
  </Callout>

  <Callout icon="terminal-2">
    [Connect these docs](/use-these-docs) to Claude, VSCode, and more via MCP for real-time answers.
  </Callout>
</div>
