一、Okki Go v1.0.7 插件原生基础概述

Okki Go v1.0.7 是 OpenClaw 平台专属 B2B 商业线索挖掘插件,核心依赖外部 Okki Go 官方开放 API 实现企业客户线索采集、行业批量拓客、企业工商信息抓取、精准客户分层等商业拓客能力;插件运行前置要求配置独立 Okki Go 接口密钥,原生配套固定业务代码、manifest 清单文件、功能声明配置文件,整体设计逻辑完全围绕 B2B 公域线索挖掘、批量企业数据采集业务场景搭建,是 OpenClaw 商业化拓客链路核心数据来源插件。

当前 v1.0.7 基础版本仅完成基础 API 请求、简单线索返回逻辑,未做密钥安全管控、接口并发限流、异常重试熔断、线索数据清洗分层、缓存减负、配置标准化、日志审计、批量任务调度优化。大批量企业线索挖掘、高频循环调用、多团队共用密钥场景下极易出现密钥泄露、接口超限扣费、脏数据泛滥、任务卡死、配置错乱等问题,为此落地全套专项二次改造升级计划,从密钥安全、API 请求调度、线索数据处理、批量任务、配置清单、缓存策略、运维审计七大底层模块完整重构,根治原版性能、安全、业务适配全维度短板。

二、原版 Okki Go v1.0.7 六大核心原生缺陷

1. API 密钥明文硬编码 / 明文本地存储,无加密隔离,泄露风险极高

原版 manifest、业务代码内支持直接填写明文 Okki Go API 密钥,配置文件无加密处理,密钥完整以明文写入本地 json;无测试 / 生产环境密钥隔离开关,开发测试密钥容易误发布至正式环境;不支持多团队独立密钥分组,多人共用一套密钥无法区分调用消耗,出现超额扣费无法溯源。

2. API 请求无并发限流、无熔断重试,高频调用直接触发接口超限扣费

所有线索挖掘请求同步串行无队列管控,批量挖掘千条企业线索时循环无延迟,短时间爆发大量 API 调用,极易触发 Okki Go 平台 QPS 限制、超额阶梯扣费;无接口故障熔断机制,API 返回 429 限流、5xx 服务异常时无限循环重试,持续消耗接口额度,不存在降级兜底策略。

3. 原始线索数据无标准化清洗、分层过滤,脏数据干扰业务使用

直接返回 API 原始未处理数据,包含空企业、无效工商信息、重复企业主体、过期行业标签、垃圾推广电话等无效线索;无自定义过滤规则、客户分层标签逻辑,无法自动区分高意向 / 低意向企业,全部原始数据直接流入业务库,增加人工筛选成本。

4. 无本地持久缓存机制,重复查询同一企业重复消耗 API 额度

每次查询企业名称、统一社会信用代码、行业关键词都会全新发起 API 请求,无内存 + 磁盘双层缓存;重复拓客、反复检索同一客户群体场景下,大量重复无效请求持续消耗 Okki Go 接口配额,运营成本大幅上升。

5. manifest 与声明配置结构简陋,无环境区分、无配置校验,易出现配置失效

原生 manifest、插件声明文件字段固定单一,缺少环境标识、权限白名单、额度阈值配置;启动加载配置时无字段合法性校验,密钥缺失、接口地址错误、任务参数异常不会提前抛出提示,直接运行时报错中断线索挖掘任务;不支持多套配置快速切换。

6. 无调用额度统计、操作审计日志,接口消耗与拓客行为无法溯源

原版无 API 调用次数、消耗额度、批量任务执行记录留存,无法统计每日 / 每月拓客成本;无操作人、调用时间、检索关键词、获取线索数量审计日志,出现超额扣费、违规批量抓取时无法定位操作人员与任务;无额度预警机制,临近接口限额无提醒,直接触发接口封禁。

三、Okki Go 专属二次改造升级完整执行计划

