当前位置:首页>python>救命!外贸同事加班到崩溃,我用AI+Python写了个工具,批量搞定个性化合同制作

救命!外贸同事加班到崩溃,我用AI+Python写了个工具,批量搞定个性化合同制作

  • 2026-09-02 20:19:48
救命!外贸同事加班到崩溃,我用AI+Python写了个工具,批量搞定个性化合同制作
作为总裁办数据运营主管,我每天最常接到的求助,不是“帮我做个标准化报表”,而是各部门同事带着哭腔找过来:“小李,这个活儿我真的搞不动了,全是乱七八糟的格式,手工做一天还总出错!”
尤其是外贸部的张姐,最近一个月几乎天天泡在办公室加班,上周找我时眼睛都红了:“我每天要处理几十份开票资料,没有一份格式是一样的,产品明细一会儿在第5行、一会儿在第15行,合同号藏得像捉迷藏,还要手动翻工厂信息表、算金额大写,一份合同要耗20分钟,月底忙到凌晨,还总因为一个数字错被财务退回……”
我太懂这种无奈了——很多办公难题,难的不是“不会做”,而是“太磨人”。那些看似毫无规律的个性化场景,Excel根本搞不定,手工处理又耗时易错,翻遍案例也找不到现成解法。
但我一直坚信:再个性化的办公场景,只要有明确的业务规则,就能用AI+Python做成自动化工具。
就像张姐的开票合同难题,我只用了半天时间,结合AI指令和Python代码,做了一个能批量运行的工具,直接把她的加班时间砍到零,财务退回率也降到了0。今天就把这个真实案例拆给大家,从需求拆解到代码落地,再到避坑技巧,手把手教你搞定个性化办公难题,学会就能直接复用!

真实场景:外贸开票合同,难就难在“没有固定格式”

先跟大家说清楚张姐的痛点,其实很多做外贸、行政、财务的朋友都遇到过类似情况,对照着看看,你是不是也在踩坑?
张姐的核心工作:把工厂给的开票资料,整理成集团合规的购销合同。但问题就出在——工厂的开票资料完全没有统一标准,全是个性化排版:
产品明细位置不固定:有时候在第5行,有时候在第15行,列名要么叫“品名”,要么叫“产品名称”,根本没法固定引用单元格;
合同号藏得隐蔽:没有固定位置,只知道前缀是TD或MI,后面跟5位数字,要在整个表格里一个个找;
甲乙方信息要手动查:看到“工厂A”“工厂B”的简称,得翻专门的《工厂信息表》找全称、地址和电话,费时间还容易错;
财务合规要求严:总金额要手动汇总,还要转换成“壹万贰仟叁佰肆拾元整”的大写格式,错一个字就会被财务退回;
合同模板要手动填:产品明细行数不固定,填模板时稍微错一个位置,整个合同就可能无效。
张姐之前全靠复制粘贴,一份合同20分钟,几十份下来就是一整天,加班到深夜是常态,还总挨财务的批评。用她的话说:“这活儿不是难,是磨人,每一份都不一样,根本找不到固定方法。”
其实不是找不到方法,而是没找对思路——个性化的核心是“规则不固定”,但只要把这些“不固定”的规则提炼出来,用Python+AI去匹配、去执行,就能实现自动化。

核心思路:从“杂乱无章”里,提炼“标准化规则”

很多人觉得自动化离自己很远,尤其是个性化场景,总觉得“我的情况太特殊,代码搞不定”。但其实,再特殊的场景,背后都有明确的业务规则,这就是自动化的核心。
针对张姐的需求,我先帮她拆解出了5个核心规则,瞬间就清晰了:
产品明细:不管在第几行,只要找到“品名/产品名称”作为开始,“备注/备注说明”作为结束,中间的就是需要的明细;
合同号:不用一个个找,用正则规则抓取“TD+5位数字”或“MI+5位数字”,不管藏在表格哪个角落,都能精准提取;
甲乙方信息:合同号前缀TD对应一个甲方,MI对应另一个甲方;工厂简称只要匹配《工厂信息表》,就能自动带出全称、地址和电话;
金额处理:自动汇总产品明细的金额,用专用函数转换成中文大写,不管明细有多少行,都不会算错;
模板写入:按集团固定模板填充信息,产品明细从第10行开始动态插入,不管明细有多少行,都不会打乱模板格式。
简单说,就是用“关键词定位+规则匹配”解决“个性化”的问题,用“模板化写入+财务合规函数”保证“标准化”的输出。思路理清后,接下来就是用AI帮我们写代码,不用自己从零敲,效率直接翻倍。

