1. RunConfig 到底在配置什么
很多刚开始碰 Agent 开发的朋友,第一次看到 ADK 的 Runtime Config(RunConfig)时,脑子里最大的疑问是:这玩意儿跟直接调模型 API 有什么区别?我一开始也有同样的困惑,直到我把它理解成"给 Agent 写工作条例"之后,一切才变得顺理成章。
举个例子,你招了一个非常有能力的实习生,他知道很多知识、会用很多工具,但你得告诉他:今天用什么电脑工作、做到什么程度算完成、最多加班几小时、出错之后怎么补救、中途的结果要不要记录。RunConfig 干的就是这件事——它不负责教 Agent 怎么想,只负责规定 Agent 怎么跑。
1.1 先搞清楚 Agent 的运行流程
要理解 RunConfig,得先看清 Agent 在 ADK 里的一次完整运行长什么样。整个流程通常分为几个阶段:接收用户输入、调用大模型生成响应或者决策、如果决策里包含工具调用就执行工具、把工具结果带回到上下文里让模型继续推理。这个过程会循环往复,直到模型认为任务完成,或者达到配置的最大轮数。
RunConfig 就夹在这个循环的各个环节之间,像一个调度员,控制每一轮循环中模型怎么调用、输出多长、最多跑几轮、卡住了怎么办。换句话说,模型决定"做什么",RunConfig 决定"怎么跑、跑多久、跑得稳不稳"。
如果你只是本地写个小 Demo,直接用默认配置确实能跑通。但只要你想让 Agent 稳定地完成多步任务、接入真实工具、或者部署到服务端,RunConfig 就必须好好调。
1.2 核心配置项一览
ADK 的 RunConfig 配置项看着多,归纳起来其实就几大类。我整理了一份速查表,方便你对照了解:
| 配置类别 | 核心参数 | 作用 | 典型取值 |
|---|---|---|---|
| 模型配置 | model | 指定 Agent 用哪个底层模型 | "gemini-2.5-flash" 等 |
| 生成参数 | temperature | 控制输出的随机性 | 0.0 到 1.0 不等 |
| 生成参数 | max_output_tokens | 限制单次输出最大 token 数 | 1024 / 2048 等 |
| 执行控制 | max_iterations | 限制 Agent 最大推理轮数 | 3 到 10 不等 |
| 会话管理 | session_state | 保存本轮对话的状态信息 | 自定义对象 |
| 输入输出 | input_schema / output_schema | 定义输入输出格式 | JSON Schema |
| 运行方式 | streaming | 是否流式输出 | True / False |
| 其他 | enable_tracing | 是否开启链路追踪 | True / False |
提示:不同版本 ADK 的 RunConfig 字段名可能会有差异,建议以官方文档为准。这里列的是我目前在用的版本,整体结构比较稳定。
1.3 为什么不能全用默认值
默认配置确实帮新手屏蔽了复杂度,但代价是你对 Agent 的行为几乎没有控制力。我见过不少朋友一开始图省事全用默认,结果 Agent 在简单工具上反复空转直到超时,或者输出被截断导致整个流程失败,最后换回来排查发现都是配置问题。
RunConfig 的价值在于:你可以提前把边界条件定好。比如把最大轮次限制在 5 轮,超出就中止而不是无限等;把 temperature 调低,让 Agent 优先执行工具调用而不是自由发挥。这些看似不起眼的参数,在实际运行中能救你很多次。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 模型配置:Agent 的大脑从哪来
2.1 模型参数怎么设置
在 ADK 里,模型配置是最直观的一块。以 Python 版本为例,你可以直接在实例化 Agent 时传入模型字符串。最简单的写法是:
python复制from google.adk.agents import Agent
agent = Agent(
name="demo_agent",
model="gemini-2.5-flash",
instruction="你是一个帮用户处理日常事务的助手。",
)
这里 model 字段就是告诉 Agent 该调用哪个大模型。你可以在 RunConfig 中进一步细化模型参数,比如通过 generation_config 把温度、最大输出 token 约束好:
python复制from google.adk.agents import Agent
from google.adk.config import GenerationConfig
generation_config = GenerationConfig(
temperature=0.2,
max_output_tokens=4096,
)
agent = Agent(
name="precise_agent",
model="gemini-2.5-flash",
instruction="你是一个严谨的数据分析助手。",
generation_config=generation_config,
)
2.2 特别注意:Kotlin 版目前仅内置 Gemini
我在 KMP(Kotlin Multiplatform)项目里尝试把服务端的 ADK Agent 逻辑移植到移动端时,遇到一个必须提前说明的限制:ADK Kotlin 版本的模型支持目前比较受限,官方内置的只有 Gemini 系列模型。
这个限制意味着两件事。第一,如果你用 Kotlin 版本开发,短期内不需要纠结模型选择,直接用 Gemini 就好,省去了不少适配工作。第二,如果你想在 Kotlin 客户端里接入其他厂商的模型,目前没有那么顺手,可能需要等待官方更新或者额外做模型网关层的适配。
如果你恰好处于"必须用 Kotlin、但业务方要求用非 Gemini 模型"的场景,我的建议是不要把模型选择直接写死在客户端 Agent 里,而是改为"客户端 Agent 只负责编排和状态管理、模型调用统一走后端代理"这样的架构。这样既绕开了限制,又不影响整体流程。
注意:由于 Kotlin 版本迭代很快,模型支持范围可能会随版本更新变化。建议以你实际使用的 SDK 版本支持的模型列表为准。
2.3 Python 版模型选择的灵活性
相比 Kotlin 版,Python 版的模型选择就宽松很多。除了 Gemini 系列之外,通过配置不同的模型适配器,还能接 OpenAI、Claude 或本地部署的模型。这块灵活性对快速原型验证特别有价值。
不过,灵活也伴随着兼容性的代价。不同模型的工具调用格式、上下文长度上限、输出风格差异都很大。同一套工具描述在 Gemini 上表现很好,换到另一个模型上可能频繁出现调用参数格式错误。所以,如果你在多个模型之间切换,一定要在切换后做一遍端到端回归测试,不要只改一个 model 字段就当完事了。
2.4 模型配置的实操心得
我自己的习惯是:把模型字符串放到环境变量或者单独的配置文件里,而不是硬编码在代码中。这样做的好处是开发、测试、生产环境可以各用各的模型,不需要改代码。
bash复制ADK_MODEL_NAME=gemini-2.5-flash
ADK_TEMPERATURE=0.3
配合 dotenv 这类工具加载环境变量,配置和代码解耦,部署的时候只改环境配置即可。这个习惯看起来很小,但实际维护多套环境时能省下大量时间。
3. 执行参数调优:控制 Agent 怎么跑
3.1 温度与随机性:什么时候该调低
Temperature 是生成式模型里最常见的超参数之一。简单来说,值越低,模型输出的确定性越高;值越高,输出越发散、更有创造力。在 Agent 场景里,我的建议是:凡是涉及工具调用、代码生成、数据处理的任务,temperature 尽量往低了设。
我最常用的区间是 0.1 到 0.3。这样 Agent 会倾向于选择最稳妥的路径,不会在工具参数里塞一些天马行空的值。只有在你明确需要 Agent 做头脑风暴、写文案或者生成创意内容时,才考虑把温度提到 0.7 以上。
3.2 max_output_tokens:输出预算要精打细算
另一个需要认真对待的参数是 max_output_tokens。它限制的是模型单次回复的最大 token 数。很多新手在这里踩坑:设得太小,Agent 一次回复还没说完就被截断,尤其在生成代码清单或者长格式文本时,经常出现半截 JSON 的惨案。设得太大,又会拉长推理延迟,增加不必要的 token 成本。
我这里给一个经验值:普通对话场景 1024 够用;涉及代码生成的场景至少给到 4096;如果 Agent 经常输出结构化 JSON 结果,建议结合你输出 schema 的复杂度来估算,宁可多给一些也不能让输出半路截断。
值得提醒的是,max_output_tokens 只限制单次输出,不考虑轮次累积。如果 Agent 需要多轮工具调用,每轮的输出上限是独立计算的。
3.3 最大轮次:防止死循环的关键
Agent 的核心机制是"推理-行动-观察"循环。这个循环理论上可以让 Agent 非常自主,但失控时也容易变成无限空转。所以,设置一个合理的 max_iterations 是必须的。
我之前做过一个带网页搜索工具的 Agent,因为没有限制最大轮次,它在一个搜索没有返回有用结果的情况下,反复尝试了十几次搜索,直到 API 配额耗尽才停下来。加上了 max_iterations=5 之后,虽然极端情况下任务可能无法完成,但至少运行成本可控、行为可预测。
3.4 超时与并发配置
如果你的 Agent 要被多个用户同时调用,你还需要关注并发相关的配置。比如每个请求的超时时间、并发上限、排队策略等。这块在不同部署方式下差别很大,如果你用 ADK 的 FastAPI 服务方式对外提供服务,务必在服务层做好限流和超时控制,否则高并发下很容易出现请求堆积和雪崩。
4. 会话、状态与上下文管理
4.1 会话状态:Agent 的备忘录
Agent 不是无状态的,它需要记住当前任务执行到哪一步、已经收集了什么信息、下一步该做什么。ADK 通过 session 这个概念来管理这类状态信息。
RunConfig 里可以配置会话状态的载体。比如你可以在 session_state 里存一个自定义对象,记录 Agent 在任务执行过程中累积的中间结果:
python复制state = {
"step": 2,
"collected_data": [],
"last_error": None,
}
这种状态管理的价值在于:即使 Agent 的运行因为外部原因中断了,你也能从保存的状态里恢复现场,而不是让用户重新描述一遍需求。
4.2 上下文窗口:能记住多少东西
上下文窗口决定了 Agent 在执行任务时"看得见"多少历史信息。模型通常有一个硬性上下文上限,但实际配置时你通常不会直接设置这个上限,而是通过控制输入的内容来控制。
我实际操作用得最多的技巧是:给 Agent 的指令里只放必要的信息,把长的背景资料放到工具返回结果里按需加载。 比如一个文档分析 Agent,不需要在系统指令里塞入整篇文档,只需要告诉它"文档路径可以从工具返回结果中获取",这样可以有效节省上下文空间。
4.3 状态持久化真的有必要吗
如果你的 Agent 只是单次问答,状态持久化不是必需。但如果你做的是多轮对话 Agent、或者希望用户中断后能继续,那状态持久化就是刚需。
ADK 提供了状态持久化的基础设施,你可以选择把状态存到内存、数据库或者云存储。我的建议是:本地开发用内存就行,生产环境至少用一个可靠的持久化存储,并给状态加版本号,防止 Agent 逻辑升级后新旧状态不兼容。
5. 常见错误与排查实战
5.1 最典型的报错:"agent execution terminated due to error"
这个报错我遇到过太多次了,基本可以把它当作家常便饭。它其实是一个通用错误信息,真正的问题隐藏在日志的堆栈里。常见诱因包括:模型 API 调用失败(配额耗尽、网络超时)、工具调用出错(传参不对、工具不存在)、上下文超长(超过模型上下文上限)、以及输出被安全机制拦截。
遇到这个报错,第一件事不是改代码,而是去看完整日志。ADK 在开启 tracing 之后会打印出完整的调用链和错误堆栈,定位一次报错通常只需要几分钟。
5.2 排查清单
| 症状 | 常见原因 | 快速检查方法 |
|---|---|---|
| 报错后立即终止 | API key 无效或配额不足 | 直接调用模型接口测试 |
| 第一轮就报错 | 工具描述格式错误 | 检查工具 schema 合法性 |
| 执行到中途报错 | 上下文超过模型限制 | 看输入 token 数统计 |
| 时好时坏 | 网络波动或超时设置太短 | 增加重试次数和超时时间 |
| 输出格式异常导致失败 | 输出被截断 | 调大 max_output_tokens |
5.3 一个隐藏很深的坑:工具返回结果过大
还有一个平时不容易注意到、但一旦踩中就特别头疼的问题:工具返回结果过大。比如你的 Agent 调用了一个数据库查询工具,返回了上万行数据,这些数据会全部塞进上下文。轻则导致后续推理变慢,重则直接撑爆上下文窗口,触发上面那个终止报错。
解决方案有两个层面。第一层,在工具内部做数据截断,只返回前几十条记录并附一个汇总信息。第二层,在 Agent 指令里明确告诉模型:"如果工具返回数据量过大,请先请求汇总接口,不要直接输出全部原始数据。"这两层叠加,基本能规避大部分这类问题。
6. 一个完整的实战示例
6.1 需求描述
理论讲了不少,我来分享一个最近实际跑通的场景。我打算做一个"会议纪要整理 Agent",输入是一段会议录音转写文本,输出是一份结构化的纪要,包含议题、结论、待办事项和负责人。
这个任务看起来简单,但实际上对 Agent 有一定的多步推理要求:先提炼议题,再归纳结论,再识别待办事项,最后按固定格式输出。如果只用原始模型直接生成,输出格式经常会乱,所以我在配置上做了比较完整的约束。
6.2 完整配置与代码
python复制import os
from google.adk.agents import Agent
from google.adk.config import GenerationConfig
agent = Agent(
name="meeting_minutes_agent",
model=os.getenv("ADK_MODEL_NAME", "gemini-2.5-flash"),
instruction="""
你是一个会议纪要整理助手。用户会给你一段会议转写文本。
请按以下步骤处理:
1. 提炼本次会议的核心议题,输出议题列表。
2. 针对每个议题,总结讨论结论。
3. 提取所有待办事项,标明负责人和截止时间(如果有)。
4. 将结果按 JSON 格式输出。
注意事项:
- 如果原文中没有明确提及负责人,请标注"待确认"。
- 如果原文信息不足,请在结论中注明"信息不足"。
""",
generation_config=GenerationConfig(
temperature=0.1,
max_output_tokens=4096,
),
)
result = agent.run("这里是会议转写文本……")
print(result)
核心配置思路是这样的:temperature 调到 0.1 保证输出风格稳定;max_output_tokens 给到 4096,因为加上了 JSON 格式约束之后,输出长度通常比自由回答要长;指令里明确了处理步骤和边界条件,让 Agent 遇到信息缺失时不至于自己脑补。
实际跑下来,几段不同风格的会议记录都能稳定输出规范 JSON。偶尔出现格式漂移,只要把示例输出格式补到指令里,问题就解决了。
6.3 运行结果与效果对比
我拿同一份会议记录分别跑了两次:一次用全默认配置,一次用上面这套配置。默认配置的输出虽然内容大体正确,但格式不稳定,有时把待办事项混在结论里,有时忘记标注负责人。改成上面这套配置之后,输出格式基本完全对齐了预期。
这个对比其实说明了 RunConfig 的核心价值:它不是给 Agent 增加能力,而是把能力约束到你需要的方向上去。
6.4 个人经验补充
最后分享一个我常用的调参技巧:先全默认把流程跑通,再一项一项加配置。 不要第一次就跑完整配置,这样出了问题很难判断是哪个参数引起的。我会先设一个 max_iterations 防止死循环,然后把 temperature 调到 0.2,接着把输出 token 调大,最后再补 schema 和指令约束,每加一层都实测一轮。这种方法虽然慢一点,但排查起来特别高效。
跑通之后,记得把整套配置存成配置文件或者环境变量,方便复用。我目前手头几个类型的 Agent(文本处理类、检索增强类、多工具编排类)都沉淀了一套各自的配置模板,新项目直接套用再微调,开发效率提升非常明显。
