把一份 42,318 行的订单明细直接塞进上下文,然后问「华东区退货率最高的三个 SKU 是什么」,它答得很快,格式也对,表格里列了三个编号和百分比。我把同一份文件用 pandas 跑了一遍,三个编号错了两个,百分比分别差了 11 倍、23 倍和不存在的 6.7%。
这不是模型不行,是问题提错了。下文是我把一个季度销售明细反复喂给模型分析、踩了三轮坑之后,最后稳定下来的流程,附实测数据和代码。
先说清一件事:模型做的是「模式匹配 + 生成」,不是「遍历 + 累加」。你让它读 4 万行去算一个 sum,本质上是要求它在一次前向里完成 4 万次加法并保持中间状态,这件事在架构上就没有保证。
所以失败不是随机的,它有固定形态。我统计了 8 个问题在纯「全文入上下文」模式下的表现:
问题类型 | 提问方式 | 8 题中答对 | 典型错法 |
|---|---|---|---|
计数(多少单) | 直接问 | 5 | 只数了它能「看见」的窗口内部分 |
求和 / 均值 | 直接问 | 3 | 数值量级像,但基数偏差 10%–30% |
分组 Top-N | 直接问 | 2 | SKU 编号不存在,是拼出来的 |
分组占比 | 直接问 | 1 | 分母口径换成订单数而非件数 |
找异常行 | 描述特征让它定位 | 7 | 定位准,描述会添油加醋 |
归类打标 | 给规则逐行判定 | 8 | 无 |
规律很清楚:凡是需要跨全量数据做累加/比较的,全错或半错;凡是单行内的判断和归类,几乎全对。 这条线划出来,后面所有做法都围绕它设计。
我拿同一份 42,318 行 CSV(约 9.6 MB)试了三种路径,任务固定为上面那 8 个问题:
做法 | 实际输入量 | 单轮耗时 | 8 题正确数 | 备注 |
|---|---|---|---|---|
A. 全量塞入上下文 | 约 380 万 token | 首发即超限 | — | 上下文装不下,必须先截断 |
A'. A 截断到前 8,000 行 | 约 72 万 token | 19 分钟超时 | 2 | 截断等于采样,答案系统性偏移 |
B. 分块摘要再汇总 | 每块 2,000 行 × 22 块 | 6 分 40 秒 | 4 | 每块自己算错,误差在汇总时放大 |
C. 先算后读(代码出数,模型解读) | 结果 JSON 约 1.2 KB | 24 秒 | 8 | 唯一全对 |
C 的耗时比 B 少一个数量级,正确率还高 4 题。原因不复杂:让代码做它擅长的事(精确遍历),让模型做它擅长的事(解释、归因、写结论)。
C 的关键不是「用工具」,而是把中间结果的体积压到模型不需要再算的程度。下面是我现在固定用的预处理脚本,输出一张窄表,字段只留模型解读需要的:
import pandas as pd
df = pd.read_csv("orders_q3.csv", dtype={"sku": str}, parse_dates=["paid_at"])
# 只保留华东区、已支付或已退款两种状态,避免模型自己猜口径
sub = df[(df["region"] == "华东") & (df["status"].isin(["paid", "refunded"]))]
agg = (
sub.groupby("sku")
.agg(orders=("order_id", "nunique"), items=("qty", "sum"))
.assign(refund_rate=lambda d: (d["refund_items"] / d["items"]).round(4))
.query("items >= 200") # 小于 200 件的样本不参与排名,避免长尾噪声
.sort_values("refund_rate", ascending=False)
.head(10)
)
# 只喂前 10 名 + 总体基线,模型不需要看全量
payload = {
"baseline_refund_rate": round(sub["refund_items"].sum() / sub["items"].sum(), 4),
"total_items": int(sub["items"].sum()),
"top10": agg.reset_index().to_dict("records"),
}
print(payload)这段脚本跑完输出 1.2 KB 的 JSON,模型拿到之后问「为什么这三个 SKU 退货率高」,回答的质量立刻不一样了——它会去比较 top10 里退款件数与订单数的比值,指出「退货集中在多件订单」,而不是瞎编一个百分比。
我第二轮犯的错更隐蔽。脚本算对了,但我在 prompt 里写「退货率按订单数算」,脚本里用的是件数。模型看到数字和描述不一致,直接按 prompt 的口径重新「解释」了一遍,把 18.7% 说成 4.2%,还编了一段理由。
模型不会质疑你给的口径,它只会顺着你的话往下编。口径必须只有一个来源,且必须在代码里。 现在我的做法是让脚本把口径字段一起打进 JSON:
{
"metrics": {
"refund_rate": {
"numerator": "refunded_qty",
"denominator": "total_qty",
"window": "2026-07-01~2026-09-30"
}
},
"baseline_refund_rate": 0.0412,
"top10": [{"sku": "SKU-88213", "items": 1840, "refund_rate": 0.187}]
}模型只负责在 metrics 的约束下解释 top10,没有解释空间可以跑偏。
上面这套是我手写的。更省事的路径是让 Agent 自己写脚本、自己跑、自己读结果——这也是我现在大部分时间用的方式,在桌面端的工作台里直接开着本地目录,让它对 CSV 做聚合再回读。这类活我用 PRIVO 的桌面端跑,好处是脚本能落盘、能重跑,出错时能看见它到底跑了什么,而不是只拿到一段看起来合理的文字。
但这里有个必须守住的前提:先让它输出 SQL 或 pandas 代码给你看,再让它执行。 我试过直接说「帮我分析这份文件」,它选了 df.mean() 去算退货率,把 0 和 1 的标记位平均了一下,得出「退货率 0.31」这种既不是件数口径也不是订单口径的数。看一眼代码就能拦住,不看就得等到结论错得离谱才发现。
说三处,都是真实发生的:
几千行交给模型分析,能不能用取决于一个判断:这个问题需不需要跨全量做精确聚合。 需要,就让代码先出数,模型只读结果;不需要,直接丢给它,单行判断和归类它做得比我快。
我现在的默认姿势是:CSV 进目录 → 模型写聚合脚本 → 我看一眼代码 → 跑 → 读 1 KB 结果 → 写结论。这条链上模型从来没算错过,因为它压根不需要算。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。