关键一步:给AI写对指令,代码直接能用(附可复制指令)

很多人用AI写代码,最后写出来的不能用,核心问题是“指令没写清”。作为总裁办数据运营主管,我总结了一个技巧:给AI的指令,一定要“规则明确、逻辑清晰、贴合业务”,既要让AI懂你的场景,也要让它知道你要的输出标准。
下面是我给AI的完整指令,大家可以直接复制,替换成自己公司的信息,就能生成适配自己场景的代码,非技术岗也能轻松搞定:
请用Python编写一个适配个性化开票资料的购销合同自动生成工具,要求贴合外贸业务财务合规要求,核心规则如下:
  1. 批量处理:读取指定目录下所有开票资料Excel(兼容.xlsx/.xls),支持多文件同时解析,满足批量处理需求;
  2. 智能定位产品明细:通过关键词模糊匹配,提取“品名”或“产品名称”所在行到“备注”或“备注说明”所在行之间的所有数据,自动适配任意行数的明细,解决开票资料个性化排版的位置不固定问题;
  3. 精准提取合同号:用正则表达式在Excel全文中抓取TD或MI开头的5位数字编号,去重并保证唯一性,无关合同号在表格中的位置;
  4. 自动匹配甲乙方信息:根据合同号前缀(TD/MI)自动匹配集团指定的甲方全称、地址、电话;根据开票资料中的工厂简称,到《工厂抬头信息表.xlsx》中精准匹配乙方工厂全称、地址、电话,解决工厂简称个性化的匹配问题;
  5. 财务合规处理:自动识别产品明细中的“金额”列(兼容“金额”“合计”列名),汇总总金额并做数值清洗(过滤非数字、空值),编写专用函数将阿拉伯数字金额转换为符合集团财务要求的中文大写格式(支持整数、浮点数、零头场景);
  6. 模板化动态写入:打开集团《购销合同模板.xlsx》,将甲乙方信息、合同号、总金额、大写金额等信息填入模板固定位置,产品明细从第10行开始动态插入,自动适配明细行数,不改变模板原有格式和条款;
  7. 规范命名与保存:生成的合同文件按“工厂简称-合同号-购销合同-当前日期.xlsx”命名,统一保存到result文件夹,方便归档;
  8. 异常处理与日志:解析过程中遇到“关键词找不到”“合同号提取失败”“工厂简称匹配不上”等问题时,自动跳过该文件并记录详细日志,不中断批量处理,日志文件保存到logs文件夹,方便追溯核对;
  9. 格式美化:给生成的合同产品明细添加标准边框,保证排版整洁,符合集团正式合同的格式要求。

可直接运行代码(附配置区,非技术岗也能操作)

