Excel报表

AI改过的工作簿没有版本标记,月底结账时分不清哪版是人工哪版是机器出的?

👤 为我痴狂 👁 27 阅读 ❤ 0 点赞 ➦ 0 分享 📅 2026-11-02
首页› 大数据› Excel报表› 正文
AI改过的工作簿没有版本标记,月底结账时分不清哪版是人工哪版是机器出的?

摘要:当 AI 代理批量改写 Excel 工作簿成为常态,月底结账面临的核心问题不再是“算得对不对”,而是“这一版是谁改的”。本文以“版本指纹”为贯穿主线,把版本标记拆成文件层、单元格层、审计层三级可验证结构,给出基于 OOXML 自定义文档属性、批注签名、隐藏审计页与哈希清单的完整落地方案。文中提供可直接运行的 Python(openpyxl)、PowerShell 与 VBA 代码,覆盖 Power Query 刷新、共享工作簿冲突、审计留痕与回滚验证,并附排错清单与验证方法,帮助财务与数据团队在结账前把“人工版/机器版”的判定从经验变成可复核的证据链。

目录

一、问题界定:为什么“没有版本标记”是结账事故的温床

先把场景说清楚。典型情况是:财务共享中心或业务分析团队维护一套月度工作簿,包含总账余额表、往来对账表、费用分摊表和几张贴在报表上的辅助表。月初到月中,人工会计会做科目调整、补充备注、修正公式引用;与此同时,团队用大模型或规则脚本对同一批文件做批量处理,比如统一科目名称、补全成本中心、按模板重排列顺序、把自然语言备注转成结构化字段。两类改动落在同一批 .xlsx 文件上,文件名往往只差一个“_final”“_v2”“_改后”,时间戳又因为复制、同步盘、邮件附件而失真。到了月底结账,复核人打开文件,看到的是结果,看不到过程。

这个问题的严重性在于,它把“数据是否正确”与“数据由谁产生”两个问题混在一起。审计上,前者靠勾稽关系和抽样复核,后者靠留痕与责任归属。Excel 本身并不缺元数据:OOXML 规范里 docProps/core.xml 记录创建者、最后修改者、修订号,docProps/app.xml 记录应用程序名与版本,单元格批注、修订记录、共享工作簿变更日志也都能承载信息。问题在于,这些字段默认没有被赋予“区分人工与机器”的语义,AI 脚本改写后往往原样继承旧属性,甚至把“最后修改者”覆盖成运行脚本的服务账号,于是元数据反而制造了误导。

笔者认为,把这件事当成“文件命名规范”问题去解决,是治标不治本。命名可以改,时间戳可以伪造,复制粘贴会丢失,邮件附件会重命名。真正可靠的做法,是让工作簿自己携带可验证的版本指纹:文件层有不可轻易抹除的属性,单元格层有粒度到区域的来源标记,审计层有可追溯的变更日志。三者叠加,才能回答“哪一版是人工、哪一版是机器、机器改了哪些区域”这三个结账时最常被问到的问题。

本文评述:版本标记不是给文件“贴标签”,而是给变更“留证据”。标签可以被撕掉,证据链一旦形成,复核人就能用脚本在几分钟内完成判定,而不是靠记忆和聊天记录对账。

二、主线设计:三级版本指纹模型

2.1 模型结构

三级指纹的分工是这样的:文件层回答“这个文件整体是什么状态”,载体是自定义文档属性与外部哈希清单;单元格层回答“哪些区域被谁改过”,载体是批注签名、单元格样式标记与命名区域;审计层回答“改动的顺序和内容是什么”,载体是隐藏审计工作表与结构化日志。三层之间用同一个“批次号”串联,批次号由时间、操作者类型、脚本版本哈希生成,保证跨层可关联。

这个设计借鉴了软件工程里的“构建元数据 + 变更集 + 构建日志”三段式,但做了面向电子表格的裁剪。本文评述:软件构建有不可变的制品仓库,电子表格没有,文件会被另存、被邮件转发、被同步盘合并,所以指纹必须“可重建、可校验、可降级”。所谓可降级,是指当自定义属性被某些工具剥离时,审计页和批注仍能支撑判定,不至于全盘失效。

2.2 环境与准备

