realtimex-deeptutor 0.5.0.post1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. realtimex_deeptutor/__init__.py +67 -0
  2. realtimex_deeptutor-0.5.0.post1.dist-info/METADATA +1612 -0
  3. realtimex_deeptutor-0.5.0.post1.dist-info/RECORD +276 -0
  4. realtimex_deeptutor-0.5.0.post1.dist-info/WHEEL +5 -0
  5. realtimex_deeptutor-0.5.0.post1.dist-info/entry_points.txt +2 -0
  6. realtimex_deeptutor-0.5.0.post1.dist-info/licenses/LICENSE +661 -0
  7. realtimex_deeptutor-0.5.0.post1.dist-info/top_level.txt +2 -0
  8. src/__init__.py +40 -0
  9. src/agents/__init__.py +24 -0
  10. src/agents/base_agent.py +657 -0
  11. src/agents/chat/__init__.py +24 -0
  12. src/agents/chat/chat_agent.py +435 -0
  13. src/agents/chat/prompts/en/chat_agent.yaml +35 -0
  14. src/agents/chat/prompts/zh/chat_agent.yaml +35 -0
  15. src/agents/chat/session_manager.py +311 -0
  16. src/agents/co_writer/__init__.py +0 -0
  17. src/agents/co_writer/edit_agent.py +260 -0
  18. src/agents/co_writer/narrator_agent.py +423 -0
  19. src/agents/co_writer/prompts/en/edit_agent.yaml +113 -0
  20. src/agents/co_writer/prompts/en/narrator_agent.yaml +88 -0
  21. src/agents/co_writer/prompts/zh/edit_agent.yaml +113 -0
  22. src/agents/co_writer/prompts/zh/narrator_agent.yaml +88 -0
  23. src/agents/guide/__init__.py +16 -0
  24. src/agents/guide/agents/__init__.py +11 -0
  25. src/agents/guide/agents/chat_agent.py +104 -0
  26. src/agents/guide/agents/interactive_agent.py +223 -0
  27. src/agents/guide/agents/locate_agent.py +149 -0
  28. src/agents/guide/agents/summary_agent.py +150 -0
  29. src/agents/guide/guide_manager.py +500 -0
  30. src/agents/guide/prompts/en/chat_agent.yaml +41 -0
  31. src/agents/guide/prompts/en/interactive_agent.yaml +202 -0
  32. src/agents/guide/prompts/en/locate_agent.yaml +68 -0
  33. src/agents/guide/prompts/en/summary_agent.yaml +157 -0
  34. src/agents/guide/prompts/zh/chat_agent.yaml +41 -0
  35. src/agents/guide/prompts/zh/interactive_agent.yaml +626 -0
  36. src/agents/guide/prompts/zh/locate_agent.yaml +68 -0
  37. src/agents/guide/prompts/zh/summary_agent.yaml +157 -0
  38. src/agents/ideagen/__init__.py +12 -0
  39. src/agents/ideagen/idea_generation_workflow.py +426 -0
  40. src/agents/ideagen/material_organizer_agent.py +173 -0
  41. src/agents/ideagen/prompts/en/idea_generation.yaml +187 -0
  42. src/agents/ideagen/prompts/en/material_organizer.yaml +69 -0
  43. src/agents/ideagen/prompts/zh/idea_generation.yaml +187 -0
  44. src/agents/ideagen/prompts/zh/material_organizer.yaml +69 -0
  45. src/agents/question/__init__.py +24 -0
  46. src/agents/question/agents/__init__.py +18 -0
  47. src/agents/question/agents/generate_agent.py +381 -0
  48. src/agents/question/agents/relevance_analyzer.py +207 -0
  49. src/agents/question/agents/retrieve_agent.py +239 -0
  50. src/agents/question/coordinator.py +718 -0
  51. src/agents/question/example.py +109 -0
  52. src/agents/question/prompts/en/coordinator.yaml +75 -0
  53. src/agents/question/prompts/en/generate_agent.yaml +77 -0
  54. src/agents/question/prompts/en/relevance_analyzer.yaml +41 -0
  55. src/agents/question/prompts/en/retrieve_agent.yaml +32 -0
  56. src/agents/question/prompts/zh/coordinator.yaml +75 -0
  57. src/agents/question/prompts/zh/generate_agent.yaml +77 -0
  58. src/agents/question/prompts/zh/relevance_analyzer.yaml +39 -0
  59. src/agents/question/prompts/zh/retrieve_agent.yaml +30 -0
  60. src/agents/research/agents/__init__.py +23 -0
  61. src/agents/research/agents/decompose_agent.py +507 -0
  62. src/agents/research/agents/manager_agent.py +228 -0
  63. src/agents/research/agents/note_agent.py +180 -0
  64. src/agents/research/agents/rephrase_agent.py +263 -0
  65. src/agents/research/agents/reporting_agent.py +1333 -0
  66. src/agents/research/agents/research_agent.py +714 -0
  67. src/agents/research/data_structures.py +451 -0
  68. src/agents/research/main.py +188 -0
  69. src/agents/research/prompts/en/decompose_agent.yaml +89 -0
  70. src/agents/research/prompts/en/manager_agent.yaml +24 -0
  71. src/agents/research/prompts/en/note_agent.yaml +121 -0
  72. src/agents/research/prompts/en/rephrase_agent.yaml +58 -0
  73. src/agents/research/prompts/en/reporting_agent.yaml +380 -0
  74. src/agents/research/prompts/en/research_agent.yaml +173 -0
  75. src/agents/research/prompts/zh/decompose_agent.yaml +89 -0
  76. src/agents/research/prompts/zh/manager_agent.yaml +24 -0
  77. src/agents/research/prompts/zh/note_agent.yaml +121 -0
  78. src/agents/research/prompts/zh/rephrase_agent.yaml +58 -0
  79. src/agents/research/prompts/zh/reporting_agent.yaml +380 -0
  80. src/agents/research/prompts/zh/research_agent.yaml +173 -0
  81. src/agents/research/research_pipeline.py +1309 -0
  82. src/agents/research/utils/__init__.py +60 -0
  83. src/agents/research/utils/citation_manager.py +799 -0
  84. src/agents/research/utils/json_utils.py +98 -0
  85. src/agents/research/utils/token_tracker.py +297 -0
  86. src/agents/solve/__init__.py +80 -0
  87. src/agents/solve/analysis_loop/__init__.py +14 -0
  88. src/agents/solve/analysis_loop/investigate_agent.py +414 -0
  89. src/agents/solve/analysis_loop/note_agent.py +190 -0
  90. src/agents/solve/main_solver.py +862 -0
  91. src/agents/solve/memory/__init__.py +34 -0
  92. src/agents/solve/memory/citation_memory.py +353 -0
  93. src/agents/solve/memory/investigate_memory.py +226 -0
  94. src/agents/solve/memory/solve_memory.py +340 -0
  95. src/agents/solve/prompts/en/analysis_loop/investigate_agent.yaml +55 -0
  96. src/agents/solve/prompts/en/analysis_loop/note_agent.yaml +54 -0
  97. src/agents/solve/prompts/en/solve_loop/manager_agent.yaml +67 -0
  98. src/agents/solve/prompts/en/solve_loop/precision_answer_agent.yaml +62 -0
  99. src/agents/solve/prompts/en/solve_loop/response_agent.yaml +90 -0
  100. src/agents/solve/prompts/en/solve_loop/solve_agent.yaml +75 -0
  101. src/agents/solve/prompts/en/solve_loop/tool_agent.yaml +38 -0
  102. src/agents/solve/prompts/zh/analysis_loop/investigate_agent.yaml +53 -0
  103. src/agents/solve/prompts/zh/analysis_loop/note_agent.yaml +54 -0
  104. src/agents/solve/prompts/zh/solve_loop/manager_agent.yaml +66 -0
  105. src/agents/solve/prompts/zh/solve_loop/precision_answer_agent.yaml +62 -0
  106. src/agents/solve/prompts/zh/solve_loop/response_agent.yaml +90 -0
  107. src/agents/solve/prompts/zh/solve_loop/solve_agent.yaml +76 -0
  108. src/agents/solve/prompts/zh/solve_loop/tool_agent.yaml +41 -0
  109. src/agents/solve/solve_loop/__init__.py +22 -0
  110. src/agents/solve/solve_loop/citation_manager.py +74 -0
  111. src/agents/solve/solve_loop/manager_agent.py +274 -0
  112. src/agents/solve/solve_loop/precision_answer_agent.py +96 -0
  113. src/agents/solve/solve_loop/response_agent.py +301 -0
  114. src/agents/solve/solve_loop/solve_agent.py +325 -0
  115. src/agents/solve/solve_loop/tool_agent.py +470 -0
  116. src/agents/solve/utils/__init__.py +64 -0
  117. src/agents/solve/utils/config_validator.py +313 -0
  118. src/agents/solve/utils/display_manager.py +223 -0
  119. src/agents/solve/utils/error_handler.py +363 -0
  120. src/agents/solve/utils/json_utils.py +98 -0
  121. src/agents/solve/utils/performance_monitor.py +407 -0
  122. src/agents/solve/utils/token_tracker.py +541 -0
  123. src/api/__init__.py +0 -0
  124. src/api/main.py +240 -0
  125. src/api/routers/__init__.py +1 -0
  126. src/api/routers/agent_config.py +69 -0
  127. src/api/routers/chat.py +296 -0
  128. src/api/routers/co_writer.py +337 -0
  129. src/api/routers/config.py +627 -0
  130. src/api/routers/dashboard.py +18 -0
  131. src/api/routers/guide.py +337 -0
  132. src/api/routers/ideagen.py +436 -0
  133. src/api/routers/knowledge.py +821 -0
  134. src/api/routers/notebook.py +247 -0
  135. src/api/routers/question.py +537 -0
  136. src/api/routers/research.py +394 -0
  137. src/api/routers/settings.py +164 -0
  138. src/api/routers/solve.py +305 -0
  139. src/api/routers/system.py +252 -0
  140. src/api/run_server.py +61 -0
  141. src/api/utils/history.py +172 -0
  142. src/api/utils/log_interceptor.py +21 -0
  143. src/api/utils/notebook_manager.py +415 -0
  144. src/api/utils/progress_broadcaster.py +72 -0
  145. src/api/utils/task_id_manager.py +100 -0
  146. src/config/__init__.py +0 -0
  147. src/config/accessors.py +18 -0
  148. src/config/constants.py +34 -0
  149. src/config/defaults.py +18 -0
  150. src/config/schema.py +38 -0
  151. src/config/settings.py +50 -0
  152. src/core/errors.py +62 -0
  153. src/knowledge/__init__.py +23 -0
  154. src/knowledge/add_documents.py +606 -0
  155. src/knowledge/config.py +65 -0
  156. src/knowledge/example_add_documents.py +236 -0
  157. src/knowledge/extract_numbered_items.py +1039 -0
  158. src/knowledge/initializer.py +621 -0
  159. src/knowledge/kb.py +22 -0
  160. src/knowledge/manager.py +782 -0
  161. src/knowledge/progress_tracker.py +182 -0
  162. src/knowledge/start_kb.py +535 -0
  163. src/logging/__init__.py +103 -0
  164. src/logging/adapters/__init__.py +17 -0
  165. src/logging/adapters/lightrag.py +184 -0
  166. src/logging/adapters/llamaindex.py +141 -0
  167. src/logging/config.py +80 -0
  168. src/logging/handlers/__init__.py +20 -0
  169. src/logging/handlers/console.py +75 -0
  170. src/logging/handlers/file.py +201 -0
  171. src/logging/handlers/websocket.py +127 -0
  172. src/logging/logger.py +709 -0
  173. src/logging/stats/__init__.py +16 -0
  174. src/logging/stats/llm_stats.py +179 -0
  175. src/services/__init__.py +56 -0
  176. src/services/config/__init__.py +61 -0
  177. src/services/config/knowledge_base_config.py +210 -0
  178. src/services/config/loader.py +260 -0
  179. src/services/config/unified_config.py +603 -0
  180. src/services/embedding/__init__.py +45 -0
  181. src/services/embedding/adapters/__init__.py +22 -0
  182. src/services/embedding/adapters/base.py +106 -0
  183. src/services/embedding/adapters/cohere.py +127 -0
  184. src/services/embedding/adapters/jina.py +99 -0
  185. src/services/embedding/adapters/ollama.py +116 -0
  186. src/services/embedding/adapters/openai_compatible.py +96 -0
  187. src/services/embedding/client.py +159 -0
  188. src/services/embedding/config.py +156 -0
  189. src/services/embedding/provider.py +119 -0
  190. src/services/llm/__init__.py +152 -0
  191. src/services/llm/capabilities.py +313 -0
  192. src/services/llm/client.py +302 -0
  193. src/services/llm/cloud_provider.py +530 -0
  194. src/services/llm/config.py +200 -0
  195. src/services/llm/error_mapping.py +103 -0
  196. src/services/llm/exceptions.py +152 -0
  197. src/services/llm/factory.py +450 -0
  198. src/services/llm/local_provider.py +347 -0
  199. src/services/llm/providers/anthropic.py +95 -0
  200. src/services/llm/providers/base_provider.py +93 -0
  201. src/services/llm/providers/open_ai.py +83 -0
  202. src/services/llm/registry.py +71 -0
  203. src/services/llm/telemetry.py +40 -0
  204. src/services/llm/types.py +27 -0
  205. src/services/llm/utils.py +333 -0
  206. src/services/prompt/__init__.py +25 -0
  207. src/services/prompt/manager.py +206 -0
  208. src/services/rag/__init__.py +64 -0
  209. src/services/rag/components/__init__.py +29 -0
  210. src/services/rag/components/base.py +59 -0
  211. src/services/rag/components/chunkers/__init__.py +18 -0
  212. src/services/rag/components/chunkers/base.py +34 -0
  213. src/services/rag/components/chunkers/fixed.py +71 -0
  214. src/services/rag/components/chunkers/numbered_item.py +94 -0
  215. src/services/rag/components/chunkers/semantic.py +97 -0
  216. src/services/rag/components/embedders/__init__.py +14 -0
  217. src/services/rag/components/embedders/base.py +32 -0
  218. src/services/rag/components/embedders/openai.py +63 -0
  219. src/services/rag/components/indexers/__init__.py +18 -0
  220. src/services/rag/components/indexers/base.py +35 -0
  221. src/services/rag/components/indexers/graph.py +172 -0
  222. src/services/rag/components/indexers/lightrag.py +156 -0
  223. src/services/rag/components/indexers/vector.py +146 -0
  224. src/services/rag/components/parsers/__init__.py +18 -0
  225. src/services/rag/components/parsers/base.py +35 -0
  226. src/services/rag/components/parsers/markdown.py +52 -0
  227. src/services/rag/components/parsers/pdf.py +115 -0
  228. src/services/rag/components/parsers/text.py +86 -0
  229. src/services/rag/components/retrievers/__init__.py +18 -0
  230. src/services/rag/components/retrievers/base.py +34 -0
  231. src/services/rag/components/retrievers/dense.py +200 -0
  232. src/services/rag/components/retrievers/hybrid.py +164 -0
  233. src/services/rag/components/retrievers/lightrag.py +169 -0
  234. src/services/rag/components/routing.py +286 -0
  235. src/services/rag/factory.py +234 -0
  236. src/services/rag/pipeline.py +215 -0
  237. src/services/rag/pipelines/__init__.py +32 -0
  238. src/services/rag/pipelines/academic.py +44 -0
  239. src/services/rag/pipelines/lightrag.py +43 -0
  240. src/services/rag/pipelines/llamaindex.py +313 -0
  241. src/services/rag/pipelines/raganything.py +384 -0
  242. src/services/rag/service.py +244 -0
  243. src/services/rag/types.py +73 -0
  244. src/services/search/__init__.py +284 -0
  245. src/services/search/base.py +87 -0
  246. src/services/search/consolidation.py +398 -0
  247. src/services/search/providers/__init__.py +128 -0
  248. src/services/search/providers/baidu.py +188 -0
  249. src/services/search/providers/exa.py +194 -0
  250. src/services/search/providers/jina.py +161 -0
  251. src/services/search/providers/perplexity.py +153 -0
  252. src/services/search/providers/serper.py +209 -0
  253. src/services/search/providers/tavily.py +161 -0
  254. src/services/search/types.py +114 -0
  255. src/services/setup/__init__.py +34 -0
  256. src/services/setup/init.py +285 -0
  257. src/services/tts/__init__.py +16 -0
  258. src/services/tts/config.py +99 -0
  259. src/tools/__init__.py +91 -0
  260. src/tools/code_executor.py +536 -0
  261. src/tools/paper_search_tool.py +171 -0
  262. src/tools/query_item_tool.py +310 -0
  263. src/tools/question/__init__.py +15 -0
  264. src/tools/question/exam_mimic.py +616 -0
  265. src/tools/question/pdf_parser.py +211 -0
  266. src/tools/question/question_extractor.py +397 -0
  267. src/tools/rag_tool.py +173 -0
  268. src/tools/tex_chunker.py +339 -0
  269. src/tools/tex_downloader.py +253 -0
  270. src/tools/web_search.py +71 -0
  271. src/utils/config_manager.py +206 -0
  272. src/utils/document_validator.py +168 -0
  273. src/utils/error_rate_tracker.py +111 -0
  274. src/utils/error_utils.py +82 -0
  275. src/utils/json_parser.py +110 -0
  276. src/utils/network/circuit_breaker.py +79 -0
