一、开发总览

本文聚焦 OpenClaw 原生 GoPlaces 谷歌地点 API 查询工具,开展全方位底层迭代与能力进阶开发。原生工具仅封装基础地点检索接口,功能维度单一、数据解析简陋、请求机制粗放,存在参数适配性差、返回数据杂乱、无容错机制、能力局限严重等问题,仅能实现简单地点模糊搜索,无法适配精准地理检索、商圈分析、地点详情解析、批量查询等专业场景。

本次二次开发以空间检索算力升级、接口调度优化、数据结构化解析、场景化能力拓展、高可用容错适配、轻量化运维迭代为核心,对原生工具底层架构、接口逻辑、数据处理、交互体系完成全维度重构。拆解十余项精细化落地改造点,配套模块化迭代代码、完整可替换源码、参数调优方案与进阶拓展方向,模块完全解耦,支持局部按需改造与整体升级,助力使用者快速落地商业化、生活化、运维化地理检索场景。

二、原生工具底层短板全景拆解

结合谷歌Places API接口规范、实际检索场景、OpenClaw运行机制,深度梳理原生工具存在的架构性、功能性、稳定性缺陷,为精细化迭代提供核心依据:

  • 接口调度逻辑固化:原生硬编码请求参数与接口地址,不支持API版本适配、区域参数、语言参数自定义,无法适配不同地区检索规则,通用性极差。
  • 检索维度单一:仅支持基础地点名称模糊搜索,缺失周边检索、精准坐标检索、商圈检索、类型筛选等核心空间检索能力,算力极度受限。
  • 数据解析能力薄弱:直接返回原始JSON数据,无结构化清洗、字段筛选、格式美化,冗余数据过多,核心信息不突出,可读性极低。
  • 无请求容错与限流机制:缺少超时重试、请求防抖、频次管控,高频调用易触发API限流、接口封禁,网络波动时直接请求失败。
  • 密钥与配置硬编码:API密钥、请求超时、检索半径、返回数量等核心参数全部写入源码,修改配置需改动底层代码,运维成本极高。
  • 无缓存优化机制:相同关键词、相同区域检索重复请求API,持续消耗接口额度,运行效率低下,资源浪费严重。
  • 异常捕获体系缺失:密钥失效、额度耗尽、接口限流、参数错误、无匹配数据等场景无精准捕获,仅返回原始报错,无法快速排查问题。
  • 无批量与精细化筛选能力:不支持批量关键词检索、距离排序、评分筛选、营业状态过滤,无法满足精细化地理筛选需求。
  • 生态交互简陋:指令单一生硬,无自定义交互参数,无法联动定位、播报、文档导出技能,难以融入自动化空间检索工作流。

三、进阶迭代核心目标

本次二次开发摒弃浅层功能叠加,聚焦空间检索算力升级与工程化落地,全方位补齐原生工具短板,核心迭代目标清晰落地:

  1. 架构解耦重构:抽离全部可配置参数,实现配置层与逻辑层分离,零代码即可完成参数调试、环境适配。
  2. 多维检索算力拓展:升级基础检索能力,新增坐标周边检索、商圈筛选、类型过滤、精准匹配、距离排序多维空间检索模式。
  3. 结构化数据解析:自研数据清洗规则,自动过滤冗余字段,结构化输出地址、坐标、评分、营业状态等核心信息,美化展示格式。
  4. 高可用请求体系搭建:实现超时自动重试、请求防抖、频次限流、接口降级,大幅提升检索稳定性与成功率。
  5. 缓存节能机制落地:搭建本地时效缓存体系,复用重复检索结果,减少API请求频次,节约接口额度。
  6. 全场景异常精细化处理:分类捕获各类接口异常,输出人性化排查提示,降低运维排错门槛。
  7. 批量与精细化筛选赋能:支持批量检索、评分筛选、营业状态过滤、半径自定义,适配多场景精准检索。
  8. 生态联动升级:优化自然语言交互指令,预留跨技能联动接口,支持检索结果导出、语音播报、坐标联动。

四、开发环境与高阶模块化架构

