引言:当"数据语言"遇见"对话语言"

在数据工作流里,长期并存两种"语言":Pandas 是人和结构化数据对话的语言,强调确定性、可复现、零歧义;提示词(Prompt) 是人和大语言模型(LLM)对话的语言,擅长语义理解、代码生成与自然语言归纳。

二者的结合,本质不是用 LLM 替代 Pandas,而是构建一种**人机协同(Human-in-the-loop)**的分析范式:把"算得准"交给 Pandas,把"说得清、写得快"交给 LLM。其价值不在于炫技,而在于把分析师从重复性编码与结果翻译中释放出来,聚焦真正的判断。

本文的核心贡献有两点:

  1. 提出一个可落地的三层协同框架(代码生成 → 确定性计算 → 语义解读),并明确每层的能力边界;
  2. 在链路最薄弱的数据采集层,给出基于代理 IP 的韧性工程方案,使整条流水线真正可生产化。

一、协同的底层逻辑:能力与边界

要让 Prompt 与 Pandas 协作得不别扭,先要承认一件事:LLM 是概率模型,不是计算器。它能出色完成语义任务,却会在确定性算术上"幻觉"。因此协同的第一原则,是按任务特征划分职责。

任务特征推荐执行方理由
聚合、过滤、统计检验、类型转换Pandas确定性强、结果可复现、零幻觉
Schema 推断、样板代码生成LLM语义理解强,显著降低编码门槛
自然语言解读、异常归因假设、建议生成LLM擅长归纳与表达
强一致性对账、合规报表、可审计计算传统工程(LLM 仅辅助)要求精确算术与完整审计链路

适用边界(何时不宜上 LLM)

  • 强一致性金融/账务场景:任何一分钱对不齐都不能接受,应以传统工程为主;
  • 超大规模数据:原始明细超出上下文窗口(Context Window),必须先在 Pandas 侧聚合降采样;
  • 严格可复现审计:LLM 输出存在随机性(即使 temperature=0 也不保证逐字节一致),关键路径需保留确定性的 Pandas 计算作为事实来源。

明确边界后,进入三层框架。

二、Layer 1 — 代码生成层:Prompt 驱动 Pandas 编码

这是最常见的切入点。要产出可维护、可验证的代码,而非一次性的"能跑就行"脚本,Prompt 需遵循结构化设计。

2.1 Prompt 工程要点

一个工程化的代码生成 Prompt,应包含四要素:

  • Role(角色锚定):限定为资深 Pandas 工程师,约束风格;
  • Context(上下文):提供精确的数据字典(列名、类型、语义);
  • Constraint(约束):输出格式、不可修改原对象、禁用危险操作;
  • Schema(结构约束):要求输出为带类型注解的函数,而非自由脚本,便于测试与复用。
你是一名资深 Pandas 数据分析工程师。请仅根据以下【数据字典】与【需求】
输出一个 Python 函数,禁止任何解释性文字。

【数据字典】
DataFrame `df`,字段:
- date: datetime64[ns],交易日期
- region: str,地区(枚举:华东/华北/华南/西部)
- channel: str,渠道
- orders: int64,订单数
- gmv: float64,成交额(元)

【需求】
计算 2024 年各地区、各渠道的 GMV 总和与订单数均值,
按 GMV 降序取前 10,返回 DataFrame。

【约束】
1. 以方法链(Method Chaining)编写,禁止中间变量
2. 结果列名使用中文
3. 禁止修改入参 `df`(使用 `.copy()` 或不可变操作)
4. 函数签名:def aggregate_2024(df: pd.DataFrame) -> pd.DataFrame
5. 仅输出函数定义

2.2 LLM 产出的专业化代码

import pandas as pd

def aggregate_2024(df: pd.DataFrame) -> pd.DataFrame:
    """按地区与渠道聚合 2024 年 GMV 与订单均值。

    Args:
        df: 含 date/region/channel/orders/gmv 列的源数据。
    Returns:
        按成交额降序的前 10 行聚合结果。
    """
    return (
        df[df["date"].dt.year == 2024]
        .groupby(["region", "channel"], observed=True)
        .agg(成交额=("gmv", "sum"), 订单数均值=("orders", "mean"))
        .reset_index()
        .sort_values("成交额", ascending=False)
        .head(10)
    )

2.3 生成代码的验证(不可省略)

LLM 生成的代码不应直接 exec 进生产。工程做法:

  • 沙箱执行:在隔离环境运行,限制文件系统与网络权限;
  • 属性测试:用 hypothesis 构造随机 DataFrame,校验输出形状、列名、单调性等不变量;
  • 快照对比:对固定输入断言输出,防止模型"悄悄改逻辑"。

工程准则:永远向 LLM 施加"输出可运行函数 + 不修改原始对象"的双重约束。前者支撑自动化测试,后者保证数据血缘(Data Lineage)不被污染。