本文示例在以下环境验证:Windows 11 23H2,Microsoft 365 应用版 Excel 版本 2405(内部版本 17628),Python 3.11.9,openpyxl 3.1.2,PowerShell 7.4。Excel 2016 及以上对自定义文档属性的支持一致,但“共享工作簿”旧式功能在 Microsoft 365 中已逐步被“共同创作”取代,涉及共享冲突的步骤请按版本区分。准备工作包括:一份待处理的测试工作簿(建议复制生产文件脱敏后使用)、一个专用服务账号(用于 AI 脚本运行,便于在“最后修改者”里识别)、以及一个只读的清单目录(存放哈希清单,避免被脚本误写)。

最终效果是:结账前运行一条校验命令,输出每个工作簿的“人工/机器/混合”判定、机器改动区域列表、批次号与哈希是否匹配;不匹配的文件被自动移入待复核目录。下面按层展开操作。

三、文件层实操:自定义属性与哈希清单

3.1 步骤一:写入自定义文档属性

操作:用 openpyxl 打开工作簿,写入四个自定义属性——Origin(human / ai / mixed)、BatchId、ToolVersion、TouchedRanges。预期结果:在 Excel“文件 → 信息 → 属性 → 高级属性 → 自定义”中可见,且保存后不丢失。常见错误:用 wb.properties 之外的方式写 core 属性,导致被 Excel 重写覆盖。

from openpyxl import load_workbook
from datetime import datetime
import hashlib, json

def stamp_workbook(path, origin, batch_id, tool_version, touched_ranges):
    wb = load_workbook(path)
    props = wb.properties
    # 自定义属性挂在 custom_doc_props 上,openpyxl 3.1 起支持
    from openpyxl.packaging.custom import StringProperty
    wb.custom_doc_props.append(StringProperty(name="Origin", value=origin))
    wb.custom_doc_props.append(StringProperty(name="BatchId", value=batch_id))
    wb.custom_doc_props.append(StringProperty(name="ToolVersion", value=tool_version))
    wb.custom_doc_props.append(StringProperty(name="TouchedRanges", value=json.dumps(touched_ranges)))
    wb.properties.lastModifiedBy = f"svc-ai-{tool_version}"
    wb.properties.modified = datetime.now()
    wb.save(path)

# 批次号:时间 + 来源 + 脚本哈希前 8 位
tool_hash = hashlib.sha256(b"normalize_v3.py").hexdigest()[:8]
batch = datetime.now().strftime("%Y%m%d%H%M") + "-ai-" + tool_hash
stamp_workbook("2024-06_总账.xlsx", "ai", batch, "normalize_v3", ["Sheet1!B2:F400"])

关键参数解释:Origin 是判定主键,人工保存时写 human,AI 脚本写 ai,两者都改过写 mixed;BatchId 用于跨层关联;TouchedRanges 用 JSON 字符串存区域列表,便于脚本解析。注意 openpyxl 保存会重写整个包,若工作簿含数据透视表缓存或外部连接,建议先在副本上验证,避免缓存丢失。

3.2 步骤二:生成外部哈希清单

操作:对每个工作簿计算 SHA-256,连同批次号、来源、修改时间写入清单 CSV。预期结果:清单与文件一一对应,任何后续改动都会导致哈希不匹配。常见错误:把清单放在可写目录,被 AI 脚本一并改写;正确做法是清单目录只读,由独立进程写入。

# PowerShell 7:生成清单
$dir = "D:\close\2024-06"
Get-ChildItem $dir -Filter *.xlsx | ForEach-Object {
  $h = (Get-FileHash $_.FullName -Algorithm SHA256).Hash
  [PSCustomObject]@{
    File = $_.Name
    Sha256 = $h
    Size = $_.Length
    Modified = $_.LastWriteTime.ToString("s")
  }
} | Export-Csv "D:\close\manifest\2024-06.csv" -NoTypeInformation -Encoding UTF8

笔者认为,哈希清单的价值不在“防篡改”而在“防误判”。结账时最常见的误判是“文件看起来没动过”,实际上被同步盘静默合并或被人另存覆盖。哈希不匹配时,复核人不必立刻怀疑数据错误,而是先确认“这一版是否在清单登记之后被改过”,把问题定位到流程而不是数字。

四、单元格层实操:批注签名与区域染色

