hookehuyr

docs(parse): 文档解析审核流程完善

- 整理审核流程文档并对齐字段命名与目录规范
- 补充审核模板修复重点与解析策略改进点

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
......@@ -77,6 +77,7 @@ pnpm lint
-**写入稳态化** - 结构化插入、重复检测与 dry-run 预览已接入
-**输出结构补齐** - 解析输出 JSON 结构与稳定 form_sn 规则已明确
-**审计与摘要** - 解析摘要与审计日志输出已接入
-**审核流程规划** - 整理审核流程方案并对齐字段与目录规范
### 测试与验证
-**回归测试** - pnpm test 通过,pnpm lint 存在 30 个现存警告
......
## [2026-02-14] - 文档解析审核方案整理
### 优化
- 整理审核流程文档并对齐字段命名与目录规范
- 补充审核模板修复重点与解析策略改进点
---
**详细信息**
- **影响文件**: docs/tasks/plan/改进文档解析工具-添加审核流程.md, README.md
- **技术栈**: 文档维护
- **测试状态**: 未运行(仅文档更新)
- **备注**: 明确审核流程现状与修复范围
---
## [2026-02-14] - markitdown 文档解析服务集成
### 新增
- 集成 markitdown CLI 工具支持 PDF/DOCX 文档解析
- 创建 parse-config.js 统一配置管理模块
- 添加配置状态检查命令 `npm run parse:docs:status`
- 创建 .env.example 环境变量模板
- 新增 scripts/README.md 使用指南
### 优化
- MD/TXT 文件直接读取,无需 markitdown 处理
- PDF/DOCX 文件通过 markitdown CLI 转换
- 添加 markitdown 失败时的本地库回退机制
---
**详细信息**
- **影响文件**: scripts/parse-config.js, scripts/parse-docs.js, scripts/.env.example, scripts/README.md, package.json
- **技术栈**: Node.js, Python (markitdown v0.1.4), child_process
- **测试状态**: 已通过(MD 文件解析验证)
- **备注**: markitdown CLI 已安装,配置已启用 (type: 'cli')
---
## [2026-02-14] - 空表单回退规则补齐
### 修复
......
......@@ -4,3 +4,4 @@
{"action":"update","backup_file":"/Users/huyirui/program/itomix/git/manulife-weapp/docs/parsed-backup/plan-templates.backup.1771077989896.js","target_file":"/Users/huyirui/program/itomix/git/manulife-weapp/src/config/plan-templates.js","form_sn_list":["savings-readme-a4296d1f"],"at":"2026-02-14T14:06:29.896Z"}
{"action":"update","backup_file":"/Users/huyirui/program/itomix/git/manulife-weapp/docs/parsed-backup/plan-templates.backup.1771078080604.js","target_file":"/Users/huyirui/program/itomix/git/manulife-weapp/src/config/plan-templates.js","form_sn_list":["savings-readme-a4296d1f"],"at":"2026-02-14T14:08:00.605Z"}
{"action":"update","backup_file":"/Users/huyirui/program/itomix/git/manulife-weapp/docs/parsed-backup/plan-templates.backup.1771078351660.js","target_file":"/Users/huyirui/program/itomix/git/manulife-weapp/src/config/plan-templates.js","form_sn_list":["savings-2-148b3acd"],"at":"2026-02-14T14:12:31.660Z"}
{"action":"update","backup_file":"/Users/huyirui/program/itomix/git/manulife-weapp/docs/parsed-backup/plan-templates.backup.1771080130974.js","target_file":"/Users/huyirui/program/itomix/git/manulife-weapp/src/config/plan-templates.js","form_sn_list":["savings-2-55bcffc2"],"at":"2026-02-14T14:42:10.974Z"}
......
......@@ -5,3 +5,9 @@
{"at":"2026-02-14T14:06:29.897Z","mode":"single","options":{"dry_run":false},"summary":{"total":1,"success":1,"failed":0,"duration_ms":2,"success_list":[{"form_sn":"savings-readme-a4296d1f","product_name":"README","file":"README.md"}],"failed_list":[]},"change_summary":{"ok":true,"dry_run":false,"updated_count":1,"form_sn_list":["savings-readme-a4296d1f"],"conflicts":[],"reason":null}}
{"at":"2026-02-14T14:08:00.605Z","mode":"single","options":{"dry_run":false},"summary":{"total":1,"success":1,"failed":0,"duration_ms":2,"success_list":[{"form_sn":"savings-readme-a4296d1f","product_name":"README","file":"README.md"}],"failed_list":[]},"change_summary":{"ok":true,"dry_run":false,"updated_count":1,"form_sn_list":["savings-readme-a4296d1f"],"conflicts":[],"reason":null}}
{"at":"2026-02-14T14:12:31.661Z","mode":"single","options":{"dry_run":false},"summary":{"total":1,"success":1,"failed":0,"duration_ms":1,"success_list":[{"form_sn":"savings-2-148b3acd","product_name":"测试计划书-智享未来2","file":"测试计划书-智享未来2.md"}],"failed_list":[]},"change_summary":{"ok":true,"dry_run":false,"updated_count":1,"form_sn_list":["savings-2-148b3acd"],"conflicts":[],"reason":null}}
{"at":"2026-02-14T14:34:05.582Z","mode":"single","options":{"dry_run":false},"summary":{"total":1,"success":1,"failed":0,"duration_ms":2,"success_list":[{"form_sn":"savings-2-148b3acd","product_name":"测试计划书-智享未来2","file":"测试计划书-智享未来2.md"}],"failed_list":[]},"change_summary":{"ok":false,"dry_run":false,"updated_count":0,"form_sn_list":[],"conflicts":["savings-2-148b3acd"],"reason":"conflict"}}
{"at":"2026-02-14T14:34:22.438Z","mode":"single","options":{"dry_run":false},"summary":{"total":1,"success":1,"failed":0,"duration_ms":2,"success_list":[{"form_sn":"savings-2-148b3acd","product_name":"测试计划书-智享未来2","file":"测试计划书-智享未来2.md"}],"failed_list":[]},"change_summary":{"ok":false,"dry_run":false,"updated_count":0,"form_sn_list":[],"conflicts":["savings-2-148b3acd"],"reason":"conflict"}}
{"at":"2026-02-14T14:34:50.292Z","mode":"single","options":{"dry_run":false},"summary":{"total":1,"success":1,"failed":0,"duration_ms":1153,"success_list":[{"form_sn":"savings-2-148b3acd","product_name":"测试计划书-智享未来2","file":"测试计划书-智享未来2.md"}],"failed_list":[]},"change_summary":{"ok":false,"dry_run":false,"updated_count":0,"form_sn_list":[],"conflicts":["savings-2-148b3acd"],"reason":"conflict"}}
{"at":"2026-02-14T14:35:12.489Z","mode":"single","options":{"dry_run":false},"summary":{"total":1,"success":1,"failed":0,"duration_ms":6,"success_list":[{"form_sn":"savings-2-148b3acd","product_name":"测试计划书-智享未来2","file":"测试计划书-智享未来2.md"}],"failed_list":[]},"change_summary":{"ok":false,"dry_run":false,"updated_count":0,"form_sn_list":[],"conflicts":["savings-2-148b3acd"],"reason":"conflict"}}
{"at":"2026-02-14T14:35:32.726Z","mode":"single","options":{"dry_run":false},"summary":{"total":1,"success":1,"failed":0,"duration_ms":2,"success_list":[{"form_sn":"savings-2-148b3acd","product_name":"测试计划书-智享未来2","file":"测试计划书-智享未来2.md"}],"failed_list":[]},"change_summary":{"ok":false,"dry_run":false,"updated_count":0,"form_sn_list":[],"conflicts":["savings-2-148b3acd"],"reason":"conflict"}}
{"at":"2026-02-14T14:42:10.975Z","mode":"single","options":{"dry_run":false},"summary":{"total":1,"success":1,"failed":0,"duration_ms":28,"success_list":[{"form_sn":"savings-2-55bcffc2","product_name":"计划书模版2","file":"计划书模版2.docx"}],"failed_list":[]},"change_summary":{"ok":true,"dry_run":false,"updated_count":1,"form_sn_list":["savings-2-55bcffc2"],"conflicts":[],"reason":null}}
......
This diff is collapsed. Click to expand it.
......@@ -24,16 +24,28 @@
- 需要"人工辅助"的半自动化方式
- 在自动解析和直接生成配置之间增加审核环节
### 现状评审与差距
1. **审核流程已接入但模板不稳定**
- parse-docs.js 已生成待审核文件并阻断写入配置,但审核模板存在重复定义与内容断裂风险
2. **字段命名不一致**
- 现有方案示例使用 name/type,与实际解析配置字段 product_name/product_type 不一致
3. **审核指引不清晰**
- 审核文件的“通过后操作”指向备份文件,未明确 pending/approved 目录治理
4. **解析结果可读性不足**
- 审核模板对 form_schema 与 submit_mapping 预览不足,无法快速确认关键字段
5. **解析方式描述需要更新**
- mammoth 的 Markdown 输出存在局限,复杂表格准确性不足,需要明确替代策略
---
## 解决方案
### 方案设计
采用 **"解析 → 审核 → 生成"** 三步流程,支持多种解析方式:
采用 **"解析 → 审核 → 人工合并"** 三步流程,支持多种解析方式:
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 选择解析方式 │ → │ 生成待审核文件 │ → │ 人工审核后移动
│ 选择解析方式 │ → │ 生成待审核文件 │ → │ 审核通过后合并
│ mammoth/MCP │ │ (markdown) │ │ 到正式配置 │
└─────────────────┘ └─────────────────┘ └─────────────────┘
```
......@@ -52,17 +64,21 @@
- **mammoth**: 快速预览、简单文档、离线使用
- **MCP**: 复杂文档、准确度要求高、有网络连接
#### 注意事项
- mammoth 的 Markdown 输出能力有限,复杂结构建议使用 HTML 输出后再转 Markdown
- 审核文件统一输出到 `docs/parse-audit/pending/`,通过后移动到 `docs/parse-audit/approved/`
### 技术实现
#### 1. 改进extractProductBasicInfo
尝试从多个位置提取产品基本信息
#### 1. 改进基础信息抽取
从多个位置提取产品基本信息(与实际配置字段对齐)
```javascript
// 尝试从文档标题、表格、特定文本模式提取
async function extractProductBasicInfo(content, fileName) {
const info = {
name: '',
type: 'savings', // 默认储蓄型
product_name: '',
product_type: 'savings',
currency: 'USD',
form_sn: generateFormSn(fileName)
}
......@@ -70,7 +86,7 @@ async function extractProductBasicInfo(content, fileName) {
// 策略1: 从文档标题提取
const titleMatch = content.match(/^#\s+(.+)$/m)
if (titleMatch) {
info.name = cleanProductName(titleMatch[1].trim())
info.product_name = cleanProductName(titleMatch[1].trim())
}
// 策略2: 从表格中提取"币种"信息
......@@ -81,9 +97,9 @@ async function extractProductBasicInfo(content, fileName) {
// 策略3: 从表格中提取"产品类型"信息
if (content.includes('重疾') || content.includes('危疾')) {
info.type = 'critical-illness'
info.product_type = 'critical-illness'
} else if (content.includes('人寿')) {
info.type = 'life-insurance'
info.product_type = 'life-insurance'
}
return info
......@@ -110,8 +126,8 @@ async function generateAuditFile(fileName, config, code) {
| 字段 | 提取值 | 需要确认 |
|------|--------|---------|
| 产品名称 | ${config.name || '未提取'} | ✅ 请核对产品名称 |
| 产品类型 | ${config.type || '未提取'} | ✅ 请确认产品类型 |
| 产品名称 | ${config.product_name || '未提取'} | ✅ 请核对产品名称 |
| 产品类型 | ${config.product_type || '未提取'} | ✅ 请确认产品类型 |
| 币种 | ${config.currency || 'USD'} | ✅ 请确认币种 |
| form_sn | \`${config.form_sn || '未生成'}` | 请确认form_sn唯一性 |
......@@ -151,9 +167,9 @@ ${code.submit_mapping || '// 请手动补充'}
### 确认无误
\`\`\`bash
# 1. 移动配置到正式文件
# 1. 移动到 approved 目录
mv docs/parse-audit/pending/${auditFileName} \\
src/config/plan-templates.backup.js
docs/parse-audit/approved/
# 2. 合并到正式配置
# 手动复制或使用工具合并
......@@ -185,28 +201,23 @@ rm docs/parse-audit/pending/${auditFileName}
## 实施计划
### 阶段1: 改进解析逻辑 (30分钟)
- [ ] 改进extractProductBasicInfo函数
- [ ] 添加文档标题提取
- [ ] 添加币种信息提取
- [ ] 添加产品类型推断
- [ ] 测试验证提取效果
### 阶段2: 实现审核文件生成 (20分钟)
- [ ] 实现generateAuditFile函数
- [ ] 创建待审核目录结构
- [ ] 测试生成markdown格式
- [ ] 添加文件路径返回
### 阶段3: 集成到主流程 (10分钟)
- [ ] 更新parse-docs.js主函数
- [ ] 添加成功提示和审核引导
- [ ] 错误处理和日志输出
### 阶段4: 测试验证 (10分钟)
- [ ] 使用实际文档测试
- [ ] 验证生成的审核文件格式
- [ ] 确认目录结构正确
### 阶段1: 修复审核模板与字段对齐
- [ ] 清理 generateAuditFile 重复定义与模板断裂问题
- [ ] 统一字段命名为 product_name/product_type/currency/form_sn
- [ ] 优化审核模板展示 form_schema 与 submit_mapping
### 阶段2: 审核流程治理
- [ ] 确认 pending/approved 目录结构
- [ ] 明确审核通过后的合并指引
- [ ] 补齐审核状态与审核意见模板
### 阶段3: 解析策略补齐
- [ ] 增加标题/币种/类型的启发式补位策略
- [ ] 补齐文档样本验证与失败兜底说明
### 阶段4: 测试验证
- [ ] 使用实际文档回归生成审核文件
- [ ] 校验审核模板完整性与可读性
---
......@@ -234,6 +245,7 @@ rm docs/parse-audit/pending/${auditFileName}
| 提取仍不准确 | 需要大量人工补充 | 提供清晰的标记和默认值 |
| 审核文件过多 | 难以管理 | 定期清理已审核文件 |
| 目录权限问题 | 无法写入文件 | 提前创建目录并检查权限 |
| mammoth 输出限制 | 表格/结构信息丢失 | 使用 HTML 输出后再转 Markdown |
---
......@@ -254,6 +266,6 @@ rm docs/parse-audit/pending/${auditFileName}
---
## 相关文档
- [mamoth使用文档](https://github.com/mwilliamtohman/mammoth)
- [mammoth 使用文档](https://github.com/mwilliamson/mammoth.js)
- [计划书模板配置规范](../../src/config/CLAUDE.md)
- [代码注释规范](~/.claude/rules/code-commenting.md)
......
......@@ -36,6 +36,7 @@
"prepare": "husky",
"parse:docs": "node scripts/parse-docs.js",
"parse:docs:list": "node scripts/parse-docs.js --list",
"parse:docs:status": "node scripts/parse-docs.js --status",
"parse:docs:file": "node scripts/parse-docs.js --file=",
"release": "standard-version"
},
......
# 文档解析服务配置示例
#
# 使用方式:
# 1. 复制此文件为 .env
# 2. 填写你的 API Key
# 3. 运行 npm run parse:docs -- --status 检查配置状态
# ========== markitdown 配置 ==========
# markitdown 服务类型: cli | docker | http | disabled
MARKITDOWN_TYPE=disabled
# markitdown HTTP API 地址(仅当 MARKITDOWN_TYPE=http 时需要)
# MARKITDOWN_URL=http://localhost:8000/convert
# ========== AI 服务配置 ==========
# AI 服务类型: openai | anthropic | openrouter | disabled
AI_SERVICE_TYPE=disabled
# OpenAI 配置
OPENAI_API_KEY=sk-your-openai-api-key-here
OPENAI_BASE_URL=https://api.openai.com/v1
OPENAI_MODEL=gpt-4-turbo
# Anthropic 配置
ANTHROPIC_API_KEY=sk-ant-your-anthropic-api-key-here
ANTHROPIC_BASE_URL=https://api.anthropic.com/v1
ANTHROPIC_MODEL=claude-3-sonnet-20240229
# OpenRouter 配置
OPENROUTER_API_KEY=sk-or-your-openrouter-api-key-here
OPENROUTER_BASE_URL=https://openrouter.ai/api/v1
OPENROUTER_MODEL=anthropic/claude-3-sonnet
# 文档解析工具使用指南
## 功能概述
文档解析工具用于将保险产品文档(PDF、DOCX)自动解析为计划书配置,支持智能识别产品类型、币种、缴费年期等信息。
## 快速开始
### 1. 查看待处理文档
```bash
npm run parse:docs:list
```
### 2. 查看配置状态
```bash
npm run parse:docs:status
```
输出示例:
```
🔧 文档解析服务配置状态:
──────────────────────────────────────────────────
📄 markitdown: ❌ 未配置
🤖 AI 服务: ❌ 未配置
──────────────────────────────────────────────────
💡 配置提示:
1. 使用 markitdown: 安装 Python 并运行 "pip install markitdown"
2. 配置 AI 服务: 设置环境变量(.env 文件)
```
### 3. 解析所有文档
```bash
npm run parse:docs
```
### 4. 解析单个文档
```bash
npm run parse:docs:file="产品说明书.pdf"
```
## 配置 AI 服务(可选)
如需启用智能解析功能,请配置以下环境变量:
### 方法 1: 使用 .env 文件
```bash
# 复制示例配置
cp scripts/.env.example scripts/.env
# 编辑 .env 文件,填写 API Key
vim scripts/.env
```
### 方法 2: 使用环境变量
```bash
export AI_SERVICE_TYPE=openai
export OPENAI_API_KEY=sk-your-key-here
npm run parse:docs
```
## 支持的 AI 服务
| 服务 | 说明 | 环境变量 |
|------|------|---------|
| OpenAI | GPT-4/GPT-3.5 | `OPENAI_API_KEY` |
| Anthropic | Claude 3 Sonnet | `ANTHROPIC_API_KEY` |
| OpenRouter | 聚合服务 | `OPENROUTER_API_KEY` |
## 解析流程
1. **文档转换**:将 PDF/DOCX 转换为可读文本
2. **AI 解析**:从文本中提取结构化配置(产品类型、币种、年期等)
3. **生成代码**:生成 `plan-templates.js` 配置代码
4. **更新配置**:自动更新到配置文件
## 当前状态
-**基础功能**:支持 PDF、DOCX 文本提取
-**启发式推断**:根据文件名和内容推断产品类型和币种
-**AI 解析**:待集成 AI 服务(需要配置 API Key)
## 文档位置
待解析文档放在:`docs/to-parse/` 文件夹
支持格式:`.pdf`, `.docx`, `.doc`, `.txt`, `.md`
/**
* 文档解析服务配置
*
* @description 配置 markitdown 和 AI 服务的访问信息
* @module scripts/parse-config
* @author Claude Code
* @created 2026-02-14
*/
/**
* markitdown 服务配置
*
* @description markitdown 是一个将 PDF/DOCX/PPTX/XLSX 等多种格式转换为 Markdown 的工具
*
* 安装方式:
* 1. Python: pip install markitdown
* 2. Docker: docker pull ghcr.io/onurtemiz/markitdown
* 3. HTTP API: 部署 markitdown 服务
*
* 使用方式:
* - CLI: markitdown input.docx output.md
* - Docker: docker run --rm -v $(pwd):/app markitdown input.docx output.md
* - HTTP: POST /convert with file upload
*/
export const MARKITDOWN_CONFIG = {
// markitdown 服务类型
// - 'cli': 使用命令行工具(需要本地安装 Python)
// - 'docker': 使用 Docker 容器
// - 'http': 使用 HTTP API
// - 'disabled': 禁用,使用本地库
type: 'cli',
// CLI 配置
cli: {
command: 'markitdown', // 命令行工具路径
timeout: 30000 // 超时时间(毫秒)
},
// Docker 配置
docker: {
image: 'ghcr.io/onurtemiz/markitdown',
timeout: 30000
},
// HTTP API 配置
http: {
url: process.env.MARKITDOWN_URL || 'http://localhost:8000/convert',
timeout: 30000
}
}
/**
* AI 解析服务配置
*
* @description 配置用于智能解析文档内容的 AI 服务
*
* 支持的 AI 服务:
* - 'openai': OpenAI GPT-4/GPT-3.5
* - 'anthropic': Anthropic Claude
* - 'openrouter': OpenRouter(聚合服务)
* - 'disabled': 禁用 AI 解析
*/
export const AI_SERVICE_CONFIG = {
// AI 服务类型
type: process.env.AI_SERVICE_TYPE || 'disabled',
// OpenAI 配置
openai: {
apiKey: process.env.OPENAI_API_KEY || '',
baseURL: process.env.OPENAI_BASE_URL || 'https://api.openai.com/v1',
model: process.env.OPENAI_MODEL || 'gpt-4-turbo',
timeout: 60000
},
// Anthropic 配置
anthropic: {
apiKey: process.env.ANTHROPIC_API_KEY || '',
baseURL: process.env.ANTHROPIC_BASE_URL || 'https://api.anthropic.com/v1',
model: process.env.ANTHROPIC_MODEL || 'claude-3-sonnet-20240229',
timeout: 60000
},
// OpenRouter 配置
openrouter: {
apiKey: process.env.OPENROUTER_API_KEY || '',
baseURL: process.env.OPENROUTER_BASE_URL || 'https://openrouter.ai/api/v1',
model: process.env.OPENROUTER_MODEL || 'anthropic/claude-3-sonnet',
timeout: 60000
}
}
/**
* 检查 markitdown 服务是否可用
*
* @returns {Promise<boolean>} 是否可用
*/
export async function checkMarkitdownAvailable() {
const { type } = MARKITDOWN_CONFIG
if (type === 'disabled') {
return false
}
if (type === 'cli') {
// 检查命令是否存在
const { exec } = require('child_process')
return new Promise((resolve) => {
exec(`${MARKITDOWN_CONFIG.cli.command} --version`, (error) => {
resolve(!error)
})
})
}
if (type === 'http') {
// 检查 HTTP 服务是否可访问
try {
const response = await fetch(MARKITDOWN_CONFIG.http.url, {
method: 'HEAD',
timeout: 5000
})
return response.ok
} catch {
return false
}
}
if (type === 'docker') {
// 检查 Docker 是否可用
const { exec } = require('child_process')
return new Promise((resolve) => {
exec('docker --version', (error) => {
resolve(!error)
})
})
}
return false
}
/**
* 检查 AI 服务是否配置
*
* @returns {boolean} 是否已配置 API Key
*/
export function checkAIServiceConfigured() {
const { type } = AI_SERVICE_CONFIG
if (type === 'disabled') {
return false
}
if (type === 'openai') {
return !!AI_SERVICE_CONFIG.openai.apiKey
}
if (type === 'anthropic') {
return !!AI_SERVICE_CONFIG.anthropic.apiKey
}
if (type === 'openrouter') {
return !!AI_SERVICE_CONFIG.openrouter.apiKey
}
return false
}
/**
* 打印配置状态
*/
export function printConfigStatus() {
console.log('\n🔧 文档解析服务配置状态:')
console.log('─'.repeat(50))
// markitdown 状态
const markitdownType = MARKITDOWN_CONFIG.type
console.log(`📄 markitdown: ${markitdownType === 'disabled' ? '❌ 未配置' : '✅ ' + markitdownType}`)
// AI 服务状态
const aiType = AI_SERVICE_CONFIG.type
console.log(`🤖 AI 服务: ${aiType === 'disabled' ? '❌ 未配置' : '✅ ' + aiType}`)
if (aiType !== 'disabled') {
const configured = checkAIServiceConfigured()
console.log(` API Key: ${configured ? '✅ 已配置' : '❌ 未配置'}`)
}
console.log('─'.repeat(50))
console.log('')
console.log('💡 配置提示:')
console.log(' 1. 使用 markitdown: 安装 Python 并运行 "pip install markitdown"')
console.log(' 2. 配置 AI 服务: 设置环境变量(.env 文件)')
console.log(' - OPENAI_API_KEY: OpenAI API Key')
console.log(' - ANTHROPIC_API_KEY: Anthropic API Key')
console.log(' - AI_SERVICE_TYPE: openai | anthropic | openrouter')
console.log('')
}
......@@ -365,7 +365,8 @@ export const PLAN_TEMPLATES = {
form_schema: savingsFormSchema,
submit_mapping: savingsSubmitMapping
}
}}
},
}
/**
* 全局功能开关
......