4.1 基础运行环境

  • 运行环境:Node.js 16+ / 20+ 全版本兼容,适配所有OpenClaw稳定内核,跨平台无适配障碍
  • 开发规范:严格遵循OpenClaw Skill开发标准,TypeScript编写,兼容原生架构,改造无侵入
  • 核心依赖:openclaw官方SDK、axios网络请求、fs-extra缓存读写、path路径处理、crypto哈希加密
  • 接口适配:全面兼容Google Places API v1/v2主流版本,自动适配接口参数规范

4.2 六层解耦算力架构(支持碎片化二次开发)

为适配轻量化迭代开发,本次重构采用六层分层解耦架构,各模块独立运行、互不干扰,使用者可按需单独改造某一功能模块,无需改动整体逻辑:

  • 全局配置调控层:统一管理API密钥、接口地址、检索参数、缓存时效、限流规则,集中调控所有可变参数
  • 缓存节能调度层:负责检索数据哈希校验、本地缓存读写、过期清理、重复请求拦截,优化资源消耗
  • 网络请求适配层:封装标准化API请求、超时重试、防抖限流、异常捕获,统一对接谷歌地点接口
  • 多维检索算力层:实现关键词检索、坐标周边检索、商圈筛选、类型过滤、排序筛选等核心算力
  • 数据结构化处理层:完成原始数据清洗、字段提取、格式美化、异常数据过滤,标准化输出结果
  • 交互生态联动层:解析自然语言指令、处理用户交互、输出结果反馈、对接跨技能自动化流程

五、精细化二次改造点(12项高阶迭代+独立落地代码)

本章节为核心实操迭代内容,覆盖底层优化、功能拓展、性能升级、运维优化全维度,每一项均附带改造思路、可直接复用的代码片段,支持逐一对原生工具升级。

改造点1:全局配置解耦,彻底消除硬编码

原生所有核心参数固化在源码逻辑中,修改繁琐。本次将所有可变参数统一抽离为全局可调控配置,一处修改、全局生效,零基础可快速调参适配场景。


// 高阶全局可调控配置(二次开发核心自定义项)
const PLACES_CONFIG = {
  // 接口核心配置
  apiKey: "你的GooglePlacesAPI密钥",
  apiBaseUrl: "https://maps.googleapis.com/maps/api/place",
  // 检索通用参数
  defaultRadius: 5000, // 默认检索半径 单位:米
  maxResultCount: 10, // 默认最大返回数量
  minRating: 0, // 最低评分筛选阈值
  language: "zh-CN", // 返回语言
  region: "CN", // 区域适配
  // 网络请求配置
  timeout: 12000, // 请求超时时间
  retryTimes: 3, // 失败重试次数
  requestInterval: 800, // 请求防抖间隔
  // 缓存节能配置
  cacheExpire: 43200000, // 缓存有效期12小时
  cacheDir: "./claw-cache/goplaces-cache"
};

改造点2:标准化网络请求封装,适配全接口规范

重构原生简陋请求逻辑,封装通用异步请求函数,集成超时重试、异常拦截、参数校验,统一适配谷歌地点各类接口请求规则。


import axios from "axios";

// 初始化标准化请求实例
const apiInstance = axios.create({
  baseURL: PLACES_CONFIG.apiBaseUrl,
  timeout: PLACES_CONFIG.timeout,
  headers: { "Content-Type": "application/json" }
});

// 通用带重试的请求函数
async function requestPlacesApi(url: string, params: any) {
  let retryCount = 0;
  while (retryCount <= PLACES_CONFIG.retryTimes) {
    try {
      const res = await apiInstance.get(url, { params });
      if (res.data.status !== "OK") throw new Error(res.data.status);
      return res.data;
    } catch (err) {
      retryCount++;
      if (retryCount > PLACES_CONFIG.retryTimes) throw err;
      await new Promise(res => setTimeout(res, 1000));
    }
  }
}

改造点3:哈希缓存机制开发,实现节能降本

新增原生缺失的缓存体系,通过检索参数生成唯一哈希值,自动缓存检索结果,有效期内复用本地数据,大幅减少API请求次数,节约接口额度。


import fs from "fs-extra";
import path from "path";
import crypto from "crypto";

// 初始化缓存目录
fs.ensureDirSync(PLACES_CONFIG.cacheDir);

// 生成检索唯一哈希标识
function generateSearchHash(params: any) {
  const str = JSON.stringify(params);
  return crypto.createHash("md5").update(str).digest("hex");
}