三、Layer 2 — 计算 + 解读层:Pandas 算准,Prompt 说清

这一层的关键词是上下文窗口管理结构化输出。常见错误是"把整个 DataFrame 直接塞进 Prompt"——既浪费 token,又因噪声过大降低解读质量。

3.1 上下文管理原则

  1. 先聚合,后发送:用 Pandas 完成所有确定性计算,仅将汇总结果(而非明细)送入 LLM;
  2. 最小暴露:剔除 PII(姓名、手机号、订单号),仅保留统计量与必要的分组维度;
  3. 降采样:长尾明细可先 describe() / 分桶,再构造 Prompt。

3.2 用 Pydantic 约束 LLM 输出(Structured Output)

不要依赖 LLM 的自由文本做下游消费。通过 Pydantic 模型 + JSON 模式(或 Function Calling),把解读结果结构化,便于程序化校验与落库。

import pandas as pd
from pydantic import BaseModel, Field

# 1) Pandas 完成确定性计算
df = pd.DataFrame({
    "region": ["华东", "华东", "华北", "华南"],
    "gmv": [120000.0, 98000.0, 75000.0, 132000.0],
    "orders": [1200, 1100, 800, 1400],
})

summary = (
    df.groupby("region", observed=True)[["gmv", "orders"]]
    .sum()
    .sort_values("gmv", ascending=False)
    .reset_index()
)
summary["客单价"] = (summary["gmv"] / summary["orders"]).round(2)

# 2) 构造最小化、脱敏后的上下文
data_text = summary.to_string(index=False)

# 3) 定义结构化输出 Schema(交由 LLM 填充)
class AnalysisResult(BaseModel):
    top_region: str = Field(description="GMV 最高的地区")
    gap_ratio: float = Field(description="首位与次位 GMV 的比值")
    anomaly_note: str = Field(description="客单价异常说明")
    recommendation: str = Field(description="不超过 50 字的可执行建议")

# 调用任意支持 Structured Output 的 LLM(伪代码,需接入 SDK)
# resp = llm.structured_output(model=AnalysisResult, prompt=build_prompt(data_text))
# print(resp.model_dump())

3.3 成本与质量权衡

  • Token 预算:发送的聚合文本应控制在数千 token 内;超大规模请先在 Pandas 侧做分层采样;
  • 确定性 vs 表达:增长率、占比、同比环比等任何精确算术都由 Pandas 完成,LLM 只负责基于这些数字做归纳与表达;
  • 可复现:在 Prompt 中固定输入快照版本,使解读可回溯。

关键 trade-off:LLM 是"语义引擎"而非"计算引擎"。把算术交给它,得到的是漂亮的错答案;把算术留给 Pandas,让它做它擅长的归纳,才是正确的分工。

四、Layer 0 — 采集层:代理 IP 的工程化选型

前三层默认"数据已在手中"。但真实链路里,第一公里(数据采集)往往最脆弱:竞品比价、舆情监测、公开榜单分析,第一步是稳定地把数据取回来。规模化采集会迅速遭遇三道瓶颈:

  • 反爬封禁:高频直连下,源站 IP 通常在分钟级被限流或拉黑;
  • 地域合规:部分数据源对访问地域有要求;
  • 链路稳定性:自建代理池维护成本高、可用性难以保障。

这正是企业级代理 IP 服务的价值所在。以生产环境长期采用的 亿牛云代理 为例:其提供 HTTP/HTTPS/SOCKS5 全协议支持,涵盖动态 IP 与隧道代理(Tunnel Proxy),多地区节点覆盖,且与 Python 生态(httpx / requests / Scrapy)开箱兼容,可作为采集层的稳定通道。

4.1 韧性采集骨架(异步 + 重试 + 限速)

下面给出一条"代理采集 → 解析 → Pandas 分析"的生产级骨架,关键点在代理调度、指数退避重试、并发限速三点。

import asyncio
import httpx
from tenacity import retry, stop_after_attempt, wait_exponential
from parsel import Selector
import pandas as pd

# 亿牛云隧道代理:无需手动轮换 IP,由服务端按请求调度
YINIU_PROXY = "http://your_user:your_pass@tunnel.yiniu.com:31200"

@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=1, max=8))
async def fetch_page(client: httpx.AsyncClient, url: str) -> str:
    """经亿牛云代理拉取页面,带指数退避重试。

    Args:
        client: 复用连接的异步客户端。
        url: 目标页面地址。
    Returns:
        页面 HTML 字符串。
    Raises:
        httpx.HTTPError: 重试耗尽后仍失败时上抛。
    """
    resp = await client.get(url, headers={"User-Agent": "Mozilla/5.0"})
    resp.raise_for_status()
    return resp.text

