大数跨境

专栏系列第一篇:最好用的专属Agent定制工具—Google的ADK

专栏系列第一篇:最好用的专属Agent定制工具—Google的ADK 静远AI出海
2026-10-27
1
导读:Google 开源 ADK 上手第一篇:52 行 Go 代码跑起第一个专用文案 Agent,控制台与 Web 双模式实测。
ADK(Agent Development Kit)是 Google 于 2025 年 4 月开源的 agent 开发框架,它的价值在于把"开发—调试—部署—观测—评测"这条完整生命周期装进一个代码优先的工具箱里。本系列手册基于一个真实项目——一人公司内容运营平台 jingyuan-opc-agents-platform(Go 语言),每一篇都从项目里实际长出来的代码出发,再下钻到 ADK 框架源码,讲清楚"怎么用"与"为什么这样设计"。第 1 篇先解决从零到一的问题:装好环境、读懂配置、让一个名叫 articleAgent 的"爆款文案工坊"在终端和浏览器里跑起来。通过本篇的学习,读者可以完整复现这个最小可运行工程,并对 ADK 的功能地图形成整体印象。

本篇主要内容: 

◆ ADK 的功能特性与架构全景 

◆ 为什么值得认真学:与 LangGraph 的对比

◆ 项目背景:一人公司内容运营,为什么需要专用 Agent 

◆ Hello World 实战:依赖安装、配置机制、代码精讲、控制台与 Web 运行

本篇亮点:

 ◆ 逐行精讲了 article-agent/agent.go 的全部 52 行代码,每个关键点都对照 ADK v2.6.0 源码给出出处

 ◆ 详细分析了 OpenAI 协议模型接入层的参数映射(TopP → top_p、ThinkingLevel → reasoning_effort)

 ◆ 给出了"裸 web 命令启动失败"等三个资料里查不到的实战坑位

版本约定:除特别指出,本手册的 Go 版 ADK 即 google.golang.org/adk/v2(v2.6.0),Go 工具链为 1.26+,操作系统以 macOS / Linux 为准。


1.1 ADK 是什么:一套自包含的 Agent 开发全家桶

在动手写代码之前,我们先用一节的篇幅把 ADK 的功能地图铺开,后面每一步实操都能在这张地图上找到落点。

1.1.1 出身与定位

ADK 在 2025 年 4 月 9 日的 Google Cloud NEXT 大会上发布,Apache-2.0 协议开源。官方发布博客的定位是:"一个来自 Google 的开源框架,旨在简化 agent 与多 agent 系统的全栈端到端开发"(full stack end-to-end development)。更值得注意的是这句——"ADK 是驱动 Google 自家产品(如 Agentspace 和 Google Customer Engagement Suite)中 agent 的同一套框架"。也就是说,它不是实验室里为论文而生的原型,而是从 Google 内部大规模 agent 实践中提炼出来、又为外部开发者重新起笔的一套框架。

提示 ADK 与 A2A 协议(Agent2Agent,agent 之间互操作的标准协议)在 2025 年 4 月同期发布。A2A 于 2025 年 6 月捐赠给 Linux Foundation,2026 年 8 月转入其下的 Agentic AI Foundation(MCP 协议也是该基金会创始项目)。ADK 对 MCP 的支持则是发布第一天就有的。这两个"开放标准"是理解 ADK 生态位的背景板:Google 想做的是互联的 agent 生态,而不是一座孤岛。

1.1.2 核心理念:工作流与 Agent 树的统一编排

ADK 最有辨识度的架构决策,是把确定性工作流(deterministic workflow)和自主 agent(autonomous agent)统一在同一棵编排树里,而不是像多数框架那样二选一。