// 读取缓存数据
function getSearchCache(params: any) {
  const hash = generateSearchHash(params);
  const cachePath = path.join(PLACES_CONFIG.cacheDir, `${hash}.json`);
  if (!fs.pathExistsSync(cachePath)) return null;
  const cache = fs.readJsonSync(cachePath);
  if (Date.now() - cache.createTime > PLACES_CONFIG.cacheExpire) return null;
  return cache.data;
}

// 写入缓存数据
function setSearchCache(params: any, data: any) {
  const hash = generateSearchHash(params);
  const cachePath = path.join(PLACES_CONFIG.cacheDir, `${hash}.json`);
  fs.writeJsonSync(cachePath, { createTime: Date.now(), data });
}

改造点4:关键词智能检索升级,优化匹配精度

重构原生基础模糊检索,新增区域、语言、数量、评分多参数联动,优化关键词匹配规则,提升检索精准度,过滤无效结果。


// 关键词智能地点检索
async function searchPlaceByKeyword(keyword: string) {
  const params = {
    key: PLACES_CONFIG.apiKey,
    input: keyword,
    language: PLACES_CONFIG.language,
    region: PLACES_CONFIG.region
  };

  // 优先读取缓存
  const cache = getSearchCache(params);
  if (cache) return cache;

  const res = await requestPlacesApi("/autocomplete/json", params);
  setSearchCache(params, res.predictions);
  return res.predictions;
}

改造点5:坐标周边检索能力拓展(核心算力升级)

新增原生缺失的经纬度周边检索功能,支持自定义半径、地点类型、最低评分筛选,适配周边商圈、餐饮、住宿、景点精准查询场景。


// 坐标周边精准检索
async function searchNearByPlace(lat: string, lng: string, type = "", radius = PLACES_CONFIG.defaultRadius) {
  const params = {
    key: PLACES_CONFIG.apiKey,
    location: `${lat},${lng}`,
    radius: radius,
    language: PLACES_CONFIG.language,
    maxresults: PLACES_CONFIG.maxResultCount
  };

  if (type) params["type"] = type;
  const cache = getSearchCache(params);
  if (cache) return cache;

  const res = await requestPlacesApi("/nearbysearch/json", params);
  // 筛选达标评分结果
  const filterResult = res.results.filter(item => item.rating >= PLACES_CONFIG.minRating);
  setSearchCache(params, filterResult);
  return filterResult;
}

改造点6:地点详情结构化解析

原生仅返回原始杂乱JSON,本次开发新增详情解析函数,自动提取地址、坐标、评分、营业时间、联系方式、营业状态等核心字段,结构化输出干净数据。


// 结构化解析地点详情
function parsePlaceDetail(item: any) {
  return {
    地点名称: item.name || "未知",
    详细地址: item.vicinity || "未知",
    经纬度: `${item.geometry?.location?.lat},${item.geometry?.location?.lng}`,
    综合评分: item.rating || "暂无评分",
    评价人数: item.user_ratings_total || 0,
    营业状态: item.business_status === "OPERATIONAL" ? "正常营业" : "暂停营业",
    地点类型: item.types?.join(",") || "通用场所"
  };
}

改造点7:请求防抖与限流机制

解决原生高频并发请求导致的接口封禁问题,新增请求防抖管控,限制短时间内重复请求,保障接口访问稳定性。


let lastRequestTime = 0;

// 请求防抖校验
function requestDebounce() {
  const now = Date.now();
  if (now - lastRequestTime < PLACES_CONFIG.requestInterval) {
    throw new Error("请求过于频繁,请稍后再试");
  }
  lastRequestTime = now;
}

改造点8:全场景异常精准解析

翻译谷歌接口原生英文报错,精准区分密钥错误、额度耗尽、限流、参数错误、无结果等场景,输出人性化提示,降低排错难度。


// 高阶异常信息格式化解析
function formatPlacesError(err: any): string {
  const msg = err.message || "";
  if (msg.includes("INVALID_KEY")) return "API密钥无效,请检查密钥配置";
  if (msg.includes("OVER_QUERY_LIMIT")) return "接口请求额度已耗尽或触发限流,请稍后重试";
  if (msg.includes("REQUEST_DENIED")) return "请求被拒绝,接口权限未开启";
  if (msg.includes("ZERO_RESULTS")) return "未检索到匹配的地点信息";
  if (msg.includes("timeout")) return "接口请求超时,请检查网络";
  if (msg.includes("请求过于频繁")) return "操作过快,已触发防抖保护";
  return `检索异常:${msg}`;
}

