feat: 初始化工程
This commit is contained in:
68
.gitignore
vendored
Normal file
68
.gitignore
vendored
Normal 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
11
Dockerfile
Normal 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
9
agent/health.py
Normal 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
21
agent/model.py
Normal 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
58
agent/prompt.py
Normal 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
50
main.py
Normal 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
12
requirements.txt
Normal 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
5
routers/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from .agent import router as agent_router
|
||||
|
||||
routers = [
|
||||
agent_router
|
||||
]
|
||||
15
routers/agent.py
Normal file
15
routers/agent.py
Normal 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
38
service/agent_service.py
Normal 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"]
|
||||
Reference in New Issue
Block a user