feat(article): 为开源龙虾全家桶文章添加配图和生成脚本
- 在文章中添加了封面、OpenClaw、Nanobot、PicoClaw、ZeroClaw、memU等多个配图 - 新增中国龙虾军团、LobsterAI、Agent TARS、选龙虾指南和安全警示等配图 - 创建scripts/gen_article_images.py批量生成文章配图的脚本 - 实现即梦AI图片生成工具jimeng_gen.py,支持文生图功能 - 配置API密钥和签名算法,实现图片生成任务提交和轮询机制
@ -4,6 +4,8 @@
|
|||||||
> 分类:技术解读 / 科普
|
> 分类:技术解读 / 科普
|
||||||
> 作者:老邓唠AI
|
> 作者:老邓唠AI
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## 导语
|
## 导语
|
||||||
|
|
||||||
2026 年初,一只"龙虾"横空出世——OpenClaw 在 72 小时内斩获 6 万 GitHub Stars,100 天内飙到 25 万+,超越了 React 用 13 年攒下的纪录。一时间,"养龙虾"成为开发者圈子里最火的话题。
|
2026 年初,一只"龙虾"横空出世——OpenClaw 在 72 小时内斩获 6 万 GitHub Stars,100 天内飙到 25 万+,超越了 React 用 13 年攒下的纪录。一时间,"养龙虾"成为开发者圈子里最火的话题。
|
||||||
@ -18,6 +20,8 @@
|
|||||||
|
|
||||||
在聊"全家桶"之前,先搞清楚 OpenClaw 到底是啥。
|
在聊"全家桶"之前,先搞清楚 OpenClaw 到底是啥。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
**OpenClaw 不是一个编程工具,而是一个"生活操作系统"。** 它是由奥地利程序员 Peter Steinberger 用 TypeScript 开发的开源个人 AI Agent,口号是 "The AI that actually does things"(真正能干活的 AI)。
|
**OpenClaw 不是一个编程工具,而是一个"生活操作系统"。** 它是由奥地利程序员 Peter Steinberger 用 TypeScript 开发的开源个人 AI Agent,口号是 "The AI that actually does things"(真正能干活的 AI)。
|
||||||
|
|
||||||
### 核心能力
|
### 核心能力
|
||||||
@ -56,6 +60,8 @@ OpenClaw 的爆火催生了一批各具特色的"龙虾亲戚"。
|
|||||||
|
|
||||||
### 2.1 Nanobot —— 4000 行代码的极简龙虾
|
### 2.1 Nanobot —— 4000 行代码的极简龙虾
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
| 维度 | 详情 |
|
| 维度 | 详情 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| 来源 | 香港大学 HKUDS 团队 |
|
| 来源 | 香港大学 HKUDS 团队 |
|
||||||
@ -67,6 +73,8 @@ Nanobot 的哲学是**"少即是多"**。它用最少的代码复现了 OpenClaw
|
|||||||
|
|
||||||
### 2.2 PicoClaw —— 跑在 10 美元开发板上的龙虾
|
### 2.2 PicoClaw —— 跑在 10 美元开发板上的龙虾
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
| 维度 | 详情 |
|
| 维度 | 详情 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| 来源 | Sipeed(矽速科技) |
|
| 来源 | Sipeed(矽速科技) |
|
||||||
@ -78,6 +86,8 @@ Nanobot 的哲学是**"少即是多"**。它用最少的代码复现了 OpenClaw
|
|||||||
|
|
||||||
### 2.3 ZeroClaw —— 用 Rust 写的安全龙虾
|
### 2.3 ZeroClaw —— 用 Rust 写的安全龙虾
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
| 维度 | 详情 |
|
| 维度 | 详情 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| 来源 | Harvard/MIT 学生及 Sundai.Club 贡献者 |
|
| 来源 | Harvard/MIT 学生及 Sundai.Club 贡献者 |
|
||||||
@ -98,6 +108,8 @@ NanoClaw 的思路是:既然 Agent 可能有风险,那就把它关进"笼子
|
|||||||
|
|
||||||
### 2.5 memU —— 记忆力最强的龙虾
|
### 2.5 memU —— 记忆力最强的龙虾
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
| 维度 | 详情 |
|
| 维度 | 详情 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| 来源 | NevaMind-AI |
|
| 来源 | NevaMind-AI |
|
||||||
@ -121,8 +133,12 @@ OpenClaw 的记忆系统是平铺式的对话日志,而 memU 把每条记忆
|
|||||||
|
|
||||||
国内大厂也没闲着,纷纷推出自己的"龙虾"。
|
国内大厂也没闲着,纷纷推出自己的"龙虾"。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
### 3.1 LobsterAI —— 网易有道的中国版 OpenClaw
|
### 3.1 LobsterAI —— 网易有道的中国版 OpenClaw
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
| 维度 | 详情 |
|
| 维度 | 详情 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| 公司 | 网易有道 |
|
| 公司 | 网易有道 |
|
||||||
@ -163,6 +179,8 @@ CoPaw 更聚焦在办公场景,提供 SaaS 和本地插件两种模式,让
|
|||||||
|
|
||||||
### 3.5 Agent TARS & Computer Use Agent —— 字节跳动的双子星
|
### 3.5 Agent TARS & Computer Use Agent —— 字节跳动的双子星
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
| 产品 | 定位 | 特点 |
|
| 产品 | 定位 | 特点 |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
| **Agent TARS** | 多模态任务 Agent | 视觉理解 + 工具集成,智能任务自动化 |
|
| **Agent TARS** | 多模态任务 Agent | 视觉理解 + 工具集成,智能任务自动化 |
|
||||||
@ -202,6 +220,8 @@ CoPaw 更聚焦在办公场景,提供 SaaS 和本地插件两种模式,让
|
|||||||
|
|
||||||
### 选龙虾指南
|
### 选龙虾指南
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
- **想要最完整的体验** → OpenClaw(但注意安全风险)
|
- **想要最完整的体验** → OpenClaw(但注意安全风险)
|
||||||
- **想快速上手、中文友好** → LobsterAI(网易有道)
|
- **想快速上手、中文友好** → LobsterAI(网易有道)
|
||||||
- **对安全性要求极高** → ZeroClaw 或 NanoClaw
|
- **对安全性要求极高** → ZeroClaw 或 NanoClaw
|
||||||
@ -216,6 +236,8 @@ CoPaw 更聚焦在办公场景,提供 SaaS 和本地插件两种模式,让
|
|||||||
|
|
||||||
## 五、安全警示:养龙虾需谨慎
|
## 五、安全警示:养龙虾需谨慎
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
最后必须提一句安全问题。OpenClaw 的 Skills 生态是社区驱动的,这意味着任何人都可以上传插件。CrowdStrike 和 Bitdefender 等安全公司已经发出警告:**存在"工具投毒"和恶意 Skills 的风险。**
|
最后必须提一句安全问题。OpenClaw 的 Skills 生态是社区驱动的,这意味着任何人都可以上传插件。CrowdStrike 和 Bitdefender 等安全公司已经发出警告:**存在"工具投毒"和恶意 Skills 的风险。**
|
||||||
|
|
||||||
养龙虾的注意事项:
|
养龙虾的注意事项:
|
||||||
|
|||||||
BIN
articles/images/001/agent_tars.png
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
BIN
articles/images/001/china_lobsters.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
articles/images/001/choose_guide.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
articles/images/001/cover.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
articles/images/001/lobsterai.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
articles/images/001/memu.png
Normal file
|
After Width: | Height: | Size: 1005 KiB |
BIN
articles/images/001/nanobot.png
Normal file
|
After Width: | Height: | Size: 503 KiB |
BIN
articles/images/001/openclaw_hero.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
articles/images/001/picoclaw.png
Normal file
|
After Width: | Height: | Size: 1008 KiB |
BIN
articles/images/001/security_warning.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
articles/images/001/zeroclaw.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
doc/avatar.png
Normal file
|
After Width: | Height: | Size: 3.4 MiB |
110
scripts/gen_article_images.py
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
"""批量生成龙虾全家桶文章配图"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
sys.path.insert(0, os.path.dirname(__file__))
|
||||||
|
from jimeng_gen import generate_image
|
||||||
|
|
||||||
|
BASE_DIR = os.path.join(os.path.dirname(__file__), '..', 'images', '001')
|
||||||
|
|
||||||
|
IMAGES = [
|
||||||
|
{
|
||||||
|
"name": "cover.png",
|
||||||
|
"prompt": "一群风格各异的卡通龙虾站在巨大的发光电路板上,每只龙虾颜色不同,有红色、蓝色、绿色、金色、紫色,背景是深蓝色科技感数据流,中间有一个大大的开源符号,扁平插画风格,色彩鲜艳,充满活力,16:9构图",
|
||||||
|
"width": 1280,
|
||||||
|
"height": 720,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "openclaw_hero.png",
|
||||||
|
"prompt": "一只巨大的红色卡通龙虾坐在服务器机房的中控台前,面前有多个全息屏幕显示着WhatsApp、Slack、Telegram的图标,龙虾戴着耳机在工作,科技感,赛博朋克风格,蓝紫色调",
|
||||||
|
"width": 1024,
|
||||||
|
"height": 1024,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nanobot.png",
|
||||||
|
"prompt": "一只迷你的白色卡通龙虾站在一本打开的Python编程书上,身体非常小巧精致,旁边有一个放大镜,上面写着4000,极简风格,白色背景,干净利落,扁平插画",
|
||||||
|
"width": 1024,
|
||||||
|
"height": 1024,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "picoclaw.png",
|
||||||
|
"prompt": "一只微型绿色卡通龙虾站在一块很小的电路板芯片上,芯片发着绿色的光,旁边放着一枚硬币做大小对比,背景是智能家居场景,温暖的灯光,科技与居家结合的风格",
|
||||||
|
"width": 1024,
|
||||||
|
"height": 1024,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "zeroclaw.png",
|
||||||
|
"prompt": "一只银色金属质感的卡通龙虾身穿铠甲,手持盾牌,盾牌上有一个锁的图标,站在数字城堡的城门前,背景是防火墙的数据流,安全感十足,金属蓝色调,科幻风格",
|
||||||
|
"width": 1024,
|
||||||
|
"height": 1024,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "memu.png",
|
||||||
|
"prompt": "一只紫色卡通龙虾的大脑上方浮现着发光的知识图谱网络,各个节点之间有彩色连线,节点上有小图标代表日历、音乐、跑步、生日蛋糕等生活场景,深色背景,科技紫色调,梦幻风格",
|
||||||
|
"width": 1024,
|
||||||
|
"height": 1024,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "china_lobsters.png",
|
||||||
|
"prompt": "五只不同颜色的卡通龙虾站在中国风格的舞台上,背景有网易、阿里巴巴、腾讯、字节跳动的抽象标志元素,舞台上有红色灯笼和科技全息投影的结合,中国红与科技蓝的碰撞,喜庆又现代",
|
||||||
|
"width": 1280,
|
||||||
|
"height": 720,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "lobsterai.png",
|
||||||
|
"prompt": "一只金色卡通龙虾面前有一个漂亮的图形化操作界面,界面上显示着中文,龙虾用钳子在触摸屏上操作,旁边有DeepSeek的logo标志,现代化办公室背景,明亮温暖的色调",
|
||||||
|
"width": 1024,
|
||||||
|
"height": 1024,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "agent_tars.png",
|
||||||
|
"prompt": "一只机械风格的卡通龙虾坐在电脑前,它的眼睛发出扫描光线照在电脑屏幕上,钳子在键盘上打字,屏幕上显示着被高亮框选的GUI元素,未来感十足,暗色调配霓虹灯效果",
|
||||||
|
"width": 1024,
|
||||||
|
"height": 1024,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "choose_guide.png",
|
||||||
|
"prompt": "一个可爱的卡通场景:一个人站在龙虾水族馆前,玻璃缸里有各种不同颜色和大小的龙虾,每只龙虾上方有小标签,人物在思考选择哪只,温馨有趣的插画风格,明亮色彩",
|
||||||
|
"width": 1280,
|
||||||
|
"height": 720,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "security_warning.png",
|
||||||
|
"prompt": "一只卡通龙虾拿着一个警告三角标志,旁边有一个打开的插件盒子,里面冒出可疑的紫色烟雾,背景是安全警示条纹,黄色和黑色配色,警示感强烈但不失可爱",
|
||||||
|
"width": 1024,
|
||||||
|
"height": 1024,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
os.makedirs(BASE_DIR, exist_ok=True)
|
||||||
|
results = []
|
||||||
|
|
||||||
|
for i, img in enumerate(IMAGES):
|
||||||
|
output_path = os.path.join(BASE_DIR, img["name"])
|
||||||
|
if os.path.exists(output_path):
|
||||||
|
print(f"[{i+1}/{len(IMAGES)}] 跳过已存在: {img['name']}")
|
||||||
|
results.append(output_path)
|
||||||
|
continue
|
||||||
|
|
||||||
|
print(f"\n[{i+1}/{len(IMAGES)}] 生成: {img['name']}")
|
||||||
|
result = generate_image(
|
||||||
|
prompt=img["prompt"],
|
||||||
|
output_path=output_path,
|
||||||
|
width=img.get("width", 1024),
|
||||||
|
height=img.get("height", 1024),
|
||||||
|
)
|
||||||
|
if result:
|
||||||
|
results.append(result)
|
||||||
|
else:
|
||||||
|
print(f" !! 生成失败: {img['name']}")
|
||||||
|
|
||||||
|
print(f"\n===== 完成 =====")
|
||||||
|
print(f"成功: {len(results)}/{len(IMAGES)}")
|
||||||
|
for r in results:
|
||||||
|
print(f" ✓ {r}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
158
scripts/jimeng_gen.py
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
"""即梦 AI 图片生成工具"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import base64
|
||||||
|
import datetime
|
||||||
|
import hashlib
|
||||||
|
import hmac
|
||||||
|
import requests
|
||||||
|
|
||||||
|
# ===== 配置 =====
|
||||||
|
ACCESS_KEY = 'AKLTOWVjYmE2NGVhZTFhNDQ2OThiYTNhZDdjZTZiYTc3ZTQ'
|
||||||
|
SECRET_KEY = 'WXpVd016azFPVEF3TURjNE5EbGxOV0psTlRNek1qaGpaalEyWkdKa1kyRQ=='
|
||||||
|
HOST = 'visual.volcengineapi.com'
|
||||||
|
ENDPOINT = 'https://visual.volcengineapi.com'
|
||||||
|
REGION = 'cn-north-1'
|
||||||
|
SERVICE = 'cv'
|
||||||
|
|
||||||
|
|
||||||
|
def _sign(key, msg):
|
||||||
|
return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest()
|
||||||
|
|
||||||
|
|
||||||
|
def _get_signature_key(key, date_stamp, region_name, service_name):
|
||||||
|
k_date = _sign(key.encode('utf-8'), date_stamp)
|
||||||
|
k_region = _sign(k_date, region_name)
|
||||||
|
k_service = _sign(k_region, service_name)
|
||||||
|
k_signing = _sign(k_service, 'request')
|
||||||
|
return k_signing
|
||||||
|
|
||||||
|
|
||||||
|
def _format_query(parameters):
|
||||||
|
return '&'.join(f'{k}={parameters[k]}' for k in sorted(parameters))
|
||||||
|
|
||||||
|
|
||||||
|
def jimeng_request(action, body_params):
|
||||||
|
"""发送即梦 API 请求"""
|
||||||
|
t = datetime.datetime.now(datetime.UTC)
|
||||||
|
current_date = t.strftime('%Y%m%dT%H%M%SZ')
|
||||||
|
datestamp = t.strftime('%Y%m%d')
|
||||||
|
|
||||||
|
query_params = _format_query({'Action': action, 'Version': '2022-08-31'})
|
||||||
|
req_body = json.dumps(body_params)
|
||||||
|
payload_hash = hashlib.sha256(req_body.encode('utf-8')).hexdigest()
|
||||||
|
|
||||||
|
signed_headers = 'content-type;host;x-content-sha256;x-date'
|
||||||
|
canonical_headers = (
|
||||||
|
f'content-type:application/json\n'
|
||||||
|
f'host:{HOST}\n'
|
||||||
|
f'x-content-sha256:{payload_hash}\n'
|
||||||
|
f'x-date:{current_date}\n'
|
||||||
|
)
|
||||||
|
canonical_request = f'POST\n/\n{query_params}\n{canonical_headers}\n{signed_headers}\n{payload_hash}'
|
||||||
|
|
||||||
|
credential_scope = f'{datestamp}/{REGION}/{SERVICE}/request'
|
||||||
|
string_to_sign = (
|
||||||
|
f'HMAC-SHA256\n{current_date}\n{credential_scope}\n'
|
||||||
|
+ hashlib.sha256(canonical_request.encode('utf-8')).hexdigest()
|
||||||
|
)
|
||||||
|
|
||||||
|
signing_key = _get_signature_key(SECRET_KEY, datestamp, REGION, SERVICE)
|
||||||
|
signature = hmac.new(signing_key, string_to_sign.encode('utf-8'), hashlib.sha256).hexdigest()
|
||||||
|
|
||||||
|
authorization = (
|
||||||
|
f'HMAC-SHA256 Credential={ACCESS_KEY}/{credential_scope}, '
|
||||||
|
f'SignedHeaders={signed_headers}, Signature={signature}'
|
||||||
|
)
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
'X-Date': current_date,
|
||||||
|
'Authorization': authorization,
|
||||||
|
'X-Content-Sha256': payload_hash,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
|
||||||
|
r = requests.post(f'{ENDPOINT}?{query_params}', headers=headers, data=req_body)
|
||||||
|
return r.json()
|
||||||
|
|
||||||
|
|
||||||
|
def _ensure_dir(file_path):
|
||||||
|
dir_name = os.path.dirname(file_path)
|
||||||
|
if dir_name:
|
||||||
|
os.makedirs(dir_name, exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_image(prompt, output_path, width=1024, height=1024):
|
||||||
|
"""文生图:提交任务并轮询获取结果,保存图片到本地"""
|
||||||
|
print(f"[即梦] 生成图片: {prompt[:50]}...")
|
||||||
|
|
||||||
|
# 1. 提交任务
|
||||||
|
submit_resp = jimeng_request('CVSync2AsyncSubmitTask', {
|
||||||
|
'req_key': 'jimeng_t2i_v40',
|
||||||
|
'prompt': prompt,
|
||||||
|
'width': width,
|
||||||
|
'height': height
|
||||||
|
})
|
||||||
|
|
||||||
|
if submit_resp.get('code') != 10000:
|
||||||
|
print(f"[即梦] 提交失败: {submit_resp}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
task_id = submit_resp['data']['task_id']
|
||||||
|
print(f"[即梦] 任务已提交, task_id: {task_id}")
|
||||||
|
|
||||||
|
# 2. 轮询查询结果
|
||||||
|
for i in range(30):
|
||||||
|
time.sleep(5)
|
||||||
|
result = jimeng_request('CVSync2AsyncGetResult', {
|
||||||
|
'req_key': 'jimeng_t2i_v40',
|
||||||
|
'task_id': task_id
|
||||||
|
})
|
||||||
|
|
||||||
|
resp_code = result.get('code')
|
||||||
|
resp_data = result.get('data', {})
|
||||||
|
|
||||||
|
# 检查任务是否完成
|
||||||
|
if resp_code == 10000:
|
||||||
|
# 尝试获取图片数据
|
||||||
|
image_urls = resp_data.get('image_urls', [])
|
||||||
|
binary_data = resp_data.get('binary_data_base64', [])
|
||||||
|
|
||||||
|
if image_urls:
|
||||||
|
# 下载图片
|
||||||
|
img_url = image_urls[0]
|
||||||
|
img_data = requests.get(img_url).content
|
||||||
|
_ensure_dir(output_path)
|
||||||
|
with open(output_path, 'wb') as f:
|
||||||
|
f.write(img_data)
|
||||||
|
print(f"[即梦] 图片已保存: {output_path}")
|
||||||
|
return output_path
|
||||||
|
|
||||||
|
if binary_data:
|
||||||
|
# base64 解码保存
|
||||||
|
img_data = base64.b64decode(binary_data[0])
|
||||||
|
_ensure_dir(output_path)
|
||||||
|
with open(output_path, 'wb') as f:
|
||||||
|
f.write(img_data)
|
||||||
|
print(f"[即梦] 图片已保存: {output_path}")
|
||||||
|
return output_path
|
||||||
|
|
||||||
|
# 任务可能还在处理中
|
||||||
|
status = resp_data.get('status', 'unknown')
|
||||||
|
if status == 'done':
|
||||||
|
print(f"[即梦] 任务完成但未找到图片数据: {json.dumps(result, ensure_ascii=False)[:200]}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
print(f"[即梦] 等待中... ({i+1}/30)")
|
||||||
|
|
||||||
|
print("[即梦] 超时")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
prompt = sys.argv[1] if len(sys.argv) > 1 else "一只可爱的卡通龙虾站在发光的电路板上,科技感,扁平插画风格,蓝色调"
|
||||||
|
output = sys.argv[2] if len(sys.argv) > 2 else "test_output.png"
|
||||||
|
generate_image(prompt, output)
|
||||||