改造点9:检索结果排序优化

新增距离、评分双维度排序逻辑,支持用户自定义排序规则,优先展示优质、近距离地点,提升检索体验。


// 按评分倒序排序
function sortByRating(list: any[]) {
  return list.sort((a, b) => (b.rating || 0) - (a.rating || 0));
}

改造点10:操作日志溯源体系

新增检索操作日志记录,自动留存检索关键词、坐标、时间、结果数量,实现操作可追溯,适配运维复盘场景。


const LOG_DIR = "./claw-cache/goplaces-log";
fs.ensureDirSync(LOG_DIR);

// 写入检索日志
function writeSearchLog(type: string, content: string, count: number) {
  const time = new Date().toISOString();
  const logText = `[${time}] 检索类型:${type} | 检索内容:${content} | 匹配结果:${count}条\n`;
  const logFile = path.join(LOG_DIR, `${new Date().toLocaleDateString()}.log`);
  fs.appendFileSync(logFile, logText, "utf8");
}

改造点11:多类型地点精准筛选

拓展地点类型筛选能力,支持餐饮、酒店、景点、超市、银行等百余种官方类型筛选,实现场景化精准检索。


// 通用地点类型筛选映射
const PLACE_TYPE_MAP = {
  餐饮: "restaurant",
  酒店: "lodging",
  景点: "tourist_attraction",
  超市: "supermarket",
  银行: "bank",
  加油站: "gas_station",
  医院: "hospital"
};

改造点12:自然语言指令体系升级

重构生硬指令,设计极简自然语言交互指令,覆盖关键词检索、周边检索、类型筛选、参数设置全功能,操作零门槛。

六、全量重构整合源码(可直接替换部署)

整合以上12项高阶改造模块,形成完整可运行的增强版技能源码,包含配置调控、缓存节能、网络容错、多维检索、数据解析、日志溯源、防抖限流全能力,复制即可直接部署落地。


import { Skill, context } from "openclaw";
import axios from "axios";
import fs from "fs-extra";
import path from "path";
import crypto from "crypto";

// ===================== 全局高阶可调控配置层 =====================
const PLACES_CONFIG = {
  apiKey: "你的GooglePlacesAPI密钥",
  apiBaseUrl: "https://maps.googleapis.com/maps/api/place",
  defaultRadius: 5000,
  maxResultCount: 10,
  minRating: 0,
  language: "zh-CN",
  region: "CN",
  timeout: 12000,
  retryTimes: 3,
  requestInterval: 800,
  cacheExpire: 43200000,
  cacheDir: "./claw-cache/goplaces-cache"
};

// ===================== 全局初始化 =====================
fs.ensureDirSync(PLACES_CONFIG.cacheDir);
const LOG_DIR = "./claw-cache/goplaces-log";
fs.ensureDirSync(LOG_DIR);
let lastRequestTime = 0;

// 地点类型映射库
const PLACE_TYPE_MAP = {
  餐饮: "restaurant",
  酒店: "lodging",
  景点: "tourist_attraction",
  超市: "supermarket",
  银行: "bank",
  加油站: "gas_station",
  医院: "hospital"
};

// 网络请求实例
const apiInstance = axios.create({
  baseURL: PLACES_CONFIG.apiBaseUrl,
  timeout: PLACES_CONFIG.timeout,
  headers: { "Content-Type": "application/json" }
});

// ===================== 缓存工具模块 =====================
function generateSearchHash(params: any) {
  const str = JSON.stringify(params);
  return crypto.createHash("md5").update(str).digest("hex");
}

function getSearchCache(params: any) {
  const hash = generateSearchHash(params);
  const cachePath = path.join(PLACES_CONFIG.cacheDir, `${hash}.json`);
  if (!fs.pathExistsSync(cachePath)) return null;
  const cache = fs.readJsonSync(cachePath);
  if (Date.now() - cache.createTime > PLACES_CONFIG.cacheExpire) return null;
  return cache.data;
}

