news 2026/4/28 8:13:31

AI - 用 FastAPI 暴露你的第一个 Google ADK Agent

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AI - 用 FastAPI 暴露你的第一个 Google ADK Agent

AI - 用 FastAPI 暴露你的第一个 Google ADK Agent

  • 1. 起点:我们已经有了一个最简单的 Agent
  • 2. 目标架构:把 Agent 变成一个 HTTP 服务
  • 3. 新增一个 api.py:FastAPI 入口 & ADK 运行时
    • 3.1 安装 FastAPI + Uvicorn + python-dotenv(可选)
    • 3.2 会话相关:InMemorySessionService + Runner
  • 4. 完整的 api.py 源码(带 session 支持)
  • 5. 逐段拆解一下关键技术点
    • 5.1 SessionService:为什么要 user_id + session_id?
    • 5.2 Runner:为什么不用直接 root_agent.run()?
    • 5.3 FastAPI:Pydantic 模型 + JSON Body
  • 6. 如何跑起来 & 测试?
    • 6.1 启动服务
    • 6.2 创建会话
    • 6.3 在会话里聊天
    • 6.4 通过 Swagger UI 测试
  • 7. 小结

在上一篇《 AI - 使用 Google ADK 创建你的第一个 AI Agent》我们已经用 Google ADK(Agent Development Kit)写了一个最简单的 Agent:会用 Gemini 模型,能调用一个工具函数,回答“某个城市现在几点”。

这一篇,我们来做一件现实世界里更有用的事情:

把这个 Agent 用 FastAPI 暴露成 HTTP API,
支持多用户、多会话(user_id + session_id),
让前端、移动端、其他服务都可以通过 REST 调用它。

过程中会顺带拆一下几个关键技术点:

  • FastAPI + Uvicorn 是怎么跑起来的
  • ADK 里的 SessionService / Runner 是干嘛的
  • user_id + session_id 在 ADK 中是怎么维护对话上下文的
  • 异步 run_async 和流式事件到底是什么

1. 起点:我们已经有了一个最简单的 Agent

先快速回顾一下我们的 agent.py

# my_agent/agent.pyfromgoogle.adk.agents.llm_agentimportAgent# 一个非常简单的工具函数:返回某个城市的时间(写死)defget_current_time(city:str)->dict:"""Returns the current time in a specified city."""return{"status":"success","city":city,"time":"10:30 AM"}# 根 Agent(ADK 约定必须叫 root_agent)root_agent=Agent(model='gemini-3-pro-preview',# 或其他 Gemini 模型name='root_agent',description="Tells the current time in a specified city.",instruction="You are a helpful assistant that tells the current time in cities. Use the 'get_current_time' tool for this purpose.",tools=[get_current_time],)

项目结构大概是这样:

my_agent/ agent.py # main agent code __init__.py .env # API keys or project IDs

现在,它可以用 adk run my_agent 或 adk web 跑起来,但只能在本机 CLI / Dev UI 里玩,对外没有 HTTP 接口。接下来我们就基于这个项目,加一个 api.py,用 FastAPI 暴露出来。

2. 目标架构:把 Agent 变成一个 HTTP 服务

我们想要的是这样一套能力:

  • POST /session:传入 user_id,创建一个新的对话会话,返回 session_id
  • POST /chat:传入 user_id + session_id + message,Agent 在对应会话上继续对话并返回回复

整体逻辑:

前端 / 其他服务 │ ├─ POST /session -> 返回 session_id │ └─ POST /chat(user_id, session_id, message) │ ▼ FastAPI 路由 │ ▼ ADK Runner + SessionService (负责会话 & state) │ ▼ root_agent.run_async(...) │ ▼ Gemini + tools

其中:

  • FastAPI:Python 高性能 Web 框架,非常适合写 JSON API。
  • Uvicorn:ASGI 服务器,用来实际跑 FastAPI 应用。
  • SessionService:ADK 中管理会话(Session)的组件,提供创建 / 获取 / 列表 / 删除等能力。
  • Runner:ADK 的“执行引擎”,负责拿到 Session、调用 Agent、处理生成的 Event 并写回 Session。

3. 新增一个 api.py:FastAPI 入口 & ADK 运行时

我们计划把 api.py 放到 my_agent/ 包里,所以最终目录变成这样:

my_agent/ ├─ agent.py # 定义 root_agent ├─ api.py # ✅ 新增:FastAPI + Runner + Session └─ __init__.py