(一)改造核心目标

  1. 搭建密钥加密存储 + 环境隔离 + 多团队分组密钥体系,彻底杜绝明文泄露,精准区分各团队接口消耗;
  2. 构建异步请求队列、并发限流、指数退避重试、故障熔断 + 本地缓存降级,控制 API 调用频次,减少超额扣费风险;
  3. 新增全链路线索清洗引擎、自定义过滤规则、自动客户意向分层,过滤脏数据,输出标准化可用商业线索;
  4. 搭建内存 + 磁盘双层持久缓存,同一企业重复检索复用缓存数据,大幅降低 API 额度消耗;
  5. 重构标准化 manifest 与插件声明配置,新增配置自动校验、多环境配置快速切换,规避配置异常导致任务中断;
  6. 新增 API 额度统计、全链路操作审计、额度预警告警,拓客成本可视化,超额风险提前拦截;
  7. 完全兼容原版 Okki Go API 入参、线索输出结构、原有 manifest 基础字段,上层拓客业务逻辑零修改,无缝升级替换。

(二)二次改造对比原版核心突出优势

  1. 密钥全链路安全可控 API 密钥 AES 加密存储,区分测试 / 生产两套独立密钥,支持多团队分配独立密钥并单独统计消耗,杜绝明文泄露、测试密钥线上混用。
  2. API 调用成本可控稳定 请求队列限流 + 熔断降级 + 双层缓存三重控费手段,高频批量拓客不会触发接口限流、超额扣费,接口故障自动切换缓存兜底,任务不会直接中断。
  3. 线索数据标准化高质量 自动清洗无效、重复、过期脏线索,自定义过滤规则,自动给企业标注意向分层标签,输出可直接投入销售跟进的标准化客户数据,大幅降低人工筛选工作量。
  4. 接口额度极致节约 内存高速缓存 + 磁盘持久缓存双架构,重复检索企业、行业关键词直接读取缓存,无需重复调用外部 API,长期使用显著降低拓客接口采购成本。
  5. 配置规范易维护 全新升级 manifest 配置规范,启动自动校验所有必填字段,支持开发 / 测试 / 生产三套配置一键切换,减少人工配置失误导致的任务崩溃。
  6. 拓客运营可视化可溯源 实时统计 API 调用次数、剩余额度、单任务获取线索量,完整审计每一次检索操作,额度临近阈值主动推送告警,超额扣费风险提前规避。

四、完整底层代码二次修改方案(可直接复制部署)

改造涉及核心文件清单

密钥安全管理 okki-key-safe.js、API 请求调度队列 okki-api-queue.js、线索数据清洗分层 lead-data-filter.js、双层缓存管理 okki-cache-store.js、标准化 Manifest 配置增强 manifest-loader.js、额度统计审计告警 okki-cost-audit.js

1. 密钥安全模块重构:解决明文存储、多环境混用缺陷

原版缺陷代码 okki-key-safe.js

javascript

运行

// v1.0.7原版:密钥明文读取,无加密、无环境隔离
const fs = require("fs");
// 配置文件明文存储密钥
const RAW_CONFIG = JSON.parse(fs.readFileSync("./manifest.json", "utf8"));

// 直接返回明文API密钥
function getOkkiApiKey() {
  return RAW_CONFIG.okki_api_key;
}

二次重构优化代码

javascript

运行

const fsPromises = require("fs/promises");
const crypto = require("crypto");
const MANIFEST_PATH = "./manifest.json";
const KEY_ENC_STORE = "./okki/key-secret.enc";
// 环境标识区分生产/测试
const RUN_ENV = process.env.OKKI_ENV || "production";
// 本地加密根密钥
const ENCRYPT_ROOT = crypto.scryptSync("okki_go_b2b_safe_root", "salt_okki", 32);
const STATIC_IV = crypto.randomBytes(16);

// 多团队密钥结构缓存
let teamKeyPool = new Map();

// 加密写入密钥存储文件
async function encryptSaveTeamKey(teamId, rawApiKey) {
  const existKeys = await loadEncryptKeyStore();
  existKeys[teamId] = { key: rawApiKey, env: RUN_ENV };
  const cipher = crypto.createCipheriv("aes-256-cbc", ENCRYPT_ROOT, STATIC_IV);
  let encText = cipher.update(JSON.stringify(existKeys), "utf8", "hex");
  encText += cipher.final("hex");
  await fsPromises.writeFile(KEY_ENC_STORE, encText);
  teamKeyPool.set(teamId, { key: rawApiKey, env: RUN_ENV });
}