AI生成代码后,我又做了优化,增加了专属配置区,非技术岗的同事不用修改核心代码,只要在配置区调整路径和集团信息,就能直接运行。甚至我还打包成了EXE文件,张姐现在双击就能用,完全不用找我帮忙。
下面是完整代码,大家可以直接复制到Python编辑器(比如PyCharm、VScode),修改配置区的信息即可使用:
from datetime import datetime, timedeltaimport loggingimport osimport pandas as pdimport reimport numpy as npfrom openpyxl import load_workbookfrom openpyxl.styles import Font, Alignmentfrom openpyxl.styles import Border, Side# -------------------------- 配置区(仅修改这里即可,无需动核心代码)--------------------------LOG_LEVEL = logging.DEBUG  # 日志级别(不用改)LOG_FILE = 'logs/app.log'  # 日志文件路径(不用改,自动生成)TEMPLATE_PATH = './data/购销合同模板.xlsx'  # 集团合同模板路径,替换成自己的路径FACTORY_INFO_PATH = './data/杂品工厂抬头信息.xlsx'  # 工厂信息表路径,替换成自己的路径INPUT_DIRECTORY = './'  # 开票资料所在目录,替换成自己的路径PARTY_A_INFO = {  # 甲方信息,替换成自己公司的信息    "TD": {"name": "威海卓达进出口有限公司", "addr": "威海市新威路17-2(威高广场)SOHO A座15层", "phone": "0631-5280617", "short_name": "卓达"},    "MI": {"name": "威海名禾渔具有限公司", "addr": "威海市环翠区新威路17-2(威高广场SOHO A座)", "phone": "0631-5280617", "short_name": "铭禾"}}# ----------------------------------------------------------------------------------------# 创建日志目录(如果不存在)log_dir = os.path.dirname(LOG_FILE)if log_dir and not os.path.exists(log_dir):    os.makedirs(log_dir)# 配置日志logging.basicConfig(    filename=LOG_FILE,  # 日志文件路径    level=LOG_LEVEL,    # 日志级别    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',  # 日志格式    datefmt='%Y-%m-%d %H:%M:%S'  # 日期格式)def number_to_chinese_amount(number):    """    将阿拉伯数字转换为中文金额大写(无需修改,直接用)    :param number: 数字(整数或浮点数)    :return: 中文金额大写字符串    """    units = ['', '万', '亿']    digits = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']    scales = ['', '拾', '佰', '仟']    if not isinstance(number, (int, float)):        return "无效输入"    # 处理负数    if number < 0:        return "负数不支持转换"    # 分离整数和小数部分    number = round(number, 2)  # 保留两位小数    integer_part, decimal_part = f"{number:.2f}".split('.')    integer_part = int(integer_part)    decimal_part = int(decimal_part)    # 转换整数部分    def convert_integer(n):        if n == 0:            return digits[0]        result = ''        unit_index = 0        while n > 0:            part = n % 10000            if part != 0:                part_str = ''                for i, digit in enumerate(str(part)[::-1]):                    if digit != '0':                        part_str = digits[int(digit)] + scales[i] + part_str                    else:                        if part_str and part_str[0] != digits[0]:                            part_str = digits[0] + part_str                result = part_str + units[unit_index] + result            else:                if result and result[0] != digits[0]:                    result = digits[0] + result            n = n // 10000            unit_index += 1        return result    # 转换小数部分(角和分)    def convert_decimal(n):        if n == 0:            return '整'        jiao = n // 10  # 角        fen = n % 10    # 分        result = ''        if jiao != 0:            result += digits[jiao] + '角'        if fen != 0:            result += digits[fen] + '分'        return result    # 组合结果    chinese_integer = convert_integer(integer_part)    chinese_decimal = convert_decimal(decimal_part)    return chinese_integer + '元' + chinese_decimaldef is_factory_in_column_name(factory_name, column_names):    """判断工厂简称是否在列名中(无需修改)"""    return any(factory_name in str(col) for col in column_names)def get_date():    """获取所需日期(无需修改)"""    today = datetime.now()    today_str = today.strftime('%Y-%m-%d')    # 获取前20天、前5个月日期(适配合同交付、签订时间,可根据需求修改天数)    five_months_ago = today - timedelta(days=5 * 30)  # 近似计算,每月按30天    five_months_ago_str = five_months_ago.strftime('%Y-%m-%d')    twenty_days_ago = today - timedelta(days=20)    twenty_days_ago_str = twenty_days_ago.strftime('%Y-%m-%d')    return twenty_days_ago_str, five_months_ago_str, today_strdef deal_with_input(file_path):    """解析开票资料,提取所需信息(无需修改)"""    result_data = {}    result_data["delivery_time"], result_data["sign_time"], result_data["today_time"] = get_date()    # 读取开票资料(兼容.xls和.xlsx格式)    if file_path.endswith('.xls'):        data = pd.read_excel(file_path, engine='xlrd', header=None)    else:        data = pd.read_excel(file_path, header=None)    # 定位第一行非空行,作为列名    first_non_empty_row = data.dropna(how='all').index[0]    data.columns = data.iloc[first_non_empty_row]    data = data.iloc[first_non_empty_row + 1:].reset_index(drop=True)    # 清洗数据,去除空行空列    data_no_na = data.dropna(how='all').reset_index(drop=True)    df = data_no_na    # 定位并提取产品明细(关键词可根据需求修改)    start_index = df[df.apply(lambda row: row.astype(str).str.contains('品名|产品名称').any(), axis=1)].index[0]    end_index = df[df.apply(lambda row: row.astype(str).str.contains('备注|备注说明').any(), axis=1)].index[0]    prod_infos = data_no_na.iloc[start_index:end_index]    prod_infos.columns = prod_infos.iloc[0]    prod_infos = prod_infos[1:].reset_index(drop=True)    # 删除可能残留的“品名”行,清洗明细数据    mask = prod_infos.apply(lambda row: row.astype(str).str.contains('品名|产品名称').any(), axis=1)    prod_infos = prod_infos[~mask].dropna(axis=1, how='all').reset_index(drop=True)    # 汇总金额并转换为中文大写    total_amount = prod_infos['金额'].sum()    chinese_amount = number_to_chinese_amount(total_amount)    # 添加金额汇总行    new_row = [f"合计人民币金额(大写):{chinese_amount}"] + [np.nan] * (len(prod_infos.columns) - 2) + [total_amount]    prod_infos.loc[len(prod_infos)] = new_row    result_data["prod_infos"] = prod_infos    # 提取合同号(正则规则可根据需求修改)    pattern = r'(TD\d{5}|MI\d{5})'    matches = df.applymap(lambda x: re.findall(pattern, str(x))).stack().tolist()    unique_matches = list(set([item for sublist in matches for item in sublist]))    # 匹配甲方信息    if len(unique_matches) == 1:        result_data["contract_num"] = unique_matches[0]        prefix = "TD" if "TD" in unique_matches[0] else "MI"        result_data["partyA_info"] = PARTY_A_INFO[prefix]        result_data["partyA_name"] = PARTY_A_INFO[prefix]["short_name"]    # 匹配乙方信息(工厂信息表)    df_factory = pd.read_excel(FACTORY_INFO_PATH, header=0)    result_flag = df_factory['工厂简称'].apply(is_factory_in_column_name, column_names=df.columns)    partyB_infos = df_factory[result_flag].iloc[0].to_dict()    result_data["partyB_info"] = partyB_infos    return result_datadef write_excel(data):    """将提取的信息写入合同模板(无需修改)"""    # 设置表格边框(美化格式)    thin_border = Border(        left=Side(style='thin'), right=Side(style='thin'),        top=Side(style='thin'), bottom=Side(style='thin')    )    # 加载集团合同模板    wb = load_workbook(TEMPLATE_PATH)    ws = wb.active    # 写入合同编号    ws['E1'] = f"编号:{data['contract_num']}"    # 写入甲乙方信息    ws['A3'] = f"供方(甲方):{data['partyA_info']['name']}                需方(乙方):{data['partyB_info']['工厂全称']}"    ws['A4'] = f"地址:{data['partyA_info']['addr']}      地址:{data['partyB_info']['地址']}"    ws['A5'] = f"电话:{data['partyA_info']['phone']}                                  电话:{data['partyB_info']['电话']}"    # 写入产品明细    df = data["prod_infos"]    start_row = 10  # 产品明细起始行,可根据模板修改    start_col = 2   # 产品明细起始列,可根据模板修改    # 写入列名并添加边框    for col_idx, col_name in enumerate(df.columns, start=start_col):        cell = ws.cell(row=start_row, column=col_idx, value=col_name)        cell.border = thin_border    # 写入明细数据并添加边框    for row_idx, row in enumerate(df.values, start=start_row + 1):        for col_idx, value in enumerate(row, start=start_col):            cell = ws.cell(row=row_idx, column=col_idx, value=value)            cell.border = thin_border    # 写入合同条款(可根据集团模板修改)    next_row = start_row + len(df) + 2    ws[f"A{next_row}"] = f"二、质量要求:以客户确认的样品质量为准,如因产品质量问题引起客户索赔,供方需付连带责任。"    ws[f"A{next_row+1}"] = f"三、交货地点及方式:{data['delivery_time']}前工厂交货。"    ws[f"A{next_row+2}"] = f"四、结算方式及期限:甲方凭增值税发票及出口货物专用缴款书装箱后30天付清。"    ws[f"A{next_row+3}"] = f"五、包装要求:出口用双瓦楞纸箱,纸箱尺寸按客户要求。"    ws[f"A{next_row+4}"] = f"六、违约责任:违约损失及连带责任由违约方承担。"    ws[f"A{next_row+5}"] = f"七、争议的解决方式:本合同在履行过程中如发生争议,由当事人双方协商解决。协商不成,提交威海市仲裁委员会仲裁。"    ws[f"A{next_row+6}"] = f"八、本合同甲、乙双方盖章签字后生效,甲乙双方各持壹份。"    ws[f"B{next_row+15}"] = f"甲方:"    ws[f"F{next_row+15}"] = f"乙方:"    ws[f"F{next_row+17}"] = data['sign_time']    # 创建结果文件夹(自动生成)    os.makedirs("./result", exist_ok=True)    # 保存合同文件(命名格式可修改)    output_path = f"./result/{data['partyB_info']['工厂简称']}-{data['contract_num']}-购销合同-{data['today_time']}.xlsx"    wb.save(output_path)    print(f"合同生成成功:{output_path}")if __name__ == "__main__":    # 批量处理指定目录下的所有Excel开票资料    excel_files = [os.path.join(INPUT_DIRECTORY, f) for f in os.listdir(INPUT_DIRECTORY) if f.endswith(('.xlsx', '.xls'))]    for file in excel_files:        try:            logging.info(f"开始处理文件:{file}")            result_data = deal_with_input(file)            write_excel(result_data)            logging.info(f"文件{file}处理成功")        except Exception as e:            logging.error(f"文件{file}处理失败,错误信息:{str(e)}")            print(f"文件{file}处理失败,可查看logs文件夹下的日志了解详情")