□ Agent 树:任何一个 agent 都可以挂 SubAgents,形成父子层级。LLM agent 之间可以按语义自动流转(transfer),父节点也可以是纯粹的结构节点。

 □ Workflow agents:SequentialAgent(顺序)、ParallelAgent(并行)、LoopAgent(循环)三种确定性编排原语,它们自己不调用模型,只负责组织子 agent 的执行次序。

 □ 图编排(ADK 2.0 新增):workflow 包提供显式的节点—边(Node/Edge)图编排,支持 agent 节点、函数节点、分支、汇合,兼顾确定性与灵活性。

为什么这个统一如此重要?

因为真实业务几乎总是两类逻辑的混合体。

以我们要做的内容平台为例:选题→写作→审稿是一条确定性流水线(顺序 + 不合格打回重写的循环),而每个环节内部又是自主的 LLM agent(它自己决定怎么写)。

在 ADK 里,这两种逻辑用同一套 agent 抽象表达;在"编排库 + agent 库"拼接的方案里,你得维护两套心智模型。

1.1.3 Go 版功能全景

ADK 家族目前官方支持五种语言(Python、Java、Go、TypeScript/JavaScript、Kotlin),Go 版仓库在 github.com/google/adk-go,模块路径 google.golang.org/adk/v2。图 1-1 是以 Go v2.6.0 源码为准的功能分层图。

  
  
  
  
launcher 启动器层(命令行入口,cmd/launcher)
├─ console ─ 终端 REPL 交互(无参数时的默认模式)
└─ web ─ HTTP 服务器(默认 127.0.0.1:8080)
   ├─ webui            ADK Web UI 调试界面(/ui/)
   ├─ api              ADK REST API(/api)
   ├─ a2a              A2A 协议端点(/a2a/v1/invoke)
   └─ pubsub / eventarc   GCP 云事件触发端点

runner 运行时 + plugin 生命周期钩子(观察/拦截 run、model、tool 各阶段)
   ├─ agent 编排层
   │  ├─ llmagent          LLM 智能体(Instruction / 工具 / 回调 / 子 agent)
   │  ├─ workflowagents    确定性工作流:Sequential / Parallel / Loop
   │  ├─ workflow          图编排(节点 + 边,ADK 2.0 新增)
   │  └─ tool              工具体系:functiontool / mcptoolset / geminitool / agenttool
   └─ model 模型接入层
      ├─ gemini            Gemini 原生接入
      └─ openaimodel       OpenAI 协议接入(官方 openai-go SDK),对接任意兼容端点

状态服务层
   ├─ session    会话与状态(InMemory / database-GORM / vertexai 三种实现)
   ├─ memory     跨会话长期记忆
   └─ artifact   版本化文件存取

telemetry 可观测层(OpenTelemetry 全链路追踪,支持 OTLP 导出)

提示 五个语言版本的成熟度是有差异的:Python 版最完整(评测 evaluation 体系是内置能力),Go 版 2025 年 11 月才官宣、2026 年 6 月底 v2.0 GA,评测等能力仍在跟进中。选 Go 版意味着接受"更年轻",换来的是单二进制部署、无运行时依赖和编译期类型检查——对我们这个要长期演进的内容平台来说,这笔账是划算的。


1.2 为什么值得认真学

框架选型是双向奔赴,先说 ADK 值得投入的理由,再说它与最强劲的对手 LangGraph 的差异。

其一,Google 出品且更新活跃。 表 1-1 是截至 2026 年 10 月 27 日的官方仓库数据:主仓 adk-python 超过 2.2 万 star,发布节奏稳定在每一到两周一个版本;五条语言线在 18 个月里全部走完了"官宣→GA"的历程。对一个开源框架来说,这种投入强度本身就是可持续性的信号。

表 1-1 ADK 五语言版本现状(数据截至 2026-10-27,来自 GitHub API)

语言
仓库
关键节点
活跃度
Python
google/adk-python
2025-04 发布,2025-05 v1.0,2026-05 v2.0
22,021 star,1~2 周一版
Java
google/adk-java
2025-05 首发,2026-03-30 v1.0 GA
1,756 star,约两周一版
Go
google/adk-go
2025-11-07 官宣,2026-06-30 v2.0 GA
8,905 star,v1/v2 双轨维护
TypeScript
google/adk-js
2026-08-21 v2.0 GA
1,451 star
Kotlin
google/adk-kotlin
2026-09-09 v1.0
300 star,面向 Android 与服务端

其二,站在巨人肩膀上,又没有历史包袱。 它把 Google 内部验证过的大规模 agent 工程经验(Agentspace、Customer Engagement Suite 同源)带了进来,但代码是 2025 年全新起笔的——没有十年前为"链式调用"设计的 API 遗产,架构干净得可以直接当教科书读。这一点笔者在读它的 launcher 源码时感受尤深:接口小、职责正交、注释诚实(连"这个 context 参数没用,只是为了签名对齐"都写在注释里)。

其三,理念优异:统一编排 + 全生命周期自包含。 与 LangGraph 的对比,社区流传一句总结得很准的话——"LangGraph accelerates experimentation; ADK industrializes it"(LangGraph 加速实验,ADK 把实验工业化)。展开说:

□ 定位不同:LangGraph 是底层编排库,强项是显式图状态机与细粒度控制,但调试要配 LangSmith、部署要配 LangGraph Platform,工具链是拼装出来的;

ADK 是应用开发框架,Dev UI 调试界面、事件追踪、会话服务、部署路径(Cloud Run / Agent Engine)开箱即用。

 □ 编排表达力已趋同:ADK 2.0 引入 graph workflows 后,显式图编排这块 LangGraph 的传统强项也被补齐了,而"agent 树 + 工作流统一抽象"是 ADK 独有的整洁。

 □ 不是零和关系:ADK 官方把 LangChain/LangGraph 列为互操作伙伴(可以作为 ADK 的组件嵌入),格局上是开放生态。

我的判断:说 ADK 完全不输 LangGraph,在工程完整性上成立,在社区生态的厚度上暂时还不成立——但方向和速度都对。对一个 2026 年才入场的新项目来说,选一个上升期的框架,比押一个已经定型的框架更有想象空间。

轻松一刻 "Hello, World!" 这个传统始于 1978 年 Kernighan 和 Ritchie 的《The C Programming Language》——在此之前,1972 年 Kernighan 写 B 语言教程时用的还是 "hello, world"(小写 w)。半个世纪里,每一代技术的入门仪式都是向机器打一声招呼、等它原样回应。到了 agent 时代,入门仪式悄悄变了味:你打一声招呼,它不光回应,还要思考一下再回应,并且记得你上一句说了什么——本篇稍后控制台里那段"思考过程"的输出,就是新时代的 hello, world。


1.3 我们要用它做什么:一人公司的内容运营产线

交代一下贯穿本系列的项目背景。笔者在经营一家一人公司(OPC,One-Person Company),自媒体内容是核心经营动作之一,这个平台就是用 ADK 把内容生产工程化的载体。

第一个 agent 是 articleAgent——一个"爆款文案工坊",负责标题、文案、内容生成。它是本篇的主角,也是未来整棵 agent 树的第一片叶子:

  
  
  
  
内容运营平台(未来的根编排,SequentialAgent 顺序流水线)
├── 选题 agent(LlmAgent:洞察热点,产出选题卡)
├── 写作 agent(LlmAgent:articleAgent,按风格产出初稿)★ 本篇主角
└── 审稿 agent(LlmAgent:按清单逐项检查,不合格打回)
        └── 打回 ──→ 回到写作 agent(LoopAgent 的循环语义)

1.3.1 通用 Agent 这么强了,为什么还要自己开发?

这是被问得最多的问题,值得认真回答。现在 Claude Code、ZCode 这类通用编码 agent 已经非常强,而且它们本身就能写文章,为什么还要用框架自己开发专用 agent?因为通用 agent 的设计目标是最大化灵活性,而高频内容生产需要的是最大化工序的稳定性。具体来说,痛点集中在三组:

第一组:控制权缺失。 通用 agent 不开放模型生成参数——top_p、温度这些恰恰是内容风格的物理旋钮:爆款文案要的就是高温多样,审稿要的就是低温严格。系统提示词同样不可定制,而系统提示词是内容平台最核心的资产——账号的语气、人设、禁忌词全在里面。这两样在 ADK 里各是一行配置的事(1.4.4 节代码清单 1-5 的④⑤两处),在通用 agent 里却无处安放。

第二组:效果不可优化。 内容是拿来投放市场的,写得好不好要用数据说话,这就要求每次改动都可归因、可对照。基于 skill(技能文件)组织的业务流程,同一个任务两次执行可能走出不同的路径,提示词改一个字的效果也无法和上周的版本对比——不可控的东西就不可优化。多 agent 协作更是如此:通用 agent 自己临时组的"团队"每次都不一样,编排水涨船落,质量全凭运气。专用平台则把流程固化在代码里(编排结构是编译期确定的),把风格固化在配置里(1.4.2 节),每一版提示词、每一个参数都对应一次可复盘的运行。

第三组:成本不可承受。 通用 agent 的 token 账单,大头不在任务本身,而在每笔任务都要背的固定开销:庞大的系统提示词、成套的工具与技能定义、agent 循环自身的管理上下文——哪怕只是起一个文章标题,这些"基础设施"也要先原样支付一遍。探索性任务付这笔钱物有所值,可内容生产是日常高频动作,固定开销乘以频次,账单就难看了。

专用 agent 反其道而行,可以做到极简:一段几句话的系统提示词、零工具冗余、单次精简请求,本篇的 articleAgent 一轮交互的有效负载只有通用 agent 的零头;哪个环节用哪个模型、什么参数,全部自己说了算。

通用 agent 依然是探索性任务的正确工具。但"稳定的产线"它给不了,这活儿该交给 ADK 这样的框架。定位分工,各得其所。


1.4 Hello World 实战:让 articleAgent 跑起来

理论铺垫到此,下面开工。本节的目标:从空目录开始,把 articleAgent agent 在终端和浏览器里跑起来,并且让它记住你说过的话(会话持久化)。

1.4.1 环境准备与依赖安装

环境假设:Go 1.26+(笔者本地 1.27.3),项目模块名 opc-platform。先看工程全貌:

  
  
  
  
jingyuan-opc-agents-platform/
├── article-agent/
│   └── agent.go        # Agent 本体:组模型、组 agent、启动
├── config/
│   └── config.go       # 配置装载:config.toml → 结构体
├── runner/
│   └── session.go      # 会话服务:SQLite 持久化
├── data/
│   └── adk.db          # 会话库(首次运行自动生成)
├── config.toml         # 配置文件(含密钥,勿入库)
└── go.mod

初始化模块并安装直接依赖,如代码清单 1-1 所示。

代码清单 1-1 终端命令:初始化模块并安装依赖

  
  
  
  
go mod init opc-platform
go get google.golang.org/adk/v2@v2.6.0        # ADK 框架本体
go get google.golang.org/genai@v1.76.0        # 生成参数协议类型(Temperature/TopP/Thinking…)
go get github.com/spf13/viper@v1.21.0         # 配置读取
go get github.com/glebarez/sqlite@v1.11.0     # 纯 Go 的 SQLite GORM 驱动
go get gorm.io/gorm@v1.31.2                   # ORM(ADK database 会话服务的底座)
go get github.com/openai/openai-go/v3         # openai协议模型调用支持

这几样各司其职:adk/v2 是框架;genai 提供跨模型统一的生成参数协议(下文会看到它和 Gemini 生态共享一套类型);viper  负责配置装载与热更新;glebarez/sqlite + gorm 负责把会话落到本地文件。

