AI Agent系列记录(第二篇)

2 阅读14分钟

Python AI Agent 项目技术学习笔记(第二篇)

1. 配置管理系统

1.1 配置结构

文件位置: config/config.pyconfig/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"))

工作原理

  1. 环境变量优先:首先尝试从环境变量中获取配置
  2. 默认值 fallback:如果环境变量不存在,使用 DEFAULT_CONFIG 中的默认值
  3. 类型转换:提供了 _get_bool_env 等辅助函数进行类型转换
  4. 集中管理:所有配置集中在 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
  • 异常属性:包含 codemessagedata 属性
  • 异常子类:创建不同类型的异常子类,如 NotFoundExceptionUnauthorizedException
  • 默认属性:为子类设置默认的 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={}))

工作原理

  1. 异常类型判断:使用 isinstance 判断异常类型
  2. 自定义异常处理:如果是 CustomException 类型,使用其属性构建响应
  3. 通用异常处理:其他异常使用默认的失败状态码和错误信息
  4. 响应格式化:使用 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_jsonfail_jsonvalidate_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 最佳实践

  1. 依赖注入:使用 injector 库进行依赖注入,提高代码的可测试性和可维护性
  2. 事务管理:使用 auto_commit 上下文管理器管理数据库事务,确保数据一致性
  3. 异常处理:使用自定义异常类和统一的异常处理机制,提高错误处理的一致性
  4. 配置管理:使用 Config 类集中管理配置,支持环境变量和默认值
  5. 响应格式:使用统一的响应格式,确保 API 响应的一致性
  6. 数据验证:使用 FlaskForm 进行请求参数验证,提高数据安全性
  7. 类型注解:使用类型注解提高代码的可读性和可维护性
  8. 代码组织:按照功能模块组织代码,提高代码的可读性和可维护性

11. 总结

本项目是一个基于 Python Flask 框架的 AI Agent 后端服务,通过第二篇技术学习笔记,我们深入了解了:

  1. 配置管理系统:如何使用环境变量和默认值管理应用配置
  2. 数据模型与数据库操作:如何定义数据模型和进行数据库操作
  3. 数据验证系统:如何使用 FlaskForm 进行请求参数验证
  4. 异常处理系统:如何定义和处理自定义异常
  5. 服务层实现:如何封装业务逻辑和进行事务管理
  6. 响应处理系统:如何统一响应格式和处理错误
  7. 项目测试系统:如何使用 pytest 进行测试
  8. LangChain 学习资源:如何使用和理解 LangChain 的各个组件
  9. 项目部署与运维:如何管理依赖和配置环境
  10. 代码优化与最佳实践:如何优化代码和遵循最佳实践

作为前端开发者,掌握这些知识可以帮助你:

  1. 更好地理解后端架构:了解后端的设计思路和实现方法
  2. 提高全栈开发能力:掌握 Python 后端开发的核心概念
  3. 优化前后端协作:理解后端 API 的设计和实现,提高前后端协作效率
  4. 扩展技术栈:学习 Python 和相关库的使用,扩展自己的技术栈
  5. AI 应用开发:了解如何集成 AI 能力到应用中

通过学习本项目,你可以构建一个完整的 AI Agent 应用,或者将 AI 能力集成到现有的前端应用中。这为你在 AI 时代的技术发展打下了坚实的基础。