BrainBank
AI 课堂/分步指南DeepLearningAI

M2 智能体式人工智能 - 图表生成

2026/8/5 17:04:18

#step-by-step#reflection#lab#data-visualization

动手实验:实现反思模式,先用大模型为咖啡销售数据生成第一版 matplotlib 图表,再用多模态大模型对其进行评判,并重新生成改进版本。

M2 智能体式人工智能 - 图表生成

欢迎来到 智能体式人工智能(Agentic AI) 课程!在这个非评分实验,以及本课程接下来的其他实验中,你将有机会亲自尝试实现讲座视频中介绍的概念和设计模式的代码示例。

可以把这些实验看作一个沙箱:一个安全的练习空间,帮助你加深对课程概念的理解、建立信心,并为之后的评分练习做好准备。在每个实验中,请尝试运行代码单元格,观察一些智能体工作流的实际运行效果,更好地理解它们的工作原理。

在一些地方,你会被鼓励尝试修改代码——例如更改提示词、用不同的大模型测试,或为工作流添加额外的查询。请尝试进行实验,看看你的改动会如何影响工作流的行为。

最重要的是,非评分实验是一个让你按照自己的节奏学习的机会,同时获得关于智能体式人工智能核心理念的实践经验。而且请记住——你并不是一个人在学习!如果你有任何问题,欢迎在 <a href="https://community.deeplearning.ai/c/course-q-a/agentic-ai/567" target="_blank">社区</a> 中提问。

1. 简介

1.1. 实验概览

在这个非评分实验中,你将在一个生成数据可视化图表的智能体工作流中,实现讲座视频中介绍的反思模式(reflection pattern)。一个多模态大模型会审阅第一版图表草稿,识别可能的改进点——例如图表类型、标签或配色选择——然后重写图表生成代码,从而生成一个更有效的可视化图表。

在视频中,Andrew 展示了以下用于分析咖啡销售数据的工作流。你将在这里用代码实现它。该工作流将执行以下步骤:

  1. 生成初始版本(V1): 使用大语言模型(LLM)创建绘图代码的第一个版本。

  2. 执行代码并生成图表: 运行生成的代码并展示生成的图表。** (check everywhere)

  3. 对输出进行反思: 使用大模型评估代码和图表,找出可以改进的地方(例如清晰度、准确性、设计)。

  4. 生成并执行改进版本(V2): 基于反思得到的洞见,生成一个改进版的绘图代码,并渲染出增强后的图表。

<img src='M2-UGL-2.png'>

🎯 1.2. 学习目标

完成本实验后,你将已经用代码实现了反思模式,并用它改进了一次数据可视化。

2. 环境搭建:初始化环境与客户端

在这一步,你将导入支撑整个工作流所需的关键库:

  • re:Python 的正则表达式模块,你将用它从大模型的输出中提取代码片段或结构化文本。
  • json:提供读写 JSON 的函数,便于处理大模型返回的结构化响应。
  • utils:本实验提供的自定义辅助模块,其中包含用于处理数据集、生成图表,以及以简洁易读的方式展示结果的工具函数。
# Standard library imports
import re
import json

# Local helper module
import utils

2.1. 加载数据集

我们先来看看咖啡销售数据,了解文件中包含哪些信息。

# Use this utils.py function to load the data into a dataframe
df = utils.load_and_prepare_data('coffee_sales.csv')

# Grab a random sample to display
utils.print_html(df.sample(n=5), title="Random Sample of Coffee Sales Data")

你将基于这份数据集构建一个智能体工作流来生成数据可视化图表,帮助你回答关于自动售货机咖啡销售情况的问题。

3. 构建流水线

3.1 第一步 — 生成创建图表的代码(V1)

在这一步,你将提示一个大模型编写 Python 代码,针对一个关于咖啡数据集的用户问题生成一张图表。该数据集包含 datecoffee_typequantityrevenue 等字段,你会把这份 schema 传给大模型,让它知道有哪些数据可用。