def parse_to_df(html: str) -> pd.DataFrame:
    """将商品列表页解析为 DataFrame。"""
    sel = Selector(text=html)
    rows = [
        {
            "title": item.css("a::text").get("").strip(),
            "price": float(item.css(".price::text").get("0").replace("¥", "")),
        }
        for item in sel.css(".item")
    ]
    return pd.DataFrame(rows)

async def collect(urls: list[str]) -> pd.DataFrame:
    # 通过 proxies 参数统一走亿牛云代理;限制并发避免对源站施压
    async with httpx.AsyncClient(
        proxies=YINIU_PROXY, timeout=15.0, verify=False, limits=httpx.Limits(max_connections=8)
    ) as client:
        htmls = await asyncio.gather(*(fetch_page(client, u) for u in urls))
    return pd.concat([parse_to_df(h) for h in htmls], ignore_index=True)

if __name__ == "__main__":
    urls = ["https://example-shop.com/products?page=" + str(i) for i in range(1, 6)]
    df = asyncio.run(collect(urls))
    print(df.sort_values("price", ascending=False).head(5))

4.2 选型对比(技术视角)

维度自建代理池亿牛云代理
接入成本高(IP 获取、健康检查、轮换逻辑自研)低(配置 endpoint 即用)
IP 质量与覆盖依赖公开资源,稳定性差企业级节点,多地区覆盖
并发与扩展受自有资源瓶颈限制隧道代理天然支持水平扩展
SLA 与可观测性自担,缺监控提供服务级保障与售后支持
合规风险来源不明,风险自担正规服务,授权清晰

合规声明:采集行为须严格遵守目标站点 robots.txt 及相关法律法规,实施合理的请求频率(Rate Limiting)与并发控制,仅用于合法合规的数据分析。代理 IP 是稳定的数据通道,而非绕过限制的工具——这一点必须在工程规范中明确。

五、工程化最佳实践与治理

把三层框架落地到生产,需配套以下治理动作:

1. Prompt 版本化与模板化

将 Prompt 抽离为 Jinja2 模板,变量仅保留 data_payloadanalysis_goal,纳入 Git 版本管理,杜绝 Prompt 漂移(Prompt Drift)。

2. 数据脱敏与最小暴露

PII 与业务明细绝不进入 Prompt。先在 Pandas 侧完成 drop / 哈希 / 聚合,仅传递必要统计量。

# 仅暴露聚合结果,明细不出域
safe_payload: dict = df.groupby("region")["gmv"].sum().round(2).to_dict()

3. 职责严格分离

重申:确定性计算归 Pandas,语义表达归 LLM。任何求和、占比、同比环比均用 Pandas,LLM 仅做基于结果的归纳。

4. 采集层韧性

重试(tenacity)、降级(代理异常自动切换备用通道)、限流(httpx.Limits / 信号量)、可观测(记录成功率、耗时指标),四者缺一不可。

5. 结果可溯源(Data Lineage)

LLM 的每条结论都应能回溯到 Pandas 计算源。做法:在结构化输出中附带指标来源列名与计算公式,便于审计。

6. 评估与成本控制

建立解读质量的评分卡(Rubric),定期抽样评估;监控 token 消耗与调用成本,对超预算的 Prompt 做裁剪。

7. 沙箱执行 LLM 代码

Layer 1 生成的代码必须经沙箱(受限文件系统/网络、超时熔断)验证后再纳入生产,禁止对不可信输出直接 exec

六、协同成熟度模型与总结

可用下表评估团队当前所处的协同阶段:

等级特征典型做法
L1 探索人工写 Prompt,人工跑 Pandas临时性、一次性分析
L2 标准化Prompt 模板化,代码生成 + 自动校验团队内可复用、可测试
L3 流水线采集→清洗→计算→解读 全自动,含治理与监控生产级、可观测、可审计

Prompt 与 Pandas 的结合,本质是把**“会算”(Pandas 的确定性)"会说/会写"(LLM 的语义能力)**拼成一条完整链路:

  • Layer 1:用 Prompt 指挥 AI 生成可验证的 Pandas 代码,降低编码门槛;
  • Layer 2:用 Pandas 算准、用 Prompt 结构化解读,互补短板;
  • Layer 0:在数据采集这一最易卡脖子的环节,引入亿牛云代理这类企业级代理 IP 服务,以稳定通道打通"第一公里",让数据真正流得进来。

当这三层串成"采集(亿牛云代理)→ 清洗(Pandas)→ 计算(Pandas)→ 解读(Prompt)"的闭环,并配上治理与监控,你会发现:原本以"天"计的数据分析交付,正逐步收敛为"写几个 Prompt、跑一段脚本"的工程动作。

下一次接到"帮我看看这组数据"的需求,不妨先问自己一句:这部分该交给 Pandas,还是交给 Prompt? 想清楚分工与边界,专业度与效率自然就上来了。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