@@ -0,0 +1,68 @@
1
+ system: |
2
+ # 角色定位
3
+ 你是一位资深的**学习规划师 (Learning Planner)**。你的核心职责是分析用户笔记本中的学习记录,理解其涵盖的知识范围,并设计一套循序渐进的学习计划。
4
+
5
+ # 核心原则
6
+ 1. **内容驱动**:基于笔记本中实际存在的内容(用户提问和系统回答)来分析知识点
7
+ 2. **递进设计**:知识点必须按照从基础到进阶的逻辑顺序排列
8
+ 3. **聚焦难点**:识别用户在理解过程中可能存在的困难和障碍
9
+ 4. **动态粒度**:根据内容复杂度灵活确定知识点数量
10
+
11
+ # 知识点数量决策规则
12
+ 根据笔记本内容的广度和深度,灵活决定知识点数量:
13
+ | 内容特征 | 知识点数量 | 适用场景 |
14
+ |---------|-----------|---------|
15
+ | 单一主题,内容简单 | 1-2个 | 笔记本只涉及一个简单概念或问题 |
16
+ | 单一主题,内容深入 | 2-3个 | 一个主题但有多个层次(基础→应用→拓展) |
17
+ | 多主题,相互独立 | 3-5个 | 笔记本涉及多个独立的知识点 |
18
+ | 多主题,高度关联 | 4-6个 | 复杂系统或框架,需要系统性学习 |
19
+ | 大量记录,内容丰富 | 5-8个 | 笔记本记录数量多,涵盖完整知识体系 |
20
+
21
+ **决策流程**:
22
+ 1. 首先评估笔记本的记录数量和内容广度
23
+ 2. 识别主要知识领域(可能是1个或多个)
24
+ 3. 对每个领域判断是否需要拆分(是否有基础/进阶之分)
25
+ 4. 合并过于细碎的点,拆分过于笼统的点
26
+ 5. 确保每个知识点都是有意义的学习单元
27
+
28
+ # 分析维度
29
+ 1. **知识点标题 (knowledge_title)**:用简洁的标题概括这一知识点
30
+ 2. **知识点摘要 (knowledge_summary)**:基于笔记本内容,完整阐述这个知识点的核心内容,包括定义、原理、公式、应用等,不限字数,说清楚为止
31
+ 3. **用户难点 (user_difficulty)**:分析用户的提问方式和问题内容,推测用户在理解这个知识点时可能遇到的困难
32
+
33
+ # 输出格式
34
+ 直接输出JSON数组(无Markdown格式):
35
+ ```json
36
+ [
37
+ {
38
+ "knowledge_title": "知识点标题",
39
+ "knowledge_summary": "详细的知识点内容阐述...",
40
+ "user_difficulty": "用户可能存在的理解困难..."
41
+ },
42
+ ...
43
+ ]
44
+ ```
45
+
46
+ # 注意事项
47
+ - 每个知识点应该是独立且完整的学习单元
48
+ - knowledge_summary 需要足够详细,包含所有必要的背景知识
49
+ - user_difficulty 应该具体、可操作,便于后续教学时针对性解决
50
+ - **数量灵活**:不强制要求固定数量,根据实际内容决定(1-8个都可以)
51
+ - 确保知识点之间有清晰的递进关系
52
+ - 宁可少而精,不要多而泛
53
+
54
+ user_template: |
55
+ ## 笔记本信息
56
+ 笔记本ID: {notebook_id}
57
+ 笔记本名称: {notebook_name}
58
+ 记录数量: {record_count}
59
+
60
+ ## 笔记本内容记录
61
+ {records_content}
62
+
63
+ ## 任务
64
+ 请仔细分析以上笔记本中的所有内容,理解用户的学习主题和范围,然后:
65
+ 1. 识别出涉及的所有核心知识点
66
+ 2. 按照从基础到进阶的顺序组织这些知识点
67
+ 3. 为每个知识点生成详细的摘要和用户可能的困难点
68
+ 4. 输出3-5个知识点的JSON数组
@@ -0,0 +1,157 @@
1
+ system: |
2
+ # 角色定位
3
+ 你是一位**学习总结专家 (Learning Summary Expert)**。你的职责是在用户完成一轮引导式学习后,生成一份全面、具体、个性化的学习总结报告。
4
+
5
+ # 核心原则
6
+ 1. **具体化优先**:所有分析必须基于实际的学习数据,引用具体的知识点、具体的问题、具体的互动内容
7
+ 2. **数据驱动**:基于用户的实际提问和互动情况进行分析,不要泛泛而谈
8
+ 3. **个性化**:针对用户的学习特点、困难点、互动模式提供个性化分析
9
+ 4. **可操作性**:所有建议必须具体、可执行,不要使用空泛的表述
10
+
11
+ # 总结维度(必须具体化)
12
+ 1. **学习内容回顾**:
13
+ - 必须列出每个知识点的具体标题
14
+ - 简要说明每个知识点的核心内容(1-2句话)
15
+ - 说明知识点之间的递进关系
16
+ - 不要只说"学习了X个知识点",要具体列出每个知识点
17
+
18
+ 2. **学习过程分析**:
19
+ - 必须引用用户的具体问题(如果用户有提问)
20
+ - 分析用户提问的频率、深度、关注点
21
+ - 识别用户的学习模式(主动提问型/被动接受型/深入探索型等)
22
+ - 如果用户没有提问,说明用户的学习方式,并分析可能的原因
23
+ - 不要使用"用户提出了几个问题"这样的泛泛表述,要具体说明问题内容
24
+
25
+ 3. **掌握程度评估**:
26
+ - 基于用户的具体问题内容,评估对每个知识点的理解程度
27
+ - 如果用户针对某个知识点提问,说明提问的类型(概念理解/应用实践/深入探索等)
28
+ - 识别用户可能存在的薄弱环节(基于提问内容或没有提问的知识点)
29
+ - 不要使用"掌握良好"这样的空泛评价,要具体说明掌握的证据
30
+
31
+ 4. **改进建议**:
32
+ - 针对用户的具体薄弱环节提供建议
33
+ - 建议必须具体、可操作,不要使用"多练习"、"多思考"这样的空泛建议
34
+ - 可以建议具体的复习重点、拓展方向、实践方法
35
+ - 如果用户提问较少,可以建议如何更主动地学习
36
+
37
+ # 报告风格
38
+ - 以导师的身份撰写,语气亲切专业
39
+ - 使用鼓励性的语言,肯定用户的学习努力
40
+ - 提供具体、可操作的建议
41
+ - 使用Markdown格式,结构清晰
42
+ - **重要**:直接输出Markdown内容,不要用代码块(```markdown)包裹
43
+ - **重要**:输出纯Markdown文本,不要添加任何markdown标记或解释
44
+ - **重要**:所有分析必须具体,引用实际的学习数据,避免空泛的表述
45
+
46
+ # 报告结构(必须包含具体内容)
47
+ ```markdown
48
+ # 📊 学习总结报告
49
+
50
+ ## 🎯 学习概览
51
+ [具体说明:学习了哪些知识点,学习时长/互动次数,整体学习特点]
52
+
53
+ 例如:
54
+ - 本次学习共涵盖3个知识点:XXX、XXX、XXX
55
+ - 在学习过程中,你提出了X个问题,主要集中在XXX方面
56
+ - 你的学习方式表现出XXX特点
57
+
58
+ ## 📚 知识点回顾
59
+ [逐一回顾,每个知识点都要具体说明]
60
+
61
+ ### 知识点1:XXX
62
+ - 核心内容:[具体说明这个知识点的核心内容]
63
+ - 学习情况:[基于互动情况,说明你在这个知识点上的学习表现]
64
+
65
+ ### 知识点2:XXX
66
+ - 核心内容:[具体说明]
67
+ - 学习情况:[具体分析]
68
+
69
+ ## 💬 学习互动分析
70
+ [必须引用具体的提问内容,具体分析]
71
+
72
+ - 提问频率:[具体数字和分布]
73
+ - 提问特点:[基于具体问题内容分析,例如"你关注的是概念理解还是实际应用"]
74
+ - 具体问题回顾:[列出1-2个典型问题,说明这些问题反映了什么]
75
+ - 学习模式:[基于互动情况,判断用户的学习模式]
76
+
77
+ ## 📈 掌握程度评估
78
+ [基于具体数据,对每个知识点进行具体评估]
79
+
80
+ - 知识点1:XXX
81
+ - 掌握证据:[具体说明,例如"你提出了关于XXX的问题,说明你理解了..."]
82
+ - 薄弱环节:[如果有,具体说明]
83
+
84
+ - 知识点2:XXX
85
+ - 掌握证据:[具体说明]
86
+ - 薄弱环节:[如果有,具体说明]
87
+
88
+ ## 🚀 后续学习建议
89
+ [必须具体、可操作]
90
+
91
+ - 复习重点:[具体列出需要重点复习的知识点或概念]
92
+ - 拓展方向:[具体建议可以学习哪些相关内容]
93
+ - 实践建议:[具体建议如何实践应用]
94
+ - 学习方法:[基于你的学习特点,给出具体的学习方法建议]
95
+
96
+ ## 🌟 结语
97
+ [鼓励性的结束语,可以总结本次学习的亮点]
98
+ ```
99
+
100
+ # 输出要求
101
+ - 所有分析必须基于提供的实际数据(知识点、对话历史)
102
+ - 必须引用具体的知识点标题、具体的问题内容
103
+ - 避免使用"很好"、"不错"、"需要加强"这样的空泛评价
104
+ - 使用具体的数据、具体的例子、具体的建议
105
+ - 如果用户没有提问,要分析原因并给出具体建议
106
+ - 直接输出Markdown格式的文本内容,不要用代码块包裹
107
+
108
+ user_template: |
109
+ ## 学习计划概览
110
+ 笔记本名称: {notebook_name}
111
+ 知识点数量: {total_points}
112
+
113
+ ## 所有知识点
114
+ {all_knowledge_points}
115
+
116
+ ## 完整对话历史
117
+ {full_chat_history}
118
+
119
+ ## 任务
120
+ 请基于以上**实际的学习数据**,生成一份**具体、详细**的学习总结报告。
121
+
122
+ ### 要求(必须严格遵守):
123
+
124
+ 1. **具体化所有内容**:
125
+ - 必须列出每个知识点的具体标题和核心内容
126
+ - 必须引用用户的具体问题(如果有)
127
+ - 必须基于实际互动情况进行分析
128
+ - 不要使用"学习了几个知识点"这样的泛泛表述
129
+
130
+ 2. **数据驱动分析**:
131
+ - 统计用户提问的次数、分布(哪个知识点提问多)
132
+ - 分析用户问题的类型(概念理解/应用/深入探索)
133
+ - 基于问题内容判断用户的理解程度
134
+ - 如果用户没有提问,分析可能的原因(快速理解/需要引导等)
135
+
136
+ 3. **个性化评估**:
137
+ - 对每个知识点,基于实际互动情况给出具体评估
138
+ - 识别用户的具体薄弱环节(如果有)
139
+ - 说明评估的依据(例如"你提出了关于XXX的问题,说明你理解了...")
140
+
141
+ 4. **可操作建议**:
142
+ - 建议必须具体,不要使用"多练习"、"多思考"这样的空泛表述
143
+ - 可以建议具体的复习重点、拓展方向、实践方法
144
+ - 基于用户的学习特点给出个性化的学习方法建议
145
+
146
+ 5. **引用实际数据**:
147
+ - 在报告中引用具体的知识点标题
148
+ - 引用用户的具体问题(如果有)
149
+ - 使用具体数字(提问次数、知识点数量等)
150
+ - 避免空泛的表述
151
+
152
+ **输出格式要求**:
153
+ - 直接输出Markdown格式的文本内容
154
+ - 不要用 ```markdown 或 ``` 代码块包裹
155
+ - 不要添加任何解释或说明文字
156
+ - 直接输出可渲染的Markdown内容
157
+ - 确保所有内容都是具体的、基于实际数据的
@@ -0,0 +1,12 @@
1
+ """
2
+ Idea Generation Agents
3
+ Agent system for generating research ideas based on notebook content
4
+ """
5
+
6
+ from .idea_generation_workflow import IdeaGenerationWorkflow
7
+ from .material_organizer_agent import MaterialOrganizerAgent
8
+
9
+ __all__ = [
10
+ "IdeaGenerationWorkflow",
11
+ "MaterialOrganizerAgent",
12
+ ]
@@ -0,0 +1,426 @@
1
+ """
2
+ Idea Generation Workflow.
3
+ Uses unified PromptManager for prompt loading.
4
+ """
5
+
6
+ import asyncio
7
+ from collections.abc import Awaitable, Callable
8
+ from datetime import datetime
9
+ import json
10
+ from pathlib import Path
11
+ import sys
12
+ from typing import Any
13
+
14
+ # Add project root to path
15
+ _project_root = Path(__file__).parent.parent.parent.parent
16
+ if str(_project_root) not in sys.path:
17
+ sys.path.insert(0, str(_project_root))
18
+
19
+ from src.agents.base_agent import BaseAgent
20
+ from src.services.prompt import get_prompt_manager
21
+
22
+
23
+ class IdeaGenerationWorkflow(BaseAgent):
24
+ """Idea generation workflow"""
25
+
26
+ def __init__(
27
+ self,
28
+ api_key: str | None = None,
29
+ base_url: str | None = None,
30
+ api_version: str | None = None,
31
+ model: str | None = None,
32
+ progress_callback: Callable[[str, Any], None | Awaitable[None]] | None = None,
33
+ output_dir: Path | None = None,
34
+ language: str = "en",
35
+ ):
36
+ """
37
+ Initialize workflow
38
+
39
+ Args:
40
+ api_key: API key
41
+ base_url: API endpoint
42
+ api_version: API version (for Azure OpenAI)
43
+ model: Model name
44
+ progress_callback: Progress callback function for streaming output
45
+ output_dir: Output directory for saving intermediate results
46
+ language: Language for prompts ("en" or "zh")
47
+ """
48
+ super().__init__(
49
+ module_name="ideagen",
50
+ agent_name="idea_generation",
51
+ api_key=api_key,
52
+ base_url=base_url,
53
+ api_version=api_version,
54
+ model=model,
55
+ language=language,
56
+ )
57
+ self.progress_callback = progress_callback
58
+ self.output_dir = output_dir
59
+ self._prompts = get_prompt_manager().load_prompts(
60
+ module_name="ideagen",
61
+ agent_name="idea_generation",
62
+ language=language,
63
+ )
64
+
65
+ async def _emit_progress(self, stage: str, data: Any):
66
+ """Emit progress update"""
67
+ if self.progress_callback:
68
+ result = self.progress_callback(stage, data)
69
+ if result is not None and asyncio.iscoroutine(result):
70
+ await result
71
+
72
+ async def loose_filter(self, knowledge_points: list[dict[str, Any]]) -> list[dict[str, Any]]:
73
+ """
74
+ Loose filtering - filter out unsuitable knowledge points
75
+
76
+ Args:
77
+ knowledge_points: Knowledge point list
78
+
79
+ Returns:
80
+ Filtered knowledge point list
81
+ """
82
+ await self._emit_progress(
83
+ "loose_filter", {"status": "start", "total": len(knowledge_points)}
84
+ )
85
+
86
+ system_prompt = self._prompts.get("loose_filter_system", "")
87
+ user_template = self._prompts.get("loose_filter_user_template", "")
88
+
89
+ points_text = ""
90
+ for i, point in enumerate(knowledge_points, 1):
91
+ points_text += (
92
+ f"\n{i}. {point['knowledge_point']}\n Description: {point['description']}\n"
93
+ )
94
+
95
+ user_prompt = user_template.format(points_text=points_text)
96
+
97
+ self.logger.info(f"Calling LLM to filter {len(knowledge_points)} knowledge points...")
98
+
99
+ response = await self.call_llm(
100
+ user_prompt=user_prompt,
101
+ system_prompt=system_prompt,
102
+ response_format={"type": "json_object"},
103
+ )
104
+
105
+ self.logger.debug(f"LLM response length: {len(response)} chars")
106
+
107
+ try:
108
+ result = json.loads(response)
109
+ filtered = result.get("filtered_points", [])
110
+
111
+ self.logger.info(
112
+ f"Parsed result: {len(filtered)} filtered points from {len(knowledge_points)} original"
113
+ )
114
+
115
+ # If filtered is empty but original list is not, filter is too strict, return original list
116
+ if not filtered and knowledge_points:
117
+ self.logger.warning("All points filtered out! Returning original list.")
118
+ filtered = knowledge_points
119
+
120
+ # Save filtered results
121
+ if self.output_dir:
122
+ with open(
123
+ self.output_dir / "02_filtered_knowledge_points.json", "w", encoding="utf-8"
124
+ ) as f:
125
+ json.dump(
126
+ {
127
+ "stage": "loose_filter",
128
+ "original_count": len(knowledge_points),
129
+ "filtered_count": len(filtered),
130
+ "filtered_points": filtered,
131
+ "timestamp": datetime.now().isoformat(),
132
+ },
133
+ f,
134
+ ensure_ascii=False,
135
+ indent=2,
136
+ )
137
+
138
+ await self._emit_progress(
139
+ "loose_filter", {"status": "complete", "filtered": len(filtered)}
140
+ )
141
+ return filtered
142
+ except json.JSONDecodeError as e:
143
+ self.logger.error(f"JSON decode error: {e}")
144
+ self.logger.debug(f"Raw response: {response[:500]}...")
145
+ await self._emit_progress("loose_filter", {"status": "error"})
146
+ return knowledge_points # If parsing fails, return original list
147
+
148
+ async def explore_ideas(self, knowledge_point: dict[str, Any]) -> list[str]:
149
+ """
150
+ 3.2 Explore knowledge points - Generate at least 5 research ideas for each knowledge point
151
+
152
+ Args:
153
+ knowledge_point: Knowledge point dictionary
154
+
155
+ Returns:
156
+ List of research ideas (at least 5)
157
+ """
158
+ system_prompt = self._prompts.get("explore_ideas_system", "")
159
+ user_template = self._prompts.get("explore_ideas_user_template", "")
160
+
161
+ user_prompt = user_template.format(
162
+ knowledge_point=knowledge_point["knowledge_point"],
163
+ description=knowledge_point["description"],
164
+ )
165
+
166
+ self.logger.info(
167
+ f"Generating ideas for: {knowledge_point.get('knowledge_point', 'unknown')}"
168
+ )
169
+
170
+ response = await self.call_llm(
171
+ user_prompt=user_prompt,
172
+ system_prompt=system_prompt,
173
+ response_format={"type": "json_object"},
174
+ )
175
+
176
+ self.logger.debug(f"LLM response length: {len(response)} chars")
177
+
178
+ try:
179
+ result = json.loads(response)
180
+ ideas = result.get("research_ideas", [])
181
+ self.logger.info(f"Generated {len(ideas)} research ideas")
182
+ # Ensure at least 5
183
+ if len(ideas) < 5:
184
+ # If less than 5, can call again or supplement
185
+ self.logger.warning(f"Only {len(ideas)} ideas generated (expected at least 5)")
186
+
187
+ # Save generated research ideas
188
+ if self.output_dir:
189
+ safe_name = (
190
+ "".join(
191
+ c
192
+ for c in knowledge_point["knowledge_point"]
193
+ if c.isalnum() or c in (" ", "-", "_")
194
+ )
195
+ .strip()[:50]
196
+ .replace(" ", "_")
197
+ )
198
+ with open(
199
+ self.output_dir / f"03_ideas_{safe_name}.json", "w", encoding="utf-8"
200
+ ) as f:
201
+ json.dump(
202
+ {
203
+ "stage": "explore_ideas",
204
+ "knowledge_point": knowledge_point,
205
+ "research_ideas": ideas,
206
+ "count": len(ideas),
207
+ "timestamp": datetime.now().isoformat(),
208
+ },
209
+ f,
210
+ ensure_ascii=False,
211
+ indent=2,
212
+ )
213
+
214
+ return ideas[:10] # Return at most 10
215
+ except json.JSONDecodeError as e:
216
+ self.logger.error(f"JSON decode error: {e}")
217
+ self.logger.debug(f"Raw response: {response[:500]}...")
218
+ return []
219
+
220
+ async def strict_filter(
221
+ self, knowledge_point: dict[str, Any], research_ideas: list[str]
222
+ ) -> list[str]:
223
+ """
224
+ 3.3 Strict filtering - Evaluate research ideas with strict standards
225
+
226
+ Args:
227
+ knowledge_point: Knowledge point dictionary
228
+ research_ideas: Research ideas list
229
+
230
+ Returns:
231
+ Retained research ideas list (at least keep 1, at least filter out 2)
232
+ """
233
+ if len(research_ideas) <= 1:
234
+ return research_ideas # If only 1 or less, return directly
235
+
236
+ system_prompt = self._prompts.get("strict_filter_system", "")
237
+ user_template = self._prompts.get("strict_filter_user_template", "")
238
+
239
+ ideas_text = ""
240
+ for i, idea in enumerate(research_ideas, 1):
241
+ ideas_text += f"{i}. {idea}\n"
242
+
243
+ user_prompt = user_template.format(
244
+ knowledge_point=knowledge_point["knowledge_point"],
245
+ description=knowledge_point["description"],
246
+ ideas_text=ideas_text,
247
+ )
248
+
249
+ self.logger.info(
250
+ f"Filtering {len(research_ideas)} ideas for: {knowledge_point.get('knowledge_point', 'unknown')}"
251
+ )
252
+
253
+ response = await self.call_llm(
254
+ user_prompt=user_prompt,
255
+ system_prompt=system_prompt,
256
+ response_format={"type": "json_object"},
257
+ )
258
+
259
+ self.logger.debug(f"LLM response length: {len(response)} chars")
260
+
261
+ try:
262
+ result = json.loads(response)
263
+ kept = result.get("kept_ideas", [])
264
+ rejected = result.get("rejected_ideas", [])
265
+ reasons = result.get("reasons", {})
266
+ self.logger.info(f"Kept {len(kept)}, rejected {len(rejected)}")
267
+
268
+ # Validation: at least keep 1, at least filter out 2
269
+ if len(kept) == 0:
270
+ # If all filtered out, at least keep the first one
271
+ kept = [research_ideas[0]]
272
+ rejected = research_ideas[1:]
273
+ elif len(rejected) < 2 and len(research_ideas) >= 3:
274
+ # If less than 2 filtered out, force filter out the least suitable
275
+ # Can add smarter logic here
276
+ if len(kept) > 1:
277
+ rejected.extend(kept[1:])
278
+ kept = [kept[0]]
279
+
280
+ # Save filtering results
281
+ if self.output_dir:
282
+ safe_name = (
283
+ "".join(
284
+ c
285
+ for c in knowledge_point["knowledge_point"]
286
+ if c.isalnum() or c in (" ", "-", "_")
287
+ )
288
+ .strip()[:50]
289
+ .replace(" ", "_")
290
+ )
291
+ with open(
292
+ self.output_dir / f"04_filtered_ideas_{safe_name}.json", "w", encoding="utf-8"
293
+ ) as f:
294
+ json.dump(
295
+ {
296
+ "stage": "strict_filter",
297
+ "knowledge_point": knowledge_point,
298
+ "original_ideas": research_ideas,
299
+ "kept_ideas": kept,
300
+ "rejected_ideas": rejected,
301
+ "reasons": reasons,
302
+ "kept_count": len(kept),
303
+ "rejected_count": len(rejected),
304
+ "timestamp": datetime.now().isoformat(),
305
+ },
306
+ f,
307
+ ensure_ascii=False,
308
+ indent=2,
309
+ )
310
+
311
+ return kept
312
+ except json.JSONDecodeError as e:
313
+ self.logger.error(f"JSON decode error: {e}")
314
+ self.logger.debug(f"Raw response: {response[:500]}...")
315
+ # If parsing fails, at least keep the first one
316
+ return research_ideas[:1] if research_ideas else []
317
+
318
+ async def generate_statement(
319
+ self, knowledge_point: dict[str, Any], research_ideas: list[str]
320
+ ) -> str:
321
+ """
322
+ 3.4 Generate statement - Generate markdown-formatted statement for knowledge points and research ideas
323
+
324
+ Args:
325
+ knowledge_point: Knowledge point dictionary
326
+ research_ideas: Retained research ideas list
327
+
328
+ Returns:
329
+ Markdown-formatted statement
330
+ """
331
+ system_prompt = self._prompts.get("generate_statement_system", "")
332
+ user_template = self._prompts.get("generate_statement_user_template", "")
333
+
334
+ ideas_text = ""
335
+ for i, idea in enumerate(research_ideas, 1):
336
+ ideas_text += f"{i}. {idea}\n"
337
+
338
+ user_prompt = user_template.format(
339
+ knowledge_point=knowledge_point["knowledge_point"],
340
+ description=knowledge_point["description"],
341
+ ideas_text=ideas_text,
342
+ )
343
+
344
+ response = await self.call_llm(user_prompt=user_prompt, system_prompt=system_prompt)
345
+
346
+ return response
347
+
348
+ async def process(self, knowledge_points: list[dict[str, Any]]) -> str:
349
+ """
350
+ Execute complete workflow
351
+
352
+ Args:
353
+ knowledge_points: Knowledge points list
354
+
355
+ Returns:
356
+ Final markdown document
357
+ """
358
+ # 3.1 Loose filtering
359
+ filtered_points = await self.loose_filter(knowledge_points)
360
+
361
+ if not filtered_points:
362
+ return "# Research Ideas Generation Result\n\nNo suitable knowledge points found."
363
+
364
+ # 3.2 and 3.3 Process each knowledge point
365
+ final_statements = []
366
+
367
+ for idx, point in enumerate(filtered_points):
368
+ await self._emit_progress(
369
+ "explore", {"status": "processing", "index": idx + 1, "total": len(filtered_points)}
370
+ )
371
+
372
+ # 3.2 Explore knowledge points
373
+ research_ideas = await self.explore_ideas(point)
374
+ await self._emit_progress(
375
+ "explore",
376
+ {"status": "complete", "index": idx + 1, "ideas_count": len(research_ideas)},
377
+ )
378
+
379
+ if not research_ideas:
380
+ continue
381
+
382
+ # 3.3 Strict filtering
383
+ kept_ideas = await self.strict_filter(point, research_ideas)
384
+ await self._emit_progress(
385
+ "filter", {"status": "complete", "index": idx + 1, "kept": len(kept_ideas)}
386
+ )
387
+
388
+ if not kept_ideas:
389
+ continue
390
+
391
+ # 3.4 Generate statement
392
+ statement = await self.generate_statement(point, kept_ideas)
393
+ final_statements.append(statement)
394
+
395
+ # Join all statements
396
+ final_markdown = "# Research Ideas Generation Result\n\n"
397
+ final_markdown += "\n\n---\n\n".join(final_statements)
398
+
399
+ # Save workflow summary
400
+ if self.output_dir:
401
+ workflow_summary = {
402
+ "stage": "workflow_summary",
403
+ "original_knowledge_points_count": len(knowledge_points),
404
+ "filtered_knowledge_points_count": len(filtered_points),
405
+ "processed_points": [
406
+ {
407
+ "knowledge_point": point["knowledge_point"],
408
+ "description": point["description"],
409
+ "statement": statement,
410
+ }
411
+ for point, statement in zip(
412
+ [
413
+ p
414
+ for p in filtered_points
415
+ if any(p["knowledge_point"] in s for s in final_statements)
416
+ ],
417
+ final_statements,
418
+ )
419
+ ],
420
+ "final_statements_count": len(final_statements),
421
+ "timestamp": datetime.now().isoformat(),
422
+ }
423
+ with open(self.output_dir / "06_workflow_summary.json", "w", encoding="utf-8") as f:
424
+ json.dump(workflow_summary, f, ensure_ascii=False, indent=2)
425
+
426
+ return final_markdown