你将向模型提出的问题与讲座中使用的问题相同: “Create a plot comparing Q1 coffee sales in 2024 and 2025 using the data in coffee_sales.csv.”(使用 coffee_sales.csv 中的数据,创建一张比较 2024 年和 2025 年第一季度咖啡销量的图表。)

大模型的输出将是使用 matplotlib 库的 Python 代码。这段代码不会直接显示图表,而是被写在 <execute_python> 标签之间,以便在后续步骤中提取并运行。你将在模块 3中进一步了解这些标签。

def generate_chart_code(instruction: str, model: str, out_path_v1: str) -> str:
    """Generate Python code to make a plot with matplotlib using tag-based wrapping."""

    prompt = f"""
    You are a data visualization expert.

    Return your answer *strictly* in this format:

    <execute_python>
    # valid python code here
    </execute_python>

    Do not add explanations, only the tags and the code.

    The code should create a visualization from a DataFrame 'df' with these columns:
    - date   (datetime64 — already parsed; use df['date'].dt.year, df['date'].dt.month, etc.)
    - time   (string, HH:MM — do NOT concatenate or combine with the date column)
    - cash_type (string: 'card' or 'cash')
    - card (string)
    - price (number)
    - coffee_name (string)
    - quarter (int, 1–4 — already computed, use directly)
    - month  (int, 1–12 — already computed, use directly)
    - year   (int, e.g. 2024 — already computed, use directly)

    User instruction: {instruction}

    Requirements for the code:
    1. Assume the DataFrame is already loaded as 'df'.
    2. Use matplotlib for plotting.
    3. Add clear title, axis labels, and legend if needed.
    4. Save the figure as '{out_path_v1}' with dpi=300.
    5. Do not call plt.show().
    6. Close all plots with plt.close().
    7. Add all necessary import python statements
    8. CRITICAL: 'date' is datetime64 — never use string concatenation on it.
       Filter by year/quarter using the 'year' and 'quarter' integer columns.
    
    Return ONLY the code wrapped in <execute_python> tags.
    """

    response = utils.get_response(model, prompt)
    return response

现在,试着运行这个函数并分析它的响应!

# Generate initial code
code_v1 = generate_chart_code(
    instruction="Create a plot comparing Q1 coffee sales in 2024 and 2025 using the data in coffee_sales.csv.", 
    model="gpt-4o-mini", 
    out_path_v1="chart_v1.png"
)

utils.print_html(code_v1, title="LLM output with first draft code")

太棒了!你已经生成了一段用于创建图表的 Python 代码!

注意,这段代码被包裹在 <execute_python> 标签之间。这些标签让下一步能够自动提取并运行这段代码变得很容易。

你现在还不需要了解这些标签的具体工作原理——你将在模块 3中学到更多相关内容。

3.2. 第二步 — 执行代码并生成图表

在这一步,你将使用正则表达式提取大模型在上一步生成的 Python 代码(即写在 <execute_python> 标签之间的部分)。提取之后,你将运行这段代码,生成第一版图表草稿

具体流程如下:

  1. 提取代码: 使用一个正则表达式模式,抓取包裹在 <execute_python> 标签内的代码。

  2. 执行代码: 提取出的代码会在一个预先定义好的全局上下文中运行,其中 DataFrame df 已经可用。这意味着你的代码可以直接使用 df,而无需重新加载数据集。

  3. 生成图表: 如果代码执行成功,它将创建一张图表并保存为 chart_v1.png

  4. 在笔记本中查看图表: 保存好的图表随后会通过 utils.print_html 以内嵌方式显示出来,便于你查看结果。

完成这一步后,你就拥有了第一版草稿可视化图表(V1)——这是反思工作流中的一个重要里程碑!