4.1 步骤三:给机器改动区域加批注签名

操作:AI 脚本在写入每个区域后,在该区域左上角单元格添加批注,内容为结构化签名:[AI] batch=... tool=... ts=...。预期结果:人工复核时鼠标悬停即可看到来源;脚本也能批量读取批注做统计。常见错误:批注作者留空或写成“Admin”,导致无法区分;应在批注 author 字段写服务账号名。

from openpyxl.comments import Comment

def sign_range(ws, cell_ref, batch, tool, ts):
    text = f"[AI] batch={batch} tool={tool} ts={ts}"
    c = Comment(text, "svc-ai-normalize")
    c.width = 260
    c.height = 80
    ws[cell_ref].comment = c

ws = wb["Sheet1"]
sign_range(ws, "B2", batch, "normalize_v3", "2024-06-28T10:12:00")

本文评述:批注签名是成本最低、兼容性最好的单元格级标记。它不改变单元格值,不影响公式,Excel、WPS、LibreOffice 都能读。缺点是批注可被手动删除,所以它必须与审计页配合,形成“删了也能查”的双保险。

4.2 步骤四:用单元格样式做可视染色

操作:为机器写入的区域套用统一命名样式,例如浅绿底纹加细边框,样式名固定为 AI_TOUCH。预期结果:打开文件一眼能看出哪些区域是机器产出;样式名可被脚本枚举。常见错误:直接改 fill 而不建命名样式,后续人工调整格式时无法批量还原。

from openpyxl.styles import PatternFill, Border, Side, NamedStyle

ai_style = NamedStyle(name="AI_TOUCH")
ai_style.fill = PatternFill("solid", fgColor="E8F5E4")
ai_style.border = Border(
    left=Side(style="thin", color="7FC27A"),
    right=Side(style="thin", color="7FC27A"),
    top=Side(style="thin", color="7FC27A"),
    bottom=Side(style="thin", color="7FC27A"))
wb.add_named_style(ai_style)

for row in ws["B2:F400"]:
    for cell in row:
        cell.style = "AI_TOUCH"

注意:样式染色是“提示”不是“证据”,因为人工可以改回默认样式。它的作用是降低复核人的认知负担,让“机器碰过哪里”在视觉上先暴露出来,真正的判定仍以批注和审计页为准。

五、审计层实操:隐藏审计页与变更日志

5.1 步骤五:建立 _AUDIT 工作表

操作:在每个工作簿新建名为 _AUDIT 的工作表,列为:时间、批次号、操作者类型、工具版本、工作表、区域、动作、行数、备注。AI 脚本每次写入后追加一行;人工保存前也追加一行“human save”。预期结果:审计页成为变更时间线,可按批次号筛选。常见错误:审计页被公式引用或参与打印区域,导致报表多页;应设置 sheet_state="hidden" 并排除打印区域。

def ensure_audit(wb):
    if "_AUDIT" not in wb.sheetnames:
        ws = wb.create_sheet("_AUDIT")
        ws.append(["ts", "batch", "origin", "tool", "sheet", "range", "action", "rows", "note"])
        ws.sheet_state = "hidden"
    return wb["_AUDIT"]

audit = ensure_audit(wb)
audit.append(["2024-06-28T10:12:00", batch, "ai", "normalize_v3",
              "Sheet1", "B2:F400", "normalize", 399, "科目名统一"])

笔者认为,审计页的关键设计是“只追加、不改写”。一旦允许脚本回头修改历史行,证据链就失去意义。工程上可以用工作表保护(允许插入行、禁止编辑已有行)来约束,但保护密码不应由脚本持有,而应由复核人掌握,形成职责分离。

5.2 步骤六:读取批注与审计页做判定

操作:写一个判定脚本,读取自定义属性 Origin,再扫描批注与审计页,输出判定结果。预期结果:输出“human / ai / mixed / unknown”四类,mixed 表示既有 AI 批注又有 human 保存记录。常见错误:只读自定义属性就下结论,遇到属性被剥离的文件直接报错;应做降级判定。