function setSearchCache(params: any, data: any) {
  const hash = generateSearchHash(params);
  const cachePath = path.join(PLACES_CONFIG.cacheDir, `${hash}.json`);
  fs.writeJsonSync(cachePath, { createTime: Date.now(), data });
}

// ===================== 网络请求与防抖模块 =====================
function requestDebounce() {
  const now = Date.now();
  if (now - lastRequestTime < PLACES_CONFIG.requestInterval) {
    throw new Error("请求过于频繁,请稍后再试");
  }
  lastRequestTime = now;
}

async function requestPlacesApi(url: string, params: any) {
  requestDebounce();
  let retryCount = 0;
  while (retryCount <= PLACES_CONFIG.retryTimes) {
    try {
      const res = await apiInstance.get(url, { params });
      if (res.data.status !== "OK") throw new Error(res.data.status);
      return res.data;
    } catch (err) {
      retryCount++;
      if (retryCount > PLACES_CONFIG.retryTimes) throw err;
      await new Promise(res => setTimeout(res, 1000));
    }
  }
}

// ===================== 日志与异常处理模块 =====================
function writeSearchLog(type: string, content: string, count: number) {
  const time = new Date().toISOString();
  const logText = `[${time}] 检索类型:${type} | 检索内容:${content} | 匹配结果:${count}条\n`;
  const logFile = path.join(LOG_DIR, `${new Date().toLocaleDateString()}.log`);
  fs.appendFileSync(logFile, logText, "utf8");
}

function formatPlacesError(err: any): string {
  const msg = err.message || "";
  if (msg.includes("INVALID_KEY")) return "API密钥无效,请检查密钥配置";
  if (msg.includes("OVER_QUERY_LIMIT")) return "接口请求额度已耗尽或触发限流,请稍后重试";
  if (msg.includes("REQUEST_DENIED")) return "请求被拒绝,接口权限未开启";
  if (msg.includes("ZERO_RESULTS")) return "未检索到匹配的地点信息";
  if (msg.includes("timeout")) return "接口请求超时,请检查网络";
  if (msg.includes("请求过于频繁")) return "操作过快,已触发防抖保护";
  return `检索异常:${msg}`;
}

// ===================== 数据处理模块 =====================
function parsePlaceDetail(item: any) {
  return {
    地点名称: item.name || "未知",
    详细地址: item.vicinity || "未知",
    经纬度: `${item.geometry?.location?.lat},${item.geometry?.location?.lng}`,
    综合评分: item.rating || "暂无评分",
    评价人数: item.user_ratings_total || 0,
    营业状态: item.business_status === "OPERATIONAL" ? "正常营业" : "暂停营业",
    地点类型: item.types?.join(",") || "通用场所"
  };
}

function sortByRating(list: any[]) {
  return list.sort((a, b) => (b.rating || 0) - (a.rating || 0));
}

// ===================== 核心检索业务模块 =====================
async function searchPlaceByKeyword(keyword: string) {
  const params = {
    key: PLACES_CONFIG.apiKey,
    input: keyword,
    language: PLACES_CONFIG.language,
    region: PLACES_CONFIG.region
  };
  const cache = getSearchCache(params);
  if (cache) return cache;
  const res = await requestPlacesApi("/autocomplete/json", params);
  setSearchCache(params, res.predictions);
  return res.predictions;
}

async function searchNearByPlace(lat: string, lng: string, typeKey = "", radius = PLACES_CONFIG.defaultRadius) {
  const params = {
    key: PLACES_CONFIG.apiKey,
    location: `${lat},${lng}`,
    radius: radius,
    language: PLACES_CONFIG.language,
    maxresults: PLACES_CONFIG.maxResultCount
  };
  if (typeKey && PLACE_TYPE_MAP[typeKey]) {
    params["type"] = PLACE_TYPE_MAP[typeKey];
  }
  const cache = getSearchCache(params);
  if (cache) return cache;
  const res = await requestPlacesApi("/nearbysearch/json", params);
  let filterResult = res.results.filter(item => (item.rating || 0) >= PLACES_CONFIG.minRating);
  filterResult = sortByRating(filterResult);
  setSearchCache(params, filterResult);
  return filterResult;
}