# Get the code within the <execute_python> tags
match = re.search(r"<execute_python>([\s\S]*?)</execute_python>", code_v1)
if match:
    initial_code = match.group(1).strip()
    utils.print_html(initial_code, title="Extracted Code to Execute")
    exec_globals = {"df": df}
    exec(initial_code, exec_globals)

# If code run successfully, the file chart_v1.png should have been generated
utils.print_html(
    content="chart_v1.png",
    title="Generated Chart (V1)",
    is_image=True
)

3.3. 第三步 — 对输出进行反思

这一步的目标是模拟人类审阅图表初稿的过程——寻找其中的优点、缺点和可改进之处。

具体流程如下:

1. 将图表提供给大模型: 生成的图表(chart_v1.png)会被分享给大模型,让它“看到”这张可视化图表。

2. 对图表进行视觉分析: 大模型会审查诸如清晰度、标签、准确性和整体可读性等要素。

3. 生成反馈: 大模型会提出改进建议——例如修正坐标轴标签、调整图表类型、改进配色方案,或补充缺失的图例。

通过这样做,你创建了一个智能反馈循环:图表不只是被生成一次,而是被主动地加以评判——为更强的第二版(V2)做好了铺垫。

def reflect_on_image_and_regenerate(
    chart_path: str,
    instruction: str,
    model_name: str,
    out_path_v2: str,
    code_v1: str,  
) -> tuple[str, str]:
    """
    Critique the chart IMAGE and the original code against the instruction, 
    then return refined matplotlib code.
    Returns (feedback, refined_code_with_tags).
    Supports OpenAI and Anthropic (Claude).
    """
    media_type, b64 = utils.encode_image_b64(chart_path)
    

    prompt = f"""
    You are a data visualization expert.
    Your task: critique the attached chart and the original code against the given instruction,
    then return improved matplotlib code.

    Original code (for context):
    {code_v1}

    OUTPUT FORMAT (STRICT):
    1) First line: a valid JSON object with ONLY the "feedback" field.
    Example: {{"feedback": "The legend is unclear and the axis labels overlap."}}

    2) After a newline, output ONLY the refined Python code wrapped in:
    <execute_python>
    ...
    </execute_python>

    3) Import all necessary libraries in the code. Don't assume any imports from the original code.

    HARD CONSTRAINTS:
    - Do NOT include Markdown, backticks, or any extra prose outside the two parts above.
    - Use pandas/matplotlib only (no seaborn).
    - Assume df already exists; do not read from files.
    - Save to '{out_path_v2}' with dpi=300.
    - Always call plt.close() at the end (no plt.show()).
    - Include all necessary import statements.
    
    IMPORTANT: The 'date' column is already a pandas datetime64 type.
    - Do NOT concatenate 'date' with 'time' using string operations.
    - To filter by year/quarter, use: df[df['year'] == 2024] or df['date'].dt.year == 2024
    - The 'quarter' and 'year' columns already exist as integers; use them directly.

    Schema (columns available in df):
    - date   (datetime64 — already parsed; use df['date'].dt.year, etc.)
    - time   (string, HH:MM — do NOT concatenate with date)
    - cash_type (string: 'card' or 'cash')
    - card   (string)
    - price  (float)
    - coffee_name (string)
    - quarter (int, 1–4)
    - month  (int, 1–12)
    - year   (int)
    
    CRITICAL TYPE RULE: 'date' is already datetime64.
    - NEVER do: df['date'] + ' ' + df['time']  ← this will crash
    - ALWAYS filter by year/quarter using the integer columns: df[df['year'] == 2024]

    Instruction:
    {instruction}
    """


    # In case the name is "Claude" or "Anthropic", use the safe helper
    lower = model_name.lower()
    if "claude" in lower or "anthropic" in lower:
        # ✅ Use the safe helper that joins all text blocks and adds a system prompt
        content = utils.image_anthropic_call(model_name, prompt, media_type, b64)
    else:
        content = utils.image_openai_call(model_name, prompt, media_type, b64)

    # --- Parse ONLY the first JSON line (feedback) ---
    lines = content.strip().splitlines()
    json_line = lines[0].strip() if lines else ""

    try:
        obj = json.loads(json_line)
    except Exception as e:
        # Fallback: try to capture the first {...} in all the content
        m_json = re.search(r"\{.*?\}", content, flags=re.DOTALL)
        if m_json:
            try:
                obj = json.loads(m_json.group(0))
            except Exception as e2:
                obj = {"feedback": f"Failed to parse JSON: {e2}", "refined_code": ""}
        else:
            obj = {"feedback": f"Failed to find JSON: {e}", "refined_code": ""}

    # --- Extract refined code from <execute_python>...</execute_python> ---
    m_code = re.search(r"<execute_python>([\s\S]*?)</execute_python>", content)
    refined_code_body = m_code.group(1).strip() if m_code else ""
    refined_code = utils.ensure_execute_python_tags(refined_code_body)

    feedback = str(obj.get("feedback", "")).strip()
    return feedback, refined_code


