首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >从能跑到能观察——AI 服务的工程化分层与日志

从能跑到能观察——AI 服务的工程化分层与日志

原创
作者头像
dsy
发布2026-08-14 10:17:23
发布2026-08-14 10:17:23
790
举报

前几篇我们把 Agent 包成了 FastAPI 接口(第 4 篇),加了字段校验、接了错误处理。功能都齐了。但有两个问题一直卡着我:

一是看不清。 有一次模型调用失败,前端只返回一句"模型调用失败",我盯着屏幕却不知道:请求到底进没进来?Service 执行到哪一步?具体炸在哪一行?服务能跑,但看不清它怎么跑。

二是改不动。 想给分析加个缓存,发现 Agent 调用、错误处理、日志全挤在一块,只能往 routers.py 里硬塞。能跑,但不等于好维护。

所以这一篇同时解决这两件事:把项目分层,再加日志。

能跑只是 Demo,能分层、能观察,才开始像可维护的服务。


一、项目现在的分层结构

分层之后,目录是这样:

代码语言:txt
复制
app/
├── main.py       应用入口
├── routers.py    API 接口层
├── schemas.py    请求和响应模型
└── services.py   业务服务层

resume_analyzer/
├── agent.py      PydanticAI Agent 调用
├── models.py     Agent 输出结构
└── tools.py      Agent 工具函数

app/ 管 Web 服务和接口,resume_analyzer/ 管 AI 能力本身。两条线分开,后面换模型、加工具都不动接口层。


二、每一层分别管什么

1. main.py —— 启动器

不碰业务。只做:创建 FastAPI app、挂载 router、配全局日志、提供 health check。

2. routers.py —— 接口层

只关心 HTTP:地址是什么、请求/响应模型是什么、异常返回什么状态码。不该知道 Agent 内部细节。

3. schemas.py —— 契约层

AnalyzeRequest / AnalyzeResponse。它告诉调用方:该传什么、会返回什么。前端对接就靠它。

4. services.py —— 业务层

负责业务编排:调 Agent、记日志,以后还能加缓存、重试、数据库、权限。

5. agent.py —— AI 能力层

只管 PydanticAI:定义 Agent、调模型、回结构化结果。

一句话类比:main 是门面,routers 是前台,schemas 是表单,services 是后台办事员,agent 是里面真正干活的专家。各司其职,谁也别越界。


三、最该理解的一层:services.py

它可能现在只是这样:

代码语言:python
复制
def analyze_resume_service(resume_text: str) -> ResumeAnalysis:
    return analyze_resume(resume_text)

看着只是"包了一层",没干啥。但它的价值不在今天,在以后:

代码语言:txt
复制
日志       → 放这
缓存       → 放这
重试       → 放这
数据库保存 → 放这
权限判断   → 放这
调用耗时   → 放这

它的存在,让 routers.py 永远只说"收到请求、返回结果",不用面对复杂业务。


四、为什么需要日志

解决了分层,还得能看清服务内部。AI 服务跑起来后,出问题的地方比普通接口多:

  • API Key 没配置
  • 模型调用失败
  • 网络超时
  • Agent 输出异常
  • 用户输入异常

没有日志时,用户只看到:

代码语言:json
复制
{
  "detail": {
    "code": "MODEL_CALL_FAILED",
    "message": "模型调用失败"
  }
}

这对用户友好,对开发者不够。开发者还需要知道:哪一次请求失败了、失败前执行到了哪、具体异常堆栈是什么。日志就是干这个的。

API 返回给用户看,日志留给开发者看。 两者面对的人不同,该说的内容也不同。


五、main 定规则,services 记业务

1. app/main.py —— 定规则,不写日志

main.py 是入口,在这里配置全局日志格式:

代码语言:python
复制
import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s %(levelname)s %(name)s - %(message)s",
)

它规定的格式是:时间 等级 模块名 - 内容。比如一次真实运行:

代码语言:shell
复制
2026-08-14 10:20:01 INFO app.services - 开始分析简历,文本长度=61

2. app/services.py —— 记业务,才真正写日志

业务层拿到 logger 后,按执行过程记录:

代码语言:python
复制
import logging

logger = logging.getLogger(__name__)
代码语言:python
复制
logger.info("开始分析简历,文本长度=%s", len(resume_text))
# ... 调用 Agent ...
logger.info("简历分析成功")

失败处:

代码语言:python
复制
logger.exception("简历分析失败")

这里有个容易混的点。main.pybasicConfig(...) 不是在写日志,是在定规则——它告诉整个项目显示什么等级、长什么样、带不带模块名。而 services.pylogger.info(...) 才记录具体的事。

代码语言:shell
复制
main.py       配置日志规则(全局一次)
services.py   记录业务日志(每次执行)

这是 Python 服务端最常见的组织方式:规则集中定,业务分散记。


六、别再用 print() 调试

print() 是临时调试,能看但不适合长期维护:

代码语言:python
复制
print("开始分析")

logging 是正式记录,优势是结构化的:

代码语言:python
复制
logger.info("开始分析简历,文本长度=%s", len(resume_text))

它有四样 print 没有的东西:

  • 日志等级(INFO/WARNING/ERROR)
  • 时间戳
  • 模块名(知道谁打的)
  • 能记异常堆栈,以后可落文件或接日志平台

服务端项目里,优先用 logging


七、logger.exception() 是排查利器

这个方法必须放在 except 里:

代码语言:python
复制
try:
    result = analyze_resume(resume_text)
except Exception:
    logger.exception("简历分析失败")
    raise

它的特别之处:不仅记错误信息,还自动带上 traceback。逻辑是——先把详细错误写进日志,再把异常继续抛给上层。routers.py 再把它转成结构化的 API 错误。

一句记住:logger.exception = 记错误 + 记堆栈 + 重新抛出。


八、错误响应和日志的分工

今天最重要的理解就一句:

代码语言:shell
复制
错误响应:给用户 / 前端看
日志记录:给开发者看

模型调用失败时,前端看到的是干净的:

代码语言:json
复制
{
  "detail": {
    "code": "MODEL_CALL_FAILED",
    "message": "模型调用失败",
    "hint": "请检查 API Key、模型名称、网络连接或上游模型服务状态"
  }
}

开发者在终端看到的是原始的:

代码语言:shell
复制
ERROR app.services - 简历分析失败
Traceback (most recent call last):
  ...

这就是分工的价值:用户体验不破,排查线索不丢。


九、今天的核心收获

一个 AI 服务不能只会调模型,还要有清晰的分层、稳定的错误边界、能观察运行过程的日志。

回顾这 5 篇走过的路,项目已经具备:

代码语言:shell
复制
结构化输出   (1) output_type
字段校验     (2) field_validator
工具调用     (2) Tool
Schema/Evidence (3)
API 封装     (4) FastAPI
错误处理     (4) HTTPException
工程分层     (5) services.py
日志记录     (5) logging

这已经是一个小型 AI 后端项目的雏形。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 一、项目现在的分层结构
  • 二、每一层分别管什么
    • 1. main.py —— 启动器
    • 2. routers.py —— 接口层
    • 3. schemas.py —— 契约层
    • 4. services.py —— 业务层
    • 5. agent.py —— AI 能力层
  • 三、最该理解的一层:services.py
  • 四、为什么需要日志
  • 五、main 定规则,services 记业务
    • 1. app/main.py —— 定规则,不写日志
    • 2. app/services.py —— 记业务,才真正写日志
  • 六、别再用 print() 调试
  • 七、logger.exception() 是排查利器
  • 八、错误响应和日志的分工
  • 九、今天的核心收获
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档