OpenClaw技能开发入门:为Qwen3.5-9B编写自定义文件转换插件

发布时间:2026/5/28 14:16:27

OpenClaw技能开发入门:为Qwen3.5-9B编写自定义文件转换插件 OpenClaw技能开发入门为Qwen3.5-9B编写自定义文件转换插件1. 为什么需要自定义文件转换技能上周我需要整理一批技术白皮书PDF手动复制粘贴了3个小时后突然意识到这不正是AI该干的活吗OpenClaw的自动化能力理论上可以解决这个问题但现有的PDF处理技能要么功能单一要么不符合我的工作流。于是决定自己动手开发一个PDF转Markdown的定制技能。与通用方案相比自定义技能的优势很明显格式控制权可以按需保留或忽略PDF中的特定元素如页眉页脚工作流整合转换后直接触发后续的Markdown处理流程模型适配针对Qwen3.5-9B的视觉-语言能力优化提示词2. 开发环境准备2.1 基础工具链配置我的开发环境是macOS VS Code关键组件如下# 确认Node.js版本需要18 node -v # v20.12.2 # 安装OpenClaw CLI工具 npm install -g openclaw/cli # 初始化技能开发目录 mkdir pdf-to-md-skill cd pdf-to-md-skill claw init --templateskill-js这个模板会自动生成以下关键文件package.json技能元数据和依赖index.js主逻辑入口config/schema.json技能配置规范test/单元测试目录2.2 本地模型服务连接由于要调用Qwen3.5-9B的视觉理解能力需要先配置模型端点。在~/.openclaw/openclaw.json中添加{ models: { providers: { local-qwen: { baseUrl: http://localhost:8080, api: openai-completions, models: [ { id: qwen3.5-9b, name: Local Qwen3.5-9B, contextWindow: 32768 } ] } } } }测试连接是否正常claw models list # 应能看到qwen3.5-9b出现在可用模型列表3. 技能核心逻辑开发3.1 文件处理模块设计PDF转Markdown的难点在于保持文档结构。我的解决方案是分两步处理视觉解析阶段用Qwen3.5的多模态能力提取文本和布局信息语义重构阶段根据内容类型标题、代码块等生成Markdown语法创建src/pdfParser.jsconst { execSync } require(child_process); const fs require(fs); class PDFParser { constructor(modelClient) { this.model modelClient; } async extractPages(pdfPath) { // 使用pdfimages提取页面图片 const tempDir ./temp_${Date.now()}; fs.mkdirSync(tempDir); try { execSync(pdfimages -png ${pdfPath} ${tempDir}/page); return fs.readdirSync(tempDir) .filter(f f.endsWith(.png)) .map(f ${tempDir}/${f}); } catch (err) { throw new Error(PDF提取失败: ${err.message}); } } async parseToMarkdown(pdfPath) { const pages await this.extractPages(pdfPath); let fullText ; for (const pageImg of pages) { const prompt [ 你是一个专业的技术文档转换器。, 请将图片中的内容转换为结构化的Markdown注意, - 保留代码块和数学公式, - 将表格转换为Markdown表格语法, - 忽略页眉页脚, 直接输出转换结果不要额外说明。 ].join(\n); const response await this.model.createCompletion({ model: qwen3.5-9b, messages: [ { role: user, content: [ { type: text, text: prompt }, { type: image_url, image_url: { url: file://${pageImg} } } ] } ], max_tokens: 4096 }); fullText response.choices[0].message.content \n\n; } return fullText; } }3.2 主技能逻辑封装在index.js中实现技能接口const { BaseSkill } require(openclaw/sdk); const PDFParser require(./src/pdfParser); module.exports class PDFToMarkdownSkill extends BaseSkill { constructor() { super({ id: pdf-to-md, name: PDF转Markdown, description: 将PDF技术文档转换为结构化的Markdown格式, version: 0.1.0 }); } async setup() { this.model this.runtime.models.get(qwen3.5-9b); this.parser new PDFParser(this.model); } async execute({ input }, context) { if (!input.pdfPath) { throw new Error(缺少pdfPath参数); } try { const startTime Date.now(); const markdown await this.parser.parseToMarkdown(input.pdfPath); return { success: true, output: { markdown, elapsed: (Date.now() - startTime) / 1000 } }; } catch (err) { this.logger.error(转换失败: ${err.message}); throw err; } } };4. 异常处理与性能优化4.1 常见错误处理在实际测试中遇到几个典型问题大文件内存溢出通过分页处理和流式写入解决模型响应格式不稳定添加输出格式校验中文编码问题强制指定UTF-8编码更新后的错误处理模块async parseToMarkdown(pdfPath) { // ...原有代码... try { for (const pageImg of pages) { // 添加重试机制 let retries 3; while (retries 0) { try { const response await this.model.createCompletion({...}); const content response.choices[0].message.content; // 校验输出是否包含Markdown特征 if (!content.includes(#) !content.includes()) { throw new Error(模型返回结果不符合Markdown格式); } fullText content; break; } catch (err) { retries--; if (retries 0) throw err; await new Promise(res setTimeout(res, 1000)); } } } } finally { // 清理临时文件 fs.rmSync(tempDir, { recursive: true }); } }4.2 性能优化技巧通过以下改进将处理速度提升了3倍并行处理页面使用Promise.all处理非顺序依赖的页面缓存模型响应对相似页面结构复用之前的转换结果预处理PDF先用pdftocairo优化图像质量async parseToMarkdown(pdfPath) { const pages await this.extractPages(pdfPath); const pagePromises pages.map(pageImg this.processPage(pageImg).catch(err { this.logger.warn(页面${pageImg}处理失败: ${err.message}); return ; }) ); const results await Promise.all(pagePromises); return results.join(\n\n); }5. 技能测试与部署5.1 本地测试方法创建测试脚本test/test.jsconst skill require(../); const { mockRuntime } require(openclaw/sdk-test); describe(PDFToMarkdownSkill, () { let runtime; beforeAll(async () { runtime mockRuntime({ models: { get: jest.fn().mockReturnValue({ createCompletion: jest.fn().mockResolvedValue({ choices: [{ message: { content: # Mock Markdown\n\nTest content } }] }) }) } }); await skill.setup.call({ runtime, logger: console }); }); it(应正确处理PDF转换, async () { const result await skill.execute({ input: { pdfPath: ./test/sample.pdf } }, {}); expect(result.success).toBeTruthy(); expect(result.output.markdown).toContain(#); }); });运行测试npm test5.2 生产环境部署将技能打包发布到ClawHubclawhub login clawhub publish --name pdf-to-md --version 0.1.0在OpenClaw中安装使用claw skills add yourname/pdf-to-md6. 实际应用效果在我的M2 MacBook Pro上测试一个15页的技术文档原始方案手动复制粘贴需要约45分钟自定义技能3分钟完成转换人工校验调整约5分钟准确率正文内容95%以上正确表格需要少量手动调整最惊喜的是代码块的保留效果——Qwen3.5-9B能准确识别不同编程语言的代码段并添加正确的Markdown标记。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。

相关新闻