165 lines
4.6 KiB
Python
165 lines
4.6 KiB
Python
"""
|
|
FastAPI 主服务 - AI 客服系统
|
|
"""
|
|
import os
|
|
import sys
|
|
import uuid
|
|
from pathlib import Path
|
|
|
|
from fastapi import FastAPI, Request, UploadFile, File
|
|
from fastapi.responses import HTMLResponse
|
|
from fastapi.staticfiles import StaticFiles
|
|
from fastapi.templating import Jinja2Templates
|
|
from pydantic import BaseModel
|
|
import uvicorn
|
|
|
|
# 确保可以导入 app 模块
|
|
sys.path.insert(0, str(Path(__file__).parent))
|
|
|
|
from config import HOST, PORT, BASE_DIR
|
|
from chatbot import get_chatbot
|
|
from knowledge import init_knowledge_base, get_knowledge_base
|
|
|
|
|
|
# 创建 FastAPI 应用
|
|
app = FastAPI(
|
|
title="AI 客服系统",
|
|
description="基于 LangChain + LangGraph 的智能客服",
|
|
version="1.0.0"
|
|
)
|
|
|
|
# 静态文件和模板
|
|
app.mount("/static", StaticFiles(directory=str(BASE_DIR / "static")), name="static")
|
|
templates = Jinja2Templates(directory=str(BASE_DIR / "templates"))
|
|
|
|
# 修复 Jinja2 模板缓存问题
|
|
templates.env.cache.clear() if hasattr(templates.env, 'cache') and hasattr(templates.env.cache, 'clear') else None
|
|
|
|
|
|
# 数据模型
|
|
class ChatRequest(BaseModel):
|
|
message: str
|
|
session_id: str = None
|
|
|
|
|
|
class ChatResponse(BaseModel):
|
|
response: str
|
|
session_id: str
|
|
|
|
|
|
# 路由
|
|
@app.get("/", response_class=HTMLResponse)
|
|
async def index(request: Request):
|
|
"""聊天界面"""
|
|
# 生成新会话ID
|
|
session_id = str(uuid.uuid4())[:8]
|
|
# 直接读取 HTML 文件
|
|
html_path = BASE_DIR / "templates" / "index.html"
|
|
with open(html_path, "r", encoding="utf-8") as f:
|
|
html_content = f.read()
|
|
# 替换 session_id
|
|
html_content = html_content.replace('{{ session_id }}', session_id)
|
|
return HTMLResponse(content=html_content)
|
|
|
|
|
|
@app.post("/chat", response_model=ChatResponse)
|
|
async def chat(request: ChatRequest):
|
|
"""发送消息"""
|
|
chatbot = get_chatbot()
|
|
|
|
# 如果没有 session_id,生成一个
|
|
session_id = request.session_id or str(uuid.uuid4())[:8]
|
|
|
|
# 获取回复
|
|
response = await chatbot.chat(request.message, session_id)
|
|
|
|
return ChatResponse(response=response, session_id=session_id)
|
|
|
|
|
|
@app.get("/history/{session_id}")
|
|
async def get_history(session_id: str):
|
|
"""获取对话历史"""
|
|
chatbot = get_chatbot()
|
|
history = chatbot.get_history(session_id)
|
|
return {"session_id": session_id, "history": history}
|
|
|
|
|
|
@app.post("/upload")
|
|
async def upload_file(file: UploadFile = File(...)):
|
|
"""上传知识库文档"""
|
|
from config import KNOWLEDGE_DIR
|
|
from knowledge import KnowledgeBase
|
|
from langchain.schema import Document
|
|
|
|
# 保存文件
|
|
file_path = KNOWLEDGE_DIR / file.filename
|
|
with open(file_path, "wb") as f:
|
|
content = await file.read()
|
|
f.write(content)
|
|
|
|
# 加载并添加到知识库
|
|
kb = get_knowledge_base()
|
|
|
|
# 简单文本加载
|
|
if file.filename.endswith(".txt") or file.filename.endswith(".md"):
|
|
with open(file_path, "r", encoding="utf-8") as f:
|
|
text = f.read()
|
|
doc = Document(page_content=text, metadata={"source": file.filename})
|
|
kb.add_documents([doc])
|
|
return {"message": f"✅ 已上传并添加到知识库: {file.filename}"}
|
|
|
|
return {"message": f"✅ 已上传: {file.filename},请重启服务加载"}
|
|
|
|
|
|
@app.get("/health")
|
|
async def health_check():
|
|
"""健康检查"""
|
|
return {"status": "ok", "service": "ai-chatbot"}
|
|
|
|
|
|
@app.on_event("startup")
|
|
async def startup_event():
|
|
"""启动时初始化"""
|
|
print("=" * 60)
|
|
print("🤖 AI 客服系统启动中...")
|
|
print("=" * 60)
|
|
|
|
# 检查 API Key
|
|
from config import LLM_API_KEY
|
|
if LLM_API_KEY == "your-deepseek-api-key-here":
|
|
print("⚠️ 警告: 请在 app/config.py 中设置 DEEPSEEK_API_KEY")
|
|
print(" 或者设置环境变量: export DEEPSEEK_API_KEY=your-key")
|
|
else:
|
|
print("✅ API Key 已配置")
|
|
|
|
# 初始化知识库
|
|
print("\n📚 初始化知识库...")
|
|
try:
|
|
kb = get_knowledge_base()
|
|
# 尝试加载已有文档
|
|
from pathlib import Path
|
|
from config import KNOWLEDGE_DIR
|
|
if KNOWLEDGE_DIR.exists():
|
|
docs = list(KNOWLEDGE_DIR.glob("*"))
|
|
if docs:
|
|
print(f" 发现 {len(docs)} 个文档文件")
|
|
else:
|
|
print(" 知识库目录为空,请在 knowledge_base/ 添加文档")
|
|
except Exception as e:
|
|
print(f" ⚠️ 知识库初始化失败: {e}")
|
|
|
|
print("\n🚀 服务已启动!")
|
|
print(f" 访问: http://{HOST}:{PORT}")
|
|
print("=" * 60)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# 开发模式运行
|
|
uvicorn.run(
|
|
"main:app",
|
|
host=HOST,
|
|
port=PORT,
|
|
reload=True,
|
|
log_level="info"
|
|
)
|