feat:增加AI生成资料接口
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
study_prompt = """
|
||||
question_prompt = """
|
||||
你是一位专业的出题专家,擅长根据学习材料生成高质量的客观题(单选题和多选题)。
|
||||
|
||||
请根据下方【学习材料】生成 5~10 道题目,严格遵守以下要求:
|
||||
@@ -51,3 +51,34 @@ study_prompt = """
|
||||
}
|
||||
]
|
||||
"""
|
||||
|
||||
knowledge_prompt = """
|
||||
你是一位资深教学与教研专家,擅长将各类专业知识系统化梳理,转化为结构清晰、易懂好记的学习资料,适用于不同基础的学习者。
|
||||
|
||||
请根据下方【学科】·【模块】·【名称】生成一份高质量学习资料,**以 Markdown 格式输出**,用于系统学习与复习。
|
||||
|
||||
要求如下:
|
||||
1. 全文控制在 **500~800字**(可根据内容复杂度±10%),语言准确、条理清晰,适合打印或电子阅读。
|
||||
2. Markdown 结构必须包含以下一级或二级标题:
|
||||
- ## 模块概述
|
||||
(定义、作用、学习意义,可简要说明该模块在主题中的地位)
|
||||
- ## 核心知识点
|
||||
(分点讲解,重点概念用 **粗体** 标注)
|
||||
- ## 对比与辨析
|
||||
(对比易混淆概念)
|
||||
- ## 记忆口诀 / 核心规律
|
||||
(简洁押韵或逻辑清晰的归纳,便于记忆)
|
||||
- ## 本章小结
|
||||
(总结本模块最核心的要点)
|
||||
3. 内容需适配当前主题的属性:
|
||||
- 理工类:突出公式、定理、推导过程、解题思路
|
||||
- 医学类:突出机制、适应症、禁忌症、注意事项
|
||||
- 文科 / 法商 / 管理类:突出概念界定、逻辑框架、典型案例
|
||||
4. 写作风格:
|
||||
- 严谨、客观,贴近国内主流教材表述
|
||||
- 避免口语化(如“同学们”“大家注意啦”)
|
||||
- 不假设学习者是在校大学生,兼顾自学者与在职进修人员
|
||||
5. 输出要求:
|
||||
- 仅输出 Markdown 正文,不要包含 `<html>`、JSON 或其他格式
|
||||
- 不要在开头重复本提示词内容
|
||||
"""
|
||||
@@ -1,9 +1,14 @@
|
||||
from langchain.agents import create_agent
|
||||
|
||||
from agent.model import model
|
||||
from agent.prompt import study_prompt
|
||||
from agent.prompt import question_prompt, knowledge_prompt
|
||||
|
||||
study_agent = create_agent(
|
||||
question_agent = create_agent(
|
||||
model=model,
|
||||
system_prompt=study_prompt
|
||||
system_prompt=question_prompt
|
||||
)
|
||||
|
||||
knowledge_agent = create_agent(
|
||||
model=model,
|
||||
system_prompt=knowledge_prompt
|
||||
)
|
||||
|
||||
77
main.py
77
main.py
@@ -1,19 +1,7 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import FastAPI, UploadFile, File, Depends, Form
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from fastapi import FastAPI
|
||||
from starlette.middleware.cors import CORSMiddleware
|
||||
from starlette.responses import StreamingResponse
|
||||
|
||||
from database import get_db
|
||||
from schemas.agent import QueryRequest
|
||||
from schemas.library import LibraryFileRequest, LibraryFileResponse
|
||||
from schemas.mistake import MistakeResponse, MistakeRequest
|
||||
from schemas.practice import PracticeRecordResponse, PracticeRecordRequest
|
||||
from service import library_service, practice_service, mistake_service
|
||||
from service.agent_service import query_agent
|
||||
from storage import upload_rustfs
|
||||
from ocr import ocr_from_url
|
||||
from routers import routers
|
||||
|
||||
app = FastAPI(title="AI Study Service")
|
||||
|
||||
@@ -24,62 +12,5 @@ app.add_middleware(
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
|
||||
@app.post("/library/upload")
|
||||
def upload(file: UploadFile = File(...), filename: str = Form(...)):
|
||||
url = upload_rustfs(file.file, filename)
|
||||
return ocr_from_url(url)
|
||||
|
||||
|
||||
@app.post("/library/question")
|
||||
def generate(query: QueryRequest):
|
||||
"""流式对话"""
|
||||
return StreamingResponse(
|
||||
query_agent(query),
|
||||
media_type="text/event-stream"
|
||||
)
|
||||
|
||||
|
||||
@app.get("/library/files", response_model=List[LibraryFileResponse])
|
||||
async def list_files(db: AsyncSession = Depends(get_db)):
|
||||
return await library_service.list_files(db)
|
||||
|
||||
|
||||
@app.get("/library/files/{id}", response_model=LibraryFileResponse)
|
||||
async def get_file(id: int, db: AsyncSession = Depends(get_db)):
|
||||
return await library_service.get_file(db, id)
|
||||
|
||||
|
||||
@app.post("/library/files", response_model=bool)
|
||||
async def add_file(data: LibraryFileRequest, db: AsyncSession = Depends(get_db)):
|
||||
return await library_service.add_file(db, data)
|
||||
|
||||
|
||||
@app.put("/library/files/{id}", response_model=bool)
|
||||
async def edit_file(id: int, data: LibraryFileRequest, db: AsyncSession = Depends(get_db)):
|
||||
return await library_service.edit_file(db, id, data)
|
||||
|
||||
|
||||
@app.get("/practice/record/{id}", response_model=List[PracticeRecordResponse])
|
||||
async def get_record(id: int, db: AsyncSession = Depends(get_db)):
|
||||
return await practice_service.get_record(db, id)
|
||||
|
||||
|
||||
@app.post("/practice/record", response_model=bool)
|
||||
async def add_record(data: PracticeRecordRequest, db: AsyncSession = Depends(get_db)):
|
||||
return await practice_service.add_record(db, data)
|
||||
|
||||
|
||||
@app.get("/mistake", response_model=List[MistakeResponse])
|
||||
async def list_mistakes(db: AsyncSession = Depends(get_db)):
|
||||
return await mistake_service.list_mistakes(db)
|
||||
|
||||
|
||||
@app.post("/mistake", response_model=bool)
|
||||
async def list_mistakes(data: list[MistakeRequest], db: AsyncSession = Depends(get_db)):
|
||||
return await mistake_service.add_mistakes(db, data)
|
||||
|
||||
|
||||
@app.delete("/mistake/{id}", response_model=bool)
|
||||
async def remove_mistake(id: int, db: AsyncSession = Depends(get_db)):
|
||||
return await mistake_service.remove_mistake(db, id)
|
||||
for router in routers:
|
||||
app.include_router(router)
|
||||
|
||||
9
routers/__init__.py
Normal file
9
routers/__init__.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from .library import router as library_router
|
||||
from .practice import router as practice_router
|
||||
from .mistake import router as mistake_router
|
||||
|
||||
routers = [
|
||||
library_router,
|
||||
practice_router,
|
||||
mistake_router,
|
||||
]
|
||||
55
routers/library.py
Normal file
55
routers/library.py
Normal file
@@ -0,0 +1,55 @@
|
||||
from typing import List
|
||||
from fastapi import APIRouter, Depends, File, Form, UploadFile
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from starlette.responses import StreamingResponse
|
||||
|
||||
from database import get_db
|
||||
from schemas.library import LibraryFileRequest, LibraryFileResponse
|
||||
from schemas.agent import QueryQuestionRequest, QueryKnowledgeRequest
|
||||
from service import library_service, agent_service
|
||||
from storage import upload_rustfs
|
||||
from ocr import ocr_from_url
|
||||
|
||||
router = APIRouter(prefix="/library", tags=["Library"])
|
||||
|
||||
|
||||
@router.post("/upload")
|
||||
def upload(file: UploadFile = File(...), filename: str = Form(...)):
|
||||
url = upload_rustfs(file.file, filename)
|
||||
return ocr_from_url(url)
|
||||
|
||||
|
||||
@router.post("/question")
|
||||
def generate_question(query: QueryQuestionRequest):
|
||||
return StreamingResponse(
|
||||
agent_service.query_question_agent(query),
|
||||
media_type="text/event-stream",
|
||||
)
|
||||
|
||||
|
||||
@router.post("/knowledge")
|
||||
def generate_knowledge(query: QueryKnowledgeRequest):
|
||||
return StreamingResponse(
|
||||
agent_service.query_knowledge_agent(query),
|
||||
media_type="text/event-stream",
|
||||
)
|
||||
|
||||
|
||||
@router.get("/files", response_model=List[LibraryFileResponse])
|
||||
async def list_files(db: AsyncSession = Depends(get_db)):
|
||||
return await library_service.list_files(db)
|
||||
|
||||
|
||||
@router.get("/files/{file_id}", response_model=LibraryFileResponse)
|
||||
async def get_file(file_id: int, db: AsyncSession = Depends(get_db)):
|
||||
return await library_service.get_file(db, file_id)
|
||||
|
||||
|
||||
@router.post("/files", response_model=bool)
|
||||
async def add_file(data: LibraryFileRequest, db: AsyncSession = Depends(get_db)):
|
||||
return await library_service.add_file(db, data)
|
||||
|
||||
|
||||
@router.put("/files/{file_id}", response_model=bool)
|
||||
async def edit_file(file_id: int, data: LibraryFileRequest, db: AsyncSession = Depends(get_db)):
|
||||
return await library_service.edit_file(db, file_id, data)
|
||||
24
routers/mistake.py
Normal file
24
routers/mistake.py
Normal file
@@ -0,0 +1,24 @@
|
||||
from typing import List
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from database import get_db
|
||||
from schemas.mistake import MistakeResponse, MistakeRequest
|
||||
from service import mistake_service
|
||||
|
||||
router = APIRouter(prefix="/mistake", tags=["Mistake"])
|
||||
|
||||
|
||||
@router.get("", response_model=List[MistakeResponse])
|
||||
async def list_mistakes(db: AsyncSession = Depends(get_db)):
|
||||
return await mistake_service.list_mistakes(db)
|
||||
|
||||
|
||||
@router.post("", response_model=bool)
|
||||
async def add_mistakes(data: list[MistakeRequest], db: AsyncSession = Depends(get_db)):
|
||||
return await mistake_service.add_mistakes(db, data)
|
||||
|
||||
|
||||
@router.delete("/{mistake_id}", response_model=bool)
|
||||
async def remove_mistake(mistake_id: int, db: AsyncSession = Depends(get_db)):
|
||||
return await mistake_service.remove_mistake(db, mistake_id)
|
||||
19
routers/practice.py
Normal file
19
routers/practice.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from typing import List
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from database import get_db
|
||||
from schemas.practice import PracticeRecordResponse, PracticeRecordRequest
|
||||
from service import practice_service
|
||||
|
||||
router = APIRouter(prefix="/practice", tags=["Practice"])
|
||||
|
||||
|
||||
@router.get("/record/{user_id}", response_model=List[PracticeRecordResponse])
|
||||
async def get_record(user_id: int, db: AsyncSession = Depends(get_db)):
|
||||
return await practice_service.get_record(db, user_id)
|
||||
|
||||
|
||||
@router.post("/record", response_model=bool)
|
||||
async def add_record(data: PracticeRecordRequest, db: AsyncSession = Depends(get_db), ):
|
||||
return await practice_service.add_record(db, data)
|
||||
@@ -1,5 +1,11 @@
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class QueryRequest(BaseModel):
|
||||
class QueryQuestionRequest(BaseModel):
|
||||
message: str
|
||||
|
||||
|
||||
class QueryKnowledgeRequest(BaseModel):
|
||||
subject: str
|
||||
module: str
|
||||
name: str
|
||||
|
||||
@@ -1,15 +1,32 @@
|
||||
from langchain_core.messages import AIMessageChunk, HumanMessage
|
||||
|
||||
from agent.study import study_agent
|
||||
from schemas.agent import QueryRequest
|
||||
from agent.study import question_agent, knowledge_agent
|
||||
from schemas.agent import QueryQuestionRequest, QueryKnowledgeRequest
|
||||
|
||||
|
||||
def query_agent(query: QueryRequest):
|
||||
def query_question_agent(query: QueryQuestionRequest):
|
||||
try:
|
||||
user_msg = f"学习内容:\n{query.message}"
|
||||
|
||||
# 流式调用Agent
|
||||
for chunk, metadata in study_agent.stream(
|
||||
for chunk, metadata in question_agent.stream(
|
||||
{"messages": [HumanMessage(content=user_msg)]},
|
||||
stream_mode="messages"
|
||||
):
|
||||
if isinstance(chunk, AIMessageChunk):
|
||||
if isinstance(chunk, AIMessageChunk) and chunk.content:
|
||||
yield chunk.content
|
||||
except Exception as e:
|
||||
print(f"\n[错误]: {str(e)}")
|
||||
yield "信息检索失败,请重新输入问题提问"
|
||||
|
||||
|
||||
def query_knowledge_agent(query: QueryKnowledgeRequest):
|
||||
try:
|
||||
user_msg = f"学科:{query.subject},模块:{query.module}, 名称:{query.name}"
|
||||
|
||||
# 流式调用Agent
|
||||
for chunk, metadata in knowledge_agent.stream(
|
||||
{"messages": [HumanMessage(content=user_msg)]},
|
||||
stream_mode="messages"
|
||||
):
|
||||
|
||||
Reference in New Issue
Block a user