请注意,模型被要求以 JSON 格式返回响应。

  • JSON 是一种轻量级的结构化格式(键值对),便于以编程方式解析大模型的输出。
  • 这里我们要求两个字段:
    • feedback:对当前图表的简短评价。
    • refined_code:一段改进后的 Python 代码片段,包裹在 <execute_python> 标签中。

我们还在提示词中加入了一个**“约束条件”部分**。这些规则(例如只使用 matplotlib、把文件保存到指定路径、最后调用 plt.close())有助于模型生成一致、可运行、且符合工作流要求的代码。如果没有这些约束,输出结果可能会差异过大,或包含不需要的格式。

3.4 第四步 — 生成并执行改进版本(V2)

在最后一步,是时候生成并运行图表的改进版本(V2)了。 运行该单元格后,你将同时看到大模型写下的反思内容(说明哪里需要改进)以及它生成的新代码。随后这段新代码会被执行,生成更新后的图表。

# Generate feedback alongside reflected code
feedback, code_v2 = reflect_on_image_and_regenerate(
    chart_path="chart_v1.png",            
    instruction="Create a plot comparing Q1 coffee sales in 2024 and 2025 using the data in coffee_sales.csv.", 
    model_name="o4-mini",
    out_path_v2="chart_v2.png",
    code_v1=code_v1,   # pass in the original code for context        
)

utils.print_html(feedback, title="Feedback on V1 Chart")
utils.print_html(code_v2, title="Regenerated Code Output (V2)")

现在你将执行反思步骤返回的改进代码。<execute_python> 标签内的代码会被提取出来,针对数据集运行,并用于生成更新后的图表。

如果执行成功,你将在下方看到新图像(chart_v2.png)作为**重新生成的图表(V2)**显示出来。

# Get the code within the <execute_python> tags
match = re.search(r"<execute_python>([\s\S]*?)</execute_python>", code_v2)
if match:
    reflected_code = match.group(1).strip()
    exec_globals = {"df": df}
    exec(reflected_code, exec_globals)

# If code run successfully, the file chart_v2.png should have been generated
utils.print_html(
    content="chart_v2.png",
    title="Regenerated Chart (V2)",
    is_image=True
)

4. 整合起来 — 构建端到端工作流

现在是时候把之前完成的一切整合成一个智能体可以从头到尾自动运行的完整工作流了。

run_workflow 函数把你之前实现的各个组件串联起来:

  1. 加载并准备数据 — 通过 utils.load_and_prepare_data(...)
  2. 生成 V1 代码 — 使用 generate_chart_code(...),返回第一版草稿 matplotlib 代码(包裹在 <execute_python> 标签中)。
  3. 立即执行 V1 — 工作流提取 <execute_python> 标签之间的代码并运行,生成第一张图表图像。
  4. 反思并改进reflect_on_image_and_regenerate(...) 会对照指令评判 V1 图像(以及原始代码),返回简明的反馈修订后的代码(V2)
  5. 立即执行 V2 — 提取并执行改进后的代码,生成改进后的图表。