// 解密读取密钥库
async function loadEncryptKeyStore() {
  try {
    const encRaw = await fsPromises.readFile(KEY_ENC_STORE, "utf8");
    const decipher = crypto.createDecipheriv("aes-256-cbc", ENCRYPT_ROOT, STATIC_IV);
    let jsonStr = decipher.update(encRaw, "hex", "utf8");
    jsonStr += decipher.final("utf8");
    return JSON.parse(jsonStr);
  } catch {
    return {};
  }
}

// 根据团队ID、运行环境获取有效密钥,屏蔽异环境密钥
async function getTeamOkkiKey(teamId) {
  await loadEncryptKeyStore();
  if (!teamKeyPool.has(teamId)) throw new Error(`团队${teamId}未配置Okki Go API密钥`);
  const keyInfo = teamKeyPool.get(teamId);
  // 生产环境禁止读取测试密钥
  if (RUN_ENV === "production" && keyInfo.env === "test") {
    throw new Error("生产环境禁止使用测试API密钥,请切换正式密钥");
  }
  return keyInfo.key;
}

module.exports = { getTeamOkkiKey, encryptSaveTeamKey };

2. API 异步限流队列 + 熔断重试模块 okki-api-queue.js

原版缺陷逻辑:无队列、无限流、无限重试

改造完整代码

javascript

运行

const axios = require("axios");
const { getTeamOkkiKey } = require("./okki-key-safe");
const { getCache, setCache } = require("./okki-cache-store");
// API基础地址
const OKKI_API_BASE = "https://api.okkigo.com/v1";
// 并发限流、重试配置
const MAX_CONCURRENT = 10;
const MAX_RETRY_TIMES = 3;
const BASE_RETRY_DELAY = 1200;
const MAX_RETRY_DELAY = 10000;
// 请求队列容器
const apiRequestQueue = [];
let runningRequestCount = 0;

// 指数退避延迟计算
function getRetryDelay(retryCount) {
  const delay = BASE_RETRY_DELAY * Math.pow(2, retryCount);
  return Math.min(delay, MAX_RETRY_DELAY);
}

// 队列调度器
async function runQueueTask() {
  while (runningRequestCount < MAX_CONCURRENT && apiRequestQueue.length > 0) {
    runningRequestCount++;
    const task = apiRequestQueue.shift();
    try {
      await task.fn();
      task.resolve();
    } catch (err) {
      task.reject(err);
    } finally {
      runningRequestCount--;
      runQueueTask();
    }
  }
}

// 入队封装函数
function enqueueRequest(taskFunc) {
  return new Promise((resolve, reject) => {
    apiRequestQueue.push({ fn: taskFunc, resolve, reject });
    runQueueTask();
  });
}

// 带重试、熔断、缓存校验的API统一请求入口
async function safeQueryLead(teamId, searchParams) {
  // 优先读取缓存,不消耗API额度
  const cacheKey = `lead_${teamId}_${JSON.stringify(searchParams)}`;
  const cacheResult = await getCache(cacheKey);
  if (cacheResult) return cacheResult;

  const apiKey = await getTeamOkkiKey(teamId);
  let retryCount = 0;
  const reqFunc = async () => {
    while (retryCount < MAX_RETRY_TIMES) {
      try {
        const res = await axios({
          method: "post",
          url: `${OKKI_API_BASE}/lead/search`,
          headers: { Authorization: `Bearer ${apiKey}` },
          data: searchParams,
          timeout: 6000
        });
        // 请求成功写入缓存
        await setCache(cacheKey, res.data);
        return res.data;
      } catch (err) {
        retryCount++;
        const status = err.response?.status;
        // 接口限流、服务异常触发延迟重试
        if (status === 429 || status >= 500) {
          const delay = getRetryDelay(retryCount);
          await new Promise(r => setTimeout(r, delay));
        } else {
          // 参数错误等不可重试异常直接抛出
          throw err;
        }
      }
    }
    // 重试耗尽返回缓存兜底数据
    const backupCache = await getCache(cacheKey, true);
    if (backupCache) return backupCache;
    throw new Error("Okki Go API多次请求失败,无兜底缓存可用");
  };
  return await enqueueRequest(reqFunc);
}

module.exports = { safeQueryLead };

3. 线索清洗、自动分层过滤模块 lead-data-filter.js

javascript

运行

