news 2026/6/12 11:53:20

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/6/9 22:57:40

谷歌移动端排名和电脑端差距大|做SEO优化要选哪个?

“你的网站在手机和电脑上的谷歌排名差距超过20位?这可能不是偶然。​​自从谷歌推行‘移动优先索引’规则后,许多企业发现移动端排名突然暴跌,甚至出现PC端有排名、移动端完全搜不到的情况。移动端和PC端的流量占比是多少?我们曾…

作者头像 李华
网站建设 2026/6/10 17:30:50

openCV深度劝学:写给所有初学者的四千字血泪心声

openCV深度劝学:写给所有初学者的四千字血泪心声学弟学妹们:此刻是凌晨三点,我刚调试完一个复杂的多目标跟踪算法。窗外寂静无声,而我的电脑屏幕上,一个个检测框正稳定地锁定着视频中的行人。这不是什么了不起的成就&a…

作者头像 李华
网站建设 2026/6/10 19:01:14

【数据结构】败者树、B树、排序、查找

目录 败者树(Loser Tree) B树(B-Tree) 排序算法总结 查找算法总结 败者树(Loser Tree) 多路平衡归并排序(胜者树、败者树)算法详解 - C语言中文网 多路归并、败者树、置换-选择…

作者头像 李华
网站建设 2026/6/10 19:01:35

Agentic AI提示工程的商业价值:如何应对AI技术的快速迭代?

Agentic AI提示工程:破解AI快速迭代困局的商业密钥 一、引言:AI时代的“迭代焦虑”,你中招了吗? 凌晨3点,某电商公司AI产品经理小李还在办公室加班。上周刚上线的智能客服Agent,今天突然收到大量用户投诉—…

作者头像 李华
网站建设 2026/6/10 17:23:54

什么是基于大模型的智能体构建?

在人工智能迅速发展的今天,大语言模型(Large Language Models, LLMs)已经从单纯的文本生成工具演变为推动新一代智能系统的核心引擎。基于大模型的智能体构建,正是这一技术浪潮中最具前景的方向之一。它不仅仅是让机器“说话”或“…

作者头像 李华
网站建设 2026/6/11 23:18:41

Java毕业设计-基于springboot开发的医院信管系统,从零到一构建项目,收藏这篇足够了

文章目录 前言一、毕设成果演示(源代码在文末)二、毕设摘要展示 1.开发说明2.需求分析3、系统功能结构 三、系统实现展示 1、系统功能模块2、管理员功能模块3、 医生功能模块 四、毕设内容和源代码获取总结 Java毕业设计-基于springboot开发的医院信管…

作者头像 李华