提示 glebarez/sqlite 是纯 Go 实现的 SQLite 驱动,不需要 CGO——这对交叉编译非常友好。常见的 mattn/go-sqlite3 功能更全但要 CGO,在容器里构建会多一层麻烦。另外,ADK 的 database 会话服务接受任意 GORM dialector,runner/session.go 里换成 gorm.io/driver/postgres 就能平移到生产数据库,代码一行不用改(只换 sqlite.Open(...) 那一个参数)。

1.4.2 配置机制:config.toml 与 viper

我们希望"改提示词不重新编译",所以模型端点、系统提示词这些经常要调的东西全部外置到 config.toml,如代码清单 1-2 所示。

代码清单 1-2 config.toml:运行配置(密钥已脱敏)

  
  
  
  
[llm]
openApiKey='sk-xxx'
openApiBase='http://localhost:8000/v1'
model='Qwen3.8-27B'

[bigModel]
openApiKey='************.****'
openApiBase='https://open.bigmodel.cn/api/paas/v4'
model='glm-4.7-flash'

[runnerConfig]
sessionDbUrl="/Users/yourname/jingyuan-opc-agents-platform/data/adk.db"

[articleAgent]
systemPrompt="你是自媒体爆款文案写作达人,唯一的目标是获取自媒体平台用户的注意力,并且吸引用户点击、阅读、点赞、转发、收藏。始终用中文极简回复"

四个配置段两两成对:[llm] 是主力的本地 vLLM 端点(Qwen,OpenAI 兼容协议),[bigModel] 是云端智谱 GLM 端点——双端点为后续"不同环节路由到不同模型"留了口子(写作用本地大模型、分类打标用便宜的 flash 档)。[runnerConfig] 指定会话库路径,[articleAgent] 就是 articleAgent 的系统提示词,1.3 节说的"账号人设资产"落在这里。表 1-2 汇总了各配置项含义。

表 1-2 config.toml 配置项说明

配置项
含义
当前取值说明
llm.openApiKey
 / openApiBase
主模型端点的密钥与地址
本地 vLLM,OpenAI 兼容
llm.model
主模型名
Qwen3.8-27B
bigModel.*
云端备用端点(同构三件套)
智谱 GLM-4.7-flash
runnerConfig.sessionDbUrl
会话库 SQLite 文件路径
建议绝对路径
articleAgent.systemPrompt
写作 agent 系统提示词
账号人设与目标,可热改

读取这份配置的代码在 config/config.go,如代码清单 1-3 所示。

代码清单 1-3 config/config.go:viper 装载配置并注册热更新

  
  
  
  
package config

import (
 "fmt"
 "log"
 "sync"

 "github.com/fsnotify/fsnotify"
 "github.com/spf13/viper"
)

// 读取配置文件config.toml

type RunnerConfig struct {
 SessionDbUrl string
}

type Config struct {
 RunnerConfig RunnerConfig `json:"runnerConfig"`
 LLM          `json:"llm"`
 BigModel     `json:"bigmodel"`
 ArticleAgent `json:"articleAgent"`
}

type LLM struct {
 OpenApiKey  string
 OpenApiBase string
 Model       string
}

type BigModel struct {
 OpenApiKey  string
 OpenApiBase string
 Model       string
}

type ArticleAgent struct {
 SystemPrompt string
}

func loadConfig() Config {
 // 把配置文件读取到结构体上
 var c Config

 viper.SetConfigName("config")
 viper.SetConfigType("toml")
 viper.AddConfigPath("$HOME/My-Agent-Space/jingyuan-opc-agents-platform")
 viper.OnConfigChange(func(e fsnotify.Event) {
  fmt.Println("Config file changed:", e.Name)
  err := viper.ReadInConfig()
  if err != nil {
   log.Fatalf("Fatal error config file: %s \n", err)
  }

  viper.Unmarshal(&config) //将最新配置文件重新绑定到全局config上
 })
 err := viper.ReadInConfig()
 if err != nil {
  log.Fatalf("Fatal error config file: %s \n", err)
 }

 viper.Unmarshal(&c) //将配置文件绑定到config上
 viper.WatchConfig()
 return c
}