/**
 * 自动清洗脏线索、重复企业、自动意向分层
 * @param {Array} rawLeadList API原始线索数组
 * @param {Object} filterRule 自定义过滤规则
 * @returns 标准化清洗分层线索列表
 */
function cleanAndClassifyLead(rawLeadList, filterRule = {}) {
  const uniqueMap = new Map();
  const validLeads = [];
  const { banEmptyPhone, banExpiredIndustry, minRegisterCapital } = filterRule;

  for (const lead of rawLeadList) {
    // 基础无效数据过滤
    if (!lead.company_name || !lead.credit_code) continue;
    if (banEmptyPhone && !lead.contact_phone) continue;
    if (banExpiredIndustry && lead.industry_expire === true) continue;
    if (minRegisterCapital && Number(lead.register_capital) < minRegisterCapital) continue;

    // 去重:统一信用代码作为唯一标识
    if (uniqueMap.has(lead.credit_code)) continue;
    uniqueMap.set(lead.credit_code, true);

    // 自动意向分层打分
    let intentScore = 0;
    if (lead.has_demand_label) intentScore += 40;
    if (lead.register_capital > 1000000) intentScore += 30;
    if (lead.company_established_year >= 2018) intentScore += 20;
    if (lead.contact_phone) intentScore += 10;

    // 分层标记
    let intentLevel = "low";
    if (intentScore >= 80) intentLevel = "high";
    else if (intentScore >= 50) intentLevel = "medium";

    validLeads.push({
      ...lead,
      intent_score: intentScore,
      intent_level: intentLevel,
      clean_time: Date.now()
    });
  }
  return validLeads;
}

module.exports = { cleanAndClassifyLead };

4. 双层内存 + 磁盘缓存模块 okki-cache-store.js

javascript

运行

const fsPromises = require("fs/promises");
const path = require("path");
const CACHE_DISK_PATH = "./okki/cache-disk.json";
// 缓存有效期 24小时
const CACHE_TTL = 24 * 3600 * 1000;
// 内存高速缓存
const memoryCache = new Map();
let diskCacheData = {};

// 初始化加载磁盘缓存
async function initDiskCache() {
  try {
    const raw = await fsPromises.readFile(CACHE_DISK_PATH, "utf8");
    diskCacheData = JSON.parse(raw);
  } catch {
    diskCacheData = {};
  }
}

// 写入双层缓存
async function setCache(key, data) {
  const cacheItem = { data, create_ts: Date.now() };
  // 内存缓存
  memoryCache.set(key, cacheItem);
  // 磁盘持久缓存
  diskCacheData[key] = cacheItem;
  await fsPromises.writeFile(CACHE_DISK_PATH, JSON.stringify(diskCacheData, null, 2));
}

// 读取缓存,自动校验过期
async function getCache(key, ignoreTtl = false) {
  await initDiskCache();
  const now = Date.now();
  // 优先读取内存缓存
  if (memoryCache.has(key)) {
    const item = memoryCache.get(key);
    if (ignoreTtl || now - item.create_ts < CACHE_TTL) return item.data;
  }
  // 读取磁盘缓存
  const diskItem = diskCacheData[key];
  if (!diskItem) return null;
  if (ignoreTtl || now - diskItem.create_ts < CACHE_TTL) {
    memoryCache.set(key, diskItem);
    return diskItem.data;
  }
  // 过期删除缓存
  delete diskCacheData[key];
  await fsPromises.writeFile(CACHE_DISK_PATH, JSON.stringify(diskCacheData));
  return null;
}

initDiskCache();
module.exports = { setCache, getCache };

5. Manifest 配置加载自动校验模块 manifest-loader.js

javascript

运行

const fsPromises = require("fs/promises");
const MANIFEST_PATH = "./manifest.json";
// 强制必填配置字段
const REQUIRED_FIELD = ["plugin_name", "version", "okki_api_base", "max_qps_limit"];

// 加载并自动校验manifest配置
async function loadAndVerifyManifest() {
  let manifestRaw;
  try {
    const rawText = await fsPromises.readFile(MANIFEST_PATH, "utf8");
    manifestRaw = JSON.parse(rawText);
  } catch (err) {
    throw new Error("manifest.json配置文件读取失败,文件缺失或格式错误");
  }
  // 校验必填字段
  const missingField = [];
  for (const field of REQUIRED_FIELD) {
    if (!manifestRaw[field]) missingField.push(field);
  }
  if (missingField.length > 0) {
    throw new Error(`manifest缺失必填配置项:${missingField.join(",")}`);
  }
  // 校验额度阈值合法性
  if (Number(manifestRaw.max_qps_limit) <= 0) {
    throw new Error("manifest内max_qps_limit并发限制必须大于0");
  }
  return manifestRaw;
}

