读取并分析Google表格数据。当用户提到“Google表格”、“电子表格”、“表格”,或发送 docs.google.com/spreadsheets 链接时触发。
Google Sheets Skill. 通过Google Sheets API v4从Google Sheets获取的对比数据,在磁盘上缓存,并回答用户对数据是一项面向实际任务的技能,主要用于Session Memory.;Maining the sext threat。
实际使用前应先确认任务范围、数据来源、运行环境、必要权限和关键参数,再依据技能说明逐步执行;若输入条件不完整,应先补齐信息或采用保守配置,避免因错误假设导致结果偏离需求。执行过程中需要关注工具调用是否成功、接口或依赖是否可用、输出格式是否符合预期,并对异常提示、缺失字段和边界情况进行处理;
涉及批量任务时,还应保存进度,避免中断后重复操作。该技能适合用于一次性任务,也可以接入自动化工作流,与其他技能或上层代理配合完成更完整的业务链路;在组合使用时,应明确每一步的输入输出关系,并避免不同步骤之间出现参数冲突。
通过 Google Sheets API v4 从 Google Sheets 获取数据,将数据缓存在本地磁盘,并基于该数据回答用户问题。
在整个对话过程中持续维护以下上下文,并在获取新信息时及时更新:
SHEET_CONTEXT = {
spreadsheetId: null, // 当前活跃的 Sheet ID
activeTab: null, // 当前正在操作的工作表(tab)
tabs: [], // 已缓存的工作表名称列表
headers: {}, // 每个工作表的已缓存表头:{ tabName: [...] }
rawData: {}, // 每个工作表的已缓存行数据:{ tabName: [[...]] }
cacheFile: null, // 磁盘上缓存文件的路径
}
规则:
spreadsheetId → 后续所有步骤均使用该 ID,不再重复询问按如下顺序检查:
SHEET_CONTEXT.spreadsheetId 已设置 → 直接使用/d/ 和 /edit 之间的 ID:
https://docs.google.com/spreadsheets/d/**SHEET_ID**/edit“您能分享一下 Google Sheet 链接或 Sheet ID 吗?我会在本次对话中记住它 😊”
收到后 → 立即保存至 SHEET_CONTEXT.spreadsheetId 并继续执行后续步骤。
使用 Google Sheets API v4。根据表格类型选择认证方式:
# 列出所有工作表(tabs)
curl -s "https://sheets.googleapis.com/v4/spreadsheets/{SHEET_ID}?key={GOOGLE_API_KEY}&fields=sheets.properties"
| python3 -c "import sys,json; d=json.load(sys.stdin); [print(s['properties']['title']) for s in d['sheets']]"
# 获取指定工作表的数据
curl -s "https://sheets.googleapis.com/v4/spreadsheets/{SHEET_ID}/values/{TAB_NAME}!A1:Z1000?key={GOOGLE_API_KEY}"
| python3 -c "import sys,json; d=json.load(sys.stdin); print(json.dumps(d.get('values',[])))"
import os, json
from google.oauth2 import service_account
from googleapiclient.discovery import build
creds = service_account.Credentials.from_service_account_file(
os.environ["GOOGLE_SERVICE_ACCOUNT_JSON"],
scopes=["https://www.googleapis.com/auth/spreadsheets.readonly"]
)
service = build("sheets", "v4", credentials=creds)
# 列出所有工作表(tabs)
meta = service.spreadsheets().get(spreadsheetId=SHEET_ID).execute()
tabs = [s["properties"]["title"] for s in meta["sheets"]]
# 获取指定工作表的数据
result = service.spreadsheets().values().get(
spreadsheetId=SHEET_ID,
range=f"{TAB_NAME}!A1:Z1000"
).execute()
rows = result.get("values", [])
将结果分别保存至 SHEET_CONTEXT.headers[tabName] 和 SHEET_CONTEXT.rawData[tabName]。
将获取的数据缓存至磁盘,避免跨轮次重复调用 API。
~/.openclaw/workspace/.cache/sheets/{spreadsheetId}/{tabName}.json
{
"spreadsheetId": "abc123",
"tabName": "Sheet1",
"fetchedAt": 1710000000,
"ttl": 300,
"headers": ["Name", "Status", "Date"],
"rows": [
["Task A", "Done", "2024-01-01"],
["Task B", "Pending", "2024-01-02"]
]
}
import os, json, time, shutil
CACHE_DIR = os.path.expanduser("~/.openclaw/workspace/.cache/sheets")
TTL = 300 # 5 分钟 — 若数据极少变动,可提升至 3600
def cache_path(sheet_id, tab):
d = os.path.join(CACHE_DIR, sheet_id)
os.makedirs(d, exist_ok=True) # 首次使用时自动创建目录
return os.path.join(d, f"{tab.replace('/', '_')}.json")
def load_cache(sheet_id, tab):
path = cache_path(sheet_id, tab)
if not os.path.exists(path):
return None
with open(path) as f:
c = json.load(f)
if time.time() - c.get("fetchedAt", 0) > c.get("ttl", TTL):
return None # 缓存已过期 — 将重新拉取
return c
def save_cache(sheet_id, tab, headers, rows):
path = cache_path(sheet_id, tab)
with open(path, "w") as f:
json.dump({
"spreadsheetId": sheet_id,
"tabName": tab,
"fetchedAt": int(time.time()),
"ttl": TTL,
"headers": headers,
"rows": rows
}, f, ensure_ascii=False)
def clear_cache(sheet_id=None):
target = os.path.join(CACHE_DIR, sheet_id) if sheet_id else CACHE_DIR
if os.path.exists(target):
shutil.rmtree(target)
cached = load_cache(SHEET_ID, TAB_NAME)
if cached:
headers, rows = cached["headers"], cached["rows"]
else:
# 从 API 拉取...
headers, rows = fetched_rows[0], fetched_rows[1:]
save_cache(SHEET_ID, TAB_NAME, headers, rows)
| 用户输入 | 操作 |
|---|---|
| “refresh”、“reload”、“get latest data” | clear_cache(SHEET_ID),然后重新拉取数据 |
| “clear cache” | clear_cache() — 清空全部缓存 |
| TTL 过期 | 下次请求时自动重新拉取 |
| 用户切换到新的 Sheet | 保留旧缓存,为新 Sheet 创建独立缓存 |
添加至 skills.entries 下(顶层字段,**不要**嵌套在 agents 内):
{
"skills": {
"entries": {
"google-sheets-soha": {
"enabled": true,
"env": {
"GOOGLE_API_KEY": "AIza..."
}
}
}
}
}
{
"skills": {
"entries": {
"google-sheets-soha": {
"enabled": true,
"env": {
"GOOGLE_SERVICE_ACCOUNT_JSON": "/home/node/.openclaw/google-sa.json"
}
}
}
}
}
| 情形 | 操作 |
|---|---|
| 未提供 Sheet ID | 仅询问一次,收到后立即保存 |
| API 返回 403 错误 | 表格为私有 → 引导用户将表格共享给 Service Account 的邮箱地址 |
| API 返回 404 错误 | Sheet ID 错误 → 请用户重新提供 |
GOOGLE_API_KEY 未设置 |
引导用户在 openclaw.json 中添加该环境变量 |
| 找不到指定 Tab | 列出 SHEET_CONTEXT.tabs 中所有可用 Tab,并请用户选择 |
| 数据量过大 | 仅分析前 200 行,并通知用户 |
容器内未安装 python3 |
在容器中运行:apt-get install -y python3 |