func GetConfig() Config {
 load := sync.OnceFunc(func() {
  config = loadConfig()
 })
 load()
 return config
}

var config Config

结构体定义与 TOML 的节一一对应,viper.Unmarshal 会把 [llm] 节的内容绑定到嵌入的 LLM 结构上。四个标注点:

用 SetConfigName + AddConfigPath 告诉 viper 去哪里找 config.toml;Unmarshal 完成配置到结构体的绑定;用 Go 1.21 的 sync.OnceFunc 把 GetConfig 做成懒加载单例——无论被多少个 goroutine 调用,磁盘只读一次。

实战经验 把系统提示词放进 config.toml 而不是写死在代码里,是内容平台的第一条工程纪律。提示词是这个平台迭代最频繁的"生产资料"——今天试一个新人设、明天加一组禁忌词,如果每次都要重新编译部署,A/B 对比的节奏就被编译周期拖垮了。配置化之后,改提示词 = 改一行 TOML + 重启(未来可以做成免重启热加载),git 里还天然留下每一版提示词的演化史。

1.4.3 会话持久化:runner/session.go

ADK 的 launcher.Config 里若不传 SessionService,会话默认存在内存里——进程一退,对话记忆清零。内容运营的 agent 需要跨进程记住上下文,所以我们用 SQLite 把会话落盘,如代码清单 1-4 所示。

代码清单 1-4 runner/session.go:基于 GORM 的 SQLite 会话服务

  
  
  
  
package runner

import (
 "log"
 "os"
 "time"

 "opc-platform/config"

 "github.com/glebarez/sqlite"
 "google.golang.org/adk/v2/session"

 "gorm.io/gorm"
 "gorm.io/gorm/logger"

 "google.golang.org/adk/v2/session/database"
)

func GetSessionService() session.Service {
 sessionService, err := newSessionService(config.GetConfig().RunnerConfig.SessionDbUrl)
 if err != nil {
  log.Fatal(err)
 }
 // 创建表并添加较新 ADK 版本所需的列。
 // 每次应用程序启动时都要运行它。
 if err := database.AutoMigrate(sessionService); err != nil {
  log.Fatal(err)
 }
 return sessionService
}

// newSessionService 基于给定的 SQLite 文件创建 ADK 数据库会话服务。
// 任何 GORM dialector 都可用,例如 PostgreSQL 可使用 gorm.io/driver/postgres。
func newSessionService(dbUrl string) (session.Service, error) {
 // GORM 默认 logger 会把 record not found 当错误打印(ADK 在
 // app_states/user_states 无记录时按正常路径初始化新对象,见
 // fetchStorageAppState/fetchStorageUserState),这里显式忽略该错误;
 // 慢查询告警与真实 DB 错误仍然保留。
 gormLogger := logger.New(
  log.New(os.Stdout, "\r\n", log.LstdFlags),
  logger.Config{
   SlowThreshold:             1000 * time.Millisecond,
   LogLevel:                  logger.Warn,
   IgnoreRecordNotFoundError: true,
   Colorful:                  true,
  },
 )
 return database.NewSessionService(sqlite.Open(dbUrl), &gorm.Config{Logger: gormLogger})
}

这个函数短小,但两处都值得停一下。database.NewSessionService 收的就是一个标准 GORM dialector,所以"换数据库"这件事的全部工作量就是换掉 sqlite.Open(...) 这一个实参——ADK 对存储的态度是把选择权留给业务方,自己只依赖 session.Service 接口(Create/Get/List/Delete/AppendEvent 五个方法)。AutoMigrate 负责建表和补列,源码注释明确说它可重复执行、只增不删——所以惯例是每次启动都调用,框架升级加了字段也能自动补齐,不需要手工管理迁移脚本。

