1. python项目目录

2. Postgres数据库安装
windows下 get.enterprisedb.com/postgresql
下载地址:www.enterprisedb.com/downloads/p...
安装后可通过
默认安装的 PostgreSQL 会开机自启,可以通过以下步骤关闭开机自启:
1. 按下 win+r 打开运行对话框,输入 services.msc 并回车。
2. 找到 postgres-x64-16,右击选择 属性,将启动方式修改为 手动。
3. 可以右击选择 停止,关闭 postgres 服务。
Windows 下的启动与停止命令
pg_ctl start -D "D:\Software\PostgreSQL\16\data"
pg_ctl stop -D "D:\Software\PostgreSQL\16\data"

3. pycharm使用数据库


4. 30行代码实现一个聊天机器人API
internal---handler---app_handler.py
python
@inject
@dataclass
class AppHandler:
""""应用控制器"""
def completion(self):
"""聊天接口"""
# 1.提取从接口中获取的输入,POST
query = request.json.get("query")
# 2.构建OpenAI客户端,并发起请求
client = OpenAI(api_key="xxxx",
base_url="https://api.xty.app/v1")
# 3.得到请求响应,然后将OpenAI的响应传递给前端
completion = client.chat.completions.create(
model="gpt-3.5-turbo-16k",
messages=[
{"role": "system", "content": "你是OpenAI开发的聊天机器人,请根据用户的输入回复对应的信息"},
{"role": "user", "content": query},
]
)
content = completion.choices[0].message.content
return content
python
@inject
@dataclass
class Router:
"""路由"""
app_handler: AppHandler
def register_router(self, app: Flask):
"""注册路由"""
# 1.创建一个蓝图
bp = Blueprint("llmops", __name__, url_prefix="")
# 2.将url与对应的控制器方法做绑定
bp.add_url_rule("/ping", view_func=self.app_handler.ping)
bp.add_url_rule("/app/completion", methods=["POST"], view_func=self.app_handler.completion)
# 3.在应用上去注册蓝图
app.register_blueprint(bp)
本地运行
python -m app.http.app
5. 校验API接口输入请求
shell
# 将env加载到环境变量中
dotenv.load_dotenv()
.env
ini
# OpenAI服务提供者
OPENAI_API_KEY=sk-OEPO9V0vInTee5WkGDE6tiKBNs5uQYYl7G5iYY8T0ec4Yuvg
OPENAI_API_BASE=https://api.xty.app/v1
internal---handler---app_handler.py
ini
# 2.构建OpenAI客户端,并发起请求
client = OpenAI(base_url=os.getenv("OPENAI_API_BASE"))
internal---schema---app_schema.py
python
class CompletionReq(FlaskForm):
"""基础聊天接口请求验证"""
# 必填、长度最大为2000
query = StringField("query", validators=[
DataRequired(message="用户的提问是必填"),
Length(max=2000, message="用户的提问最大长度是2000"),
])
internal---handler---app_handler.py
perl
# 1.提取从接口中获取的输入,POST
req = CompletionReq()
if not req.validate():
return req.errors
internal---server---app_handler
python
class Http(Flask):
"""Http服务引擎"""
def __init__(
self,
*args,
conf: Config,
db: SQLAlchemy,
migrate: Migrate,
router: Router,
**kwargs,
):
# 1.调用父类构造函数初始化
super().__init__(*args, **kwargs)
# 2.初始化应用配置
self.config.from_object(conf)
# 5.注册应用路由
router.register_router(self)
ruby
class Config:
def __init__(self):
# 关闭wtf的csrf保护
self.WTF_CSRF_ENABLED = _get_bool_env("WTF_CSRF_ENABLED")
6. 异常错误状态统一设计与实现
pkg---response---http_code.py
ini
class HttpCode(str, Enum):
"""HTTP基础业务状态码"""
SUCCESS = "success" # 成功状态
FAIL = "fail" # 失败状态
NOT_FOUND = "not_found" # 未找到
UNAUTHORIZED = "unauthorized" # 未授权
FORBIDDEN = "forbidden" # 无权限
VALIDATE_ERROR = "validate_error" # 数据验证错误
internal---exception---exception.py
python
class CustomException(Exception):
"""基础自定义异常信息"""
code: HttpCode = HttpCode.FAIL
message: str = ""
data: Any = field(default_factory=dict)
def __init__(self, message: str = None, data: Any = None):
super().__init__()
self.message = message
self.data = data
class FailException(CustomException):
"""通用失败异常"""
pass
class NotFoundException(CustomException):
"""未找到数据异常"""
code = HttpCode.NOT_FOUND
class UnauthorizedException(CustomException):
"""未授权异常"""
code = HttpCode.UNAUTHORIZED
class ForbiddenException(CustomException):
"""无权限异常"""
code = HttpCode.FORBIDDEN
class ValidateErrorException(CustomException):
"""数据验证异常"""
code = HttpCode.VALIDATE_ERROR
internal---exception---init.py
csharp
from .exception import (
CustomException,
FailException,
NotFoundException,
UnauthorizedException,
ForbiddenException,
ValidateErrorException,
)
__all__ = [
"CustomException",
"FailException",
"NotFoundException",
"UnauthorizedException",
"ForbiddenException",
"ValidateErrorException",
]
python
class Http(Flask):
"""Http服务引擎"""
def __init__(
self,
*args,
conf: Config,
db: SQLAlchemy,
migrate: Migrate,
router: Router,
**kwargs,
):
# 1.调用父类构造函数初始化
super().__init__(*args, **kwargs)
# 2.初始化应用配置
self.config.from_object(conf)
# 3.注册绑定异常错误处理
self.register_error_handler(Exception, self._register_error_handler)
# 5.注册应用路由
router.register_router(self)
def _register_error_handler(self, error: Exception):
# 1.异常信息是不是我们的自定义异常,如果是可以提取message和code等信息
if isinstance(error, CustomException):
return json(Response(
code=error.code,
message=error.message,
data=error.data if error.data is not None else {},
))
# 2.如果不是我们的自定义异常,则有可能是程序、数据库抛出的异常,也可以提取信息,设置为FAIL状态码
if self.debug or os.getenv("FLASK_ENV") == "development":
raise error
else:
return json(Response(
code=HttpCode.FAIL,
message=str(error),
data={},
))
7. PyTest配置与API测试用例
test---interval---handler---test_app_handler.py
python
class TestAppHandler:
"""app控制器的测试类"""
@pytest.mark.parametrize("query", [None, "你好,你是谁?"])
def test_completion(self, query, client):
resp = client.post("/app/completion", json={"query": query})
assert resp.status_code == 200
if query is None:
assert resp.json.get("code") == HttpCode.VALIDATE_ERROR
else:
assert resp.json.get("code") == HttpCode.SUCCESS
8. Flask-SQLAlchemy扩展的配置与使用
.env
ini
SQLALCHEMY_DATABASE_URI=postgresql://root:xxxx@localhost:5432/llmops?client_encoding=utf8
SQLALCHEMY_POOL_SIZE=30
SQLALCHEMY_POOL_RECYCLE=3600
SQLALCHEMY_ECHO=True
python
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):
# 关闭wtf的csrf保护
self.WTF_CSRF_ENABLED = _get_bool_env("WTF_CSRF_ENABLED")
# 配置数据库配置
self.SQLALCHEMY_DATABASE_URI = _get_env("SQLALCHEMY_DATABASE_URI")
self.SQLALCHEMY_ENGINE_OPTIONS = {
"pool_size": int(_get_env("SQLALCHEMY_POOL_SIZE")),
"pool_recycle": int(_get_env("SQLALCHEMY_POOL_RECYCLE")),
}
self.SQLALCHEMY_ECHO = _get_bool_env("SQLALCHEMY_ECHO")
ini
# 4.初始化flask扩展
db.init_app(self)
migrate.init_app(self, db, directory="internal/migration")
python
class ExtensionModule(Module):
"""扩展模块的依赖注入"""
def configure(self, binder: Binder) -> None:
binder.bind(SQLAlchemy, to=db)
binder.bind(Migrate, to=migrate)
9. 应用ORM模型的创建与增删改查
ini
class App(db.Model):
"""AI应用基础模型类"""
__tablename__ = "app"
__table_args__ = (
PrimaryKeyConstraint("id", name="pk_app_id"),
Index("idx_app_account_id", "account_id"),
)
id = Column(UUID, default=uuid.uuid4, nullable=False)
account_id = Column(UUID, nullable=False)
name = Column(String(255), default="", nullable=False)
icon = Column(String(255), default="", nullable=False)
description = Column(Text, default="", nullable=False)
status = Column(String(255), default="", nullable=False)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now, nullable=False)
created_at = Column(DateTime, default=datetime.now, nullable=False)
internal---service---app_service.py
python
@inject
@dataclass
class AppService:
"""应用服务逻辑"""
db: SQLAlchemy
def create_app(self) -> App:
with self.db.auto_commit():
# 1.创建模型的实体类
app = App(name="测试机器人", account_id=uuid.uuid4(), icon="", description="这是一个简单的聊天机器人")
# 2.将实体类添加到session会话中
self.db.session.add(app)
return app
def get_app(self, id: uuid.UUID) -> App:
app = self.db.session.query(App).get(id)
return app
def update_app(self, id: uuid.UUID) -> App:
with self.db.auto_commit():
app = self.get_app(id)
app.name = "慕课聊天机器人"
return app
def delete_app(self, id: uuid.UUID) -> App:
with self.db.auto_commit():
app = self.get_app(id)
self.db.session.delete(app)
return app
internal---router---
ini
bp.add_url_rule("/app", methods=["POST"], view_func=self.app_handler.create_app)
bp.add_url_rule("/app/<uuid:id>", view_func=self.app_handler.get_app)
bp.add_url_rule("/app/<uuid:id>", methods=["POST"], view_func=self.app_handler.update_app)
bp.add_url_rule("/app/<uuid:id>/delete", methods=["POST"], view_func=self.app_handler.delete_app)
python
# 4.初始化flask扩展
db.init_app(self)
with self.app_context():
_ = App()
db.create_all()
对于这类规律性+重复性的代码,可以考虑使用 ChatGPT 来生成,创建 ORM 模型提示词## 角色
你是一个拥有10年经验的资深Python工程师,精通Flask,Flask-SQLAlchemy,Postgres,以及其他Python开发工具,能够为用户提出的需求或者提供的代码段生成指定的完整代码。
技能说明
-
如果需要实现Flask-SQLAlchemy的ORM类,集成
db.Model时,从from internal.extension.database_extension import db这里导入db; -
创建ORM模型时,表名
__tablename__及类名全部都是单数; -
所有的字段都要添加
nullable=False代表字段不允许为空; -
UUID类型的字段添加默认值
default=uuid.uuid4,String类型的字段长度均设置为String(255),默认值设置为default=""; -
所有模型都有
updated_at和created_at字段,类型均是DateTime,其中updated_at包含default和onupdate,而created_at仅包含default,值全部都是datetime.now; -
请给ORM模型添加上
__table_args__属性,涵盖PrimaryKeyConstraint为主键,所有模型都以id为主键,主键的类型为UUID,如果用户声明其他约束,例如UniqueConstraint,Index等时,请按照需求进行添加; -
属性的类型全部从
sqlalchemy包中导入,例如:from sqlalchemy import (Column, UUID, String, DateTime, PrimaryKeyConstraint, UniqueConstraint); -
uuid.uuid4从import uuid中导入,datetime.now从from datetime import datetime导入; -
对于
description等字段,通过字面意思,可以看出是描述,一般内容比较长,可以使用Text类型; -
用户如果表明了某个字段类型为json,则统一设置成
JSONB,并从from sqlalchemy.dialects.postgresql import JSONB导入,这是Postgres特有的; -
其他的规范请根据你的知识库进行操作,项目使用的数据库是Postgres;


10. 重写SQLAlchemy核心类实现自动提交
pkg---sqlalchemy---sqlalchemy.py
python
class SQLAlchemy(_SQAlchemy):
"""重写Flask-SQLAlchemy中的核心类,实现自动提交"""
@contextmanager
def auto_commit(self):
try:
yield
self.session.commit()
except Exception as e:
self.session.rollback()
raise e
python
with self.db.auto_commit():
11. Flask-Migrate扩展介绍与使用
ini
# 4.初始化flask扩展
db.init_app(self)
migrate.init_app(self, db, directory="internal/migration")
csharp
# 初始化脚本
flask --app app.http.app db init
# 生成迁移脚本
flask --app app.http.app db migrate -m "项目初始化"
flask --app app.http.app db upgrade
flask --app app.http.app db downgrade
flask --app app.http.app db downgrade base
12. LangChain安装
pip install langchain langchain-community