feat: 初始化工程

This commit is contained in:
2026-08-31 15:22:00 +08:00
commit 825e097989
10 changed files with 287 additions and 0 deletions

68
.gitignore vendored Normal file
View File

@@ -0,0 +1,68 @@
# Python 字节码文件
__pycache__/
*.py[cod]
*$py.class
# C 扩展
*.so
# 分发/打包
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# 虚拟环境
venv/
env/
ENV/
.env
.venv
# 测试
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
# Django 相关
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
media/
# PyCharm IDE
.idea/
*.iml
*.iws
*.ipr
# VS Code
.vscode/
*.code-workspace
.history/
# 其他
.DS_Store
logs/
packages/

11
Dockerfile Normal file
View File

@@ -0,0 +1,11 @@
FROM python:3.12-slim
WORKDIR /app
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' > /etc/timezone
COPY ./packages /app/packages
COPY requirements.txt /app/
RUN pip install --no-cache-dir --no-index --find-links=/app/packages -r requirements.txt
COPY . /app/
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
# pip download -r requirements.txt -d ./packages --only-binary=:all: --platform manylinux2014_x86_64 -i https://pypi.tuna.tsinghua.edu.cn/simple

9
agent/health.py Normal file
View File

@@ -0,0 +1,9 @@
from langchain.agents import create_agent
from agent.model import model
from agent.prompt import tip_prompt
tip_agent = create_agent(
model=model,
system_prompt=tip_prompt
)

21
agent/model.py Normal file
View File

@@ -0,0 +1,21 @@
import os
from dotenv import load_dotenv
from langchain.chat_models import init_chat_model
load_dotenv()
model = init_chat_model(
model=os.getenv('MODEL_NAME'),
model_provider="openai",
base_url=os.getenv('MODEL_BASE_URL'),
api_key=os.getenv('MODEL_API_KEY')
)
# MODEL_NAME=deepseek-v4-flash
# MODEL_BASE_URL=https://api.deepseek.com
# MODEL_API_KEY=sk-0b237d41f6bc44fc9732ea66bd7eade0
# MODEL_NAME="glm-5.2"
# MODEL_BASE_URL="https://dashscope.aliyuncs.com/compatible-mode/v1"
# MODEL_API_KEY="sk-52bcd98e9c1d45908437c4e8706eefff"

58
agent/prompt.py Normal file
View File

@@ -0,0 +1,58 @@
tip_prompt = """
你是一个专业的健康科普作者。请一次性生成5篇健康科普文章分别对应以下5个分类每个分类各1篇。
【分类】
1. 饮食营养
2. 运动健身
3. 睡眠作息
4. 心理健康
5. 常见病预防
【选题要求】
请你从每个分类中自行选择一个具体、实用的细分主题5篇文章的主题互不重复。选题应贴近日常生活适合普通大众阅读。
【每篇文章要求】
1. 标题简洁有力吸引普通读者点击控制在10字以内
2. 概要控制在50字以内概括文章核心观点
3. 正文500字左右语言通俗易懂适合大众阅读避免过多专业术语
4. 内容需有科学依据给出3-5条可操作的实用建议
5. 正文结尾附一句总结金句
6. 文末附免责声明:"本文仅供科普参考,不构成医疗建议。"
7. 不要使用Markdown格式正文中的换行用\n表示
8. 5篇文章的主题和内容互不重复
请严格按以下JSON数组格式输出不要输出任何其他内容
[
{
"title": "文章标题",
"summary": "50字以内的概要",
"category": "分类名称",
"content": "正文内容500字左右"
},
{
"title": "文章标题",
"summary": "50字以内的概要",
"category": "分类名称",
"content": "正文内容500字左右"
},
{
"title": "文章标题",
"summary": "50字以内的概要",
"category": "分类名称",
"content": "正文内容500字左右"
},
{
"title": "文章标题",
"summary": "50字以内的概要",
"category": "分类名称",
"content": "正文内容500字左右"
},
{
"title": "文章标题",
"summary": "50字以内的概要",
"category": "分类名称",
"content": "正文内容500字左右"
}
]
"""

50
main.py Normal file
View File

@@ -0,0 +1,50 @@
from contextlib import asynccontextmanager
from datetime import datetime
from apscheduler.schedulers.background import BackgroundScheduler
from fastapi import FastAPI
from starlette.middleware.cors import CORSMiddleware
from routers import routers
from service import agent_service
scheduler = BackgroundScheduler()
def job_task():
"""定时执行的任务"""
try:
print(f"定时任务执行: {datetime.now()}")
agent_service.generate_tip_agent()
except Exception as e:
print(f"定时任务异常: {e}")
scheduler.add_job(job_task, trigger="cron", hour=2, minute=0, second=0, id="daily_gen_tips", replace_existing=True)
@asynccontextmanager
async def lifespan(app: FastAPI):
# ========== 服务启动阶段 ==========
if not scheduler.running:
scheduler.start()
print("调度器已启动")
yield # 此处服务正常运行,接收请求
# ========== 服务关闭阶段 ==========
scheduler.shutdown()
print("调度器已关闭")
app = FastAPI(title="AI Health Service", lifespan=lifespan)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
for router in routers:
app.include_router(router)

12
requirements.txt Normal file
View File

@@ -0,0 +1,12 @@
fastapi~=0.140.0
python-dotenv~=1.2.2
requests~=2.34.2
langchain~=1.3.14
langchain-core~=1.5.1
langchain-openai~=1.4.1
starlette~=1.3.1
pydantic~=2.13.4
SQLAlchemy~=2.0.51
asyncpg~=0.30.0
uvicorn~=0.23.0
APScheduler~=3.11.3

5
routers/__init__.py Normal file
View File

@@ -0,0 +1,5 @@
from .agent import router as agent_router
routers = [
agent_router
]

15
routers/agent.py Normal file
View File

@@ -0,0 +1,15 @@
from fastapi import APIRouter
from service import agent_service
router = APIRouter(prefix="/agent", tags=["Agent"])
@router.post("/tip/generate")
def generate_tip():
return agent_service.generate_tip_agent()
@router.get("/tip/latest")
def latest_tip():
return agent_service.get_latest_tip()

38
service/agent_service.py Normal file
View File

@@ -0,0 +1,38 @@
import json
from typing import Dict, Any
from langchain_core.messages import HumanMessage
from agent.health import tip_agent
memory_store: Dict[str, Any] = {
"articles": []
}
def generate_tip_agent():
try:
print("开始生成今日5篇健康科普文章")
resp = tip_agent.invoke({
"messages": [HumanMessage(content="请生成今日5篇健康科普文章")]
})
last_msg = resp["messages"][-1]
raw_text = last_msg.content.strip()
# 解析大模型返回的json字符串
articles = json.loads(raw_text)
# 写入内存缓存
memory_store["articles"] = articles
print("结束生成今日5篇健康科普文章")
return True
except json.JSONDecodeError as je:
print(f"[JSON解析错误] {str(je)}")
memory_store["articles"] = []
return False
except Exception as e:
print(f"\n[错误]: {str(e)}")
memory_store["articles"] = []
return False
def get_latest_tip():
return memory_store["articles"]