
每到月底盘点日我都要面对同一堆烂摊子进货单、出库单、退换记录散落在不同Excel文件里表头叫法五花八门还有一堆合并单元格和手打的合计行。过去我都是打开表格手工筛选归类再一个个核对数量折腾四五个小时才凑出一张勉强能用的盘点表。后来我把这条流程改成了一条本地化的AI自动处理流水线让本地大模型直接读原始流水自动整理成规范库存盘点表格全程不用上传数据到任何平台也不依赖在线服务。这篇文章就是把这条流水线的完整做法写出来包含工具选型、核心原理、完整代码和踩坑记录适合经常和Excel台账打交道的仓管、采购、运营同学也适合想在校内小项目里落地AI数据处理的学生团队参考。1. 盘点表为什么必须自动化手工流程的痛点与AI切入点1.1 手工盘点表最常见的四类低效场景我做过的库存整理工作凡是需要人工处理的翻来覆去就是这四个问题。第一类是格式不统一。同一个仓库入库单有人写成“进货数”有人写成“来货量”还有人直接写“50”出库单有的写“出库数”有的写“销量”还有的写“-30”。如果这些表来自不同的人或不同的月份光是把字段名对齐就要花不少时间。第二类是合并单元格和错位行。Excel里有人喜欢把同一种货品跨多行合并还有人会在中间插一行“小计”“合计”之类的汇总行。程序读到这些行时要么读出None要么把汇总数当成普通数量后续求和全部崩掉。第三类是货品命名混乱。“苹果”和“苹 果”、“苹果(红富士)”和“红富士苹果”在人工眼里是一回事在Excel里却是完全不同的两行。盘点表要汇总就得先把这些名称归一到同一个标准写法上。第四类是版本管理混乱。每个人手里都有一份“最终版”发来发去又变成“最终版2”“最终版3(定稿)”。等到真正要盘点时根本说不清哪个文件才是截止到月底的那一份。这些问题单个看不严重叠加在一起就是一场灾难。更麻烦的是它们恰恰是重复性最高、最不值得花人工时间的事情。我算过一笔账假设仓库有500个SKU手工整理一份盘点表平均要花3到6个小时其中至少七成时间花在“看懂数据、对齐字段、归类文字”上真正需要人来判断的数量核对反而只有很少一部分。1.2 AI在盘点表场景里到底能做哪些事有人一听到“AI生成盘点表”第一反应是让AI凭空造一张表出来。这是个错误的理解。在库存盘点这个场景里AI真正擅长的不是“生成”而是理解和规整。具体来说本地大模型可以承担这么几个环节字段语义识别给它一段原始表头它能判断出“进货数”“来货量”“50”这些其实都表示入库数量。货品名归一把“苹 果”“苹果(红富士)”“红富士苹果”归一到统一的“苹果”或者归一到你指定的SKU主数据。记录分类和异常标注它能根据备注里的“破损”“退换”“临期”等字样给记录打上分类标记方便后续人工优先处理。自然语言筛选你可以直接问“找出所有数量为负数的出库记录”它能把这些记录挑出来形成子表。结构化输出它能把一段杂乱文本整理成固定的JSON结构交给程序进一步处理。这些工作的共同点是有明确规则但不完全规则化需要语言理解能力同时重复量很大。这正好是大模型相对传统脚本的优势区域——传统脚本能处理“完全规整”的数据但对“写得比较随意”的文本毫无办法而仓库台账恰恰充满了随意写法。1.3 哪些环节不适合交给AI必须泼一盆冷水AI只是辅助工具不该碰库存管理中需要“担责”的环节。盘点表的最终数量确认不能交给AI。因为AI没有亲眼看过实物它只能根据你给的文件做账面推算。账面数据再漂亮和实物对不对得上得靠人去仓库里数。另一个不能交出去的是财务对账。金额、成本、损耗这些涉及钱的数据任何一步都应该有明确的计算公式和人工复核不能依赖模型的模糊判断。还有一点AI生成的盘点表只能作为“草表”或“参考表”最终对外发布前必须有专人审核签字。这不是技术问题是流程责任问题。我的原则是AI负责把脏活累活干完给人省出时间去处理真正需要判断的事情。自动化不代表甩手不管而是把人的精力从“整理表格”挪到“核对异常”上。2. 为什么偏向本地化数据主权与可用性的胜负手2.1 在线AI工具的三个顾虑市面上的在线AI工具确实方便粘贴一段数据就能出结果。但在库存数据这个场景我始终不太敢用。不是工具不好用而是有三个无法忽略的顾虑。第一是数据上传风险。库存数据看着不起眼实际上包含了SKU结构、进货渠道、销售节奏、库存成本这些信息拼在一起能推断出不少商业情报。把整份台账粘贴到在线平台等于把这些数据交给了平台方哪怕只是训练数据也让人不踏实。第二是网络链路不稳定。在线工具依赖网络而仓库或工厂的办公网络经常不稳定。卡在半路、请求超时、结果丢失都是常事。更麻烦的是有些在线服务对单次请求的长度有限制表格一长就得手动拆成好几段来回粘贴非常痛苦。第三是定制能力太弱。在线工具不会为你的仓库专门调整输出格式。我想要“货品编号品名规格结存数量”的标准盘点表它给我一段散文式的总结我提示词写了半天它还是按照自己的格式来。当然要说清楚如果你手头的库存数据完全不敏感、只有几十行、整理频率一年就两三次用在线工具确实更省事。但如果你和我一样要长期、反复、批量地处理库存台账本地化几乎是必然选择。2.2 本地化方案的取舍本地化方案的核心思路是把开源大模型部署在你自己的电脑或内网服务器上数据全程不出内网处理流程完全由你控制。这样做换来的是三个明确的好处。一是数据封闭原始台账、中间结果、最终盘点表都存在本地模型不会把数据发到任何外部接口。二是可按需定制提示词、输出格式、后处理脚本全部自己说了算想怎么调就怎么调。三是离线可用没有外网也能跑断网断电都不影响在机房内网运行。代价也很现实本地模型的能力和云端商业模型有明显差距尤其是复杂推理能力。7B量级的量化模型在理解长文本、处理多维嵌套逻辑时明显不如云端大模型。此外部署本身有门槛普通办公电脑如果配置低内存低于8G跑模型会比较吃力需要压缩模型规模或改用CPU推理的轻量版本。我个人的感受是这个取舍是值得的。因为盘点表场景不需要模型做复杂的创造性推理只需要它稳定地完成“提取、归一、结构化”这些中等难度的任务本地模型完全够用而且胜在稳定可控。2.3 软硬件准备清单先别急着写代码确认一下你的基础环境。我列一张清单按重要性排序项目最低要求建议配置说明内存8GB16GB以上决定能否流畅运行7B量化模型CPU4核8核以上纯CPU推理速度更快硬盘20GB空闲50GB以上存放模型文件和中间数据操作系统Windows 10 / Ubuntu 20.04任意64位系统主要看能不能装Python环境Python3.93.11推荐用3.10以上版本关键库pandas、openpyxl、requests同左核心数据处理库软件层面不需要安装什么重型平台我用的组合是Python脚本 pandas处理Excel 本地大模型推理服务。本地模型通过一个兼容接口暴露给Python脚本调用脚本负责读取原始表格、构造提示词、解析模型输出、写出最终盘点表。其中pandas负责表格读写和数值计算openpyxl负责处理Excel格式细节比如识别合并单元格requests负责和本地模型服务通信。这个组合非常轻量安装简单不需要GPU也能跑得动。3. 核心思路让AI“读表”而不是“生成表”3.1 提示词设计决定输出质量很多人以为本地模型效果差其实是提示词没写对。盘点表场景的提示词和平时聊天完全不是一回事它需要的是一个带完整上下文的作业指令。我写提示词的经验是不要只说“帮我整理成盘点表”而是要把目标表结构、字段含义、命名规则、输出格式、异常处理方式全部写清楚最好再附一小段示例。模型拿到越明确的任务说明输出的稳定性就越高。我常用的提示词结构是这样的你是一个库存盘点数据整理助手。现在给你一段原始库存记录请你完成以下任务 1. 将每条记录归类为“入库”“出库”“退换”或“未知类型”。 2. 将货品名称做归一化处理去掉多余空格、括号内容统一保留、同一货品合并成统一名称。 3. 输出JSON数组每个元素包含字段record_id、item_name、item_type、quantity、unit、record_date、note。 4. 如果某条记录信息不完整在note字段中标注“信息缺失:xxx”不要跳过。 原始记录 {原始数据文本} 输出格式只输出JSON不要额外解释 [{...}]这里的核心是把“读表”逻辑讲透。模型不是一个表格编辑器它是一个阅读理解引擎——你给它什么上下文它就按你的要求提取和重组。它不需要知道仓库里有多少货只需要根据给定文本做规范化输出。3.2 结构化输出的两种实现方式从模型拿到“能用的结果”我在实践中积累出两种可靠方式。方式A让模型返回JSON程序解析。我把一段原始台账文本塞进提示词要求模型只回复一个JSON数组。脚本拿到后用json.loads解析。这种方式适合记录量中等每次20到50行、字段相对规整的场景。优点是结构清晰、后续处理方便缺点是模型偶尔会在JSON前后加一些解释性文字必须写一个清洗函数过滤掉Markdown代码块标记和多余文本。方式B让模型填充表格模板。我先定义好目标盘点表的列结构把模板字段表放在提示词里让模型按模板逐条填充。这种方式适合输出字段固定、层级简单的场景但模型对模板的理解不如对JSON来得准确所以我现在基本都用方式A。无论用哪种方式都建议在程序侧做一道Schema校验检查返回结果是否是合法JSON字段名是否全部存在数量字段能否转换成数字。校验不通过的记录自动标记为“待人工处理”而不是直接丢弃——这样可以防止模型偶尔抽风导致数据缺失。3.3 先清洗后建模数据预处理的两条铁律我踩过最大的坑就是把一坨乱七八糟的数据直接丢给模型。结果模型输出比原数据更乱害得我花了更多时间去修正。后来我总结出两条铁律原始数据太乱AI也救不了清洗环节必须放在模型调用之前。铁律一统一文本编码再进模型。我实际遇到的Excel文件有的用UTF-8编码有的用GBK编码还有的是CSV格式但分隔符是中文逗号。如果不先把这些统一转成UTF-8纯文本模型读到的就是乱码。清洗方法很简单用pd.read_excel读入后把非文本列全部转成字符串再用str.replace清理多余空格和换行符。铁律二数值列先做基础运算不需要模型代劳。比如“结存数量期初入库-出库”这类加减运算直接用pandas做比让模型用文字描述准确得多。让模型做复杂算术是幻觉的高发区一定要避免。模型的职责是理解语义和归类数据处理的计算部分永远交给脚本。4. 从零搭建本地盘点表生成流水线实操步骤4.1 第一步准备好样本数据与表结构模板任何自动化流程第一步永远是定义“输入是什么、输出是什么”。我建议先准备一份典型的原始台账文件以及一份目标盘点表的模板。原始台账通常长这样我拿一个虚拟例子说明Sheet“入库明细” 合并单元格水果类 货品编号 品名 进货日期 进货数量 备注 A001 苹 果 2024-11-02 50 红富士 A002 香蕉 2024-11-03 30 无 A003 苹果(红富士) 2024-11-05 60 精品果同一份工作簿里还有“出库明细”“退换记录”等Sheet字段各不相同。这些不规则内容就是流水线要处理的“脏数据”。目标盘点表模板我定义为货品编号 品名 规格 单位 期初数量 入库数量 出库数量 结存数量 异常标记有了输入样例和输出模板后面写脚本就有了坐标不会被数据牵着鼻子走。4.2 第二步脚本读取原始表格并做基础清洗核心脚本的第一步用pandas把Excel读进来并把多个Sheet合并成一个DataFrame。这里有一个容易忽略的细节合并单元格在pandas里读出来是NaN必须用ffill()向前填充补上。import pandas as pd def load_and_clean(file_path): sheets pd.read_excel(file_path, sheet_nameNone, headerNone) frames [] for sheet_name, df in sheets.items(): # 删除完全为空的行 df df.dropna(howall) # 如果第一列存在合并单元格向前填充 df df.ffill() frames.append(df) merged pd.concat(frames, ignore_indexTrue) # 统一转成字符串清理多余空格 merged merged.astype(str).apply(lambda x: x.str.strip()) return merged这一段做了三件事把多Sheet合并、清除空行、规整文本格式。做完这些数据就可以进提示词了。有一点要提醒不要试图让模型一次性读整个大文件。本地模型上下文长度有限硬塞几千行进去轻则速度极慢重则直接截断导致结果残缺。正确做法是把数据切成小块我一般每块20到30条记录分批喂给模型最后合并结果。4.3 第三步调用本地模型批量生成盘点记录清洗之后就可以把每块数据构造成提示词发给本地模型服务。我这里用一个通用的调用方式把提示词发送到本地推理服务的兼容接口模型名用local-model代表。import requests import json def call_local_model(prompt, urlhttp://127.0.0.1:8000/v1/chat/completions): payload { model: local-model, messages: [{role: user, content: prompt}], temperature: 0.1, # 盘点场景要低温度追求稳定 max_tokens: 2000 } resp requests.post(url, jsonpayload, timeout120) resp.raise_for_status() return resp.json()[choices][0][message][content] def build_prompt(chunk, schema_text): prompt f 你是库存盘点数据整理助手。请根据以下原始记录按给定字段抽取并输出JSON数组。 字段定义 {schema_text} 要求 - 将“入库”“出库”“退换”等类型归类到type字段 - 品名统一规范写法去除多余空格 - 数字字段必须为纯数字不要带单位 - 只输出JSON数组不要输出任何解释 原始记录 {chunk.to_json(force_asciiFalse)} return prompt温度参数我固定在0.1。盘点数据整理需要的是稳定复现不是创意发挥。温度越高模型输出的随机性越大同一批数据跑两遍可能得到两个不同版本的盘点表这在数据场景是绝对不能接受的。还有一点如果模型偶尔返回空结果或超时脚本里要加重试逻辑。我一般做三次重试每次间隔5秒。实测下来本地模型偶尔会因为内存不足或线程繁忙而卡住重试能解决大部分问题。4.4 第四步解析模型输出并写入Excel模型返回的是一段文本需要把它清洗成真正的JSON再转成DataFrame最后写出Excel。import re import pandas as pd def parse_model_output(text): # 去掉Markdown代码围栏 text re.sub(r(json)?, , text).strip() # 截取第一个 [ 到最后一个 ] 之间的内容 start text.find([) end text.rfind(]) if start -1 or end -1: raise ValueError(模型输出不包含JSON数组) array_text text[start:end1] data json.loads(array_text) return pd.DataFrame(data) def build_final_table(df, period_start, period_end): # 按货品编号和品名分组汇总 summary df.groupby([item_name, item_type])[quantity].sum().unstack(fill_value0) # 这里用pandas计算而不是让模型算 # 合并得到最终盘点表结构 final_df summary.reset_index() # 计算结存数量 期初 入库 - 出库 # 期初数需要从期初库存表单独导入 return final_df实际项目里这种解析函数很容易在JSON解析这一步报错。原因往往是模型在JSON前后写了一句“好的这是整理后的结果”。所以清洗逻辑不能只做一次而是要循环尝试第一次直接json.loads失败就去掉围栏再试还失败就截取方括号内容最后仍然失败就标记该块为“待人工处理”。写Excel时还有一个小技巧在最终盘点表旁边单独生成一页“异常记录”把程序解析失败、字段缺失、数量为负的记录全部汇总到这一页。这样审核人员打开文件先看异常页再核对主表效率比逐行扫高很多。4.5 第五步校验环节最后一个环节是校验绝对不要跳过。我推荐至少做两层校验。第一层是脚本自动校验账面平衡校验。对于每个货品必须满足“期初数量 入库数量 - 出库数量 结存数量”如果有差异自动标记异常。这一步用pandas一行就能算出来看着简单能挡住大多数错误。第二层是抽样人工核对从最终盘点表里随机抽10到20个SKU回到原始台账比对。抽样时优先抽那些数量异常大、名称包含“退换”“破损”的记录因为这些最容易出错。校验通过后这份盘点表才能算真正完成。整个过程跑下来500个SKU的台账从读文件到出结果我实测一般10到20分钟就能完成其中大部分时间都是在等模型推理。5. 实测演示一份凌乱出库单怎么变成盘点表5.1 模拟一份典型的乱台账为了把整个流程讲透我构造了一份高度“仿真”的原始台账尽量还原仓库里最常见的那种混乱程度。文件名叫“仓存流水2024-Q4.xlsx”包含三个Sheet。“入库明细”里有合并单元格“水果类”跨了五行品名写的是“苹 果”“苹果(红富士)”“出库明细”中品名写的是“红富士苹果”数量既有正数也有负数负数表示退货“退换记录”里数量列写的是“退3”“换5”这种带文字的内容。三个Sheet的表头编号、日期格式、备注列各不相同。这份数据如果靠人工整理先得把三个Sheet对齐字段再把“苹 果”和“苹果(红富士)”归成同一个品名还得把“退3”这种文本量转换成数字3并标记类型——每步都是重复劳动。5.2 完整运行流水线后的实际效果我把这份模拟数据丢进上面说的流水线跑完整流程得到的结果如下节选货品编号品名入库数量出库数量退换数量结存数量异常标记A001苹果11065540无A002香蕉3012018无A003葡萄252023数量偏低建议复核注意几个细节“苹 果”“苹果(红富士)”“红富士苹果”被统一成“苹果”“退3”“换5”被识别为退换记录数量被程序解析成3和5结存数量全部由脚本计算不是模型猜的。这一份原表如果手工整理我估计要半小时以上流水线实际运行时间是4分12秒其中模型推理占了绝大部分真正脚本处理不到十秒。5.3 原文件和盘点表的差距在哪里从原始台账到最终盘点表差距主要体现在四个维度一是字段标准化多Sheet的异构字段被统一为标准列名。二是语义归一化同货品的不同叫法被合并成主数据标准名称。三是类型标签化入库、出库、退换被自动分类数量正负不用人工再看。四是异常显性化程序把解析失败和可疑记录集中到异常页审核人员只需要处理少数异常行。用户拿到这份表做实物盘点时只需要带着“品名账面数量”去仓库对照实物回来把实际数量填进“实盘数量”列差异部分再单独复盘。这才是库存盘点应有的速度。6. 踩坑记录与改进建议让盘点表不再返工6.1 编码和Excel格式的一堆暗坑第一次跑通流水线我很兴奋但第二天就被打击了。同事发来一个.xls格式的老文件我的脚本直接报错——openpyxl不认.xls只有.xlsx才能读。后来我在读取函数里加了格式判断遇到.xls先用另一个库转成xlsx再读。更烦人的是编码问题。有些Excel是从老系统导出的CSV虽然扩展名是.csv实际编码是GBK。pandas默认按UTF-8读直接乱码。解决方法是pd.read_csv(path, encodinggbk)但不同文件编码不一样最好写一个探测函数尝试多种编码读哪个不报错用哪个。还有一个平时不会注意但关键时刻能坑死人的细节pandas读合并单元格时只有左上角第一格有值其余全是NaN。我第一次没做ffill()结果几十条记录的品类全部缺失。后来在清洗函数里固定加上向前填充这类问题才算根治。6.2 模型“幻觉”数字的防范办法本地模型在生成数字时偶尔会“编”出不存在的数量。有一次模型把“进货50”写成“进货100”盘点表上凭空多出50件货。这件事给我提了个醒绝对不要让模型直接生成最终数量它只负责抽取和分类所有加减汇总必须由脚本计算。我现在有三条防幻觉的硬性规则提示词里明确写“如果原始记录中没有数量请在quantity字段填0不要推测”。脚本对模型输出的quantity做边界检查超过原始记录最大值的直接标记异常。任何涉及库存数量的最终表都跑一遍“期初入-出结存”的平衡校验不通过就不输出正式版。这些规则不复杂但能挡住绝大多数模型瞎编的情况。核心思路是模型可以有理解偏差但数字必须经过程序逻辑的过滤和校验。6.3 上下文长度与分批策略本地模型上下文长度有限这是很多初用者最容易忽略的瓶颈。我曾经贪方便一次性把300条记录塞给模型结果模型只处理了前150条后面全部被截断结果还像模像样地输出了一个“完整”JSON——这个错误最危险因为看起来像正常结果实际数据是残缺的。所以我的建议是模型调用前先检查数据量超过30条的批次必须切分。每批返回后立即写入结果最后再合并。宁可多调几次接口也不要冒险让上下文被截断。分块处理还有一个附带好处某一块模型抽风时只需要重跑那一块不用整个流程推倒重来。批次大小没有绝对标准取决于模型的上下文长度。我的经验是6B到9B量级的模型每批15到30条比较稳妥。记录本身字段多、文本长时取小值字段少时取大值。6.4 从单表到多仓库的扩展思路如果这套流水线验证可行后面可以往两个方向扩展。一个方向是沉淀标准模板。把归一名称、输出字段、提示词、校验规则全部固定成仓库的“标准盘点规范”以后无论谁来交接只要按这个规范投喂原始流水出来的盘点表格式永远一致。这样就能解决“每个人的表格风格都不同”的长期问题。另一个方向是做定时自动盘点。如果原始台账是每天或者每周自动更新的可以利用操作系统的定时任务每天凌晨自动跑一遍流水线自动生成当日库存快照。月底盘点时直接基于这些快照做汇总和差异分析不用再去翻旧账。实测下来把流水线挂到定时任务里以后我每个月的盘点整理时间从四五个小时降到了半小时以内剩下的时间全部用来做异常复核。最后再分享一个我个人的习惯跑任何自动化流程之前先保留一份原始文件的只读备份。AI处理数据再顺手也不能确保每次都完美。有备份兜底遇到问题随时可以退回人工处理心里不慌。这套本地化AI盘点流水线对我最大的价值不是省了多少时间而是把月底那种焦躁的重复劳动变成了一个稳定的脚本任务让我能腾出精力去处理真正需要判断的库存问题。