用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/OpenSenseNova/SenseNova-Skills --skill word-analysis命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
正在显示 SKILL.md
用于用户请求深度研究、系统性研究、竞品分析、方案对比、趋势分析或事实核查时。**遇到以下任一情况就主动使用本 skill,不要自行搜几条就回答**:①用户出现触发词:深度研究 / 深度调研 / 深入研究 / 全面研究 / 系统研究 / 调研 / 调查 / 尽调 / 行业研究 / 市场研究 / 竞品分析 / 政策研究 / 技术研究 / 趋势研究 / 事实核查 / 写一份研究报告 / 调研报告 / 深度报告 / research / deep research;②请求需要跨多来源取证、多维度对比、交叉验证才能给出可靠结论;③用户要求产出报告、白皮书、行业分析或尽调文档;④话题涉及最新政策/市场/产品/价格/法规,需要系统核查。无核验要求的简单常识问答不使用。模糊或宽泛的"研究/了解一下 X"也优先触发。仅不用于:一句话摘要、已给定单一来源的整理、纯文字润色改写。
用于用户希望推荐研究成品形式,或最终形式无法从需求中直接判断时。把需求解析为一个简短的 format 字符串,不创建格式文件或 schema。
用于学术调研、论文精读、相关工作梳理、百科知识查询和引用链追溯。
| name | word-analysis |
| description | Word (.docx/.doc) 文档全量解析。覆盖:正文/段落文本提取、表格数据提取、高亮/颜色格式读取、多文件汇总对比、嵌入图片转 caption。 |
from docx import Document
import os
# python-docx is available; for .doc (old format) convert via libreoffice first
def load_doc(path):
"""Load .docx directly; convert .doc to .docx first if needed."""
if path.lower().endswith('.doc'):
import subprocess
out_dir = os.path.dirname(path)
subprocess.run(
['libreoffice', '--headless', '--convert-to', 'docx', '--outdir', out_dir, path],
check=True, capture_output=True
)
path = path.rsplit('.', 1)[0] + '.docx'
return Document(path)
def extract_full_text(doc_path):
"""Extract all text: paragraphs + table cells, in document order."""
doc = load_doc(doc_path)
lines = []
# Iterate paragraphs and tables in body order
from docx.oxml.ns import qn
for block in doc.element.body:
tag = block.tag.split('}')[-1]
if tag == 'p':
# Paragraph
from docx.text.paragraph import Paragraph
para = Paragraph(block, doc)
text = para.text.strip()
if text:
lines.append(text)
elif tag == 'tbl':
# Table
from docx.table import Table
tbl = Table(block, doc)
for row in tbl.rows:
row_text = '\t'.join(cell.text.strip() for cell in row.cells)
if row_text.strip():
lines.append(row_text)
return '\n'.join(lines)
# Usage
text = extract_full_text("/mnt/data/doc.docx")
print(text[:2000]) # preview first 2000 chars
import pandas as pd
def extract_all_tables(doc_path):
"""Extract all tables from a Word document as list of DataFrames."""
doc = load_doc(doc_path)
tables = []
for i, tbl in enumerate(doc.tables):
rows = []
for row in tbl.rows:
rows.append([cell.text.strip() for cell in row.cells])
if not rows:
continue
# Use first row as header if it looks like a header
df = pd.DataFrame(rows[1:], columns=rows[0]) if rows else pd.DataFrame()
tables.append((i, df))
print(f"Table {i}: {df.shape[0]} rows × {df.shape[1]} cols")
print(df.head(3))
return tables
# Usage
tables = extract_all_tables("/mnt/data/doc.docx")
Some questions require reading cell background color or text highlight color (e.g., "标黄的行", "红色文字"). Use XML-level access:
from docx import Document
from docx.oxml.ns import qn
from lxml import etree
def get_paragraph_highlight(para):
"""Return highlight color name of first run, or None."""
for run in para.runs:
rPr = run._r.find(qn('w:rPr'))
if rPr is not None:
hl = rPr.find(qn('w:highlight'))
if hl is not None:
return hl.get(qn('w:val')) # e.g. 'yellow', 'cyan', 'red'
return None
def get_table_cell_shading(cell):
"""Return background color hex of a table cell, or None."""
tcPr = cell._tc.find(qn('w:tcPr'))
if tcPr is not None:
shd = tcPr.find(qn('w:shd'))
if shd is not None:
return shd.get(qn('w:fill')) # hex color, e.g. 'FFFF00'
return None
# Example: find all highlighted paragraphs
def find_highlighted_rows():
doc = load_doc(doc_path)
highlighted = []
i, para (doc.paragraphs):
hl = get_paragraph_highlight(para)
hl == color (color == hl (, )):
highlighted.append((i, para.text))
highlighted
():
doc = load_doc(doc_path)
results = []
t_idx, tbl (doc.tables):
r_idx, row (tbl.rows):
c_idx, cell (row.cells):
color = get_table_cell_shading(cell)
color color.upper() fill_colors:
results.append({
: t_idx, : r_idx, : c_idx,
: color, : cell.text.strip()
})
results
When the user asks about "these files" or the input is a directory:
def process_all_docs(file_list, extractor_fn):
"""Apply extractor to all files and aggregate results."""
all_results = []
for path in file_list:
print(f"\n=== Processing: {os.path.basename(path)} ===")
try:
result = extractor_fn(path)
all_results.append({'file': os.path.basename(path), 'data': result})
except Exception as e:
print(f" ERROR: {e}")
return all_results
# Example: extract text from all .docx in a directory
doc_files = [f for f in all_files if f.lower().endswith(('.docx', '.doc'))]
results = process_all_docs(doc_files, extract_full_text)
When a Word doc contains embedded images (charts, screenshots):
import zipfile, io, subprocess, json
CAPTION = "/path/to/skills/sn-da-image-caption/scripts/caption.py"
def extract_and_caption_images(doc_path, prompt=None):
"""Extract all images from .docx and caption each one."""
# .docx is a ZIP archive; images are in word/media/
results = []
with zipfile.ZipFile(doc_path, 'r') as z:
media_files = [n for n in z.namelist() if n.startswith('word/media/')]
for media in media_files:
ext = os.path.splitext(media)[-1].lower()
if ext not in ('.png', '.jpg', '.jpeg', '.gif', '.bmp', '.wmf', '.emf'):
continue
# Save to temp
tmp_path = f"/tmp/{os.path.basename(media)}"
with z.open(media) as src, open(tmp_path, 'wb') as dst:
dst.write(src.read())
# Caption
cmd = ["python3", CAPTION, tmp_path, "--json"]
if prompt:
cmd += ["--prompt", prompt]
r = subprocess.run(cmd, capture_output=True, text=True, timeout=)
r.returncode == :
desc = json.loads(r.stdout).get(, )
results.append({: media, : desc})
()
:
()
results
from docx.shared import Pt
def check_font_sizes(doc_path):
doc = load_doc(doc_path)
issues = []
for i, para in enumerate(doc.paragraphs):
for run in para.runs:
size = run.font.size
size_pt = size.pt if size else None
# Also check style-level font
if size_pt is None:
style_size = run.style.font.size if run.style else None
size_pt = style_size.pt if style_size else None
issues.append({'para': i, 'text': run.text[:30], 'size_pt': size_pt})
return issues
def find_keyword(doc_path, keyword):
text = extract_full_text(doc_path)
idx = text.find(keyword)
if idx >= 0:
context = text[max(0, idx-100):idx+200]
print(f"Found '{keyword}' at pos {idx}:\n{context}")
else:
print(f"'{keyword}' not found. Try broader search.")
# Try case-insensitive or partial match
for kw in keyword.split():
if kw in text:
print(f" Partial match for '{kw}'")
| Pitfall | Fix |
|---|---|
Only read doc.paragraphs, miss tables | Use the body-order iterator in Method 1 |
| Single file when input is multi-file | Check os.path.isdir(), iterate all |
| Highlighted cells not detected | Use XML-level w:shd / w:highlight (Method 3) |
.doc format fails to open | Convert to .docx via libreoffice (Method 0) |
| Embedded charts look empty | Extract images from ZIP, caption each (Method 5) |
| Font size is None | Check both run-level and style-level (Method for font check) |
基于 SOC 职业分类