3.1 安装 FastAPI + Uvicorn + python-dotenv(可选)

在你的虚拟环境里安装:

pipinstallfastapi uvicorn[standard]python-dotenv
  • FastAPI:写 API 路由,用 Pydantic 定义请求 / 响应模型
  • Uvicorn:跑 ASGI 服务器
  • python-dotenv:方便从 .env 自动加载 GOOGLE_API_KEY 等环境变量

3.2 会话相关:InMemorySessionService + Runner

在 ADK 里,一个 Session 就代表一段连续的对话,里面包含:

  • id:session_id
  • user_id:对应用户
  • events:所有历史对话 / tool 调用等事件
  • state:当前会话的状态字典(短期记忆)

Session 的生命周期由 SessionService 管理,例如:

  • InMemorySessionService:存在内存里,应用重启就没了,很适合本地开发 / demo
  • DatabaseSessionService:持久化到数据库,适合生产环境
  • VertexAiSessionService:把 Session 存到 Vertex AI 的 Agent Engine 里

而 Runner 则负责:

  • 根据 user_id + session_id 找到对应 Session
  • 把新一轮 new_message 加到 Session 的事件里
  • 调用 root_agent.run_async(…),接收流式 Event 并处理
  • 把新的事件和状态写回 Session

4. 完整的 api.py 源码(带 session 支持)

下面是一份可以直接用的 my_agent/api.py,你可以一次性拷贝过去:

# my_agent/api.py""" 用 FastAPI 暴露基于 Google ADK 的第一个 Agent。 - /session : 创建会话,返回 session_id - /chat : 带 user_id + session_id 发消息,支持多轮对话 """importosimportuvicornfromfastapiimportFastAPI,HTTPExceptionfrompydanticimportBaseModelfromdotenvimportload_dotenvfromgoogle.adk.sessionsimportInMemorySessionServicefromgoogle.adk.runnersimportRunnerfromgoogle.genaiimporttypesasgenai_types# 导入我们在 agent.py 里定义好的 root_agentfrom.agentimportroot_agent# ===================== 环境变量 =====================# 加载同级或上级目录下的 .env(包含 GOOGLE_API_KEY 等)load_dotenv()ifnotos.getenv("GOOGLE_API_KEY"):# 不强制报错,只打印一个友好提示print("[WARN] GOOGLE_API_KEY 未设置,""请在 .env 或环境变量中配置,否则模型调用会失败。")# ===================== ADK:SessionService + Runner =====================# 应用名:ADK 用它来区分不同应用的会话空间APP_NAME="my_first_adk_agent"# 1) 会话服务:这里用内存版,适合本地开发 / demosession_service=InMemorySessionService()# 2) Runner:负责从 Session 取历史、调用 Agent、写回事件runner=Runner(app_name=APP_NAME,agent=root_agent,session_service=session_service,)# ===================== FastAPI 初始化 =====================app=FastAPI(title="My First ADK Agent API",description="基于 Google ADK + FastAPI 的简单对话 Agent,支持 user_id + session_id 会话。",)# ===================== 请求 / 响应模型 =====================classCreateSessionRequest(BaseModel):"""创建会话请求:只需要 user_id。"""user_id:strclassCreateSessionResponse(BaseModel):user_id:strsession_id:strclassChatRequest(BaseModel):"""对话请求:必须带 user_id + session_id + message。"""user_id:strsession_id:strmessage:strclassChatResponse(BaseModel):user_id:strsession_id:strreply:str# ===================== 路由:创建会话 =====================@app.post("/session",response_model=CreateSessionResponse)asyncdefcreate_session(req:CreateSessionRequest):""" 创建一个新的 Session: - 输入: user_id - 输出: 自动生成的 session_id """# 注意:SessionService 在新版本 ADK 里是异步的,需要 await。session=awaitsession_service.create_session(app_name=APP_NAME,user_id=req.user_id,state={},# 初始 state,你也可以在这里塞一些默认值)returnCreateSessionResponse(user_id=req.user_id,session_id=session.id,)# ===================== 路由:在指定会话里聊天 =====================@app.post("/chat",response_model=ChatResponse)asyncdefchat(req:ChatRequest):""" 使用 user_id + session_id + message 调用 Runner, 在对应的 Session 上继续对话并返回模型回复。 """# 把用户输入包装成 google-genai 的 Content/Part 结构,# Runner.run_async 需要的就是这个类型。user_content=genai_types.Content(role="user",parts=[genai_types.Part.from_text(text=req.message)],)reply_chunks:list[str]=[]try:# Runner.run_async 是一个异步生成器,会不断 yield Event。asyncforeventinrunner.run_async(user_id=req.user_id,session_id=req.session_id,new_message=user_content,):# 把事件中的文本部分收集起来ifevent.contentandevent.content.parts:forpartinevent.content.parts:ifgetattr(part,"text",None):reply_chunks.append(part.text)# is_final_response() 为 True 时,说明这是最终回复,可以结束本轮ifevent.is_final_response():breakexceptExceptionase:msg=str(e)# 一个常见错误是 session_id 不存在if"session"inmsg.lower()and"not found"inmsg.lower():raiseHTTPException(status_code=404,detail="会话不存在,请先调用 /session 创建会话。",)raiseHTTPException(status_code=500,detail=f"Agent 调用失败:{msg}",)reply_text="".join(reply_chunks).strip()or"(Agent 没有返回任何文本内容)"returnChatResponse(user_id=req.user_id,session_id=req.session_id,reply=reply_text,)# ===================== 本地启动(可选) =====================if__name__=="__main__":# 在项目根目录运行:python -m my_agent.apiuvicorn.run("my_agent.api:app",host="0.0.0.0",port=8001,reload=True)

