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,228 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ ManagerAgent - Queue management Agent
5
+ Responsible for managing DynamicTopicQueue state and task distribution
6
+ """
7
+
8
+ import asyncio
9
+ from pathlib import Path
10
+ import sys
11
+ from typing import Any
12
+
13
+ project_root = Path(__file__).parent.parent.parent.parent
14
+ sys.path.insert(0, str(project_root))
15
+
16
+ from src.agents.base_agent import BaseAgent
17
+ from src.agents.research.data_structures import DynamicTopicQueue, TopicBlock
18
+
19
+
20
+ class ManagerAgent(BaseAgent):
21
+ """Queue management Agent"""
22
+
23
+ def __init__(
24
+ self,
25
+ config: dict[str, Any],
26
+ api_key: str | None = None,
27
+ base_url: str | None = None,
28
+ api_version: str | None = None,
29
+ ):
30
+ language = config.get("system", {}).get("language", "zh")
31
+ super().__init__(
32
+ module_name="research",
33
+ agent_name="manager_agent",
34
+ api_key=api_key,
35
+ base_url=base_url,
36
+ api_version=api_version,
37
+ language=language,
38
+ config=config,
39
+ )
40
+ self.queue: DynamicTopicQueue | None = None
41
+ self.primary_topic: str | None = None
42
+ self._lock = asyncio.Lock() # Lock for thread-safe operations in parallel mode
43
+
44
+ def set_queue(self, queue: DynamicTopicQueue) -> None:
45
+ """Set queue to manage"""
46
+ self.queue = queue
47
+
48
+ def set_primary_topic(self, topic: str | None) -> None:
49
+ """Update primary research topic for subsequent topic consistency judgment"""
50
+ self.primary_topic = (topic or "").strip() or None
51
+
52
+ def get_next_task(self) -> TopicBlock | None:
53
+ """
54
+ Get next task to research
55
+
56
+ Returns:
57
+ Next TopicBlock with PENDING status, returns None if none available
58
+ """
59
+ if not self.queue:
60
+ return None
61
+
62
+ block = self.queue.get_pending_block()
63
+ if block:
64
+ # Mark as researching
65
+ self.queue.mark_researching(block.block_id)
66
+ print(f"\nšŸ“‹ ManagerAgent: Assigned task {block.block_id}")
67
+ print(f" Topic: {block.sub_topic}")
68
+
69
+ return block
70
+
71
+ def complete_task(self, block_id: str) -> bool:
72
+ """
73
+ Mark task as completed
74
+
75
+ Args:
76
+ block_id: Topic block ID
77
+
78
+ Returns:
79
+ Whether successfully marked
80
+ """
81
+ if not self.queue:
82
+ return False
83
+
84
+ success = self.queue.mark_completed(block_id)
85
+ if success:
86
+ print(f"āœ“ ManagerAgent: Task {block_id} completed")
87
+
88
+ return success
89
+
90
+ async def get_next_task_async(self) -> TopicBlock | None:
91
+ """
92
+ Thread-safe async version of get_next_task for parallel mode
93
+
94
+ Returns:
95
+ Next TopicBlock with PENDING status, returns None if none available
96
+ """
97
+ async with self._lock:
98
+ return self.get_next_task()
99
+
100
+ async def complete_task_async(self, block_id: str) -> bool:
101
+ """
102
+ Thread-safe async version of complete_task for parallel mode
103
+
104
+ Args:
105
+ block_id: Topic block ID
106
+
107
+ Returns:
108
+ Whether successfully marked
109
+ """
110
+ async with self._lock:
111
+ return self.complete_task(block_id)
112
+
113
+ async def fail_task_async(self, block_id: str, reason: str = "") -> bool:
114
+ """
115
+ Thread-safe async version of fail_task for parallel mode
116
+
117
+ Args:
118
+ block_id: Topic block ID
119
+ reason: Failure reason
120
+
121
+ Returns:
122
+ Whether successfully marked
123
+ """
124
+ async with self._lock:
125
+ return self.fail_task(block_id, reason)
126
+
127
+ async def add_new_topic_async(self, sub_topic: str, overview: str) -> TopicBlock:
128
+ """
129
+ Thread-safe async version of add_new_topic for parallel mode
130
+
131
+ Args:
132
+ sub_topic: Sub-topic name
133
+ overview: Topic overview
134
+
135
+ Returns:
136
+ Newly created TopicBlock
137
+ """
138
+ async with self._lock:
139
+ return self.add_new_topic(sub_topic, overview)
140
+
141
+ def fail_task(self, block_id: str, reason: str = "") -> bool:
142
+ """
143
+ Mark task as failed
144
+
145
+ Args:
146
+ block_id: Topic block ID
147
+ reason: Failure reason
148
+
149
+ Returns:
150
+ Whether successfully marked
151
+ """
152
+ if not self.queue:
153
+ return False
154
+
155
+ success = self.queue.mark_failed(block_id)
156
+ if success:
157
+ print(f"āœ— ManagerAgent: Task {block_id} failed")
158
+ if reason:
159
+ print(f" Reason: {reason}")
160
+
161
+ return success
162
+
163
+ def add_new_topic(self, sub_topic: str, overview: str) -> TopicBlock:
164
+ """
165
+ Add new topic to queue
166
+
167
+ Args:
168
+ sub_topic: Sub-topic name
169
+ overview: Topic overview
170
+
171
+ Returns:
172
+ Newly created TopicBlock
173
+ """
174
+ if not self.queue:
175
+ raise RuntimeError("Queue not initialized")
176
+
177
+ normalized = (sub_topic or "").strip()
178
+ if not normalized:
179
+ raise ValueError("New topic title cannot be empty")
180
+ if self.queue.has_topic(normalized):
181
+ print(f"āš ļø ManagerAgent: Topic怊{normalized}怋already exists, skipping addition")
182
+ return None
183
+
184
+ block = self.queue.add_block(normalized, overview)
185
+ print(f"āœ“ ManagerAgent: Added new topic {block.block_id}")
186
+ print(f" Topic: {sub_topic}")
187
+
188
+ return block
189
+
190
+ def is_research_complete(self) -> bool:
191
+ """
192
+ Check if research is complete (all tasks are completed)
193
+
194
+ Returns:
195
+ Whether all tasks are completed
196
+ """
197
+ if not self.queue:
198
+ return False
199
+
200
+ return self.queue.is_all_completed()
201
+
202
+ def get_queue_status(self) -> dict[str, Any]:
203
+ """
204
+ Get queue status
205
+
206
+ Returns:
207
+ Queue statistics
208
+ """
209
+ if not self.queue:
210
+ return {}
211
+
212
+ stats = self.queue.get_statistics()
213
+ print("\nšŸ“Š Queue Status:")
214
+ print(f" Total Topics: {stats['total_blocks']}")
215
+ print(f" Pending: {stats['pending']}")
216
+ print(f" Researching: {stats['researching']}")
217
+ print(f" Completed: {stats['completed']}")
218
+ print(f" Failed: {stats['failed']}")
219
+ print(f" Total Tool Calls: {stats['total_tool_calls']}")
220
+
221
+ return stats
222
+
223
+ async def process(self, *args, **kwargs) -> Any:
224
+ """Main processing logic of Manager Agent"""
225
+ # Manager Agent is mainly called through other methods, no independent process method needed
226
+
227
+
228
+ __all__ = ["ManagerAgent"]
@@ -0,0 +1,180 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ NoteAgent - Recording Agent
5
+ Responsible for information compression and summary generation, converting raw data returned by tools into usable knowledge summaries
6
+ """
7
+
8
+ from pathlib import Path
9
+ from string import Template
10
+ import sys
11
+ from typing import Any, Optional
12
+
13
+ project_root = Path(__file__).parent.parent.parent.parent
14
+ sys.path.insert(0, str(project_root))
15
+
16
+ from src.agents.base_agent import BaseAgent
17
+ from src.agents.research.data_structures import ToolTrace
18
+
19
+ from ..utils.json_utils import extract_json_from_text
20
+
21
+
22
+ class NoteAgent(BaseAgent):
23
+ """Recording Agent"""
24
+
25
+ def __init__(
26
+ self,
27
+ config: dict[str, Any],
28
+ api_key: Optional[str] = None,
29
+ base_url: Optional[str] = None,
30
+ api_version: Optional[str] = None,
31
+ ):
32
+ language = config.get("system", {}).get("language", "zh")
33
+ super().__init__(
34
+ module_name="research",
35
+ agent_name="note_agent",
36
+ api_key=api_key,
37
+ base_url=base_url,
38
+ api_version=api_version,
39
+ language=language,
40
+ config=config,
41
+ )
42
+
43
+ async def process(
44
+ self,
45
+ tool_type: str,
46
+ query: str,
47
+ raw_answer: str,
48
+ citation_id: str,
49
+ topic: str = "",
50
+ context: str = "",
51
+ ) -> ToolTrace:
52
+ """
53
+ Process raw data returned by tool, generate summary and create ToolTrace
54
+
55
+ Args:
56
+ tool_type: Tool type
57
+ query: Query statement
58
+ raw_answer: Raw answer returned by tool
59
+ citation_id: Citation ID (REQUIRED, must be obtained from CitationManager)
60
+ topic: Topic (for context)
61
+ context: Additional context
62
+
63
+ Returns:
64
+ ToolTrace object
65
+
66
+ Note:
67
+ citation_id must be obtained from CitationManager before calling this method.
68
+ Use CitationManager.get_next_citation_id() or its async variant.
69
+ """
70
+ print(f"\n{'=' * 70}")
71
+ print("šŸ“ NoteAgent - Information Recording and Summary")
72
+ print(f"{'=' * 70}")
73
+ print(f"Tool: {tool_type}")
74
+ print(f"Query: {query}")
75
+ print(f"Citation ID: {citation_id}")
76
+ print(f"Raw Answer Length: {len(raw_answer)} characters\n")
77
+
78
+ # Generate summary
79
+ summary = await self._generate_summary(
80
+ tool_type=tool_type, query=query, raw_answer=raw_answer, topic=topic, context=context
81
+ )
82
+
83
+ print(f"āœ“ Summary generation completed ({len(summary)} characters)")
84
+
85
+ # Create ToolTrace with the provided citation ID
86
+ tool_id = self._generate_tool_id()
87
+ trace = ToolTrace(
88
+ tool_id=tool_id,
89
+ citation_id=citation_id,
90
+ tool_type=tool_type,
91
+ query=query,
92
+ raw_answer=raw_answer,
93
+ summary=summary,
94
+ )
95
+
96
+ return trace
97
+
98
+ @staticmethod
99
+ def _convert_to_template_format(template_str: str) -> str:
100
+ """
101
+ Convert {var} style placeholders to $var style for string.Template.
102
+ This avoids conflicts with LaTeX braces like {\rho}.
103
+ """
104
+ import re
105
+
106
+ # Only convert simple {var_name} patterns, not nested or complex ones
107
+ return re.sub(r"\{(\w+)\}", r"$\1", template_str)
108
+
109
+ async def _generate_summary(
110
+ self, tool_type: str, query: str, raw_answer: str, topic: str = "", context: str = ""
111
+ ) -> str:
112
+ """
113
+ Generate summary
114
+
115
+ Args:
116
+ tool_type: Tool type
117
+ query: Query statement
118
+ raw_answer: Raw answer
119
+ topic: Topic
120
+ context: Additional context
121
+
122
+ Returns:
123
+ Generated summary
124
+ """
125
+ system_prompt = self.get_prompt("system", "role")
126
+ if not system_prompt:
127
+ raise ValueError(
128
+ "NoteAgent missing system prompt, please configure system.role in prompts/{lang}/note_agent.yaml"
129
+ )
130
+
131
+ user_prompt_template = self.get_prompt("process", "generate_summary")
132
+ if not user_prompt_template:
133
+ raise ValueError(
134
+ "NoteAgent missing generate_summary prompt, please configure process.generate_summary in prompts/{lang}/note_agent.yaml"
135
+ )
136
+
137
+ # Use string.Template to avoid conflicts with LaTeX braces like {\rho}
138
+ # Convert {var} to $var format, then use safe_substitute
139
+ template_str = self._convert_to_template_format(user_prompt_template)
140
+ template = Template(template_str)
141
+ user_prompt = template.safe_substitute(
142
+ tool_type=tool_type,
143
+ query=query,
144
+ raw_answer=raw_answer,
145
+ topic=topic,
146
+ context=context,
147
+ )
148
+
149
+ response = await self.call_llm(
150
+ user_prompt=user_prompt,
151
+ system_prompt=system_prompt,
152
+ stage="generate_summary",
153
+ verbose=False,
154
+ )
155
+
156
+ # Parse JSON output (strict validation)
157
+ from ..utils.json_utils import ensure_json_dict, ensure_keys
158
+
159
+ data = extract_json_from_text(response)
160
+ try:
161
+ obj = ensure_json_dict(data)
162
+ ensure_keys(obj, ["summary"])
163
+ summary = obj.get("summary", "")
164
+ # Ensure summary is string type
165
+ if not isinstance(summary, str):
166
+ summary = str(summary) if summary else ""
167
+ return summary
168
+ except Exception:
169
+ # Fallback: directly use text prefix
170
+ return (response or "")[:1000]
171
+
172
+ def _generate_tool_id(self) -> str:
173
+ """Generate tool ID"""
174
+ import time
175
+
176
+ timestamp = int(time.time() * 1000)
177
+ return f"tool_{timestamp}"
178
+
179
+
180
+ __all__ = ["NoteAgent"]
@@ -0,0 +1,263 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ RephraseAgent - Topic rephrasing Agent
5
+ Responsible for rephrasing and optimizing user input
6
+ """
7
+
8
+ from pathlib import Path
9
+ import sys
10
+ from typing import Any
11
+
12
+ project_root = Path(__file__).parent.parent.parent.parent
13
+ sys.path.insert(0, str(project_root))
14
+
15
+ from src.agents.base_agent import BaseAgent
16
+
17
+ from ..utils.json_utils import extract_json_from_text
18
+
19
+
20
+ class RephraseAgent(BaseAgent):
21
+ """Topic rephrasing Agent"""
22
+
23
+ def __init__(
24
+ self,
25
+ config: dict[str, Any],
26
+ api_key: str | None = None,
27
+ base_url: str | None = None,
28
+ api_version: str | None = None,
29
+ ):
30
+ language = config.get("system", {}).get("language", "zh")
31
+ super().__init__(
32
+ module_name="research",
33
+ agent_name="rephrase_agent",
34
+ api_key=api_key,
35
+ base_url=base_url,
36
+ api_version=api_version,
37
+ language=language,
38
+ config=config,
39
+ )
40
+ # Store complete conversation history for multi-turn optimization
41
+ self.conversation_history: list[dict[str, Any]] = []
42
+
43
+ def reset_history(self):
44
+ """Reset conversation history for a new research session"""
45
+ self.conversation_history = []
46
+
47
+ def _format_conversation_history(self) -> str:
48
+ """Format conversation history for prompt"""
49
+ if not self.conversation_history:
50
+ return ""
51
+
52
+ history_parts = []
53
+ for entry in self.conversation_history:
54
+ role = entry.get("role", "unknown")
55
+ iteration = entry.get("iteration", 0)
56
+ content = entry.get("content", "")
57
+
58
+ if role == "user":
59
+ if iteration == 0:
60
+ history_parts.append(f"[User - Initial Input]\n{content}")
61
+ else:
62
+ history_parts.append(f"[User - Feedback (Round {iteration})]\n{content}")
63
+ elif role == "assistant":
64
+ topic = content.get("topic", "") if isinstance(content, dict) else str(content)
65
+ history_parts.append(f"[Assistant - Rephrased Topic (Round {iteration})]\n{topic}")
66
+
67
+ return "\n\n".join(history_parts)
68
+
69
+ async def process(
70
+ self, user_input: str, iteration: int = 0, previous_result: dict[str, Any] = None
71
+ ) -> dict[str, Any]:
72
+ """
73
+ Rephrase and optimize user input, supports user interaction confirmation
74
+ Uses complete conversation history for better context understanding
75
+
76
+ Args:
77
+ user_input: User's original input (first time) or feedback (subsequent iterations)
78
+ iteration: Iteration count (for tracking rephrasing rounds)
79
+ previous_result: Previous rephrasing result (for backward compatibility)
80
+
81
+ Returns:
82
+ Dictionary containing rephrasing results
83
+ {
84
+ "topic": str, # Optimized research topic (a clear, explicit statement)
85
+ "iteration": int, # Iteration count
86
+ }
87
+ """
88
+ print(f"\n{'=' * 70}")
89
+ print(f"šŸ”„ RephraseAgent - Topic Rephrasing (Iteration {iteration})")
90
+ print(f"{'=' * 70}")
91
+
92
+ # Reset history for new session (iteration 0)
93
+ if iteration == 0:
94
+ self.reset_history()
95
+ print(f"Original Input: {user_input}\n")
96
+ else:
97
+ print(f"User Feedback: {user_input}\n")
98
+ print(f"Conversation History: {len(self.conversation_history)} entries\n")
99
+
100
+ # Add current user input to history
101
+ self.conversation_history.append(
102
+ {
103
+ "role": "user",
104
+ "content": user_input,
105
+ "iteration": iteration,
106
+ }
107
+ )
108
+
109
+ # Get system prompt
110
+ system_prompt = self.get_prompt("system", "role")
111
+ if not system_prompt:
112
+ raise ValueError(
113
+ "RephraseAgent missing system prompt, please configure system.role in prompts/{lang}/rephrase_agent.yaml"
114
+ )
115
+
116
+ # Get user prompt template
117
+ user_prompt_template = self.get_prompt("process", "rephrase")
118
+ if not user_prompt_template:
119
+ raise ValueError(
120
+ "RephraseAgent missing rephrase prompt, please configure process.rephrase in prompts/{lang}/rephrase_agent.yaml"
121
+ )
122
+
123
+ # Format conversation history for prompt
124
+ history_text = self._format_conversation_history()
125
+
126
+ # Format user prompt with full history
127
+ user_prompt = user_prompt_template.format(
128
+ user_input=user_input,
129
+ iteration=iteration,
130
+ conversation_history=history_text,
131
+ previous_result=history_text, # For backward compatibility with old templates
132
+ )
133
+
134
+ # Call LLM
135
+ response = await self.call_llm(
136
+ user_prompt=user_prompt, system_prompt=system_prompt, stage="rephrase"
137
+ )
138
+
139
+ # Parse JSON output
140
+ data = extract_json_from_text(response)
141
+ from ..utils.json_utils import ensure_json_dict, ensure_keys
142
+
143
+ try:
144
+ result = ensure_json_dict(data)
145
+ ensure_keys(result, ["topic"])
146
+ except Exception:
147
+ # Fallback: use user input or last assistant topic
148
+ fallback_topic = user_input
149
+ for entry in reversed(self.conversation_history):
150
+ if entry.get("role") == "assistant":
151
+ content = entry.get("content", {})
152
+ if isinstance(content, dict) and content.get("topic"):
153
+ fallback_topic = content["topic"]
154
+ break
155
+ result = {"topic": fallback_topic}
156
+
157
+ result["iteration"] = iteration
158
+
159
+ # Add assistant response to history
160
+ self.conversation_history.append(
161
+ {
162
+ "role": "assistant",
163
+ "content": result,
164
+ "iteration": iteration,
165
+ }
166
+ )
167
+
168
+ print("\nāœ“ Rephrasing Completed:")
169
+ print(f" Optimized Research Topic: {result.get('topic', '')}")
170
+
171
+ return result
172
+
173
+ async def check_user_satisfaction(
174
+ self, rephrase_result: dict[str, Any], user_feedback: str
175
+ ) -> dict[str, Any]:
176
+ """
177
+ Determine user satisfaction with rephrasing result
178
+
179
+ Args:
180
+ rephrase_result: Current rephrasing result
181
+ user_feedback: User feedback
182
+
183
+ Returns:
184
+ Dictionary containing judgment results
185
+ {
186
+ "user_satisfied": bool, # Whether user is satisfied
187
+ "should_continue": bool, # Whether to continue rephrasing
188
+ "interpretation": str, # Interpretation of user intent
189
+ "suggested_action": str # Suggested next action
190
+ }
191
+ """
192
+ print(f"\n{'=' * 70}")
193
+ print("šŸ¤” RephraseAgent - Judging User Intent")
194
+ print(f"{'=' * 70}")
195
+ print(f"User Feedback: {user_feedback}\n")
196
+
197
+ system_prompt = self.get_prompt("system", "role")
198
+ if not system_prompt:
199
+ raise ValueError(
200
+ "RephraseAgent missing system prompt, please configure system.role in prompts/{lang}/rephrase_agent.yaml"
201
+ )
202
+
203
+ user_prompt_template = self.get_prompt("process", "check_satisfaction")
204
+ if not user_prompt_template:
205
+ raise ValueError(
206
+ "RephraseAgent missing check_satisfaction prompt, please configure process.check_satisfaction in prompts/{lang}/rephrase_agent.yaml"
207
+ )
208
+
209
+ user_prompt = user_prompt_template.format(
210
+ topic=rephrase_result.get("topic", ""), user_feedback=user_feedback
211
+ )
212
+
213
+ response = await self.call_llm(
214
+ user_prompt=user_prompt,
215
+ system_prompt=system_prompt,
216
+ stage="check_satisfaction",
217
+ verbose=False,
218
+ )
219
+
220
+ # Parse JSON output
221
+ data = extract_json_from_text(response)
222
+ from ..utils.json_utils import ensure_json_dict, ensure_keys
223
+
224
+ try:
225
+ result = ensure_json_dict(data)
226
+ ensure_keys(result, ["user_satisfied", "should_continue", "interpretation"])
227
+ except Exception:
228
+ # Fallback: judge based on keywords
229
+ feedback_lower = user_feedback.lower()
230
+ satisfied_keywords = ["ok", "yes", "satisfied", "good", "fine", "agree", "approved"]
231
+ continue_keywords = [
232
+ "modify",
233
+ "change",
234
+ "adjust",
235
+ "no",
236
+ "need",
237
+ "want",
238
+ "should",
239
+ "hope",
240
+ ]
241
+
242
+ user_satisfied = any(kw in feedback_lower for kw in satisfied_keywords) and not any(
243
+ kw in feedback_lower for kw in continue_keywords
244
+ )
245
+
246
+ result = {
247
+ "user_satisfied": user_satisfied,
248
+ "should_continue": not user_satisfied,
249
+ "interpretation": "Judged based on keywords",
250
+ "suggested_action": (
251
+ "Continue rephrasing" if not user_satisfied else "Proceed to next stage"
252
+ ),
253
+ }
254
+
255
+ print("\nšŸ“Š Judgment Result:")
256
+ print(f" User Satisfied: {'Yes' if result.get('user_satisfied') else 'No'}")
257
+ print(f" Continue Rephrasing: {'Yes' if result.get('should_continue') else 'No'}")
258
+ print(f" Intent Interpretation: {result.get('interpretation', '')}")
259
+
260
+ return result
261
+
262
+
263
+ __all__ = ["RephraseAgent"]