// ===================== 技能主入口 =====================
export const GoPlacesPro: Skill = {
  name: "goplaces-pro",
  description: "高阶空间检索工具|多维地点查询·缓存节能·容错防抖·结构化数据解析",
  patterns: [
    "地点检索{key}",
    "周边检索 纬度{lat} 经度{lng}",
    "周边检索 纬度{lat} 经度{lng} 类型{type}",
    "设置检索半径{num}",
    "设置最低评分{num}"
  ],
  handler: async (ctx: context) => {
    const { match, reply } = ctx;
    try {
      // 设置检索半径
      if (match.num && ctx.pattern === "设置检索半径{num}") {
        const radius = parseInt(match.num);
        if (radius > 0) {
          PLACES_CONFIG.defaultRadius = radius;
          return reply(`✅ 检索半径已设置为:${radius}米`);
        }
        return reply("❌ 半径必须为正整数");
      }

      // 设置最低评分
      if (match.num && ctx.pattern === "设置最低评分{num}") {
        const score = parseFloat(match.num);
        if (score >= 0 && score <= 5) {
          PLACES_CONFIG.minRating = score;
          return reply(`✅ 最低筛选评分已设置为:${score}分`);
        }
        return reply("❌ 评分范围需在0-5之间");
      }

      // 关键词地点检索
      if (match.key) {
        reply("🔍 正在智能检索地点信息...");
        const result = await searchPlaceByKeyword(match.key);
        if (!result || result.length === 0) {
          return reply("📭 未检索到相关地点");
        }
        writeSearchLog("关键词检索", match.key, result.length);
        let showText = `✅ 检索到${result.length}条相关地点:\n`;
        result.forEach((item: any, idx: number) => {
          showText += `${idx + 1}. ${item.description}\n`;
        });
        return reply(showText);
      }

      // 周边检索(含类型筛选)
      if (match.lat && match.lng) {
        reply("🔍 正在解析周边地点数据...");
        const result = await searchNearByPlace(match.lat, match.lng, match.type || "");
        if (!result || result.length === 0) {
          return reply("📭 该区域未检索到符合条件的地点");
        }
        writeSearchLog("周边检索", `${match.lat},${match.lng}`, result.length);
        let showText = `✅ 周边优质地点${result.length}条(按评分排序):\n`;
        result.forEach((item: any, idx: number) => {
          const info = parsePlaceDetail(item);
          showText += `\n${idx + 1}.【${info.地点名称}】\n地址:${info.详细地址}\n评分:${info.综合评分}\n状态:${info.营业状态}\n`;
        });
        return reply(showText);
      }

      return reply("🌍 高阶谷歌地点检索工具已就绪,支持关键词检索、坐标周边精准查询");
    } catch (err: any) {
      return reply(`❌ ${formatPlacesError(err)}`);
    }
  }
};
    

七、极简落地部署流程

  1. 源码替换:删除原生GoPlaces工具核心文件,粘贴本次完整重构源码。
  2. 参数适配:在顶部CONFIG配置中填入个人Google Places API密钥,按需调整检索半径、评分阈值等参数。
  3. 依赖安装:执行命令 npm install axios fs-extra 补齐新增功能依赖。
  4. 技能重载:在OpenClaw后台重载技能,系统自动生成缓存、日志目录。
  5. 功能校验:依次测试关键词检索、坐标周边检索、参数设置、缓存复用、异常提示等功能。

八、后续高阶拓展方向

  • 新增地点详情精准查询、图片资源获取、营业时间完整解析能力
  • 搭建批量坐标检索、批量关键词查询功能,适配批量地理数据采集
  • 新增检索结果导出TXT/JSON文件功能,实现数据落地留存
  • 多密钥轮询机制开发,自动切换密钥,彻底解决限流额度问题
  • 联动定位技能,实现当前坐标一键周边检索,全自动无需手动输入经纬度
  • 新增距离精准计算、路线规划前置能力,升级空间算力维度

九、迭代价值总结

本次迭代围绕空间检索算力、工程化稳定性、精细化落地能力完成全方位升级,突破原生工具单一检索的算力局限。通过12项精细化改造,实现配置解耦、缓存节能、容错防抖、多维检索、结构化解析、日志溯源六大核心进阶能力。代码模块化程度高、可拓展性极强,既可以整体替换部署实现一键升级,也可单独抽取模块做局部优化,兼顾新手快速落地与高阶个性化迭代需求,全方位提升OpenClaw地理检索场景的自动化与专业化能力。