简介:这份《华润万家IT运维服务外包工作说明书》面向IT运维服务商、外包项目经理及企业信息部门人员,用于厘清甲方与乙方在IT运维外包中的服务边界、职责划分与考核标准。文档系统定义了第三方厂商、保内保外设备、外包场所等关键概念,并逐项展开设备故障受理与排除、软硬件升级、备件备机、年度两次免费巡检、新店现场支持、关店服务、紧急服务协调及服务流程等内容,还给出SLA响应标准与按月考核的违约金梯度,可直接作为投标响应、合同附件或运维流程落地的参照模板。资源包共1个docx文件,约114KB,属于纯文档型资料,目录层级清晰,便于按服务模块检索引用。目前已有166人学习下载,适合需要撰写或核对运维外包工作说明书、搭建服务考核机制的从业者参考。
1. 一份 IT 运维服务外包工作说明书.docx 到底约束了什么
门店收银机蓝屏,外包工程师到场后说这台机器不在服务清单里,要走变更单;甲方 IT 经理翻出合同附件,里面只写了一句“负责门店 IT 设备日常运维”。这种扯皮在零售连锁项目里太常见,根子就埋在这份 IT 运维服务外包工作说明书.docx 上。
它不是法务文书,而是合同的执行附件:把“运维”拆成一条条可交付项,写清设备清单、响应与到场时限、考核口径、计费方式、交接与退出条件。写细了,外包团队进场第一天就知道先干什么;写含糊了,月底考核只能靠吵。
这份文档主要面向三类人:甲方 IT 负责人与采购、外包项目经理与售前、以及真正驻场的运维工程师。它划定的正是桌面运维、网络运维、服务器运维的边界,也决定了运维工程师每天被什么指标考核。
2. 工作说明书的可执行骨架:服务目录、SLA 与人员配置
2.1 先有服务目录,再谈 SLA:把 IT 运维拆成可交付项
我一般按“对象 + 动作 + 渠道”三要素拆服务目录,对象是设备或系统,动作是巡检、故障处理、变更配合、备件更换,渠道分现场、远程、电话。零售连锁常见的目录项大致是:门店终端与桌面运维(收银机、电子秤、小票打印机、门店 PC)、门店网络运维(接入交换机、无线 AP、专线链路)、机房与服务器运维、云上资源与中间件运维、办公区桌面运维。
每一条目录项后面都要跟两列:包含范围、排除范围。排除范围比包含范围值钱,耗材与配件费用、运营商专线月租、软件授权与二次开发、设备报废处置,这些必须在文字里点名排除,否则季度对账时一定被算进外包工作量。设备清单建议以附件形式挂数量与位置,正文只引用附件编号,避免正文和 Excel 清单两处维护。
2.2 SLA 指标与考核口径:三张表定胜负
SLA 只写“及时响应”等于没写,必须落成可量化的等级表。下面这张表是我在连锁零售项目里常用的结构,等级按对营业的影响划分,P1 是影响收银或门店断网。
| 服务项 | 等级 | 首次响应 | 到场 | 解决时限 | 可用率 | 月度扣分 |
|---|---|---|---|---|---|---|
| 门店收银终端 | P1 | 15 分钟 | 2 小时 | 8 小时 | 99.5% | 每单 0.5 分 |
| 门店网络链路 | P1 | 15 分钟 | 2 小时 | 4 小时 | 99.9% | 每单 1 分 |
| 办公区桌面 | P2 | 30 分钟 | 4 小时 | 24 小时 | 99.0% | 每单 0.2 分 |
| 服务器与中间件 | P2 | 15 分钟 | 远程 | 4 小时 | 99.9% | 每单 1 分 |
| 巡检与报表 | P3 | 1 工作日 | 不适用 | 3 工作日 | 不考核 | 漏一次 0.5 分 |
光有表还不够,计时起点必须写死。常见争议是“用户报修”和“工单创建”之间的时间差,我的做法是统一以运维值班电话接通或工单系统创建时间为起点,以业务方确认恢复为终点,双方在每月 3 日前用同一份工单导出数据对账。可用率的计算公式也要写进正文,别只写百分比,否则月度统计口径能吵三轮。
2.3 人员配置与运维技能图谱:驻场、远程、值班怎么分
人员部分别只写“配备 3 名工程师”,要写清角色、人数、工作地与工作时间。典型配置是一名驻场负责人加两名驻场工程师,覆盖 8×5 现场,非营业时间走远程值班;跨区域的连锁企业再加一个远程支持组承担二级升级。升级路径要写明:一线驻场 30 分钟未定位,升级到二线;二线 2 小时未恢复,升级到外包项目经理并同步甲方 IT。
技能要求可以借用运维技能图谱的思路分层写出,让面试和考核有共同语言。一线要求桌面运维基本功:Windows 域与组策略、外设驱动、收银外设调试、常用网络排查命令;二线要求 Linux 运维常用命令、主流数据库备份恢复、虚拟化或云主机操作、监控告警处理;三线或专家岗要求自动化运维脚本编写能力、批量配置下发、故障根因分析。写清技能层级的好处是,续签时可以直接按层级调价,而不是笼统地谈“涨一个人头多少钱”。
2.4 用 YAML 管住条款源头,避免 docx 反复手改
服务目录和 SLA 一旦超过二十条,直接手改 docx 就一定会出错:编号错位、表格串行、附件与正文对不上。我的做法是把条款数据抽到 YAML 里做唯一数据源,docx 只是渲染产物。
# docs/sow_data.yml —— 条款数据的唯一来源,改这里而不是改 docx meta: doc_no: SOW-IT-2025-001 # 文档编号,正文页眉引用 version: "1.2" # 版本号,续签时必须递增 effective_date: 2025-01-01 services: - code: SD-01 # 服务项编码,工单系统沿用同一编码 name: 门店收银终端桌面运维 include: [收银机, 电子秤, 小票打印机, 门店PC] exclude: [耗材与配件费用, 收银软件二次开发] channel: 现场 - code: SD-02 name: 门店网络运维 include: [接入交换机, 无线AP, 专线链路] exclude: [运营商月租, 设备报废处置] channel: 现场 sla: - code: SD-01 level: P1 response_min: 15 # 首次响应,单位分钟 onsite_min: 120 # 到场时限,单位分钟 resolve_hour: 8 # 解决时限,单位小时 availability: 99.5 # 可用率百分比,仅统计营业时段这样做有三个直接好处。第一,编码统一:服务项编码 SD-01 同时也是工单系统里的分类字段,月度统计可以直接按编码聚合,不用人工对照文字。第二,排除项显式化:exclude 列表渲染时会生成独立的“不在服务范围内的事项”小节,甲方采购和外包项目经理都能一眼看到。第三,变更可追溯:YAML 进 Git,谁在什么时候把到场时限从 120 分钟改成 180 分钟,diff 记录清清楚楚,比 docx 的修订记录可靠得多。
3. 用 python-docx 把模板渲染成正式的 docx 工作说明书
3.1 环境准备与母版文件的两个要求
渲染脚本本身不复杂,麻烦的是母版。我建议先手工做一份templates/sow_base.docx,在里面把页眉页脚、文档编号、页码域、标题样式(Heading 1/2/3)、表格样式(Table Grid 或自定义的“SOW 表格”)全部调好,脚本只负责往里填内容。这样生成的文档字体字号和公司模板一致,不会出现“内容对了但排版要手工重排”的尴尬。
python -m venv .venv && source .venv/bin/activate pip install python-docx pyyaml mkdir -p templates out母版有两个硬性要求。一是标题样式必须真实使用 Word 内置的 Heading 1/2/3,而不是手工加粗放大,否则后续生成目录和条款交叉引用会失效。二是表格样式要在母版里先应用一次,python-docx 对不存在的样式名不会报错,只会静默套用默认样式,生成出来没框线,很容易被当成脚本 bug 排查半天。
3.2 样式映射:让 Heading 层级对应条款编号
编号策略越早定越好。我习惯用“数字点分”编号,正文里引用写成“见 4.3.2 条”,这要求每级标题的文字前缀就是编号本身,而不是依赖 Word 的自动编号域(自动编号域在不同版本里渲染结果不稳定,转 PDF 时常错位)。
| 条款层级 | docx 样式 | 编号格式 | 生成方式 |
|---|---|---|---|
| 章 | Heading 1 | 1、2、3 | add_heading(level=1) |
| 节 | Heading 2 | 3.1、3.2 | add_heading(level=2) |
| 小节 | Heading 3 | 3.2.1 | add_heading(level=3) |
| 正文条款 | Normal | 无编号或 (1)(2) | add_paragraph() |
| 数据表 | Table Grid | 表 3-1 | add_table() |
样式名在不同语言版本的 Office 里可能显示为“标题 1”,但底层 XML 标识始终是 Heading 1,所以 python-docx 里引用样式用doc.styles["Heading 1"]是安全的。真正需要留意的是 WPS:部分版本保存后段落样式名会变成中文,解析旧文档时要同时匹配“标题”和“Heading”两类关键字。
3.3 渲染服务目录表与 SLA 表的完整脚本
import yaml from docx import Document from docx.shared import Pt def add_clause(doc, no, text, level=1): """写入带编号的标题,编号写在文字前缀里,兼容转 PDF 场景""" h = doc.add_heading(level=level) run = h.add_run(f"{no} {text}") run.font.size = Pt(16 - 2 * level) # 逐级缩小字号,母版未定义时兜底 return h def add_table(doc, headers, rows): """通用表格渲染,style 必须与母版中已存在的样式名一致""" table = doc.add_table(rows=1, cols=len(headers)) table.style = "Table Grid" for i, name in enumerate(headers): table.rows[0].cells[i].text = name for row in rows: cells = table.add_row().cells for i, val in enumerate(row): cells[i].text = "" if val is None else str(val) return table data = yaml.safe_load(open("docs/sow_data.yml", encoding="utf-8")) doc = Document("templates/sow_base.docx") add_clause(doc, "1", "服务范围与交付标准", level=1) add_clause(doc, "1.1", "服务目录", level=2) add_table( doc, ["服务项编码", "服务项名称", "包含范围", "排除范围", "服务渠道"], [[s["code"], s["name"], "、".join(s["include"]), "、".join(s["exclude"]), s["channel"]] for s in data["services"]], ) add_clause(doc, "1.2", "服务级别指标", level=2) add_table( doc, ["服务项", "等级", "响应(分钟)", "到场(分钟)", "解决(小时)", "可用率(%)"], [[s["code"], s["level"], s["response_min"], s["onsite_min"], s["resolve_hour"], s["availability"]] for s in data["sla"]], ) add_clause(doc, "2", "人员配置与技能要求", level=1) level_map = {"一线": "桌面运维与网络排查基础", "二线": "Linux运维与数据库操作", "三线": "自动化运维与根因分析"} for role, skill in level_map.items(): doc.add_paragraph(f"{role}岗位:{skill}") doc.save(f"out/{data['meta']['doc_no']}-v{data['meta']['version']}.docx")脚本逻辑分三层。数据层从 YAML 读入,脚本不写死任何条款;渲染层用add_clause和add_table两个函数把编号和表格统一风格;输出层按“文档编号 + 版本”命名,避免sow_final_new2.docx这种命名。参数上有两处可以按需改:run.font.size的公式控制标题字号,如果母版样式已经定义好字号,这行可以删掉,交给样式表接管;table.style的值必须和母版里的实际样式名逐字一致,配错时不会抛异常,只会生成无框线表格。
提示:
add_heading之后再add_run追加文字时,段落里会保留一个空 run,转 PDF 时可能出现多余空行。介意的话改用doc.add_paragraph(style="Heading 1")再add_run。
3.4 生成后自检:编号唯一、占位符清空
生成完必须跑一遍自检,不然漏改一个编号,评审会上就会被法务和采购同时问住。检查两个最要命的点:条款编号是否重复、模板占位符是否残留。
import re from docx import Document doc = Document("out/SOW-IT-2025-001-v1.2.docx") texts = [p.text.strip() for p in doc.paragraphs if p.text.strip()] # 1) 编号唯一性:抓取形如 3.2.1 开头的段落编号 nos = [m.group(1) for t in texts if (m := re.match(r"^(\d+(?:\.\d+){0,2})[\s、]", t))] dup = sorted({n for n in nos if nos.count(n) > 1}) # 2) 占位符与未填内容扫描,包括表格单元格 holes = [t for t in texts if re.search(r"\{\{.*?\}\}|待定|TODO|XXX", t)] for t in doc.tables: for row in t.rows: for c in row.cells: if re.search(r"\{\{.*?\}\}|待定|TODO", c.text): holes.append(c.text.strip()) print("重复编号:", dup or "无") print("疑似未填内容:", holes or "无")自检脚本的判定逻辑很简单:编号正则限定一到三层点分数字,再统计重复;占位符扫描要同时覆盖正文段落和表格单元格,因为 SLA 表里最容易留下“待定”字样。跑完无输出即视为通过,接入流水线后每次生成自动执行,比人工翻页检查快得多。
4. 从既有 docx 反查条款:解析、比对与续签复用
4.1 读取段落与表格:先分清三类节点
接手一份已经在执行的工作说明书时,第一件事是把 docx 变成可处理的结构化数据。python-docx 的doc.paragraphs只返回正文段落,表格和文本框是拿不到的,所以解析函数必须分路处理段落和表格,并保留顺序信息。
from docx import Document def dump_blocks(path): """把 docx 拆成有序块列表,正文段落与表格行各自成块""" doc = Document(path) blocks, pending_tables = [], list(doc.tables) for p in doc.paragraphs: if not p.text.strip(): continue blocks.append({ "type": "para", "style": p.style.name, # Heading 1 / Normal / 标题 2 "text": p.text.strip(), }) for ti, t in enumerate(pending_tables): for ri, row in enumerate(t.rows): cells = [c.text.strip() for c in row.cells] blocks.append({"type": "table", "idx": f"T{ti}R{ri}", "text": " | ".join(cells)}) return blocks逻辑说明:段落块用 style 名区分标题层级,为后面的条款树重建做准备;表格块用T{表序号}R{行号}做定位标识,便于在比对结果里直接指回原文位置。注意p.text拿不到文本框和艺术字里的内容,SLA 数值如果被放在文本框里,这一段会静默丢失,所以解析结果必须人工抽查一遍服务范围章节。
4.2 正则抽取 SLA 数值与责任方
拿到块列表后,用正则把散落在正文里的指标抽成字段,和表格里的数值交叉验证。常见写法是“P1 故障 15 分钟内响应”“甲方在 2 小时内提供备件”。
import re RESP_PAT = re.compile(r"(P[1-4])[^\d]{0,8}(\d+)\s*(分钟|min|小时|h)") OWNER_PAT = re.compile(r"(甲方|乙方|外包方|供应商)[^。;]{0,30}(负责|提供|承担)") for b in dump_blocks("old.docx"): if b["type"] != "para": continue for lvl, num, unit in RESP_PAT.findall(b["text"]): minutes = int(num) * 60 if unit in ("小时", "h") else int(num) print(f"[{lvl}] 时限 {minutes} 分钟 <- {b['text'][:40]}") for who, verb in OWNER_PAT.findall(b["text"]): print(f"责任方 {who} {verb} <- {b['text'][:40]}")参数说明:[^\d]{0,8}控制等级与数字之间的最大间隔字符数,写太小会漏掉带括号的写法,写太大会误抓跨句内容,8 是个比较稳的经验值;findall返回元组列表,序号与单位分开处理,单位同时兼容中英文。抽出来的结果要和表格数值做一次对照,两边不一致的条目就是条款冲突,续签时必须统一。
4.3 新旧版本条款 diff:续签前先看改了什么
续签谈判最怕的是不知道自己上一版签了什么。用difflib对新旧两版做行级比对,能快速定位新增、删除和改动的条款。
import difflib old = [b["text"] for b in dump_blocks("out/SOW-IT-2024.docx") if b["type"] == "para"] new = [b["text"] for b in dump_blocks("out/SOW-IT-2025.docx") if b["type"] == "para"] for line in difflib.unified_diff(old, new, fromfile="2024", tofile="2025", lineterm="", n=0): print(line)n=0表示不输出上下文行,结果更紧凑;实际评审时建议改成n=1,方便看清改动落在哪个条款下面。表格内容不参与这次 diff,所以表格指标要单独比对,做法是把表格行拼成字符串再走一次同样的比对流程。
4.4 docx 解析最常见的四个坑
| 现象 | 原因 | 处理方式 |
|---|---|---|
| 表格读出来行列错乱 | 存在合并单元格,row.cells会重复返回同一对象 | 用cell._tc去重,或按gridSpan手工展开 |
| 文本内容整段丢失 | 内容放在文本框、艺术字或页眉里 | 用docx2python或直接解析 document.xml |
| 段落样式名是中文 | WPS 保存后样式名本地化 | 关键字匹配同时判断“Heading”和“标题” |
| 修订痕迹导致重复内容 | 文档带未接受的修订,插入与删除文本同时存在 | 先接受全部修订再解析,或过滤w:ins/w:del节点 |
另外两个实际遇到过的小问题值得记一下:WPS 在某些环境下不能默认新建 docx,双击新建出的是 wps 格式,交付文档时最好明确要求对方另存为 docx;还有终端上无法预览 doc,只能看 docx 或 PDF,交付版本建议同时给一份 PDF,避免现场用手机打不开。
5. 让工作说明书真正约束住外包的三个技巧
第一个技巧是把条款编码写进工单系统,而不是留在文档里。服务项编码 SD-01、SLA 等级 P1 这些字段,要在工单系统的分类和质量属性里一一对应建好,工程师建单时选分类、系统自动带出该分类的响应与解决时限。这样一来,月度考核不需要人工翻文档核对,直接从工单系统按编码和等级聚合,超时单量、平均解决时长、可用率都是算出来的而不是估出来的。运维效率工具的价值就在这里:把文本条款变成结构化字段。
-- 按服务项编码和等级统计月度达成率,口径与工作说明书一致 SELECT s.service_code, s.sla_level, COUNT(*) AS ticket_cnt, SUM(CASE WHEN t.resolved_at <= t.deadline THEN 1 ELSE 0 END) AS on_time_cnt, ROUND(AVG(EXTRACT(EPOCH FROM (t.resolved_at - t.created_at)) / 3600), 2) AS avg_hours FROM tickets t JOIN service_sla s ON s.service_code = t.service_code AND s.sla_level = t.sla_level WHERE t.created_at >= DATE '2025-01-01' AND t.created_at < DATE '2025-02-01' GROUP BY s.service_code, s.sla_level ORDER BY s.service_code;这段查询的关键是deadline字段在建单时由服务项编码和等级推导写入,而不是事后计算,这样即使工作说明书改版,历史工单的达成判定也不会被追溯篡改。EXTRACT(EPOCH ...)把时间差转成秒再除以 3600 得小时,避免跨天时差计算错误。
第二个技巧是把条款变更走数据源和 Git,禁止直接改交付版 docx。外包方在续签时常常直接发回一份改好的文档,改动藏在几十页里,甲方很难发现到场时限从 2 小时悄悄变成 4 小时。改成 YAML 加脚本渲染之后,每次变更都产生一个 diff,评审就变成看几行改动,效率完全不同。第三个技巧是把知识沉淀写进条款。运维知识库的更新频率、交接文档清单、新员工培训时长,这些看起来软性的内容,恰恰是外包团队更换人员时最容易断档的地方,写进工作说明书之后,人员更替就有了硬性交付物。
验证条款是否真的可执行,有个很土但有效的办法:拿最近一个月真实发生过的十个工单,按工作说明书逐条套一遍,看能不能判出超时、能不能找到对应的扣分项、责任方是否明确。套不进去的条款,说明它在现场根本落不了地。
本文还有配套的精品资源,点击获取