Python AI Agent 项目技术学习笔记(第二篇)
1. 配置管理系统
1.1 配置结构
文件位置: config/config.py 和 config/default_config.py
核心代码:
from typing import Any
from .default_config import DEFAULT_CONFIG
import os
def _get_env(key: str) -> Any:
# 从环境变量中获取配置项,如果不存在则使用默认值
return os.getenv (key, DEFAULT_CONFIG.get(key))
def _get_bool_env(key: str) -> bool:
value:str = _get_env(key)
return value.lower() == "true" if value is not None else False
class Config:
# 应用配置
def __init__(self):
# 禁用CSRF保护
self.WTF_CSRF_ENABLED = _get_bool_env("WTF_CSRF_ENABLED")
# 数据库配置
self.SQLALCHEMY_DATABASE_URI = _get_env("SQLALCHEMY_DATABASE_URI")
self.SQLALCHEMY_ENG_OPTIONS = {
"pool_size": int(_get_env("SQLALCHEMY_POOL_SIZE")),
"pool_recycle": int(_get_env("SQLALCHEMY_POOL_RECYCLE")),
}
self.SQLALCHEMY_POOL_RECYCLE = int(_get_env("SQLALCHEMY_POOL_RECYCLE"))
工作原理:
- 环境变量优先:首先尝试从环境变量中获取配置
- 默认值 fallback:如果环境变量不存在,使用
DEFAULT_CONFIG中的默认值 - 类型转换:提供了
_get_bool_env等辅助函数进行类型转换 - 集中管理:所有配置集中在
Config类中,方便统一管理
前端类比:
- 类似于前端的
.env文件配置管理 - 类似于 React 的
createContext或 Vue 的provide/inject模式 - 类似于前端的配置管理库(如
dotenv)
1.2 配置使用方式
在 app.py 中的使用:
# 应用配置
conf = Config()
app = Http(
__name__,
router=inject.get(Router),
db=inject.get(SQLAlchemy),
migrate=inject.get(Migrate),
conf=conf,
)
在 Http 类中的使用:
# 应用配置
self.config.from_object(conf)
2. 数据模型与数据库操作
2.1 数据模型定义
文件位置: internal/model/app.py
核心代码:
from typing import Any
from sqlalchemy import (
Column,
Integer,
String,
Text,
DateTime,
UUID,
PrimaryKeyConstraint,
Index,
)
from datetime import datetime
import uuid
from internal.extension.database_extension import db
class App(db.Model):
"""应用表模型"""
__tablename__ = "app"
__table_args__ = (
PrimaryKeyConstraint("id", name="pk_app_id"),
Index("idx_app_account_id", "account_id"),
)
id = Column(UUID, primary_key=True, default=uuid.uuid4, nullable=False, comment="应用ID")
account_id = Column(UUID, nullable=False, comment="账户标识")
name = Column(String(255), default="应用名称", nullable=False, comment="应用名称")
icon = Column(String(255), default="", nullable=False, comment="应用图标")
description = Column(Text, default="", nullable=False, comment="应用描述")
updated_at = Column(
DateTime,
default=datetime.now,
onupdate=datetime.now,
nullable=False,
comment="更新时间",
)
created_at = Column(
DateTime, default=datetime.now, nullable=False, comment="创建时间"
)
def __repr__(self):
return f"<App(id={self.id}, name='{self.name}')>"
关键概念:
- 表结构:使用
__tablename__指定表名 - 约束:使用
__table_args__添加主键约束和索引 - 字段定义:使用
Column定义表字段,包括类型、默认值、约束等 - UUID 类型:使用
UUID类型作为主键,自动生成唯一标识 - 时间字段:使用
DateTime类型,自动设置创建和更新时间 - 魔术方法:使用
__repr__方法定义对象的字符串表示
前端类比:
- 类似于前端的 TypeScript 接口定义
- 类似于前端的 ORM 库(如 Sequelize、Prisma)
- 类似于前端的数据模型定义
2.2 数据库扩展
文件位置: pkg/sqlalchemy/sqlalchemy.py
核心代码:
from flask_sqlalchemy import SQLAlchemy as _SQLAlchemy
class SQLAlchemy(_SQLAlchemy):
# 自定义SQLAlchemy类,用于添加上下文管理器
@contextmanager
def auto_commit(self):
try:
yield
self.session.commit()
except Exception as e:
self.session.rollback()
raise e
关键概念:
- 上下文管理器:使用
@contextmanager装饰器创建上下文管理器 - 事务管理:在
auto_commit方法中处理事务的提交和回滚 - 异常处理:捕获异常并回滚事务,然后重新抛出异常
使用方式:
with self.db.auto_commit():
app = App(name="机器人应用", account_id=uuid.uuid4(), icon="test-icon", description="描述句话")
self.db.session.add(app)
前端类比:
- 类似于前端的 try-catch-finally 结构
- 类似于前端的事务管理库
- 类似于前端的 Promise 链式调用
3. 数据验证系统
3.1 表单验证
文件位置: internal/schema/app_schema.py
核心代码:
from flask_wtf import FlaskForm
from wtforms import StringField
from wtforms.validators import DataRequired
class CompletionReq(FlaskForm):
# 必填
query = StringField(
"query",
validators=[
DataRequired(message="Query 不能为空")
],
)
关键概念:
- 表单类:继承自
FlaskForm创建表单验证类 - 字段定义:使用
StringField定义字符串字段 - 验证器:使用
DataRequired验证器确保字段不为空 - 错误消息:为验证器提供自定义错误消息
使用方式:
# 1.定义请求参数的验证规则
req = CompletionReq(data=request.json)
if not req.validate():
return validate_error_json(req.errors)
前端类比:
- 类似于前端的表单验证库(如 Formik、React Hook Form)
- 类似于前端的验证规则定义
- 类似于前端的错误消息处理
4. 异常处理系统
4.1 异常类定义
文件位置: internal/exception/exception.py
核心代码:
from dataclasses import field
from typing import Any
from pkg.response.http_code import HttpCode
class CustomException(Exception):
code: HttpCode = HttpCode.FAIL
message: str = ""
data: Any = None
def __init__(self, code: HttpCode = HttpCode.FAIL, message: str = "", data: Any = None):
self.code = code
self.message = message
self.data = data
class FailedException(CustomException):
# 通用失败异常
pass
# 未找到数据异常
class NotFoundException(CustomException):
# 未找到数据异常
code = HttpCode.NOT_FOUND
# 未授权异常
class UnauthorizedException(CustomException):
# 未授权异常
code = HttpCode.UNAUTHORIZED
# 无权限异常
class ForbiddenException(CustomException):
# 无权限异常
code = HttpCode.FORBIDDEN
# 验证异常
class ValidationException(CustomException):
# 验证异常
code = HttpCode.VALIDATE_ERROR
关键概念:
- 基础异常类:
CustomException继承自 Python 内置的Exception - 异常属性:包含
code、message和data属性 - 异常子类:创建不同类型的异常子类,如
NotFoundException、UnauthorizedException等 - 默认属性:为子类设置默认的
code属性
使用方式:
# 抛出异常
raise NotFoundException(message="应用不存在")
# 捕获异常
try:
# 业务逻辑
pass
except CustomException as e:
# 处理自定义异常
pass
except Exception as e:
# 处理其他异常
pass
前端类比:
- 类似于前端的自定义错误类
- 类似于前端的错误处理机制
- 类似于前端的错误码定义
4.2 异常处理机制
文件位置: internal/server/http.py
核心代码:
def _register_error_handler(self, error: Exception):
print("异常:", error)
# 处理自定义异常
if isinstance(error, CustomException):
return json(
Response(
code=error.code,
message=error.message,
data=error.data if error.data is not None else {},
)
)
return json(Response(code=HttpCode.FAIL, message=str(error), data={}))
工作原理:
- 异常类型判断:使用
isinstance判断异常类型 - 自定义异常处理:如果是
CustomException类型,使用其属性构建响应 - 通用异常处理:其他异常使用默认的失败状态码和错误信息
- 响应格式化:使用
json函数将Response对象转换为 JSON 响应
前端类比:
- 类似于前端的 try-catch 错误处理
- 类似于前端的错误边界(Error Boundary)
- 类似于前端的全局错误处理中间件
5. 服务层实现
5.1 业务逻辑处理
文件位置: internal/service/app_service.py
核心代码:
from pkg.sqlalchemy import SQLAlchemy
from dataclasses import dataclass
from injector import Injector, inject
from internal.model.app import App
import uuid
@inject
@dataclass
class AppService:
"""应用服务"""
db: SQLAlchemy
def create_app(self):
# 1.创建应用
with self.db.auto_commit():
app = App(name="机器人应用", account_id=uuid.uuid4(), icon="test-icon", description="描述句话")
# 2.将实体添加到数据库会话
self.db.session.add(app)
# 3.提交事务
# self.db.session.commit()
return app
def get_app(self, id: uuid.UUID) -> App:
#根据应用ID获取应用
app = self.db.session.query(App).get(id)
return app
def update_app(self, id: uuid.UUID) -> App:
#根据应用ID更新应用
with self.db.auto_commit():
app = self.db.session.query(App).get(id)
app.name = "更新后的应用"
return app
def delete_app(self, id: uuid.UUID) -> App:
#根据应用ID删除应用
with self.db.auto_commit():
app = self.db.session.query(App).get(id)
self.db.session.delete(app)
return app
关键概念:
- 依赖注入:使用
@inject装饰器注入SQLAlchemy实例 - 数据类:使用
@dataclass装饰器创建数据类 - 数据库操作:使用
db.session进行数据库操作 - 事务管理:使用
with self.db.auto_commit()管理事务 - 类型注解:使用
-> App等类型注解指定返回类型
前端类比:
- 类似于前端的服务层(Service)
- 类似于前端的依赖注入
- 类似于前端的业务逻辑封装
6. 响应处理系统
6.1 响应格式定义
文件位置: pkg/response/response.py
核心代码:
from typing import Any, Dict, Optional
class Response:
def __init__(self, code: str, message: str, data: Any = None):
self.code = code
self.message = message
self.data = data
def to_dict(self) -> Dict[str, Any]:
return {
"code": self.code,
"message": self.message,
"data": self.data if self.data is not None else {},
}
def json(response: Response):
from flask import jsonify
return jsonify(response.to_dict())
def success_json(data: Any = None) -> Any:
from pkg.response.http_code import HttpCode
return json(Response(code=HttpCode.SUCCESS, message="操作成功", data=data))
def fail_json(data: Any = None) -> Any:
from pkg.response.http_code import HttpCode
return json(Response(code=HttpCode.FAIL, message="操作失败", data=data))
def validate_error_json(errors: dict = None) -> Any:
from pkg.response.http_code import HttpCode
# 检查是否有错误信息,如果有则获取第一个错误字段的名称
first_key = next(iter(errors)) if errors else None
# 如果找到错误字段,提取第一个错误信息作为响应消息
if first_key is not None:
msg = errors.get(first_key)[0]
else:
msg = ""
# 创建并返回验证错误响应
return json(Response(code=HttpCode.VALIDATE_ERROR, message=msg, data=errors))
def success_message(message: str) -> Any:
from pkg.response.http_code import HttpCode
return json(Response(code=HttpCode.SUCCESS, message=message, data={}))
def fail_message(message: str) -> Any:
from pkg.response.http_code import HttpCode
return json(Response(code=HttpCode.FAIL, message=message, data={}))
关键概念:
- 响应类:
Response类定义统一的响应格式 - 响应方法:提供
success_json、fail_json、validate_error_json等辅助方法 - JSON 序列化:使用
jsonify将响应对象转换为 JSON - 错误处理:
validate_error_json方法处理表单验证错误
使用方式:
# 成功响应
return success_json({"content": content})
# 失败响应
return fail_json({"error": "操作失败"})
# 验证错误响应
return validate_error_json(req.errors)
# 成功消息响应
return success_message("应用创建成功")
前端类比:
- 类似于前端的响应拦截器
- 类似于前端的统一响应格式
- 类似于前端的错误处理函数
6.2 HTTP 状态码
文件位置: pkg/response/http_code.py
核心代码:
class HttpCode:
"""HTTP状态码"""
SUCCESS = "success" # 成功
FAIL = "fail" # 失败
NOT_FOUND = "not_found" # 未找到
UNAUTHORIZED = "unauthorized" # 未授权
FORBIDDEN = "forbidden" # 无权限
VALIDATE_ERROR = "validate_error" # 验证错误
关键概念:
- 状态码定义:使用类属性定义统一的状态码
- 语义化:状态码使用语义化的字符串,便于理解
- 集中管理:所有状态码集中在一个类中,方便统一管理
前端类比:
- 类似于前端的常量定义
- 类似于前端的错误码枚举
- 类似于前端的状态管理
7. 项目测试系统
7.1 测试结构
文件位置: test/ 目录
核心文件:
test/conftest.py:测试配置和夹具test/internal/handler/test_app_handler.py:应用处理器测试test/test.py:简单测试脚本
conftest.py 核心代码:
import pytest
from app.http.app import app
@pytest.fixture
def client():
# 设置测试模式
app.config["TESTING"] = True
# 创建测试客户端
with app.test_client() as client:
yield client
测试夹具:
- 使用
@pytest.fixture装饰器创建测试夹具 client夹具创建一个测试客户端,用于发送 HTTP 请求- 使用
with语句和yield关键字管理测试客户端的生命周期
前端类比:
- 类似于前端的测试工具(如 Jest)
- 类似于前端的测试夹具(如测试环境设置)
- 类似于前端的集成测试
8. LangChain 学习资源
8.1 学习目录结构
文件位置: study/ 目录
核心目录:
1-Prompt组件及使用技巧/:Prompt 组件的使用方法2-Model组件及使用技巧/:模型组件的使用方法3-OutputParser组件及使用技巧/:输出解析器的使用方法4-LCEL表达式与Runnable可运行协议/:LCEL 表达式的使用方法5-两个Runnable核心类的讲解与使用/:Runnable 类的使用方法6-利用回调功能调试链应用-让过程更透明/:回调功能的使用方法7-Python+OpenAI原生SDK实现记忆功能/:记忆功能的实现方法8-ChatMessageHistory组件上手与源码解析/:聊天消息历史组件的使用方法9-Memory组件运行流程及不同记忆分类/:记忆组件的运行流程10-LangChain缓冲记忆组件的使用与解析/:缓冲记忆组件的使用方法11-LangChain摘要记忆组件的使用与解析/:摘要记忆组件的使用方法12-LangChain实体记忆组件的使用与解析/:实体记忆组件的使用方法13-内置 Chain 组件的使用与解读/:内置 Chain 组件的使用方法14-RunnableWithMessageHistory简化代码与使用/:RunnableWithMessageHistory 的使用方法
学习资源价值:
- 组件详解:详细介绍 LangChain 的各个组件
- 使用技巧:提供实用的使用技巧和示例
- 源码解析:深入解析组件的实现原理
- 进阶功能:介绍 LangChain 的高级功能
前端类比:
- 类似于前端的学习文档
- 类似于前端的示例代码库
- 类似于前端的教程资源
9. 项目部署与运维
9.1 依赖管理
文件位置: requirements.txt
核心依赖:
Flask==3.1.3:Web 框架flask_wtf==1.2.2:表单验证injector==0.24.0:依赖注入openai==2.30.0:OpenAI API 客户端python-dotenv==1.2.2:环境变量管理WTForms==3.2.1:表单验证Flask-SQLAlchemy==3.1.1:ORM 框架langchain-core==1.2.27:LangChain 核心库langchain-openai==1.1.12:LangChain OpenAI 集成langchain==1.2.15:LangChain 完整库langchain-community==0.4.1:LangChain 社区库langchain-classic==1.0.3:LangChain 经典库
安装依赖:
pip3 install -r requirements.txt
前端类比:
- 类似于前端的
package.json - 类似于前端的
npm install - 类似于前端的依赖管理
9.2 环境配置
文件位置: .env(需要创建)
配置示例:
# 数据库配置
SQLALCHEMY_DATABASE_URI="postgresql://username:password@localhost:5432/llmops"
SQLALCHEMY_POOL_SIZE="5"
SQLALCHEMY_POOL_RECYCLE="3600"
# moonshot API 配置
MOONSHOT_API_KEY="your_api_key"
MOONSHOT_API_URL="https://api.moonshot.cn/v1"
# Flask 配置
FLASK_APP="app/http/app.py"
FLASK_ENV="development"
WTF_CSRF_ENABLED="false"
前端类比:
- 类似于前端的
.env文件 - 类似于前端的环境变量配置
- 类似于前端的配置管理
10. 代码优化与最佳实践
10.1 代码优化建议
10.1.1 配置管理优化
当前实现:
# 从环境变量获取API密钥
moonshot_api_key = os.getenv("MOONSHOT_API_KEY")
moonshot_api_url = os.getenv("MOONSHOT_API_URL")
优化建议:
# 从配置对象获取,集中管理配置
moonshot_api_key = self.conf.MOONSHOT_API_KEY
moonshot_api_url = self.conf.MOONSHOT_API_URL
# 添加配置验证
if not moonshot_api_key:
raise CustomException(code=HttpCode.FAIL, message="MOONSHOT_API_KEY 未配置")
if not moonshot_api_url:
raise CustomException(code=HttpCode.FAIL, message="MOONSHOT_API_URL 未配置")
10.1.2 错误处理优化
当前实现:
def _register_error_handler(self, error: Exception):
print("异常:", error)
# 处理自定义异常
if isinstance(error, CustomException):
return json(
Response(
code=error.code,
message=error.message,
data=error.data if error.data is not None else {},
)
)
return json(Response(code=HttpCode.FAIL, message=str(error), data={}))
优化建议:
def _register_error_handler(self, error: Exception):
import traceback
# 记录详细错误信息
print("异常:", error)
traceback.print_exc()
# 处理自定义异常
if isinstance(error, CustomException):
return json(
Response(
code=error.code,
message=error.message,
data=error.data if error.data is not None else {},
)
)
# 处理不同类型的异常
if isinstance(error, RateLimitError):
return json(Response(code=HttpCode.FAIL, message="API 速率限制,请稍后再试", data={}))
elif isinstance(error, FileNotFoundError):
return json(Response(code=HttpCode.FAIL, message="文件不存在", data={}))
return json(Response(code=HttpCode.FAIL, message=str(error), data={}))
10.1.3 记忆管理优化
当前实现:
memory = ConversationBufferWindowMemory(
k=3,
input_key="query",
output_key="output",
return_messages=True,
chat_memory=FileChatMessageHistory("./storage/memory/chat_history.txt"),
)
优化建议:
# 使用应用ID作为文件名,实现每个应用独立的聊天历史
chat_history_file = f"./storage/memory/chat_history_{app_id}.txt"
# 确保目录存在
os.makedirs(os.path.dirname(chat_history_file), exist_ok=True)
memory = ConversationBufferWindowMemory(
k=5, # 增加历史记录数量
input_key="query",
output_key="output",
return_messages=True,
chat_memory=FileChatMessageHistory(chat_history_file),
)
10.2 最佳实践
- 依赖注入:使用
injector库进行依赖注入,提高代码的可测试性和可维护性 - 事务管理:使用
auto_commit上下文管理器管理数据库事务,确保数据一致性 - 异常处理:使用自定义异常类和统一的异常处理机制,提高错误处理的一致性
- 配置管理:使用
Config类集中管理配置,支持环境变量和默认值 - 响应格式:使用统一的响应格式,确保 API 响应的一致性
- 数据验证:使用
FlaskForm进行请求参数验证,提高数据安全性 - 类型注解:使用类型注解提高代码的可读性和可维护性
- 代码组织:按照功能模块组织代码,提高代码的可读性和可维护性
11. 总结
本项目是一个基于 Python Flask 框架的 AI Agent 后端服务,通过第二篇技术学习笔记,我们深入了解了:
- 配置管理系统:如何使用环境变量和默认值管理应用配置
- 数据模型与数据库操作:如何定义数据模型和进行数据库操作
- 数据验证系统:如何使用 FlaskForm 进行请求参数验证
- 异常处理系统:如何定义和处理自定义异常
- 服务层实现:如何封装业务逻辑和进行事务管理
- 响应处理系统:如何统一响应格式和处理错误
- 项目测试系统:如何使用 pytest 进行测试
- LangChain 学习资源:如何使用和理解 LangChain 的各个组件
- 项目部署与运维:如何管理依赖和配置环境
- 代码优化与最佳实践:如何优化代码和遵循最佳实践
作为前端开发者,掌握这些知识可以帮助你:
- 更好地理解后端架构:了解后端的设计思路和实现方法
- 提高全栈开发能力:掌握 Python 后端开发的核心概念
- 优化前后端协作:理解后端 API 的设计和实现,提高前后端协作效率
- 扩展技术栈:学习 Python 和相关库的使用,扩展自己的技术栈
- AI 应用开发:了解如何集成 AI 能力到应用中
通过学习本项目,你可以构建一个完整的 AI Agent 应用,或者将 AI 能力集成到现有的前端应用中。这为你在 AI 时代的技术发展打下了坚实的基础。