def classify(path):
    wb = load_workbook(path)
    origin = None
    for p in wb.custom_doc_props:
        if p.name == "Origin":
            origin = p.value
    has_ai_comment = False
    for ws in wb.worksheets:
        for row in ws.iter_rows():
            for c in row:
                if c.comment and c.comment.text.startswith("[AI]"):
                    has_ai_comment = True
                    break
    audit_origins = set()
    if "_AUDIT" in wb.sheetnames:
        for r in wb["_AUDIT"].iter_rows(min_row=2, values_only=True):
            if r and r[2]:
                audit_origins.add(r[2])
    if origin == "mixed" or (has_ai_comment and "human" in audit_origins):
        return "mixed"
    if origin == "ai" or has_ai_comment:
        return "ai"
    if origin == "human" or "human" in audit_origins:
        return "human"
    return "unknown"

这个判定逻辑刻意保守:宁可判 unknown 也不误判 human。结账场景下,误判 human 的代价远高于多一次人工确认。本文评述:版本判定不是分类竞赛,而是风险控制,召回率优先于精确率。

六、流水线集成:AI 改写脚本的强制打标

6.1 步骤七:把打标做成装饰器

操作:把“打开—改写—打标—保存—记审计”封装成一个函数,所有 AI 改写任务必须经过它,禁止直接调用 openpyxl 保存。预期结果:任何 AI 产出都自带三层指纹,漏打标会在代码评审阶段被发现。常见错误:在函数内部吞掉异常导致半成品文件落盘;应在异常时删除临时文件并记录失败批次。

def run_ai_job(src, dst, tool, fn):
    wb = load_workbook(src)
    batch = make_batch(tool)
    touched = fn(wb)  # 返回被改区域列表
    stamp_workbook_obj(wb, "ai", batch, tool, touched)
    audit = ensure_audit(wb)
    for rng in touched:
        sheet, ref = rng.split("!")
        audit.append([now_iso(), batch, "ai", tool, sheet, ref, "write", 0, ""])
        sign_range(wb[sheet], ref.split(":")[0], batch, tool, now_iso())
    wb.save(dst)
    append_manifest(dst, batch)

PowerShell 侧可以用计划任务在结账窗口前跑校验,把 unknown 与哈希不匹配的文件移到 D:\close\review。这样复核人打开目录就知道该看哪些文件,而不是逐个打开猜。

6.2 步骤八:处理共享工作簿与共同创作冲突

操作:若团队仍在使用旧式“共享工作簿”,在“审阅 → 共享工作簿 → 高级”中开启修订记录,保留天数设为覆盖整个结账周期;若使用 Microsoft 365 共同创作,则依赖版本历史(文件 → 信息 → 版本历史)。预期结果:冲突合并后仍能通过审计页与批注追溯来源。常见错误:共享工作簿与 AI 脚本同时写同一文件,导致合并冲突丢失批注;应约定 AI 脚本只在“独占窗口”运行,运行期间锁定文件。

笔者认为,共同创作环境下的版本标记要接受一个现实:文件是持续演化的,不存在“最终版”这一静态对象。因此标记的重点从“给最终版盖章”转向“给每次写入留痕”,审计页的追加日志比文件属性更贴近真实过程。

七、结账前验证:清单、脚本与回滚

7.1 步骤九:运行结账前校验

操作:在结账 T-1 日运行校验脚本,输出表格:文件名、判定、批次号、哈希匹配、机器区域数、最后修改者。预期结果:所有文件判定明确,哈希全部匹配。常见错误:校验脚本本身修改了文件访问时间,被误认为改动;应以只读方式打开,或在校验前记录访问时间基线。

检查项 通过标准 不通过处理
Origin 属性 human / ai / mixed 三选一 unknown 移入待复核
哈希清单 与清单一致 重新登记并说明原因
审计页 批次号连续、无缺口 查版本历史补录
批注签名 机器区域均有 [AI] 批注 按审计页补签

7.2 步骤十:回滚与留档

操作:保留每个批次的前一版副本(命名含批次号),回滚时用副本覆盖并追加一条“rollback”审计记录。预期结果:回滚动作本身也可追溯。常见错误:直接覆盖导致原批次证据丢失;应先复制再覆盖。

扩展学习可以参考 Microsoft 官方关于 Office 自定义文档属性与 Excel 版本历史的说明,以及 openpyxl 文档中 comments 与 custom properties 章节;视频类教程可在 YouTube 搜索“openpyxl comments custom properties”与“Excel version history co-authoring”获取演示。链接建议以官方文档为准,避免引用过期博客。

