ScoutExtract API实战:从文档中智能提取结构化数据的完整指南
1. 项目概述告别繁琐解析一键提取结构化数据作为一名和文档数据打了十几年交道的开发者我太懂那种感觉了财务同事甩过来一堆PDF发票业务部门需要从几百份简历里筛人或者你自己想分析一下手头的合同条款。面对这些非结构化的文档传统做法是什么先找个OCR库把图片转文字然后写一堆正则表达式去“抠”数据最后还得处理各种格式错位、识别错误、特殊字符的“惊喜”。整个过程耗时耗力代码脆弱得像纸糊的换个模板就得重写。为了解决这个痛点我深度体验并整合了ScoutExtract这个工具它核心就一句话用一个API调用从任何文档文本、PDF、图片中提取你定义好的结构化JSON数据。无论你是想自动化处理发票、收据还是解析简历、合同甚至是自定义的报表ScoutExtract试图将整个过程简化为两步1. 告诉它你要什么通过预定义或自定义的Schema2. 把文档丢给它。剩下的比如文本定位、实体识别、类型转换都交给API背后的模型去处理。它返回的不仅是数据还附带了每个字段的置信度评分这为后续的自动化决策如自动入库、转人工审核提供了关键依据。这篇文章我将带你从零开始彻底搞懂如何利用这个API将文档数据提取的复杂度降到最低适合所有被非结构化数据困扰的开发者、数据分析师和产品经理。2. 核心原理与架构设计解析2.1 传统方案为何“费力不讨好”在深入ScoutExtract之前我们必须先理解传统方法的瓶颈这样才能明白新方案的价值所在。传统的文档数据提取通常是一个多步骤的“流水线”作业。第一步是文本获取。对于扫描件或图片你需要集成像Tesseract、PaddleOCR这样的OCR引擎。这一步本身就坑很多图片质量、字体、排版、语言都会影响识别准确率。对于PDF情况更复杂有的是文本型PDF可以直接读取文字有的是扫描型PDF本质是图片需要先判断类型再选择处理路径。第二步是文本解析与数据提取。拿到一堆纯文本后你需要用规则主要是正则表达式去“钓”出你想要的数据。比如找发票号你可能会写类似INVOICE\s*[#:]?\s*([A-Z0-9-])这样的模式。但现实是文档的写法千变万化“Invoice No.”, “Inv.#”, “发票编号”一个正则很难覆盖所有情况。更头疼的是表格数据OCR后格式经常错乱简单的行分割根本不管用。第三步是数据清洗与类型转换。提取出来的文本可能是“$1,234.56”你需要去掉货币符号和千分位分隔符转换成浮点数1234.56。日期可能有“2024-03-15”、“15/03/2024”、“March 15, 2024”等多种格式都需要归一化。这个流程的脆弱性在于任何一个环节出错都会导致最终结果失败。而且这套规则是针对特定文档模板设计的一旦模板变更整个流程就需要调整维护成本极高。2.2 ScoutExtract的“一体化”智能提取逻辑ScoutExtract的设计哲学是端到端的智能提取。它试图将上述多步流水线封装在一个黑盒里对外只暴露一个简单的接口。其核心逻辑可以分解为以下几个层次统一文档预处理层无论你传入的是纯文本、Base64编码的PDF还是Base64编码的图片JPG, PNG等API内部会先进行统一的预处理。对于PDF它会自动判断是文本型还是扫描型并分别采用相应的技术如直接解析或OCR将其转换为统一的中间文本表示。这一步对使用者是透明的你不需要关心文档的具体格式。基于Schema的意图理解层这是与传统方法最大的不同。你不再需要写“如何提取”的规则而是声明“你想提取什么”。通过schema参数你明确地定义了目标数据的结构。例如定义一个invoice的Schema就相当于告诉AI“请在这份文档里找到类似发票号码、日期、供应商、金额列表、总计这些信息。” AI模型会基于对海量文档的预训练知识去理解文档的语义和布局并定位这些信息。多模态信息融合与推理层模型并非单纯进行关键词匹配。它会综合分析文本的语义这个词是什么意思、句法这个词在句子中的角色、版面布局这个数字是不是在“Total:”的右边以及视觉特征在图片中某些信息可能以加粗、放大的形式出现。通过这种多模态的融合分析模型能够更准确地判断“$3,371.86”这个数字对应的是“总金额”而不是某个商品的单价。置信度评估与输出层模型对每个提取出的字段都会计算一个置信度分数0.0到1.0。这个分数综合反映了模型对该字段识别结果的把握程度。高置信度如0.9可能意味着字段位置典型、格式清晰低置信度如0.7可能意味着字段模糊、存在歧义或与训练数据差异较大。这个分数是后续业务流程自动化如自动审核阈值的关键输入。注意虽然ScoutExtract极大地简化了流程但它并非万能。其效果严重依赖于背后AI模型的能力和训练数据。对于极其非标、手写潦草或布局极其复杂的文档效果可能会打折扣。此时置信度分数就是一个重要的“熔断机制”提示你需要人工介入。2.3 预定义Schema与自定义Schema的权衡ScoutExtract提供了预定义Schema如invoice,receipt和自定义Schema两种方式这对应着两种不同的使用场景和成本考量。预定义Schema的优势在于开箱即用。开发团队已经针对这些常见文档类型进行了大量的模型调优和字段定义。你只需要传入schema: invoice就能得到一个结构良好的发票数据对象里面包含了invoice_number、date、vendor、line_items、total等字段。这适用于标准化程度较高的通用场景能最大程度地减少你的开发工作量。自定义Schema则提供了无限的灵活性。当你的业务涉及特定行业的报表、定制化的订单、或是内部特有的文件格式时预定义Schema就无法满足了。此时你需要像定义JSON Schema一样描述你期望的输出结构。例如提取产品页面信息{ product_name: {type: string}, price_usd: {type: number}, in_stock: {type: boolean}, features: { type: array, items: {type: string} } }你需要权衡的是自定义Schema虽然灵活但效果可能不如针对特定类型深度优化的预定义Schema。模型需要根据你的Schema去“泛化”地寻找信息对于它从未见过的字段名或结构初期准确率可能需要通过提供一些示例文档如果API支持微调来提升。3. 从零开始的详细实操指南3.1 环境准备与首次API调用让我们抛开概念直接上手。假设你是一个Python开发者需要处理发票。首先确保你的环境已安装requests库。如果没有通过pip install requests安装。第一步获取API密钥。前往ScoutExtract官网注册账号通常在控制台或设置页面你能找到你的Bearer Token。这个密钥是访问所有API的凭证务必妥善保管不要硬编码在客户端代码中建议使用环境变量。接下来我们完成一个最简单的文本发票提取。我将详细解释请求体中的每一个参数import requests import os # 从环境变量读取API密钥更安全 API_KEY os.getenv(SCOUTEXTRACT_API_KEY) API_ENDPOINT https://api.ramlabs.dev/v1/extract # 准备请求数据 invoice_text INVOICE #2024-0892 Vendor: CloudStack Solutions Inc. Date: March 15, 2024 API Integration 1 $2,500.00 Cloud Hosting 3 $199.00 Subtotal: $3,097.00 Tax (8.875%): $274.86 Total: $3,371.86 payload { document: invoice_text, # 核心参数1待解析的文档内容。这里是纯文本。 schema: invoice # 核心参数2指定数据模式。使用预定义的“发票”模式。 } headers { Authorization: fBearer {API_KEY}, # 认证头格式固定为 Bearer {你的密钥} Content-Type: application/json # 声明请求体为JSON格式 } try: response requests.post(API_ENDPOINT, jsonpayload, headersheaders) response.raise_for_status() # 如果HTTP状态码不是200抛出异常 result response.json() except requests.exceptions.RequestException as e: print(fAPI请求失败: {e}) if hasattr(e, response) and e.response is not None: print(f错误详情: {e.response.text}) exit(1) # 解析并打印结果 if result.get(success): data result[data] print(f✅ 发票解析成功) print(f 发票号: {data[invoice_number][value]}) print(f 日期: {data[date][value]}) print(f 供应商: {data[vendor][value]}) print(f 总金额: ${data[total][value]:.2f}) # 遍历行项目 if line_items in data and data[line_items][value]: print(f 行项目:) for item in data[line_items][value]: print(f - {item.get(description, N/A)}: {item.get(quantity, 1)} x ${item.get(unit_price, 0):.2f}) else: print(f❌ 解析失败: {result.get(error, 未知错误)})关键点解析document字段直接接收字符串文本。这是最直接的用法。schema: invoice告诉API“请用你内置的、最擅长的发票模型来处理这份文档。”返回的data是一个字典键是字段名如invoice_number值是一个包含value和confidence的子字典。这种结构统一且易于程序化处理。务必做好错误处理try-except和状态码检查网络请求和API服务都可能出错。3.2 处理PDF与图片文件实际业务中文档更多是以PDF或图片文件的形式存在。ScoutExtract通过要求你将文件进行Base64编码来支持这些格式。Base64是一种将二进制数据编码成ASCII字符串的方法便于在JSON中传输。以下是处理本地PDF文件的完整示例import base64 import requests import os API_KEY os.getenv(SCOUTEXTRACT_API_KEY) API_ENDPOINT https://api.ramlabs.dev/v1/extract def extract_from_pdf(file_path, schema_typeinvoice): 从PDF文件中提取结构化数据。 Args: file_path (str): PDF文件的本地路径。 schema_type (str): 预定义的模式名称如 invoice, receipt。 Returns: dict: API返回的JSON结果。 # 1. 读取并编码PDF文件 try: with open(file_path, rb) as pdf_file: # 以二进制模式打开 pdf_bytes pdf_file.read() except FileNotFoundError: print(f错误文件 {file_path} 未找到。) return None except IOError as e: print(f读取文件时出错: {e}) return None # 将二进制数据转换为Base64字符串 pdf_b64 base64.b64encode(pdf_bytes).decode(utf-8) # 2. 构建请求负载 payload { document: pdf_b64, # 传入Base64字符串 documentType: pdf, # **关键**必须指定文档类型为pdf schema: schema_type } headers { Authorization: fBearer {API_KEY}, Content-Type: application/json } # 3. 发送请求 try: response requests.post(API_ENDPOINT, jsonpayload, headersheaders, timeout30) # 设置超时 response.raise_for_status() return response.json() except requests.exceptions.Timeout: print(请求超时请检查网络或文件大小。) return None except requests.exceptions.RequestException as e: print(fAPI请求异常: {e}) return None # 使用示例 if __name__ __main__: result extract_from_pdf(path/to/your/invoice.pdf, invoice) if result and result.get(success): # 处理结果数据... total result[data][total][value] print(f提取的总金额为: ${total})处理图片文件如JPG, PNG的流程几乎完全相同唯一的区别在于documentType参数。对于图片你应该将其设置为image。# 假设你有一个收据图片 receipt.jpg with open(receipt.jpg, rb) as img_file: image_b64 base64.b64encode(img_file.read()).decode(utf-8) payload_for_image { document: image_b64, documentType: image, # 指定类型为image schema: receipt # 使用预定义的收据模式 }实操心得文件大小Base64编码会使文件体积增大约33%。对于非常大的PDF如超过10MB需要考虑API的上传限制和网络传输时间。如果可能先对PDF进行优化如压缩图片、移除冗余页面。documentType参数至关重要这个参数是API内部路由的关键。传pdf它会启动PDF解析器传image它会调用OCR引擎不传或传text则按纯文本处理。传错类型会导致解析失败或结果混乱。错误处理网络超时、文件损坏、编码错误都是常见问题。在生产环境中务必用try-except包裹核心代码并记录详细的日志便于排查。3.3 玩转自定义Schema当预定义模式无法满足需求时自定义Schema是你的强大武器。其核心是定义一个JSON对象精确描述你希望输出的数据结构。假设你要从产品描述文本中提取信息import requests import os API_KEY os.getenv(SCOUTEXTRACT_API_KEY) API_ENDPOINT https://api.ramlabs.dev/v1/extract # 1. 定义你的自定义Schema # 这就像你给AI的一张“寻物启事”告诉它要找什么类型的数据叫什么名字。 my_product_schema { product_name: { type: string, # 字段类型字符串 description: The name of the product # 可选字段描述帮助AI理解 }, price_usd: { type: number, # 字段类型数字整数或浮点数 description: The price in US dollars }, in_stock: { type: boolean, # 字段类型布尔值True/False description: Whether the product is currently in stock }, features: { type: array, # 字段类型数组 items: { # 定义数组内元素的类型 type: string }, description: List of key features or specifications }, warranty_months: { type: integer, # 字段类型整数 description: Warranty period in months } } # 2. 准备文档内容 product_description **旗舰级无线耳机 - SoundMax Pro 3000** 体验无与伦比的沉浸式音质。 价格$299.99 库存状态有货 产品特点 - 主动降噪ANC技术消除99%环境噪音 - 长达40小时的超长续航 - IPX7级防水防汗 - 支持蓝牙5.3及多点连接 - 内置语音助手兼容 保修提供24个月2年有限保修。 # 3. 构建请求 payload { document: product_description, # 直接将自定义Schema字典作为schema参数的值 schema: my_product_schema } headers {Authorization: fBearer {API_KEY}, Content-Type: application/json} # 4. 发送请求并处理结果 response requests.post(API_ENDPOINT, jsonpayload, headersheaders) result response.json() if result.get(success): data result[data] print(自定义Schema提取结果) for field, info in data.items(): confidence info.get(confidence, 0) value info.get(value, N/A) print(f {field}: {value} (置信度: {confidence:.2f})) # 根据置信度做业务逻辑 if confidence 0.8: print(f ⚠️ 字段{field}置信度较低建议人工复核。)自定义Schema的设计技巧类型匹配准确使用string,number,integer,boolean,array,object等类型。这能帮助AI进行正确的数据转换例如将“$299.99”转为数字299.99。字段命名使用清晰、语义化的英文命名如product_name而非pn这有助于AI理解字段含义。描述可选但有益description字段不是必须的但提供清晰的描述如“客户的全名”可以作为额外的提示信息在字段含义模糊时辅助模型判断。嵌套结构对于复杂数据你可以定义嵌套的object类型。例如一个customer字段可以是包含name,email,address子字段的对象。测试与迭代自定义Schema的效果需要验证。准备一批代表性的文档进行测试观察提取结果。如果某个字段识别不准可以尝试调整字段名、描述或者考虑是否文档中的信息本身就存在歧义。4. 置信度驱动的自动化策略与错误处理ScoutExtract返回的置信度分数不是摆设它是连接AI提取与可靠业务自动化之间的桥梁。盲目相信所有提取结果会导致错误数据入库而完全依赖人工审核又失去了自动化的意义。一个健壮的系统必须基于置信度建立分级处理策略。4.1 理解置信度分数的含义置信度是一个介于0.0到1.0之间的浮点数通常由模型内部计算得出综合了文本匹配度、语义相关性、位置逻辑等多个因素。高置信度例如 0.9模型非常确定。通常对应格式标准、位置明确、语义清晰的字段。例如发票上明确标有“Total: $100.00”的总金额。中置信度例如 0.7 - 0.9模型比较确定但存在一些不确定性。可能因为格式略有差异、有干扰信息、或OCR识别存在轻微模糊。例如日期“2024-03-15”被识别为“2024-03-15”但周围有其他数字干扰。低置信度例如 0.7模型不确定。可能因为字段缺失、格式极其非标、手写难以辨认、或与Schema定义严重不符。例如试图从一个没有明确价格标签的段落中提取价格。4.2 实现分级自动化处理流程基于置信度我们可以设计一个简单的规则引擎将提取结果分流到不同的处理流程def process_extraction_result(api_result): 根据置信度处理API提取结果。 Args: api_result (dict): ScoutExtract API返回的完整结果字典。 Returns: dict: 处理状态和相关信息。 if not api_result.get(success): return {status: api_error, message: api_result.get(error)} data api_result[data] all_fields_processed True needs_review [] needs_human [] for field_name, field_info in data.items(): value field_info.get(value) confidence field_info.get(confidence, 0) # 默认置信度为0 # 策略1高置信度 - 自动入库 if confidence 0.9: success save_to_database_automatically(field_name, value) if not success: # 即使置信度高但入库失败也转人工 needs_human.append({field: field_name, value: value, reason: db_fail, confidence: confidence}) # 策略2中置信度 - 加入审核队列 elif confidence 0.7: # 可以在这里加入一些简单的规则验证比如金额是否为负数日期是否在未来等 if is_plausible(field_name, value): needs_review.append({field: field_name, value: value, confidence: confidence}) else: # 规则验证失败置信度虽中但直接转人工 needs_human.append({field: field_name, value: value, reason: validation_fail, confidence: confidence}) # 策略3低置信度或缺失 - 转人工处理 else: needs_human.append({field: field_name, value: value, reason: low_confidence, confidence: confidence}) all_fields_processed False # 后续操作 if needs_review: send_to_review_queue(needs_review) # 发送到内部审核系统 if needs_human: create_human_task(needs_human) # 创建人工处理工单 return { status: processed_with_review if needs_review or needs_human else fully_automated, auto_saved: all_fields_processed and not needs_human, review_count: len(needs_review), human_count: len(needs_human) } def is_plausible(field_name, value): 简单的合理性校验规则 if field_name total and isinstance(value, (int, float)): return value 0 # 总金额应为正数 if field_name date and isinstance(value, str): # 简单检查是否为近似日期格式实际应用应用更复杂的解析 return - in value or / in value return True # 默认通过 def save_to_database_automatically(field_name, value): 模拟自动入库函数 # 这里实现你的数据库插入逻辑 print(f[自动入库] {field_name}: {value}) return True # 假设成功 def send_to_review_queue(items): 模拟发送到审核队列 print(f[待审核] 有 {len(items)} 个字段需要人工复核) for item in items: print(f - {item[field]}: {item[value]} (置信度: {item[confidence]:.2f})) def create_human_task(items): 模拟创建人工处理任务 print(f[转人工] 有 {len(items)} 个字段需要人工处理) for item in items: print(f - {item[field]}: {item[value]} (原因: {item[reason]}))这个流程确保了高置信度数据直接产生价值无需人工干预自动化流程畅通。中置信度数据得到快速复核可能只需在管理后台点一下“确认”效率远高于从头处理。低置信度数据和异常情况被有效捕获避免错误数据污染系统同时将最难的部分交给人类专家。4.3 常见错误排查与API调优即使有了完善的策略在实际集成中你仍可能遇到问题。下面是一个常见问题排查表问题现象可能原因排查步骤与解决方案HTTP 401 未授权错误API密钥错误、过期或未提供。1. 检查Authorization头格式是否为Bearer YOUR_KEY。2. 登录控制台确认密钥有效且未过期。3. 确保密钥没有暴露在客户端代码中。HTTP 400 错误请求请求体格式错误、缺少必要参数、文档格式不支持。1. 检查Content-Type: application/json。2. 确认document和schema参数已提供且格式正确。3. 对于文件确认documentTypepdf/image设置正确。4. 检查Base64编码是否正确应是字符串无换行。HTTP 413 请求实体过大上传的文档Base64后超过API大小限制。1. 压缩PDF中的图片质量。2. 如果文档页数多考虑分页提取。3. 联系服务商确认具体限制。HTTP 429 请求过多超出速率限制RPM或月度调用限额。1. 在代码中增加请求间隔如time.sleep(0.1)。2. 升级API套餐以获得更高限额。3. 检查是否有循环错误导致无限重试。提取结果为空或字段缺失文档质量差、Schema不匹配、文档语言不支持。1. 检查原始文档内容是否清晰可读。2. 确认使用的预定义Schema与文档类型匹配别用receipt解析简历。3. 对于自定义Schema检查字段定义是否合理尝试提供description。4. 如果是非英文文档确认API是否支持该语言。置信度普遍偏低文档布局复杂、手写体、或模型对该类文档训练不足。1. 尝试使用documentType明确指定类型。2. 如果使用自定义Schema考虑简化字段或提供1-2个示例文档如果API支持few-shot learning。3. 作为备选方案可以结合传统OCR规则作为兜底。类型转换错误如数字提取为字符串Schema中字段类型定义与文档内容不匹配。1. 在自定义Schema中明确指定type: number或type: integer。2. 检查文档中该字段是否包含非数字字符如“USD 100”可能需要预处理或更复杂的模型。API调优建议批量处理如果需要处理大量文档查看API是否支持批量请求这比循环调用单次接口更高效。重试机制对于网络超时5xx错误或速率限制429错误实现指数退避的重试机制。缓存结果对于相同的文档内容可以考虑缓存提取结果避免重复调用产生费用。监控与告警监控API调用的成功率、平均响应时间以及低置信度结果的比例。当异常比例升高时触发告警。5. 进阶应用场景与成本考量5.1 构建端到端的自动化流水线将ScoutExtract API嵌入到一个完整的业务流水线中能最大化其价值。设想一个自动化发票处理的流程文件捕获通过邮箱监听、文件上传接口或扫描仪FTP获取原始发票文件PDF/图片。预处理对文件进行简单预处理如统一格式、压缩如果需要、安全扫描。调用ScoutExtract API使用invoiceschema 提取结构化数据。置信度分流根据上一章的策略高置信度数据进入步骤5中低置信度进入人工审核台。数据验证与丰富对提取的数据进行业务逻辑验证如校验供应商是否在系统中、金额是否在预算内并可能调用其他API如工商信息查询丰富数据。集成与推送将验证通过的数据写入财务系统如SAP、用友、数据库或触发审批工作流。人工审核台为需要复核的条目提供一个简洁的Web界面展示原始文档、AI提取结果和置信度供操作员快速修正或确认。在这个流水线中ScoutExtract承担了最核心且最耗时的“信息抽取”环节将人类从重复的视觉查找和键盘录入中解放出来。5.2 成本分析与优化策略ScoutExtract采用按次计费的模式理解其定价有助于控制成本。免费层25次/月非常适合原型验证、初期测试和极低流量场景。Starter$49/月1000次单价约$0.049/次。适合初创项目或低频内部工具。Pro$199/月5000次单价约$0.0398/次。适合有一定规模的生产应用。Scale$499/月25000次单价约$0.01996/次。适合文档处理量大的企业。成本优化建议预处理过滤在调用付费API前先通过简单的规则如文件大小、类型、关键词匹配过滤掉明显不符合要求的文档避免无谓的调用。缓存策略对于完全相同的文档内容可通过MD5等哈希值判断直接返回缓存的结果无需重复调用。异步与队列对于非实时任务将文档放入处理队列在业务低峰期如夜间集中处理避免因突发流量导致升级到更高套餐。结果抽样审计定期对高置信度自动入库的结果进行抽样人工审计确保模型效果没有漂移。如果发现准确率下降及时调整置信度阈值或重新评估Schema。结合传统方法对于格式极其固定、简单的文档如某种特定模板的表格可以保留一套轻量级的正则表达式方案作为首选仅在传统方案失败时再fallback到AI提取。这种混合策略能在保证效果的同时降低成本。5.3 与其他方案的对比与选型思考ScoutExtract并非唯一选择。下表将其与几种常见方案进行对比帮助你在具体场景中做出技术选型。方案优点缺点适用场景ScoutExtract类AI API开发快维护成本低泛化能力强能处理非标文档提供置信度。持续使用成本对极端非标或手写文档效果可能不稳定数据隐私需考量API调用。文档类型多样、格式变化、追求快速上线和降低长期维护成本的项目。自研OCR规则引擎前期单次成本可能较低数据完全私有对固定模板优化后精度可极高。开发周期长维护成本高每变一个模板就要改代码泛化能力差难以处理复杂布局。文档格式极其固定、数量巨大、对数据隐私要求极高且有能力投入研发团队的场景。通用大模型提示工程(如GPT-4V)极其灵活无需定义严格Schema通过自然语言指令即可提取。成本极高尤其是图片响应慢输出格式不稳定需要复杂的后处理。探索性、研究性或对格式要求极其灵活、愿意为顶级灵活性支付高额成本的场景。传统PDF解析库(如PyPDF2)免费速度快对文本型PDF直接提取文本简单。只能处理文本型PDF无法处理扫描件提取的文本无结构需要自己写复杂的解析逻辑。仅限处理已知的、纯文本的、结构简单的PDF文件。选型决策树你的文档主要是扫描件或图片吗是 → 排除传统PDF解析库。你的文档格式是否统一且永不变化是 → 考虑自研规则引擎可能更经济。否 → 倾向于AI API。开发时间和资源是否紧张是 → AI API如ScoutExtract是更快上线的选择。对数据隐私和离线的要求是否绝对是 → 只能选择自研或本地部署的商用SDK。预算是否非常有限是 → 从免费方案开始或用传统方案处理最简单部分。对于大多数寻求在效率、成本和灵活性之间取得平衡的团队从类似ScoutExtract的AI API服务开始是一个风险较低、见效快速的明智选择。它让你能快速验证想法、构建MVP待业务规模扩大、模式稳定后再根据实际情况考虑是否迁移到混合或自研方案。