该工作流接受的参数

  • dataset_path:输入 CSV 文件的位置。
  • user_instructions:图表请求内容(例如 “Create a plot comparing Q1 coffee sales in 2024 and 2025 using the data in coffee_sales.csv.”)。
  • generation_model:用于初始代码生成的模型。
  • reflection_model:用于基于图像的反思与代码改进的模型。
  • image_basename:保存图表图像所用的基础文件名(例如 chart_v1.pngchart_v2.png)。

注意:图表执行步骤被有意硬编码为在代码生成/改进之后立即运行。这与讲座中的工作流一致,确保你在进入下一步之前,能看到每一版草稿的输出结果。

def run_workflow(
    dataset_path: str,
    user_instructions: str,
    generation_model: str,
    reflection_model: str,   
    image_basename: str = "chart",
):
    """
    End-to-end pipeline:
      1) load dataset
      2) generate V1 code
      3) execute V1 → produce chart_v1.png
      4) reflect on V1 (image + original code) → feedback + refined code
      5) execute V2 → produce chart_v2.png

    Returns a dict with all artifacts (codes, feedback, image paths).
    """
    # 0) Load dataset; utils handles parsing and feature derivations (e.g., year/quarter)
    df = utils.load_and_prepare_data(dataset_path)
    utils.print_html(df.sample(n=5), title="Random Sample of Dataset")

    # Paths to store charts
    out_v1 = f"{image_basename}_v1.png"
    out_v2 = f"{image_basename}_v2.png"

    # 1) Generate code (V1)
    utils.print_html("Step 1: Generating chart code (V1)… 📈")
    code_v1 = generate_chart_code(
        instruction=user_instructions,
        model=generation_model,
        out_path_v1=out_v1,
    )
    utils.print_html(code_v1, title="LLM output with first draft code (V1)")

    # 2) Execute V1 (hard-coded: extract <execute_python> block and run immediately)
    utils.print_html("Step 2: Executing chart code (V1)… 💻")
    match = re.search(r"<execute_python>([\s\S]*?)</execute_python>", code_v1)
    if match:
        initial_code = match.group(1).strip()
        exec_globals = {"df": df}
        exec(initial_code, exec_globals)
    utils.print_html(out_v1, is_image=True, title="Generated Chart (V1)")

    # 3) Reflect on V1 (image + original code) to get feedback and refined code (V2)
    utils.print_html("Step 3: Reflecting on V1 (image + code) and generating improvements… 🔁")
    feedback, code_v2 = reflect_on_image_and_regenerate(
        chart_path=out_v1,
        instruction=user_instructions,
        model_name=reflection_model,
        out_path_v2=out_v2,
        code_v1=code_v1,  # pass original code for context
    )
    utils.print_html(feedback, title="Reflection feedback on V1")
    utils.print_html(code_v2, title="LLM output with revised code (V2)")

    # 4) Execute V2 (hard-coded: extract <execute_python> block and run immediately)
    utils.print_html("Step 4: Executing refined chart code (V2)… 🖼️")
    match = re.search(r"<execute_python>([\s\S]*?)</execute_python>", code_v2)
    if match:
        reflected_code = match.group(1).strip()
        exec_globals = {"df": df}
        exec(reflected_code, exec_globals)
    utils.print_html(out_v2, is_image=True, title="Regenerated Chart (V2)")

    return {
        "code_v1": code_v1,
        "chart_v1": out_v1,
        "feedback": feedback,
        "code_v2": code_v2,
        "chart_v2": out_v2,
    }

4.2. 试运行工作流

现在轮到你亲自把整个工作流用讲座中更新后的示例跑起来了。

  • 要使用的指令: “Create a plot comparing Q1 coffee sales in 2024 and 2025 using the data in coffee_sales.csv.”