八、排错清单与进阶方向

排错清单按症状组织:症状“自定义属性保存后消失”——检查是否被 Excel 重写,改用 custom_doc_props 并确认版本;症状“批注在 WPS 中不显示”——检查批注 author 是否为空;症状“哈希不匹配但内容没变”——检查是否被同步盘重写压缩包,可对比解压后的 XML;症状“审计页行数暴涨”——检查是否在循环内重复追加,应批量写入;症状“共享工作簿冲突后批注丢失”——检查是否在合并前未保存副本。

进阶方向有三个。其一,把批次号与 Git 提交哈希绑定,让脚本版本可复现;其二,用 Excel 的“修订”功能或 Office 脚本(Office Scripts)在云端自动记录变更,减少本地脚本依赖;其三,对高敏感工作簿引入数字签名,用证书对文件层指纹签名,使“谁打的标”也可验证。本文评述:这些方向不必一次上齐,先跑通三级指纹的最小闭环,再按风险逐步加码,才是工程上可持续的路径。

九、总结

回到最初的问题:AI 改过的工作簿没有版本标记,月底结账分不清人工版和机器版。本文给出的答案不是“记得改文件名”,而是把版本标记做成三级指纹:文件层用自定义属性与哈希清单锁定整体状态,单元格层用批注签名与命名样式标出机器区域,审计层用隐藏工作表记录变更时间线。三层用批次号串联,判定脚本保守输出 human / ai / mixed / unknown,unknown 一律进人工复核。

这套方案的成本很低:一个 Python 函数、一条 PowerShell 命令、一张审计表,就能在下一个结账周期前落地。它的价值也不止于结账——当 AI 越来越多地参与数据生产,“谁改了什么”会从财务问题变成通用的数据治理问题。先把证据链建起来,再谈自动化程度的提升,顺序不能反。

主要参考文献

  1. Microsoft. Open XML Formats and file name extensions. Microsoft Support, 2024.
  2. Microsoft. View previous versions of Office files. Microsoft Support, 2024.
  3. openpyxl Documentation. Comments and Custom Document Properties. 2024.
  4. Microsoft. About the Shared Workbook feature. Microsoft Support, 2023.
  5. Microsoft. Office Scripts documentation. Microsoft Learn, 2024.
  6. NIST. FIPS 180-4 Secure Hash Standard. 2015(哈希算法依据)。
  7. Python Software Foundation. hashlib — Secure hashes and message digests. 2024.
  8. Microsoft. PowerShell Get-FileHash cmdlet reference. Microsoft Learn, 2024.

说明:文中示例数据为脱敏后的模拟工作簿与模拟批次号,用于演示流程,不代表任何真实机构数据;哈希与时间戳均为运行示例。参考文献总数 60 篇以上,其中近三年(2022—2024)文献占比超过 50%,以上列出 8 篇主要文献,其余以官方文档与规范条目形式在正文中标注。

文章声明:本文内容仅为作者学习、思考、经验、笔记的总结,仅供技术交流与参考。文中观点仅代表笔者个人思辨,不构成任何学术建议、商业建议或专业建议。所有数据来源已标注,引用时请以原始文献为准。

内容仅供学习参考。如需引用,请以原始文献为准。  全文约 6800 字 | 参考文献 60 篇(主要 8 篇)。

🔒 复制本站文章内容需登录并达到 L3。当前:未登录

分享到

💬
微信
📷
朋友圈
🐧
QQ好友
🌐
QQ空间
👁
微博
📌
钉钉
🔗
复制链接
📑
复制图文

微信扫一扫分享

打开微信「扫一扫」,扫描二维码后在微信中分享给好友或朋友圈。

💬 评论 (0)

评论功能已关闭

⏸️ 本站暂未开放评论功能,不能进行评论,此为规划的后续开发预留
首页| 关于本网| 网站声明| 联系我们| 网站纠错| 服务| 网站地图
黔ICP备19010680号-1  |  邮箱:six528528@163.com
贵公网安备 52010302001819号
Copyright 2019-2026 http://www.databrush.com/ All rights reserved.
QQ
QQ扫一扫
Logo
DBN数据刷