5. 逐段拆解一下关键技术点

5.1 SessionService:为什么要 user_id + session_id?

ADK 把一段连续对话抽象成一个 Session 对象,由 SessionService 统一管理:

session=awaitsession_service.create_session(app_name=APP_NAME,user_id=req.user_id,state={})
  • app_name:区分是哪个应用的会话(你完全可以在一个 Python 进程里跑多个 Agent)。
  • user_id:谁的会话。
  • session.id:ADK 生成的唯一 session_id。

在后续每一轮对话里,我们都要显式带上这两个维度:

asyncforeventinrunner.run_async(user_id=req.user_id,session_id=req.session_id,new_message=user_content,):...

这相当于告诉 Runner:

“请在 APP_NAME 这个应用里,
找到 user_id = X、session_id = Y 的那条会话,
然后在它的历史基础上继续处理这条新消息。”

ADK 会帮你自动:

  • 从 SessionService 读出该 Session 的 events 和 state
  • 把新消息加入事件链
  • 把 Agent 产生的新事件 / state 变化写回该 Session

所以:

  • user_id 让你可以区分不同用户
  • session_id 让你可以区分同一用户的多段对话(多窗口 / 多任务)

如果以后要做“跨会话的长期记忆”,可以再加上 MemoryService(比如 InMemoryMemoryService / VertexAiMemoryBankService),那就是另外一层了。

5.2 Runner:为什么不用直接 root_agent.run()?

理论上你也可以在 FastAPI 里直接调用:

awaitroot_agent.run(...)

但官方更推荐用 Runner 来跑 Agent,因为 Runner 额外做了很多底层活:

  • 创建 / 获取 Session
  • 把新消息写入 Session 的事件链
  • 处理 Agent 生成的每个 Event,包括:
    • 工具调用
    • 状态变化(state_delta)
    • 产出的内容(content)
  • 把这些变化提交给 SessionService、MemoryService 等

也就是说:

Runner = Agent 的“执行引擎 + 会话协调器”

对你来说,Runner 有两个重要特性:

  • run_async(…) 是异步的(适合 FastAPI 等 async 框架)
  • 返回的是一个 异步生成器(AsyncGenerator[Event]),可以边生成边消费,天然契合“流式输出”的场景

在我们的 /chat 接口里,就是用一个 async for 来消费这些 Event:

asyncforeventinrunner.run_async(...):ifevent.contentandevent.content.parts:...ifevent.is_final_response():break

现在我们只是把所有文本 part 拼起来,做一个简单的“整段回复”。
但你以后完全可以玩得更高级:

  • 逐块推给前端做“打字机效果”
  • 根据不同类型的 Event 做不同 UI(例如工具调用 / 代码执行)

5.3 FastAPI:Pydantic 模型 + JSON Body

FastAPI 的一个特点是:用 Pydantic 模型来声明请求体 / 响应体,自动帮你做验证和文档生成。
比如我们定义:

classChatRequest(BaseModel):user_id:strsession_id:strmessage:str

然后在路由里写:

@app.post("/chat",response_model=ChatResponse)asyncdefchat(req:ChatRequest):...

