本文来自《AI 应用开发课程》月份 1 课程文档,已整理为网站文章版本。
学习目标
学完本节后,你应当能够:
- 用 Pydantic 为 FastAPI 设计请求模型和响应模型。
- 明确月份 1 API 的最小接口形状。
- 理解错误响应为什么也要建模。
前置知识
- 已完成 Pydantic 模块
- 已能启动 FastAPI 最小服务
1. 月份 1 的 API 目标
本月只要求稳定实现 3 个接口:
GET /healthPOST /chatPOST /tools/run
所以你的请求响应模型必须围绕这 3 个接口收束,不要过度设计。
2. 统一模型建议
from typing import Any, Literal
from pydantic import BaseModel, Field
ChatRole = Literal["system", "user", "assistant", "tool"]
class ChatMessage(BaseModel):
role: ChatRole
content: str
class ChatRequest(BaseModel):
messages: list[ChatMessage]
model: str = "deepseek-chat"
temperature: float = Field(default=0.2, ge=0, le=2)
class ChatResponse(BaseModel):
answer: str
model: str
class ToolRunRequest(BaseModel):
tool_name: str
arguments: dict[str, Any]
class ToolRunResponse(BaseModel):
tool_name: str
success: bool
output: str
class ErrorResponse(BaseModel):
error_code: str
message: str
3. 为什么错误响应也要有模型
错误如果没有统一结构,客户端和日志都很难稳定处理。
例如:
{
"error_code": "TOOL_NOT_FOUND",
"message": "未找到工具 summarize_diff"
}
这比直接返回一段不稳定字符串更适合系统协作。
4. 请求模型设计原则
- 字段尽量少而必要
- 默认值要合理
- 约束能写就写,例如
temperature范围 - 名称要和核心业务一致
5. 响应模型设计原则
- 优先返回调用方真正需要的数据
- 不要暴露内部实现细节
- 成功结构和错误结构分开
6. 实操任务
- 在
app/models.py中定义上述模型 - 在
/chat路由里使用ChatRequest和ChatResponse - 在
/tools/run路由里使用ToolRunRequest和ToolRunResponse
7. 自测题
- 为什么请求模型和响应模型最好分开定义?
- 为什么
temperature应该做范围约束? - 为什么错误响应不能只返回“请求失败”四个字?
8. 作业与验收
验收标准:
- 核心接口都有请求响应模型
- 错误结构有统一格式
- 你能解释每个模型服务于哪个接口
9. 常见错误
- 一个模型同时兼任请求和响应
arguments字段没有说明用途- 错误响应结构和正常响应结构混用
10. 本章与前文关系
上一章起的是“服务壳子”,这一章要往壳子里放“协议结构”。这一步的意义在于:
- 让客户端知道要发什么
- 让服务端知道要收什么
- 让错误也有稳定结构
没有这一层,后面 /chat 和 /tools/run 很快就会退化成“接口能跑,但不知道协议是否清晰”。
11. 本章在研发助手项目中的位置
综合项目里的:
- CLI 输入
- FastAPI 请求体
- LLM service 输入
- Tool service 输出
都会围绕这些统一模型做映射。你现在设计的不是“接口字段名”,而是项目不同层之间的共同契约。
12. 为什么请求模型和响应模型必须分开
这是一个很基础但很容易被忽略的工程原则。
请求模型关心输入意图
例如:
- 传了哪些消息
- 用什么模型
- 温度是多少
响应模型关心输出结果
例如:
- 最终回答是什么
- 使用了哪个模型
- 工具是否成功执行
如果你把两者混在一个模型里,后面维护会非常混乱。
13. 错误示例 vs 正确示例
错误示例:一个模型包打天下
class ChatData(BaseModel):
messages: list[dict]
answer: str | None = None
问题:
- 输入和输出混在一起
- 字段边界不清晰
- 文档自动生成效果也会变差
正确示例:按职责拆开
class ChatRequest(BaseModel):
...
class ChatResponse(BaseModel):
...
这会让你的协议在结构上更稳定。
14. 进一步解释 ToolRunRequest 和 ToolRunResponse
很多人对 /tools/run 的理解会有偏差,以为它只是“传一段文本,看结果”。其实它更像一个结构化执行接口。
ToolRunRequest
回答的问题是:
- 我现在要执行哪个工具?
- 工具参数是什么?
ToolRunResponse
回答的问题是:
- 执行成功了吗?
- 返回内容是什么?
如果你不把这两个问题拆开,工具接口通常会变得难以维护。
15. 一个更接近工程的增强点
你可以为错误响应加上更明确的错误码:
class ErrorResponse(BaseModel):
error_code: str
message: str
这看似简单,但价值很大,因为:
- CLI 可以据此打印更明确提示
- API 调用方可以据此分支处理
- 日志里更好统计错误类型
16. 调试与排错:本章最常见问题
问题一:请求数据和模型字段对不上
这时 422 往往不是坏事,而是在提前告诉你协议没对齐。
问题二:错误响应结构没有统一
这会让调用方很难写稳定处理逻辑。
问题三:模型字段命名不断漂移
月份 1 最需要的是稳定命名,不要每个文件都写一套自己的字段名。
17. 本章完成后你应该具备的能力
完成本章后,你应当做到:
- 能解释请求模型、响应模型和错误模型的区别。
- 能围绕
/chat和/tools/run设计清晰 schema。 - 能理解这些 schema 不只是给 FastAPI 用,也是给项目内部不同层做统一契约。
18. 如果你卡在这里,先回看哪几章
- Pydantic 还不稳:回看 03-pydantic与配置建模.md
- 消息结构不稳:回看 02-消息结构与上下文管理.md
19. 从本章过渡到下一章的桥接说明
接下来进入 03-chat与tools接口实现.md。
你现在已经有:
- 服务壳子
- 请求响应模型
下一步就是把它们真正接起来:让 /chat 调用聊天 service,让 /tools/run 调用工具 service,并保持路由层、业务层和 client 层边界清晰。