运行之后,data/adk.db 里会出现四张表:sessions(会话元信息)、events(事件流,每轮对话的完整记录)、app_states 与 user_states(应用级/用户级状态),1.4.5 节会实际查验。

1.4.4 Agent 本体:agent.go 逐行精讲

主角登场。article-agent/agent.go 全文 52 行,是本篇的 Hello World,也是以后每个更复杂 agent 的骨架,如代码清单 1-5 所示。

代码清单 1-5 article-agent/agent.go:articleAgent 的完整定义与启动

  
  
  
  
package main

import (
 "context"
 "log"
 "opc-platform/config"
 "opc-platform/runner"
 "os"

 "google.golang.org/adk/v2/agent"
 "google.golang.org/adk/v2/agent/llmagent"
 "google.golang.org/adk/v2/cmd/launcher"
 "google.golang.org/adk/v2/cmd/launcher/full"
 "google.golang.org/adk/v2/model/openaimodel"
 "google.golang.org/genai"
)

func main() {
 //加载统一配置
 conf := config.GetConfig()

 ctx := context.Background()

 // 实例化openai 协议的模型
 model, err := openaimodel.NewModel(context.Background(), conf.LLM.Model,
  &openaimodel.ClientConfig{
   //从配置中设置 api key和 api baseurl
   APIKey:  conf.LLM.OpenApiKey,
   BaseURL: conf.LLM.OpenApiBase,

   //注意,默认是走openai的 response api协议,这里设置成 chat completions api
   API: openaimodel.APIChatCompletions,
  },
 )
 if err != nil {
  log.Fatal(err)
 }

 var topP float32 = 0.95

 //创建 agent
 articleAgent, err := llmagent.New(llmagent.Config{
  Name:                  "articleAgent",
  Model:                 model,
  GenerateContentConfig: &genai.GenerateContentConfig{TopP: &topP, ThinkingConfig: &genai.ThinkingConfig{ThinkingLevel: genai.ThinkingLevelLow}},
  Description:           "负责爆款内容、文案、标题生成",
  Instruction:           conf.ArticleAgent.SystemPrompt,
 })
 if err != nil {
  log.Fatalf("创建智能体失败: %v", err)
 }

 //agent 运行配置:会话怎么存,记忆怎么存等等
 config := &launcher.Config{
  //SessionService不传的话默认是在内存中记录会话,这里我们使用 SQLite 存储会话状态
  SessionService: runner.GetSessionService(),
  AgentLoader:    agent.NewSingleLoader(articleAgent),
 }
 //agent 运行工具,支持直接通过 cli、web、api 调用 我们前面的agent
 l := full.NewLauncher()
 if err = l.Execute(ctx, config, os.Args[1:]); err != nil {
  log.Fatalf("运行失败: %v\n\n%s", err, l.CommandLineSyntax())
 }
}

1.4.5 运行起来:控制台与 Web 两种模式

代码就绪,ADK 给了两种开箱即用的运行形态。进入到 article-agent目录:

  
  
  
  
cd article-agent

1.控制台模式(console)

最简单的启动方式——不带任何参数,默认进 console:

  
  
  
  
go run agent.go           
img.png

2.Web 模式(web + 子服务)

Web 模式把 agent 暴露成 HTTP 服务,自带调试 UI。启动命令:

  
  
  
  
go run agent.go web -port 8098 webui api

实战经验 这是本篇最大的坑:裸写 web 不带子服务关键词,启动会直接失败——no active sublaunchers found - please specify them in the command line. Possible values: [webui a2a pubsub eventarc api]。这与 Python 版 adk web 一条命令全包的行为不同,Go 版要求显式声明要起哪些子服务。日常开发记住 webui api 这对组合即可。

启动成功后控制台输出:

  
  
  
  