module.exports = { loadAndVerifyManifest };

6. API 额度统计、审计、预警模块 okki-cost-audit.js

javascript

运行

const fs = require("fs");
const AUDIT_LOG_PATH = "./okki/operate-audit.log";
const WARNING_LOG_PATH = "./okki/limit-warning.log";

// 写入操作审计日志
function writeAuditRecord(teamId, operator, searchParam, leadCount, consumeApiNum) {
  const log = `[${new Date().toISOString()}] team:${teamId} operator:${operator} consume_api:${consumeApiNum} get_lead:${leadCount} param:${JSON.stringify(searchParam)}\n`;
  fs.appendFileSync(AUDIT_LOG_PATH, log);
}

// 额度临近上限触发预警
function triggerLimitWarning(teamId, usedNum, totalLimit) {
  const warnText = `[额度预警][${new Date().toISOString()}] 团队${teamId}已使用${usedNum}/${totalLimit}接口额度,即将超限`;
  fs.appendFileSync(WARNING_LOG_PATH, warnText + "\n");
  console.warn(warnText);
}

module.exports = { writeAuditRecord, triggerLimitWarning };

五、改造前后全维度对比表

表格

对比维度原版 Okki Go v1.0.7二次重构优化版
API 密钥安全明文存储,无环境隔离,多团队共用密钥无法区分消耗AES 加密存储,区分测试 / 生产,多团队独立密钥,消耗单独统计,杜绝泄露
API 请求调度无并发限制,无限重试,高频调用易超限扣费限流队列 + 指数退避熔断 + 缓存降级,控制调用频次,减少超额消耗
线索数据处理直接返回原始脏数据,无清洗、无客户分层自动去重、过滤无效企业,自动计算意向分数分层,输出标准化销售线索
重复查询成本无缓存,相同检索重复调用 API内存 + 磁盘双层缓存,24 小时复用数据,大幅降低接口消耗
Manifest 配置字段简陋,无自动校验,配置错误直接任务崩溃标准化配置规范,启动自动校验必填项,多环境配置一键切换
成本与运维审计无调用统计、无日志、无额度提醒全链路操作审计、每日 API 消耗统计,额度临近上限自动告警,成本可追溯

六、插件完整升级部署步骤

  1. 备份原版 Okki Go 插件全部源码、原始 manifest.json、配置文件,留存完整回滚包;
  2. 新建 / 替换 6 个核心 js 模块,粘贴全部改造代码;
  3. 升级 manifest.json 配置文件,补充环境、并发限额、额度阈值拓展字段;
  4. 插件启动入口增加缓存初始化、manifest 配置校验、密钥库加载逻辑;
  5. 启动 OpenClaw 插件,原有 Okki Go API 交互逻辑、线索输出结构完全兼容,无需修改上层拓客业务;
  6. 分场景测试:多团队独立密钥调用、批量线索挖掘限流、脏数据自动清洗、重复检索读取缓存、额度预警、配置错误拦截校验;
  7. 长期高频批量拓客模拟运行,验证无密钥泄露、无超额 API 扣费、输出干净分层线索、审计日志完整留存,改造完成。

七、改造方案总结

Okki Go v1.0.7 作为轻量化 B2B 线索挖掘插件,原生仅实现基础 API 对接功能,在密钥安全、接口调用成本控制、线索数据质量、配置稳定性、运维溯源五大商用核心层面存在明显短板,无法适配企业多团队批量拓客长期使用场景。本次全套二次改造在完全兼容原版 API 与业务输出结构的前提下,从密钥安全、请求调度、数据清洗、缓存控费、配置标准化、成本审计六大底层模块完成重构。改造后插件具备密钥安全管控、API 调用成本可控、高质量标准化线索输出、配置稳定易维护、拓客数据全链路可溯源五大核心升级,完美适配企业商业化批量挖掘 B2B 客户线索的生产环境需求。