policy-audit-review
SKILL_514672737 · vv1.0 · 风控 · Owner:— · 发布于 2026-07-30
调用 0
下载 1
点赞 0
浏览 0
- 简介
- 企业制度与流程智能审核工具,自动分类并输出结构化意见与风险提示,默认生成带批注的Word修订稿。
- 触发词
- 制度审核,制度评审,流程审核,合规审核,生成修订稿
- 分发渠道
- —
- 功能测试
- ✅ 通过 · 业务评审:✅ 通过
- 技能包文件
- policy-audit-review/SKILL.md、policy-audit-review/agents/openai.yaml、policy-audit-review/references/review-framework.md、policy-audit-review/scripts/generate_revision.py
使用示例:请审核这份公司管理制度,并输出带批注的修订版Word。
SKILL.md 全文
Frontmatter
| name | policy-audit-review |
|---|---|
| description | 审核公司制度、管理办法、规定、细则、操作规程、操作指引、作业手册、SOP 或流程文件,并判断其属于管理类制度还是操作手册类制度;默认优先输出带批注、带修订标记的 Word 修订稿(w:del/w:ins + 气泡批注),同时附结构化审核意见、风险提示与具体修改建议。当用户提到"制度审核""制度评审""流程审核""内控制度优化""合规审核""生成修订稿""按审核意见修改制度""生成带批注word""生成批注版"等场景时使用,适用于 .docx 制度文件、制度概要或直接粘贴的制度正文。用户明确说"只要审核意见""不需要Word"时,才跳过修订稿生成。只生成一份修订稿文件({制度名称}_审核修订版_{YYYYMMDD}.docx),不额外生成独立的审核意见 Word 文件。 |
Policy Audit Review
Overview
使用本 skill 审核企业制度文本,重点识别制度类型、结构完整性、权责设计、内控闭环、监督问责与归档要求是否清晰,并输出可复核的审核意见。 优先保持审慎、克制、审核型语言;侧重"是否明确""是否存在""建议补充",不直接替代管理决策,不直接作出法律或合规结论。Workflow
1. 确认输入与交付物
默认行为(无需用户额外指令):- 只要用户提供了制度文本(.docx 文件、粘贴正文或制度摘要),完成审核后必须自动生成带批注、带修订标记的 Word 修订稿,无需等待用户单独要求。
- 用户明确说"只要审核意见""不需要 Word 文件""仅输出文字"时,才跳过修订稿生成步骤。
- 完整 .docx 文件 → 直接作为
source_path传入生成函数 - 粘贴正文或摘要 → 先将正文写入临时 .docx,再执行修订稿生成
- 仅要求设计审核框架(无制度文本)→ 输出审核框架,提示用户提供制度文本后可生成修订稿
2. 先判断制度类型(强制步骤)
必须先判断制度属于以下哪一类,并说明判断依据:- 管理类制度:侧重治理原则、授权边界、职责分配、决策与监督机制。
- 操作手册类制度:侧重岗位动作、具体步骤、系统路径、表单填写、执行顺序与操作要求。
3. 按统一审核框架审阅
至少覆盖以下七类维度: 1. 制度基本信息 2. 制度目标与适用范围 3. 制度结构与逻辑一致性 4. 权责划分与内控设计 5. 执行与监督机制 6. 违规问责与文件归档 7. 风险点与改进提示4. 根据制度类型追加差异化审核
- 管理类制度:重点关注治理与授权逻辑、制度体系协同性、外部法规符合性。
- 操作手册类制度:重点关注操作可执行性、关键控制点、合规要求向操作动作的转化。
5. 单独提示高风险内容
- 违反法律法规的内容
- 模糊授权、兜底授权
- 与上位制度明显冲突的条款
- 无法执行、无法监督、无法问责的原则性表述
Output Requirements
标准输出顺序: 1. 制度类型识别 2. 审核意见表(维度 / 原文内容 / 修改原因 / 具体修改建议) 3. 差异化审核关注点 4. 高风险内容提示 5. 总体审慎提示 6. 自动生成带批注 Word 修订稿(默认执行,无需用户额外要求),告知输出文件路径只输出一份文件:{制度名称}_审核修订版_{YYYYMMDD}.docx。不额外生成独立的"审核意见"Word 文件。
若用户明确表示不需要 Word 文件,跳过第 6 步。
Word 批注生成流程
当用户要求生成带批注的 Word 文件时,执行以下 Python 脚本。 效果说明(与图片一致):w:del:原文对应片段显示为删除线w:ins:修订内容紧接其后显示为下划线w:comment:右侧气泡批注,说明修改原因和类型- 在 Word"审阅"面板可逐条接受或拒绝每处修订;接受全部后文档即为正式修订版
Step 1:检查并安装依赖
import subprocess, sys
try:
from docx import Document
except ImportError:
subprocess.check_call([sys.executable, "-m", "pip", "install", "python-docx"])
from docx import Document
Step 2:核心工具函数
from docx import Document
from docx.oxml.ns import qn
from docx.oxml import OxmlElement
from docx.shared import Pt, RGBColor
import datetime, copy
REVISION_AUTHOR = "审核意见"
REVISION_DATE = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ")
── 全局修订/批注 ID 计数器 ──────────────────────────────
_rev_id = 0
_cmt_id = 0
def _next_rev_id():
global _rev_id; _rev_id += 1; return str(_rev_id)
def _next_cmt_id():
global _cmt_id; _cmt_id += 1; return str(_cmt_id)
── 基础 XML 构造 ────────────────────────────────────────
def _copy_rpr(source_run_elem):
"""复制原 run 的 w:rPr,保留字体/字号/加粗等格式"""
if source_run_elem is not None:
rpr = source_run_elem.find(qn("w:rPr"))
if rpr is not None:
return copy.deepcopy(rpr)
return OxmlElement("w:rPr")
def _make_del(text, source_run_elem=None):
"""w:del > w:r > w:delText (显示为删除线)"""
el = OxmlElement("w:del")
el.set(qn("w:id"), _next_rev_id())
el.set(qn("w:author"), REVISION_AUTHOR)
el.set(qn("w:date"), REVISION_DATE)
r = OxmlElement("w:r")
r.append(_copy_rpr(source_run_elem))
dt = OxmlElement("w:delText")
dt.text = text
dt.set("{http://www.w3.org/XML/1998/namespace}space", "preserve")
r.append(dt)
el.append(r)
return el
def _make_ins(text, source_run_elem=None):
"""w:ins > w:r > w:t (显示为下划线新增)"""
el = OxmlElement("w:ins")
el.set(qn("w:id"), _next_rev_id())
el.set(qn("w:author"), REVISION_AUTHOR)
el.set(qn("w:date"), REVISION_DATE)
r = OxmlElement("w:r")
r.append(_copy_rpr(source_run_elem))
t = OxmlElement("w:t")
t.text = text
t.set("{http://www.w3.org/XML/1998/namespace}space", "preserve")
r.append(t)
el.append(r)
return el
── Word 原生气泡批注 ────────────────────────────────────
def _ensure_comments_part(doc):
"""确保 document 存在 comments.xml part,返回其根元素"""
from docx.opc.part import Part
import docx.opc.packuri as packuri
rel_type = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments"
try:
cp = doc.part.part_related_by(rel_type)
return cp._element
except Exception:
xml = (
b'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
b'<w:comments xmlns:w="http://schemas.openxmlformats.org/'
b'wordprocessingml/2006/main"/>'
)
cp = Part(
packuri.PackURI("/word/comments.xml"),
"application/vnd.openxmlformats-officedocument"
".wordprocessingml.comments+xml",
xml,
doc.part.package,
)
doc.part.relate_to(cp, rel_type)
return cp._element
def _add_bubble_comment(doc, p_elem, ins_node, comment_text):
"""
在 ins_node 对应的文字上挂 Word 气泡批注。
结构:commentRangeStart … ins_node … commentRangeEnd + commentReference
"""
cid = _next_cmt_id()
comments_root = _ensure_comments_part(doc)
# 写入 w:comment 定义
cmt = OxmlElement("w:comment")
cmt.set(qn("w:id"), cid)
cmt.set(qn("w:author"), REVISION_AUTHOR)
cmt.set(qn("w:date"), REVISION_DATE)
cmt.set(qn("w:initials"), "审")
cp = OxmlElement("w:p")
cr = OxmlElement("w:r")
ct = OxmlElement("w:t")
ct.text = comment_text
ct.set("{http://www.w3.org/XML/1998/namespace}space", "preserve")
cr.append(ct)
cp.append(cr)
cmt.append(cp)
comments_root.append(cmt)
# commentRangeStart 插在 ins_node 之前
crs = OxmlElement("w:commentRangeStart")
crs.set(qn("w:id"), cid)
ins_node.addprevious(crs)
# commentRangeEnd + commentReference 插在 ins_node 之后
cre = OxmlElement("w:commentRangeEnd")
cre.set(qn("w:id"), cid)
ins_node.addnext(cre)
ref_r = OxmlElement("w:r")
ref = OxmlElement("w:commentReference")
ref.set(qn("w:id"), cid)
ref_r.append(ref)
cre.addnext(ref_r)
── 段落级修订替换 ───────────────────────────────────────
def _apply_revision(para, original, revised, comment_text):
"""
在段落中精确匹配 original,替换为 w:del + w:ins,
并在 w:ins 上挂气泡批注。保留原 run 格式。
返回 True 表示成功。
"""
run_texts = [r.text for r in para.runs]
combined = "".join(run_texts)
start = combined.find(original)
if start == -1:
return False
end = start + len(original)
# 定位跨越的 run 范围
pos, first_i = 0, None
last_i = None
for i, rt in enumerate(run_texts):
if first_i is None and pos + len(rt) > start:
first_i = i
if pos + len(rt) >= end:
last_i = i
break
pos += len(rt)
if first_i is None or last_i is None:
return False
runs = para.runs
first_run = runs[first_i]
offset = start - sum(len(run_texts[j]) for j in range(first_i))
suffix = runs[last_i].text[end - sum(len(run_texts[j]) for j in range(last_i)):]
# 截断 first_run 为前缀
first_run.text = first_run.text[:offset]
ref = first_run._r
# 插入 w:del
del_node = _make_del(original, first_run._r)
ref.addnext(del_node)
# 插入 w:ins
ins_node = _make_ins(revised, first_run._r)
del_node.addnext(ins_node)
# 清空被覆盖的中间 run
for i in range(first_i + 1, last_i + 1):
runs[i].text = ""
# 追加后缀 run
if suffix:
sfx = copy.deepcopy(first_run._r)
for te in sfx.findall(qn("w:t")):
sfx.remove(te)
tn = OxmlElement("w:t")
tn.text = suffix
tn.set("{http://www.w3.org/XML/1998/namespace}space", "preserve")
sfx.append(tn)
ins_node.addnext(sfx)
# 挂气泡批注到 ins_node
_add_bubble_comment(doc_ref[0], para._p, ins_node, comment_text)
return True
generate_annotated_docx 调用时注入 doc 引用
doc_ref = [None]
── 主函数 ───────────────────────────────────────────────
def generate_annotated_docx(source_path, revisions, output_path):
"""
source_path : 原始 .docx 路径;传 None 则新建空文档
revisions : 见 Step 4 数据结构
output_path : 输出路径
"""
global _rev_id, _cmt_id
_rev_id = 0
_cmt_id = 0
doc = Document(source_path) if source_path else Document()
doc_ref[0] = doc
for rev in revisions:
original = rev["original"]
revised = rev["revised"]
rev_type = rev.get("type", "建议补充")
issue = rev.get("issue", "")
suggestion = rev.get("suggestion", "")
comment_txt = f"【{rev_type}】{issue}\n建议:{suggestion}"
for para in doc.paragraphs:
if original in para.text:
_apply_revision(para, original, revised, comment_txt)
break
doc.save(output_path)
return output_path
Step 3:调用示例
import datetime
revisions = [
{
"original": "应同步告知或抄送国际运营部门",
"revised": "须通过OA系统向国际运营部门提交正式审批申请,经国际运营部门审批通过后方可执行",
"type": "控制缺口",
"issue": "仅抄送不构成有效审批节点,无法留痕",
"suggestion": "升级为OA系统正式审批节点,由国际运营部门审批把关并留存记录"
},
{
"original": "特制定本细则",
"revised": "特制定本细则(版本号:V1.0,解释权归证券结算部,如有修订另行通知)",
"type": "建议补充",
"issue": "制度缺少版本号与解释权归属",
"suggestion": "在总则或封面补充版本号、解释权归属部门及修订记录表"
},
]
output_path = f"证券股票转出转入流程_审核修订版_{datetime.date.today().strftime('%Y%m%d')}.docx"
result = generate_annotated_docx(
source_path="原始制度文件.docx", # 传入原始文件路径;无原始文件传 None
revisions=revisions,
output_path=output_path
)
print(f"文件已生成:{result}")
Step 4:数据构造规则
| 字段 | 来源 | |---|---| |original | 审核意见"原文内容"列的精确原文片段(需能在原文中精确匹配) |
| revised | 直接可用的替换文字 |
| type | 控制缺口 / 执行断点 / 职责不清 / 归档缺失 / 建议补充 |
| issue | 问题说明(简短) |
| suggestion | 修改建议说明 |
若某处是纯新增内容(原文无对应片段),将 original 设为该段落末尾的标志性文字,revised 设为该文字 + 新增内容。
Step 5:输出规范
- 文件名格式:
{制度名称}_审核修订版_{YYYYMMDD}.docx - 保存至工作区根目录
- 生成后告知用户完整文件路径