2026/10/27 16:23:22 No artifact service configured. Using an in-memory one, so whatever it holds is lost when the process exits.
2026/10/27 16:23:22 No memory service configured. Using an in-memory one, so whatever it holds is lost when the process exits.
2026/10/27 16:23:22 Web servers starts on http://localhost:8098
2026/10/27 16:23:22        webui:  you can access API using http://localhost:8098/ui/
2026/10/27 16:23:22        api:  you can access API using http://localhost:8098/api
2026/10/27 16:23:22        api:      for instance: http://localhost:8098/api/list-apps
2026/10/27 16:23:22        api:      debug excluded: for the web UI's Traces and agent structure panels, use -include_debug_api

开头两行是 1.4.4 节⑥处说过的"降级提醒":未配置的 artifact/memory 服务退化为内存实现。浏览器打开 http://localhost:8098/ui/ 就是 ADK Web UI——可以选 agent、开会话、看事件流的调试界面(想让 Traces 和 agent 结构图面板亮起来,启动时加 -include_debug_api)。

REST API 同步可用。注册的 agent 列表:

  
  
  
  
$ curl -s http://127.0.0.1:8098/api/list-apps
["articleAgent"]

返回的正是代码清单 1-5 里③处的 Name——单 agent 装载器把它注册成了应用名。再查会话列表,之前 console 模式跑出来的会话赫然在列:

  
  
  
  
$ curl -s http://127.0.0.1:8098/api/apps/console_app/users/console_user/sessions
[{"id":"10f1dec4-963e-4348-bcf4-9e0591b6f62d","appName":"console_app","userId":"console_user",
  "lastUpdateTime":1793089324,"events":[],"state":{}}, …]

3.会话落库验证

最后验一下 1.4.3 节的承诺。Web 模式是独立进程,它却能列出 console 进程写入的会话——说明会话确实落在 SQLite 里跨进程共享了。直接查库:

  
  
  
  
$ sqlite3 data/adk.db .tables
app_states   events       sessions     user_states

四张表与 1.4.3 节所述一致。至此,Hello World 全链路闭环:配置装载 → OpenAI 协议接入本地模型 → agent 定义 → 会话持久化 → 双模式运行。

提示 web 模式默认只绑定 127.0.0.1(loopback),不暴露到网络——这是刻意的安全默认值。要在局域网访问需显式 -host 0.0.0.0,但请注意这些端点默认无认证,公网部署前必须配好 launcher.Config 里的 Authenticator/Authorizer 与 -allow_origins。另外 config.toml 含真实密钥,务必加进 .gitignore 再做 git 提交。


1.5 小结

本篇介绍了 ADK 的出身与功能全景——一套把开发、调试、部署、观测装进同一个工具箱、以"agent 树与工作流统一编排"为核心抽象的框架;同时介绍了它值得认真学的理由:Google 出品、更新活跃、架构干净、五语言并进,与 LangGraph 相比胜在工程完整性与自包含。我们还交代了贯穿系列的项目背景:一人公司的内容运营平台,以及为什么高频内容生产需要专用 agent 而非通用 agent——控制权、可优化性、成本三组理由。

最后以 articleAgent 为例,完整走通了 Hello World:六项依赖、viper 配置机制、SQLite 会话持久化、53 行 agent.go 的逐行精讲,以及 console 与 web 两种运行模式的实测。

第一个 agent 已经会写文案、会记事了,但它还是"光杆司令":没有工具(不能查资料、不能读文件)、没有同伴(孤 agent 谈不上编排)、没有记忆(换个会话就不认识你)。

后续我将持续完善,待你抽丝剥茧,敬请期待。

【声明】内容源于网络
0
0
静远AI出海
AI SaaS应用开发脚手架项目Fast SaaS的作者。致力于分享各类AI工具和大型模型的使用经验,探索高效的AI应用开发方法,专注AI应用的出海与变现。
内容 454
粉丝 0
静远AI出海 AI SaaS应用开发脚手架项目Fast SaaS的作者。致力于分享各类AI工具和大型模型的使用经验,探索高效的AI应用开发方法,专注AI应用的出海与变现。
总阅读3.9k
粉丝0
内容454