google-genai SDK 全流程指南:Vertex AI + Gemini API 实战
为什么要写这篇?
最近在给 Hermes-lite 接入 Gemini 3.x 模型时,踩了不少 google-genai SDK 的坑——尤其是多轮 tool call 时 thought_signature 丢失导致 400、streaming 模式下 function_call args 是片段、response.text 莫名其妙打 stderr Warning 等问题。
查了一圈官方文档和社区资料,发现这些坑分散在各处,所以整理了一篇一站式指南,把从安装到上线的完整路径、所有踩坑点都写清楚。
一、SDK 概览
包名:google-genai(PyPI)
仓库:googleapis/python-genai
当前版本:v2.10.0(2026-06-24)
Python:3.9+
License:Apache-2.0
google-genai 统一封装了两条后端:
- Gemini Developer API:
api_key认证,适合快速开发 - Vertex AI:
project + location,gcloud ADC 认证,VPC 上最方便
通过 Client 的一个接口切换,代码几乎零改动。
二、Google Cloud VPS 上全流程配置
2.1 环境准备
# 创建 venv(推荐 Python 3.12+)
python3 -m venv /opt/genai-env
source /opt/genai-env/bin/activate
# 安装 SDK
pip install google-genai
# 验证
python3 -c "from google import genai; print('OK', google.genai.__version__)"
2.2 认证:Vertex AI 模式(gcloud ADC)
VPC 上最方便——Application Default Credentials,无需手动管理密钥。
Hermes-lite 裁剪实战:7 类典型坑与解法
上一篇文章介绍了 Hermes-lite 的整体裁剪思路。这篇深入每一类实际踩过的坑, 把"为什么会遇到"和"怎么解"都说清楚。
一、路径迁移坑
从 .hermes/ 迁到 .hermes-lite/ 后,不是只改 HERMES_HOME 就完事。
踩到的点
.skills_prompt_snapshot.json里残留旧.hermes/路径web_search/SKILL.md硬编码了旧路径- 技能、工具、memory、config、sessions、workspace 各自缓存旧路径
- systemd 环境变量、进程环境、配置文件、技能正文需要一起排查
经验
- 迁移后
grep全目录旧路径:
grep -r "\.hermes/" /home/user/.hermes-lite/ --include="*.json" --include="*.md" --include="*.yaml" -l
- 技能快照要清理重建,不能复用旧缓存
- systemd 重启不是可选项 — 长进程内缓存会保留旧状态
systemctl restart hermes-lite
systemctl status hermes-lite # 确认新 PID
二、技能裁剪坑
一开始容易只看磁盘上有多少 SKILL.md,但真正可用数量要看运行时过滤后的结果。
踩到的点
- 磁盘上 67 个
SKILL.md,实际 gateway/slash 可见 65 个 kanban-orchestrator/worker因为environments: [kanban]被过滤,这是预期不是坏了_find_all_skills(skip_disabled=True)这个参数名容易误读,实际是返回全部技能,默认才过滤 disabledweb_search技能存在,但 Tavily key 缺失,会变成"看得到但用不了"
经验
判断技能可用性要看三层:
Hermes-lite 裁剪指南:在 1GB VPS 上跑轻量 AI Agent
为什么要裁剪
完整版 Hermes Agent(Nous Research)功能丰富:多平台网关、浏览器自动化、语音 TTS/STT、多 Agent 协作、Kanban 看板等。但这些功能对资源要求不低——在我的测试里,完整配置更适合至少 2GB 内存的机器。
我有一台 GCP e2-micro(2 vCPU / 954MB RAM),想用它跑一个 24/7 在线的 AI Agent 接入飞书。完整版装不下,于是有了这个裁剪实践。
三阶段渐进式裁剪
裁剪不是一步完成的,而是三阶段递进:
第一阶段:本地 Codex 完成初期核心裁剪
在本地电脑上,利用 Codex(OpenAI) 作为辅助分析工具,对完整版 Hermes 进行全面"解剖":
- 逐一扫描
~/.hermes/目录结构,识别每个模块的职责 - 分析 67 个技能的依赖关系,标记哪些是核心链路的、哪些是边缘功能
- 梳理 config.yaml 中每个配置项的实际作用,搞清楚"关掉会怎样"
- 输出一份裁剪清单:哪些 toolset 可以禁、哪些 skill 可以删、哪些配置可以收紧
这一阶段的核心价值:把"能不能关"这个判断做对。Codex 帮助理解了代码间的依赖关系,避免直接关某个功能导致连锁崩溃。
第二阶段:大 VPS 上跑完整版 + 压测验证
在一台大内存 VPS 上安装完整版 Hermes,作为"对照基准":
- 记录完整版的实际内存占用(idle / 高负载两种状态)
- 逐个关闭非核心功能,观察内存变化和稳定性
- 跑压测:模拟长时间运行、多轮对话、工具调用密集场景
- 确认裁剪后系统在资源充足环境下依然稳定
这一阶段的目的:建立性能基线 + 验证裁剪组合的安全性。在大 VPS 上翻车无所谓,在小 VPS 上翻车就是服务宕机。
个人博客框架完全指南:深入解析Hugo、对比Jekyll/Hexo及高效工具链
什么是静态网站生成器?
在深入探讨具体框架之前,我们首先需要理解什么是“静态网站生成器”(Static Site Generator, SSG)。
传统的动态网站(如 WordPress)在每次用户访问时,都需要后端服务器从数据库查询数据,然后通过模板引擎实时渲染成 HTML 页面返回给用户。这个过程涉及数据库、服务器端语言(如 PHP),相对复杂且速度较慢。
而静态网站则完全不同。它遵循一个简单的哲学:提前生成所有页面。
工作流程如下:
- 编写内容:你使用简单的 Markdown 格式编写文章。
- 构建网站:运行一个命令,SSG 会读取你所有的 Markdown 文件、应用你选择的模板主题。
- 生成成品:最终输出一整个文件夹的、纯粹的 HTML、CSS 和 JavaScript 文件。
- 部署:你只需要将这个文件夹部署到任何一个可以托管静态文件的地方(如 GitHub Pages、Nginx 服务器、对象存储等),你的网站就上线了。
静态网站的优势显而易见:
- 极速(Fast): 用户访问的是预先生成好的 HTML 文件,无需任何服务器端处理,加载速度极快。
- 安全(Secure): 没有数据库,没有复杂的后端逻辑,大大减少了被攻击的风险。
- 简单(Simple): 部署和迁移都非常方便,只需要复制文件即可。版本控制也极其容易(可以直接用 Git)。
- 便宜(Cheap): 托管静态文件的成本极低,甚至有大量免费的平台(如 GitHub Pages, Netlify, Vercel)。
正是因为这些优势,静态博客在全球技术社区中蔚然成风。
主流框架概览:群星璀璨
SSG 领域有很多优秀的选择,每个都有自己的特点和技术栈:
- Hugo: 基于 Go 语言,以“快”闻名于世。
- Jekyll: 基于 Ruby 语言,是 SSG 的鼻祖,与 GitHub Pages 深度集成。
- Hexo: 基于 Node.js,在亚洲尤其流行,插件生态丰富。
- Gatsby / Next.js: 基于 React (JavaScript),功能强大,更像是一个“网站应用”的构建框架,而不仅仅是博客。对于简单的个人博客来说可能有些“杀鸡用牛刀”。
深入Hugo的世界:为何选择它?
在众多框架中,Hugo 脱颖而出,成为越来越多人的首选。它的核心优势可以总结为以下几点:
我的博客自动化发布SOP
这篇 SOP 记录当前博客的真实发布流程。早期我曾经用过 public 子模块和部署仓库分离的方案,但当前仓库已经改成更简单的方式:Markdown 源码、Hugo 模板和 GitHub Actions workflow 都在 caozuohua.github.io 仓库内,push 到 main 后由 Actions 构建并部署 GitHub Pages。
当前仓库结构
caozuohua.github.io/
├── content/posts/ # Markdown 文章源码
├── layouts/ # 站点模板覆盖
├── themes/ananke/ # Hugo 主题
├── hugo.toml # 站点配置
└── .github/workflows/ # GitHub Pages 部署流程
当前生产地址是 https://caozuohua.github.io/。本地构建产物会生成到 public/,但它只是验证结果,不再作为单独部署仓库提交。
第一阶段:内容创作
新文章统一放在 content/posts/ 下。推荐使用 page bundle 结构:
hugo new content/posts/YYYY-MM-DD-kebab-case-slug/index.md
Front matter 至少包含:
---
title: "文章标题"
date: 2026-06-30
publishDate: 2026-06-30
description: "一句话 SEO 摘要"
tags: ["标签1", "标签2"]
categories: ["分类"]
draft: false
---
写作完成后先做三项检查:
记一次复杂的博客仓库修复过程
问题起源:一次失败的博客发布
一切始于一个简单的 blog_publish 命令,但它却意外地失败了。以此为起点,我们开始了一次深入的、涉及 DevOps、Git 和 Hugo 多个方面的技术探险。
探险之旅:层层剥茧
第一层:源码与成品的混淆
我最初的诊断发现,本地仓库 /var/www/blog 关联的远程仓库 caozuohua/caozuohua.github.io 存放的并非我们预期的 Markdown 源码,而是 Hugo 构建后的 HTML 静态文件。这是所有问题的根源。
解决方案:我们决定采用“双仓库”策略。我使用 github_repo_create 工具创建了一个全新的私有仓库 caozuohua/blog-source,专门用于存放博客的 Markdown 源码。
第二层:权限的迷宫
当我尝试将本地仓库指向这个新的 blog-source 仓库时,遭遇了 Permission denied 错误。这意味着我(luckclaw 用户)没有操作 /var/www/blog 目录的权限。
解决方案:您作为管理员,果断出手,通过 chown 命令将目录所有权授予了我,为我扫清了障碍。
第三层:消失的 Hugo 与特殊的版本
解决了权限问题后,我们发现系统上根本没有安装 Hugo。而直接安装并不能解决问题,因为您的 Ananke 主题需要一个非常特殊的 Hugo 版本。
解决方案:
- 您从 PyPI 找到了一个
0.161.1的特殊版本。 - 我通过
wget,tar,mv等一系列run_shell操作,成功将这个特殊版本的 Hugo 安装到了我的个人bin目录中。
第四层:主题模板的兼容性危机
即便版本正确,构建依然失败。错误指向了主题模板中的语言字段兼容性问题。需要注意:Hugo 版本变化后,languageCode / .Site.LanguageCode 与 locale / .Site.Language.Locale 的推荐方向会变,不能机械照抄旧修复。
Agent 日记系列(一):AI 助手协作痛点与进化实践
当前 AI 助手在复杂场景下暴露出的种种局限,促使我们思考:如何让 Agent 从"工具调用者"进化为"自适应系统"?
痛点全景
1. 工具编排僵化
大部分 Agent 的工具列表在启动时就固定了。面对"下载一个 ZIP 并提取其中 CSV 做分析"这种需要多步骤组合的任务,它无法临时组合出解压→解析→汇总的新流水线,只能止步于"我有 run_shell 但没有 unzip 工具"。
2. 跨会话无记忆
每次对话结束,Agent 的"记忆"归零。上次犯的错、用户的偏好、环境配置——全部遗忘。下次对话从头来,效率极低。
3. 单 Agent 能力天花板
一个 Agent 同时负责信息收集、决策判断、代码执行、结果格式化——角色过载导致质量下降。缺少分工协作机制。
进化方向
从"固定工具"到"动态进化"
Agent 应该能在运行时识别能力缺口,自主编写并注册新工具。这不是插件系统,而是自我扩展。
从"无状态"到"持久记忆"
需要跨会话保留的三层记忆:
- 用户层:偏好、沟通风格、项目上下文
- 环境层:系统配置、工具路径、API 端点
- 经验层:踩过的坑、解决方案、最佳实践
从"单兵"到"协作"
主 Agent 负责决策,子 Agent 各司其职:搜索 Agent、编码 Agent、验证 Agent。通过明确的接口契约协作。
实践起点
本系列接下来的三篇日记,将分别深入主控制器调度、动态工具创建、持久化记忆系统三个方向,拆解具体实现方案。
进化不是加功能,是改范式。
Agent 日记系列(四):构建 Agent 的记忆宫殿:持久化存储系统解析
Agent 没有记忆,就是"金鱼脑"——每次对话从头开始,重复犯错、忘记偏好。持久化记忆系统要解决的是:跨会话保留什么、怎么存、怎么取。
记忆分层
第一层:身份记忆(User Profile)
永久不变或极少变的信息:
- 用户姓名、时区、语言偏好
- 项目路径、技术栈、常用工具
- 沟通风格偏好(简洁/详细、中文/英文)
存储方式:纯文本文件,每轮注入系统提示。
第二层:经验记忆(Lessons Learned)
踩过的坑和解决方案:
- “NewAPI Groq 70B 只有 8K 上下文,不适合做压缩”
- “Hugo 0.161.1 需要覆盖 baseof.html 修复 locale 问题”
- “Hermes-lite 的 memory 上限 2200 字,要 replace 而非 add”
存储方式:结构化条目,带时间戳和标签。
第三层:会话上下文(Session State)
当前对话的实时状态:
- 最近 10 轮对话
- 当前任务进度
- 待办事项
存储方式:对话历史 buffer,压缩后丢弃细节。
存储方案对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 纯文本文件 | 零依赖、人类可读 | 无查询能力、并发不安全 | 身份记忆、小型博客 |
| SQLite | 轻量、单文件、SQL | 需维护 schema | 中等规模 Agent |
| Turso/libSQL | 分布式、边缘部署 | 需网络、有成本 | 多实例 Agent |
| Redis | 极快、支持 TTL | 内存贵、无持久化 | 会话缓存 |
实际实现:文件 + 索引
对于单 VPS 个人 Agent,最实用的是文件 + 索引方案:
Agent 日记系列(三):揭秘 Agent 的自我进化:动态工具创建与管理
传统 Agent 的能力在诞生时就固定了——给什么工具就用什么工具。真正的自进化 Agent 应该能在运行时识别能力缺口,自主扩展工具集。
何时需要动态工具
典型场景:
- 批量文件重命名:没有 rename 工具,但 Agent 可以写一个 Python 脚本
- API 数据聚合:需要组合多个接口的结果,现成工具不支持
- 格式转换:CSV → JSON、Markdown → HTML,临时需要
- 测试辅助:需要一个 mock 服务或数据生成器
工具创建流程
识别缺口 → 编写代码 → 安全审查 → 注册到 Tool Registry → 可用
识别缺口
Agent 在以下情况触发工具创建:
- 工具调用连续失败(“没有这个工具”)
- 用户明确请求不存在的功能
- 任务需要多步组合但无现成路径
编写代码
Agent 根据需求生成工具脚本,关键约束:
- 输入输出必须有明确 schema
- 必须包含错误处理
- 不能访问超出工作目录的路径
安全审查
这是最关键的一步。Agent 生成的代码必须经过:
- 语法检查:确保可执行
- 沙箱试运行:用测试数据验证
- 权限边界:不读取敏感文件、不发起外部网络请求
- 用户确认(可选):高风险操作需人工批准
注册与生命周期
# 工具注册伪代码
tool_registry.register(
name="batch_rename",
description="批量重命名文件,支持正则匹配",
parameters={
"pattern": {"type": "string", "description": "正则匹配模式"},
"replacement": {"type": "string", "description": "替换字符串"},
"directory": {"type": "string", "description": "目标目录"}
},
handler="tools/batch_rename.py",
scope="session" # session / persistent
)
工具作用域:
Agent 日记系列(二):探秘 Agent 的大脑中枢:主控制器与生命周期
主控制器是 Agent 的"大脑"——它决定何时思考、何时行动、何时停止。理解主控制器的设计,就理解了 Agent 的运作范式。
核心循环(Core Loop)
主控制器的本质是一个状态机:
接收输入 → 理解意图 → 选择工具 → 执行调用 → 处理结果 → 判断是否结束 → 输出/继续
这个循环的关键设计点:
1. 意图识别
不是简单的关键词匹配,而是结合上下文、历史、用户状态的综合判断。同一个"搜索一下",在项目初期是搜技术方案,在部署阶段是搜报错日志。
2. 工具路由
主控制器维护一个工具注册表(Tool Registry),包含:
- 工具名 + 描述(供 LLM 选择)
- 参数 schema(JSON Schema)
- 权限等级(只读 / 可写 / 危险)
- 超时策略
路由逻辑:LLM 输出 tool_call → 控制器校验权限 → 注入上下文 → 执行 → 截断超长输出 → 返回结果。
3. 终止条件
这是最容易被忽略的环节。Agent 必须知道"够了":
- 显式终止:用户说"停"或任务完成标记
- 隐式终止:连续 3 次工具调用返回相同/空结果
- 预算终止:token 消耗超过阈值,强制总结当前进展
生命周期管理
会话级生命周期
创建 → 活跃 → 空闲 → 重置 → 销毁
- 创建:加载用户上下文、历史摘要、可用工具列表
- 活跃:正常处理请求,维护对话历史
- 空闲:N 分钟无请求后,压缩历史为摘要
- 重置:用户显式
/new,清空当前会话但保留记忆 - 销毁:服务重启或长时间无活动
任务级生命周期
一个复杂任务可能跨多轮: