writing.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. """
  2. 写作辅助API路由
  3. """
  4. from fastapi import APIRouter, HTTPException
  5. from typing import Dict, Any, Optional
  6. from pydantic import BaseModel
  7. import logging
  8. from core.config import get_config
  9. from core.llm_adapter import get_llm_adapter
  10. logger = logging.getLogger(__name__)
  11. router = APIRouter()
  12. # 初始化 LLM 适配器(基于 HelloAgent)
  13. config = get_config()
  14. try:
  15. llm = get_llm_adapter() if config.llm.api_key else None
  16. except Exception as e:
  17. logger.warning(f"LLM 初始化失败: {str(e)}")
  18. llm = None
  19. # Pydantic模型
  20. class WritingAssistanceRequest(BaseModel):
  21. user_id: str
  22. task_type: str # explain, polish, mimic, suggest
  23. content: str
  24. context: Optional[Dict[str, Any]] = {}
  25. class ExplainRequest(BaseModel):
  26. user_id: str
  27. concept: str
  28. context: Optional[Dict[str, Any]] = {}
  29. class PolishRequest(BaseModel):
  30. user_id: str
  31. text: str
  32. target_style: Optional[str] = "academic"
  33. class WritingCoachRequest(BaseModel):
  34. text: str
  35. style: str = "formal"
  36. task: str = "polish" # polish, translate, explain, expand
  37. context: Optional[Dict[str, Any]] = {}
  38. class MimicRequest(BaseModel):
  39. user_id: str
  40. text: str
  41. target_style: str
  42. reference_papers: Optional[list] = []
  43. context: Optional[Dict[str, Any]] = {}
  44. class SuggestRequest(BaseModel):
  45. user_id: str
  46. text: str
  47. context: Optional[Dict[str, Any]] = {}
  48. @router.post("/coach", response_model=Dict[str, Any])
  49. async def writing_coach(request: WritingCoachRequest):
  50. """写作助手 - 使用真实的 AI 处理"""
  51. try:
  52. if not llm:
  53. raise HTTPException(status_code=503, detail="AI 服务未配置,请设置 OPENAI_API_KEY")
  54. logger.info(f"处理写作任务: {request.task}, 风格: {request.style}")
  55. # 根据任务类型生成提示词
  56. prompts = {
  57. "polish": f"""作为一位专业的学术写作编辑,请帮我润色以下文本,使其符合{request.style}学术写作标准:
  58. 原文:
  59. {request.text}
  60. 请提供:
  61. 1. 润色后的文本(保持原意,提升表达质量)
  62. 2. 具体的改进说明
  63. 3. 写作建议
  64. 要求:
  65. - 保持学术严谨性
  66. - 提升表达清晰度
  67. - 使用恰当的学术用语
  68. - 改善句子结构和逻辑流畅性""",
  69. "translate": f"""请将以下中文学术文本翻译成专业的英文学术论文表达:
  70. 原文:
  71. {request.text}
  72. 要求:
  73. - 保持学术专业性和准确性
  74. - 使用地道的英文学术表达
  75. - 符合{request.style}风格的学术写作规范
  76. - 保持技术术语的准确性""",
  77. "explain": f"""请详细解释以下概念或内容:
  78. {request.text}
  79. 要求:
  80. - 用通俗易懂的语言解释
  81. - 保持技术准确性
  82. - 提供具体例子
  83. - 说明应用场景和重要性""",
  84. "expand": f"""请扩展以下内容,使其更加详细和完整:
  85. 原文:
  86. {request.text}
  87. 要求:
  88. - 添加必要的背景信息
  89. - 补充相关的理论支持
  90. - 扩展方法论描述
  91. - 增加潜在影响和应用
  92. - 保持逻辑连贯性
  93. - 符合{request.style}学术写作风格"""
  94. }
  95. prompt = prompts.get(request.task, prompts["polish"])
  96. # 调用 LLM 处理
  97. response = await llm.ainvoke(prompt)
  98. result_content = response.content if hasattr(response, 'content') else str(response)
  99. return {
  100. "success": True,
  101. "task": request.task,
  102. "style": request.style,
  103. "original": request.text,
  104. "result": result_content
  105. }
  106. except HTTPException:
  107. raise
  108. except Exception as e:
  109. logger.error(f"写作助手处理失败: {str(e)}")
  110. raise HTTPException(status_code=500, detail=f"处理失败: {str(e)}")
  111. @router.post("/explain", response_model=Dict[str, Any])
  112. async def explain_concept(request: ExplainRequest):
  113. """解释复杂概念"""
  114. try:
  115. # 模拟概念解释
  116. return {
  117. "success": True,
  118. "concept": request.concept,
  119. "explanation": f"[Detailed explanation of {request.concept} in accessible terms while maintaining technical accuracy]",
  120. "examples": ["Example 1", "Example 2"],
  121. "timestamp": "2024-01-15T10:30:00Z"
  122. }
  123. except Exception as e:
  124. logger.error(f"概念解释失败: {str(e)}")
  125. raise HTTPException(status_code=500, detail=str(e))
  126. @router.post("/polish", response_model=Dict[str, Any])
  127. async def polish_text(request: PolishRequest):
  128. """润色文本"""
  129. try:
  130. # 模拟文本润色
  131. return {
  132. "success": True,
  133. "original": request.text,
  134. "improved": f"Based on {request.target_style} writing standards, the text can be improved: [Enhanced version]",
  135. "suggestions": ["Use more precise terminology", "Improve sentence structure"],
  136. "timestamp": "2024-01-15T10:30:00Z"
  137. }
  138. except Exception as e:
  139. logger.error(f"文本润色失败: {str(e)}")
  140. raise HTTPException(status_code=500, detail=str(e))
  141. @router.post("/mimic", response_model=Dict[str, Any])
  142. async def mimic_style(request: MimicRequest):
  143. """模仿写作风格"""
  144. try:
  145. # 模拟风格模仿
  146. return {
  147. "success": True,
  148. "original": request.text,
  149. "mimicked": f"[Text rewritten in {request.target_style} style]",
  150. "style_analysis": f"Analysis of {request.target_style} writing characteristics",
  151. "timestamp": "2024-01-15T10:30:00Z"
  152. }
  153. except Exception as e:
  154. logger.error(f"风格模仿失败: {str(e)}")
  155. raise HTTPException(status_code=500, detail=str(e))
  156. @router.post("/suggest", response_model=Dict[str, Any])
  157. async def suggest_improvements(request: SuggestRequest):
  158. """建议改进"""
  159. try:
  160. # 模拟改进建议
  161. return {
  162. "success": True,
  163. "original": request.text,
  164. "suggestions": [
  165. "Consider adding more specific examples",
  166. "Strengthen the introduction",
  167. "Include recent citations",
  168. "Clarify the methodology"
  169. ],
  170. "improved_version": "[Improved version with suggestions applied]",
  171. "timestamp": "2024-01-15T10:30:00Z"
  172. }
  173. except Exception as e:
  174. logger.error(f"改进建议失败: {str(e)}")
  175. raise HTTPException(status_code=500, detail=str(e))
  176. @router.get("/user/{user_id}/style")
  177. async def get_user_writing_style(user_id: str):
  178. """获取用户写作风格"""
  179. try:
  180. # 这里需要实现用户写作风格分析
  181. # 暂时返回模拟结果
  182. style_profile = {
  183. "user_id": user_id,
  184. "writing_style": {
  185. "tone": "formal_academic",
  186. "complexity": "medium",
  187. "sentence_length": "medium",
  188. "vocabulary_richness": "high",
  189. "clarity": "good"
  190. },
  191. "preferred_patterns": [
  192. "句式模式1",
  193. "句式模式2"
  194. ],
  195. "common_phrases": [
  196. "常用短语1",
  197. "常用短语2"
  198. ],
  199. "improvement_areas": [
  200. "改进领域1",
  201. "改进领域2"
  202. ],
  203. "style_evolution": {
  204. "last_month": "上个月的风格变化",
  205. "trend": "improving"
  206. }
  207. }
  208. return {
  209. "success": True,
  210. "style_profile": style_profile
  211. }
  212. except Exception as e:
  213. logger.error(f"获取用户写作风格失败: {str(e)}")
  214. raise HTTPException(status_code=500, detail=str(e))
  215. @router.get("/user/{user_id}/templates")
  216. async def get_writing_templates(user_id: str):
  217. """获取写作模板"""
  218. try:
  219. # 这里需要实现写作模板推荐
  220. # 暂时返回模拟结果
  221. templates = {
  222. "user_id": user_id,
  223. "templates": [
  224. {
  225. "id": "abstract_template",
  226. "name": "摘要模板",
  227. "category": "academic",
  228. "structure": [
  229. "背景介绍",
  230. "问题陈述",
  231. "方法概述",
  232. "主要结果",
  233. "结论意义"
  234. ],
  235. "example": "摘要示例...",
  236. "usage_count": 15
  237. },
  238. {
  239. "id": "introduction_template",
  240. "name": "引言模板",
  241. "category": "academic",
  242. "structure": [
  243. "研究背景",
  244. "相关工作",
  245. "研究空白",
  246. "主要贡献",
  247. "论文结构"
  248. ],
  249. "example": "引言示例...",
  250. "usage_count": 8
  251. }
  252. ],
  253. "recommended_templates": [
  254. "推荐模板1",
  255. "推荐模板2"
  256. ]
  257. }
  258. return {
  259. "success": True,
  260. "templates": templates
  261. }
  262. except Exception as e:
  263. logger.error(f"获取写作模板失败: {str(e)}")
  264. raise HTTPException(status_code=500, detail=str(e))
  265. @router.post("/check/grammar", response_model=Dict[str, Any])
  266. async def check_grammar(text: str, user_id: Optional[str] = None):
  267. """语法检查"""
  268. try:
  269. # 这里需要实现语法检查逻辑
  270. # 暂时返回模拟结果
  271. grammar_check = {
  272. "text": text,
  273. "errors": [
  274. {
  275. "type": "grammar",
  276. "message": "语法错误描述",
  277. "position": {"start": 10, "end": 20},
  278. "suggestion": "修改建议",
  279. "severity": "medium"
  280. }
  281. ],
  282. "suggestions": [
  283. {
  284. "type": "style",
  285. "message": "风格建议",
  286. "position": {"start": 30, "end": 40},
  287. "suggestion": "风格改进建议"
  288. }
  289. ],
  290. "score": 85,
  291. "corrected_text": "修正后的文本..."
  292. }
  293. return {
  294. "success": True,
  295. "grammar_check": grammar_check
  296. }
  297. except Exception as e:
  298. logger.error(f"语法检查失败: {str(e)}")
  299. raise HTTPException(status_code=500, detail=str(e))
  300. @router.post("/check/plagiarism", response_model=Dict[str, Any])
  301. async def check_plagiarism(text: str, user_id: Optional[str] = None):
  302. """抄袭检查"""
  303. try:
  304. # 这里需要实现抄袭检查逻辑
  305. # 暂时返回模拟结果
  306. plagiarism_check = {
  307. "text": text,
  308. "similarity_score": 15.5,
  309. "sources": [
  310. {
  311. "title": "相似文献标题",
  312. "authors": ["作者1", "作者2"],
  313. "similarity": 12.3,
  314. "matched_text": "匹配的文本片段...",
  315. "url": "文献链接"
  316. }
  317. ],
  318. "originality_score": 84.5,
  319. "risk_level": "low",
  320. "recommendations": [
  321. "建议1",
  322. "建议2"
  323. ]
  324. }
  325. return {
  326. "success": True,
  327. "plagiarism_check": plagiarism_check
  328. }
  329. except Exception as e:
  330. logger.error(f"抄袭检查失败: {str(e)}")
  331. raise HTTPException(status_code=500, detail=str(e))