feat: bootstrap gemini web ops skill with image/text flows
This commit is contained in:
57
SKILL.md
Normal file
57
SKILL.md
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
---
|
||||||
|
name: gemini-web-ops
|
||||||
|
description: 通过 Gemini 官网(gemini.google.com)执行问答与生图操作。用户提到“问问Gemini/让Gemini回答/去Gemini问”,或出现“生图/画图/绘图/nano banana/nanobanana/生成图片”等关键词时触发。默认使用可用模型中最强档(优先 Gemini 3.1 Pro),按任务切换文本问答或图片生成流程,并把结果回传给用户。
|
||||||
|
---
|
||||||
|
|
||||||
|
# Gemini Web Ops
|
||||||
|
|
||||||
|
## 核心规则
|
||||||
|
|
||||||
|
1. 使用 OpenClaw 内置浏览器,`profile="openclaw"`。
|
||||||
|
2. 涉及生图关键词(如:生图、绘图、画一张、nano banana)时,优先用无头浏览器流程执行。
|
||||||
|
3. 文本问答任务(如“问问Gemini xxx”)走 Gemini 文本提问链路。
|
||||||
|
4. 默认模型:可用列表中最强模型,优先 `Gemini 3.1 Pro`。
|
||||||
|
5. 执行生图后先向用户回报“正在绘图中”,完成后回传图片。
|
||||||
|
|
||||||
|
## 任务分流
|
||||||
|
|
||||||
|
- **文本问答**触发词:`问问Gemini`、`让Gemini回答`、`去Gemini问`。
|
||||||
|
- **生图任务**触发词:`生图`、`画`、`绘图`、`海报`、`nano banana`、`nanobanana`、`image generation`。
|
||||||
|
- 若请求含糊,先确认:是文本回答还是要出图。
|
||||||
|
|
||||||
|
## 标准执行流程
|
||||||
|
|
||||||
|
### A. 文本问答
|
||||||
|
1. 打开 `https://gemini.google.com`。
|
||||||
|
2. 校验登录态(头像/输入框可见)。
|
||||||
|
3. 选择最强可用模型(优先 Gemini 3.1 Pro)。
|
||||||
|
4. 将用户问题原样输入并发送。
|
||||||
|
5. 等待完整输出,提炼后回传(必要时附原文要点)。
|
||||||
|
|
||||||
|
### B. 生图流程
|
||||||
|
1. 打开 Gemini 页面并确认登录。
|
||||||
|
2. 选择最强可用模型(优先 Gemini 3.1 Pro)。
|
||||||
|
3. 将用户提示词原样输入。
|
||||||
|
4. 开启/勾选图片生成能力(若 UI 有“生成图片/图片”开关)。
|
||||||
|
5. 发送后立即通知用户:正在绘图中。
|
||||||
|
6. 结果出现后:
|
||||||
|
- 优先用“下载原图”按钮获取原图。
|
||||||
|
- 若无下载按钮或失败,可对图片右键另存(通常是标清图)。
|
||||||
|
7. 把图片返回用户;若有多张,按顺序全部回传。
|
||||||
|
|
||||||
|
## 失败回退
|
||||||
|
|
||||||
|
1. 元素定位失败:刷新页面后重试一次。
|
||||||
|
2. 模型不可用:降级到次优 Gemini 模型并告知。
|
||||||
|
3. 生成超时:回报“仍在生成中”,继续等待一次;再次超时则请用户换短提示词。
|
||||||
|
|
||||||
|
## 低 token 优先策略
|
||||||
|
|
||||||
|
- 优先使用 `scripts/gemini_ui_shortcuts.js` 的快捷选择器。
|
||||||
|
- 先 evaluate 批量动作,再 snapshot 精准兜底。
|
||||||
|
- 避免高频全量快照。
|
||||||
|
|
||||||
|
## 参考
|
||||||
|
|
||||||
|
- 详细执行与回退:`references/gemini-flow.md`
|
||||||
|
- 关键词与路由:`references/intent-routing.md`
|
||||||
34
references/gemini-flow.md
Normal file
34
references/gemini-flow.md
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# Gemini Flow
|
||||||
|
|
||||||
|
## 1) 登录校验
|
||||||
|
|
||||||
|
最小校验项:
|
||||||
|
- 页面存在可输入提问的输入框
|
||||||
|
- 右上角有用户头像或账户入口
|
||||||
|
|
||||||
|
若未登录:提示用户先在 openclaw profile 浏览器中登录。
|
||||||
|
|
||||||
|
## 2) 模型策略
|
||||||
|
|
||||||
|
优先级:
|
||||||
|
1. Gemini 3.1 Pro
|
||||||
|
2. 当前页面可见的次优 Pro/Advanced 模型
|
||||||
|
|
||||||
|
若切换失败,保留默认并告知用户。
|
||||||
|
|
||||||
|
## 3) 生图结果获取
|
||||||
|
|
||||||
|
优先顺序:
|
||||||
|
1. 图片右上角“下载原图”
|
||||||
|
2. 右键另存为(标清)
|
||||||
|
|
||||||
|
下载到本地后再通过渠道回传。
|
||||||
|
|
||||||
|
## 4) 用户提示文案(建议)
|
||||||
|
|
||||||
|
- 开始生图:
|
||||||
|
- `已收到,正在用 Gemini 给你绘图中 🎨`
|
||||||
|
- 生成中超时:
|
||||||
|
- `还在渲染中,我继续盯着,马上回你。`
|
||||||
|
- 完成:
|
||||||
|
- `画好了,给你发图啦~`
|
||||||
24
references/intent-routing.md
Normal file
24
references/intent-routing.md
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Intent Routing
|
||||||
|
|
||||||
|
## 文本问答触发
|
||||||
|
|
||||||
|
- 问问Gemini xxx
|
||||||
|
- 去Gemini帮我问 xxx
|
||||||
|
- 让Gemini总结 xxx
|
||||||
|
|
||||||
|
动作:走文本问答链路。
|
||||||
|
|
||||||
|
## 生图触发
|
||||||
|
|
||||||
|
- 给我画一只小猫
|
||||||
|
- 生图:未来城市海报
|
||||||
|
- nano banana 风格来一张
|
||||||
|
|
||||||
|
动作:走生图链路,并先反馈“正在绘图中”。
|
||||||
|
|
||||||
|
## 混合请求
|
||||||
|
|
||||||
|
例如:
|
||||||
|
- 先问Gemini这个产品定位,再出一张封面图
|
||||||
|
|
||||||
|
动作:拆成两步执行,先问答后生图。
|
||||||
83
scripts/gemini_ui_shortcuts.js
Normal file
83
scripts/gemini_ui_shortcuts.js
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
(function initGeminiOps(){
|
||||||
|
const S = {
|
||||||
|
promptInput: [
|
||||||
|
'textarea[aria-label*="Gemini"]',
|
||||||
|
'textarea[placeholder*="Gemini"]',
|
||||||
|
'div[contenteditable="true"]'
|
||||||
|
],
|
||||||
|
sendBtn: [
|
||||||
|
'button[aria-label*="发送"]',
|
||||||
|
'button[aria-label*="Send"]',
|
||||||
|
'button:has-text("发送")'
|
||||||
|
],
|
||||||
|
imageToggle: [
|
||||||
|
'button:has-text("图片")',
|
||||||
|
'button:has-text("生成图片")',
|
||||||
|
'[role="button"]:has-text("Image")'
|
||||||
|
],
|
||||||
|
modelBtn: [
|
||||||
|
'button:has-text("Gemini")',
|
||||||
|
'[role="button"][aria-haspopup="menu"]'
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
function visible(el){
|
||||||
|
if(!el) return false;
|
||||||
|
const r=el.getBoundingClientRect();
|
||||||
|
const st=getComputedStyle(el);
|
||||||
|
return r.width>0 && r.height>0 && st.display!=='none' && st.visibility!=='hidden';
|
||||||
|
}
|
||||||
|
|
||||||
|
function q(sel){
|
||||||
|
try{
|
||||||
|
if(sel.includes(':has-text(')){
|
||||||
|
const m=sel.match(/^(.*):has-text\("(.*)"\)$/);
|
||||||
|
if(!m) return null;
|
||||||
|
const nodes=[...document.querySelectorAll(m[1]||'*')];
|
||||||
|
return nodes.find(n=>visible(n)&&n.textContent?.includes(m[2]))||null;
|
||||||
|
}
|
||||||
|
return [...document.querySelectorAll(sel)].find(visible)||null;
|
||||||
|
}catch{return null;}
|
||||||
|
}
|
||||||
|
|
||||||
|
function find(key){
|
||||||
|
for(const s of (S[key]||[])){
|
||||||
|
const el=q(s);
|
||||||
|
if(el) return el;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function click(key){
|
||||||
|
const el=find(key);
|
||||||
|
if(!el) return {ok:false,key,error:'not_found'};
|
||||||
|
el.click();
|
||||||
|
return {ok:true,key};
|
||||||
|
}
|
||||||
|
|
||||||
|
function fillPrompt(text){
|
||||||
|
const el=find('promptInput');
|
||||||
|
if(!el) return {ok:false,error:'prompt_not_found'};
|
||||||
|
el.focus();
|
||||||
|
if(el.tagName==='TEXTAREA'){
|
||||||
|
el.value=text;
|
||||||
|
el.dispatchEvent(new Event('input',{bubbles:true}));
|
||||||
|
}else{
|
||||||
|
document.execCommand('selectAll',false,null);
|
||||||
|
document.execCommand('insertText',false,text);
|
||||||
|
el.dispatchEvent(new Event('input',{bubbles:true}));
|
||||||
|
}
|
||||||
|
return {ok:true};
|
||||||
|
}
|
||||||
|
|
||||||
|
function probe(){
|
||||||
|
return {
|
||||||
|
promptInput: !!find('promptInput'),
|
||||||
|
sendBtn: !!find('sendBtn'),
|
||||||
|
imageToggle: !!find('imageToggle'),
|
||||||
|
modelBtn: !!find('modelBtn')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
window.GeminiOps = {probe, click, fillPrompt, selectors:S, version:'0.1.0'};
|
||||||
|
})();
|
||||||
Reference in New Issue
Block a user