FastAPI 会自动:

  • 把 JSON 请求体解析成 ChatRequest 对象
  • 在 /docs 里生成 Swagger UI 文档
  • 检查字段类型,不符合直接 422 返回

这一点和 ADK 本身的“结构化输出”(output_schema)非常契合——
前端 / 调用方看的是 FastAPI 的 schema,
Agent 内部看的是 ADK 自己的 state / schema,
中间用 Runner 做桥接。

6. 如何跑起来 & 测试?

6.1 启动服务

在项目根目录(包含 my_agent/ 那层)执行:

uvicorn my_agent.api:app --reload --port8001

或直接运行 api.py 文件,看到类似日志说明启动成功:

INFO: Uvicorn running on http://0.0.0.0:8001(Press CTRL+C to quit)

6.2 创建会话

第一次聊天前,先创建一个 Session:

curl-X POST"http://127.0.0.1:8000/session"\-H"Content-Type: application/json"\-d'{"user_id": "user_123"}'

返回示例:

{"user_id":"user_123","session_id":"c2a4a3d8-2a4e-4c2c-87a0-xxxxxx"}

前端要把这个 session_id 存起来(例如存在 localStorage / cookie / 内存)。

6.3 在会话里聊天

后续每一轮对话都用这个 user_id + session_id:

curl-X POST"http://127.0.0.1:8000/chat"\-H"Content-Type: application/json"\-d'{ "user_id": "user_123", "session_id": "c2a4a3d8-2a4e-4c2c-87a0-xxxxxx", "message": "现在东京几点?" }'

你会收到类似:

{"user_id":"user_123","session_id":"c2a4a3d8-2a4e-4c2c-87a0-xxxxxx","reply":"东京现在是 10:30 AM。"}

再发第二条:

{"user_id":"user_123","session_id":"c2a4a3d8-2a4e-4c2c-87a0-xxxxxx","message":"那和上海差多少?"}

ADK 会自动在同一条 Session 里拼接上下文,让 Agent 有“短期记忆”。

{"user_id":"user_123","session_id":"c2a4a3d8-2a4e-4c2c-87a0-xxxxxx","reply":"东京和上海的当前时间都是上午10:30,所以它们之间没有时差。"}

6.4 通过 Swagger UI 测试

可以访问 http://localhost:8001/docs 通过 UI 来进行会话测试。

7. 小结

我们这篇文章做了几件实打实的事:

  • 在已有的 root_agent 基础上
  • 新增 api.py,用 FastAPI + Uvicorn 暴露 HTTP API
  • 用 InMemorySessionService + Runner 支持 user_id + session_id 的对话会话
  • 拆解了 Session / Runner / FastAPI / GenAI Content 等关键技术点

一句话总结就是:

用 ADK 写 Agent,用 FastAPI 做壳,用 SessionService + Runner 管会话,
你就从“玩具 demo”迈进了“真正可被任何服务调用的 Agent 服务”。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/20 15:55:03

auto后面为赋值或者引用的区别

在C中,auto 用于类型推导,其后续是否使用赋值()或引用(&/&&)会影响推导出的类型和变量的行为。以下是关键区别和示例说明: auto 赋值()—— 值拷贝 行为&am…

作者头像 李华
网站建设 2026/4/25 4:21:26

[特殊字符] Django 4.2+ 从入门到精通:超全面学习指南

作为 Python 生态中最成熟的 Web 框架,Django 以 "电池已内置" 的设计哲学,让开发者无需重复造轮子就能快速构建高性能 Web 应用。本文整理了 Django 完整学习体系,从环境搭建到最佳实践,涵盖核心知识点与实战技巧&…

作者头像 李华
网站建设 2026/4/20 18:51:33

模具管理系统新解:如何用数字化打通全生命周期,降本30%?

模具全生命周期管理的现状与挑战模具作为现代制造业的核心工艺装备,其管理水平直接关系到生产效率、产品质量与综合成本。然而,当前许多制造企业仍停留在“重使用、轻管理”的传统模式中,模具从设计、制造、验收、使用到报废的各个环节往往相…

作者头像 李华
网站建设 2026/4/20 16:54:06

12月最新论文降AI率全流程,附免费降AI方法+降AI率工具

马上就要截稿了,很多同学此时的心情大概是:查重率过了,以为万事大吉,结果一查AI率直接炸了 。 虽然论文是你一个个字敲出来的(或者真的借鉴了一点点),但只要AI检测报告上显示红色预警&#xff…

作者头像 李华