当你用这条指令运行工作流时,它将:

  1. 生成用于创建图表的第一版草稿代码。
  2. 立即执行该代码,生成第一版图表(V1)。
  3. 对图表和原始代码进行反思,产出反馈和修订后的代码(V2)。
  4. 执行改进后的代码,生成改进版图表(V2)。

自定义与实验

在尝试完上面的示例之后,欢迎用你自己的图表提示词更新 user_instructions 参数。 别忘了同时调整 image_basename,让每次运行的结果都保存在新的文件名下——这样可以让你的图表保持有条理,也能避免覆盖之前的输出。

选择模型

你可以为生成和反思分别混合搭配不同的模型。例如:

  • 使用速度更快的模型进行初始代码生成(gpt-4.1-minigpt-3.5-turbo)。
  • 使用推理能力更强的模型进行反思(gpt-4.1claude-sonnet-4-6)。

这种灵活性让你可以探索速度与质量之间的权衡。

👉 行动号召: 现在就用讲座中的示例指令运行这个工作流,然后尝试用你自己的提示词,看看智能体是如何适应的!

# Here, insert your updates
user_instructions="Create a plot comparing Q1 coffee sales in 2024 and 2025 using the data in coffee_sales.csv." # write your instruction here
generation_model="gpt-4o-mini"
reflection_model="o4-mini"
# reflection_model="claude-sonnet-4-6"
image_basename="drink_sales"

# Run the complete agentic workflow
_ = run_workflow(
    dataset_path="coffee_sales.csv",
    user_instructions=user_instructions,
    generation_model=generation_model,
    reflection_model=reflection_model,
    image_basename=image_basename
)

5. 最终总结

在这个实验中,练习了使用反思来改进图表输出。 你学会了:

  • 生成初始图表(V1)。
  • 对其进行评判并改进为更好的版本(V2)。
  • 用不同的模型自动化整个工作流。

核心理念是:反思能帮助创建更清晰、更准确、也更有效的可视化图表。

<div style="border:1px solid #22c55e; border-left:6px solid #16a34a; background:#dcfce7; border-radius:6px; padding:14px 16px; color:#064e3b; font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif;">

🎉 <strong>恭喜!</strong>

你已经完成了构建智能体式图表生成工作流的实验。 在此过程中,练习了生成图表、评判其质量,并将其改进为更清晰、更有效的可视化图表。

有了这些技能,已经准备好设计能够自动创建数据可视化的智能体流水线,同时保持结果的准确、可解释与精美。🌟

</div>

学习地图

本页是「DeepLearningAI > Agentic AI Lab」的第 1 / 7 页——这是一份真实的 DeepLearning.AI 笔记本(代码 + 讲解文字),而非「Agentic AI」板块中那种模板化内容。这是本板块的第一个实验。完成后可继续阅读「M2 - 智能体式人工智能 - 用反思改进 SQL 生成」。代码单元格完全保留了源笔记本中的原样——请在你自己的 Python 环境中按顺序运行它们(它们依赖 utils.py 等本地辅助模块,这里并未包含)。

动手实践——分步指南

搭建好这份笔记本所需的本地依赖(代码单元格中导入的辅助模块,例如 utils.py、display_functions.py,以及文中引用的各个工具模块),然后按照「M2 智能体式人工智能 - 图表生成」自身的分步讲解,从上到下依次运行每个代码单元格。在进入下一个实验之前,先尝试文中建议的实验(更换模型、修改提示词、提出你自己的请求)。

三大推荐资源

  1. 1
    DeepLearning.AI Course Catalog

    The DeepLearning.AI catalog these lab notebooks are drawn from.

    https://www.deeplearning.ai/courses/

  2. 2
    Claude Docs: Tool Use

    Anthropic's reference for defining and orchestrating tool calls, the same pattern these labs implement with aisuite.

    https://docs.claude.com/en/docs/agents-and-tools/tool-use/overview

链接由 AI 推荐——使用前建议快速核实。