非技术岗使用指南:双击就能用,不用懂代码

很多同事看到代码就头疼,放心,我已经把操作步骤简化到极致,非技术岗也能独立操作,张姐现在每天自己处理,再也不用找我帮忙了,步骤如下:
环境准备:把所有开票资料放到代码同目录,在代码同目录新建一个“data”文件夹,里面放入《工厂抬头信息表.xlsx》和《购销合同模板.xlsx》(集团统一模板即可);
修改配置:打开代码,找到“配置区”,把甲方信息、文件路径替换成自己公司的,其他内容不用动;
一键运行:要么用Python编辑器运行代码,要么找技术同事帮忙打包成EXE文件,双击就能运行,全程不用手动操作;
查看结果:运行完成后,“result”文件夹里会生成所有标准化合同,“logs”文件夹里会生成日志,方便核对和追溯。
重点说下效率提升:原本张姐处理一份合同要20分钟,现在批量处理几十份,只需要几分钟,加班彻底消失,财务退回率也直接降到了0,她现在逢人就夸这个工具“救了命”。

总裁办避坑笔记:4个细节,避免自动化翻车

做办公自动化这么久,我总结了一个经验:个性化场景的自动化,最怕“细节翻车”。结合这个案例,我整理了4个核心避坑点,不管是外贸、行政还是财务,只要做个性化自动化,都能用得上:
关键词匹配失效:如果工厂开票资料的关键词变了(比如变成“产品品名”“备注说明”),不用重构代码,只要在deal_with_input函数里,修改关键词匹配的内容即可;
金额列非纯数字:如果金额列带“元”“¥”“逗号”,不用手动清洗,代码里已经做了数值过滤,会自动提取纯数字计算;
工厂简称匹配不上:如果开票资料的工厂简称没出现在列名中,可修改匹配逻辑,比如从固定单元格(如A2)提取简称,不用改核心代码;
模板格式保护:代码是“加载模板、另存为新文件”,不会修改原模板,不用担心模板被篡改,可放心让各部门使用。

最后想说:自动化,是为了把人从“磨人活”里解放出来

很多人觉得“AI+Python离自己很远”,但其实,它就在我们身边——不是要你成为程序员,而是用工具解决重复、磨人、易出错的工作,把时间花在更有价值的事情上。
张姐的案例只是一个缩影,不管是外贸的合同生成、行政的报表汇总,还是财务的票据处理,只要有明确的业务规则,都能用上这套思路,用AI+Python做成自动化工具。
接下来我还会分享更多总裁办真实自动化案例,从需求拆解到代码落地,手把手教你避坑,让你也能轻松搞定个性化办公难题,彻底摆脱加班。
最后,需要我把代码里的配置区再简化一步,或者帮你生成打包EXE的步骤,让非技术岗同事直接双击就能用吗?

最新文章

随机文章