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.
- realtimex_deeptutor/__init__.py +67 -0
- realtimex_deeptutor-0.5.0.post1.dist-info/METADATA +1612 -0
- realtimex_deeptutor-0.5.0.post1.dist-info/RECORD +276 -0
- realtimex_deeptutor-0.5.0.post1.dist-info/WHEEL +5 -0
- realtimex_deeptutor-0.5.0.post1.dist-info/entry_points.txt +2 -0
- realtimex_deeptutor-0.5.0.post1.dist-info/licenses/LICENSE +661 -0
- realtimex_deeptutor-0.5.0.post1.dist-info/top_level.txt +2 -0
- src/__init__.py +40 -0
- src/agents/__init__.py +24 -0
- src/agents/base_agent.py +657 -0
- src/agents/chat/__init__.py +24 -0
- src/agents/chat/chat_agent.py +435 -0
- src/agents/chat/prompts/en/chat_agent.yaml +35 -0
- src/agents/chat/prompts/zh/chat_agent.yaml +35 -0
- src/agents/chat/session_manager.py +311 -0
- src/agents/co_writer/__init__.py +0 -0
- src/agents/co_writer/edit_agent.py +260 -0
- src/agents/co_writer/narrator_agent.py +423 -0
- src/agents/co_writer/prompts/en/edit_agent.yaml +113 -0
- src/agents/co_writer/prompts/en/narrator_agent.yaml +88 -0
- src/agents/co_writer/prompts/zh/edit_agent.yaml +113 -0
- src/agents/co_writer/prompts/zh/narrator_agent.yaml +88 -0
- src/agents/guide/__init__.py +16 -0
- src/agents/guide/agents/__init__.py +11 -0
- src/agents/guide/agents/chat_agent.py +104 -0
- src/agents/guide/agents/interactive_agent.py +223 -0
- src/agents/guide/agents/locate_agent.py +149 -0
- src/agents/guide/agents/summary_agent.py +150 -0
- src/agents/guide/guide_manager.py +500 -0
- src/agents/guide/prompts/en/chat_agent.yaml +41 -0
- src/agents/guide/prompts/en/interactive_agent.yaml +202 -0
- src/agents/guide/prompts/en/locate_agent.yaml +68 -0
- src/agents/guide/prompts/en/summary_agent.yaml +157 -0
- src/agents/guide/prompts/zh/chat_agent.yaml +41 -0
- src/agents/guide/prompts/zh/interactive_agent.yaml +626 -0
- src/agents/guide/prompts/zh/locate_agent.yaml +68 -0
- src/agents/guide/prompts/zh/summary_agent.yaml +157 -0
- src/agents/ideagen/__init__.py +12 -0
- src/agents/ideagen/idea_generation_workflow.py +426 -0
- src/agents/ideagen/material_organizer_agent.py +173 -0
- src/agents/ideagen/prompts/en/idea_generation.yaml +187 -0
- src/agents/ideagen/prompts/en/material_organizer.yaml +69 -0
- src/agents/ideagen/prompts/zh/idea_generation.yaml +187 -0
- src/agents/ideagen/prompts/zh/material_organizer.yaml +69 -0
- src/agents/question/__init__.py +24 -0
- src/agents/question/agents/__init__.py +18 -0
- src/agents/question/agents/generate_agent.py +381 -0
- src/agents/question/agents/relevance_analyzer.py +207 -0
- src/agents/question/agents/retrieve_agent.py +239 -0
- src/agents/question/coordinator.py +718 -0
- src/agents/question/example.py +109 -0
- src/agents/question/prompts/en/coordinator.yaml +75 -0
- src/agents/question/prompts/en/generate_agent.yaml +77 -0
- src/agents/question/prompts/en/relevance_analyzer.yaml +41 -0
- src/agents/question/prompts/en/retrieve_agent.yaml +32 -0
- src/agents/question/prompts/zh/coordinator.yaml +75 -0
- src/agents/question/prompts/zh/generate_agent.yaml +77 -0
- src/agents/question/prompts/zh/relevance_analyzer.yaml +39 -0
- src/agents/question/prompts/zh/retrieve_agent.yaml +30 -0
- src/agents/research/agents/__init__.py +23 -0
- src/agents/research/agents/decompose_agent.py +507 -0
- src/agents/research/agents/manager_agent.py +228 -0
- src/agents/research/agents/note_agent.py +180 -0
- src/agents/research/agents/rephrase_agent.py +263 -0
- src/agents/research/agents/reporting_agent.py +1333 -0
- src/agents/research/agents/research_agent.py +714 -0
- src/agents/research/data_structures.py +451 -0
- src/agents/research/main.py +188 -0
- src/agents/research/prompts/en/decompose_agent.yaml +89 -0
- src/agents/research/prompts/en/manager_agent.yaml +24 -0
- src/agents/research/prompts/en/note_agent.yaml +121 -0
- src/agents/research/prompts/en/rephrase_agent.yaml +58 -0
- src/agents/research/prompts/en/reporting_agent.yaml +380 -0
- src/agents/research/prompts/en/research_agent.yaml +173 -0
- src/agents/research/prompts/zh/decompose_agent.yaml +89 -0
- src/agents/research/prompts/zh/manager_agent.yaml +24 -0
- src/agents/research/prompts/zh/note_agent.yaml +121 -0
- src/agents/research/prompts/zh/rephrase_agent.yaml +58 -0
- src/agents/research/prompts/zh/reporting_agent.yaml +380 -0
- src/agents/research/prompts/zh/research_agent.yaml +173 -0
- src/agents/research/research_pipeline.py +1309 -0
- src/agents/research/utils/__init__.py +60 -0
- src/agents/research/utils/citation_manager.py +799 -0
- src/agents/research/utils/json_utils.py +98 -0
- src/agents/research/utils/token_tracker.py +297 -0
- src/agents/solve/__init__.py +80 -0
- src/agents/solve/analysis_loop/__init__.py +14 -0
- src/agents/solve/analysis_loop/investigate_agent.py +414 -0
- src/agents/solve/analysis_loop/note_agent.py +190 -0
- src/agents/solve/main_solver.py +862 -0
- src/agents/solve/memory/__init__.py +34 -0
- src/agents/solve/memory/citation_memory.py +353 -0
- src/agents/solve/memory/investigate_memory.py +226 -0
- src/agents/solve/memory/solve_memory.py +340 -0
- src/agents/solve/prompts/en/analysis_loop/investigate_agent.yaml +55 -0
- src/agents/solve/prompts/en/analysis_loop/note_agent.yaml +54 -0
- src/agents/solve/prompts/en/solve_loop/manager_agent.yaml +67 -0
- src/agents/solve/prompts/en/solve_loop/precision_answer_agent.yaml +62 -0
- src/agents/solve/prompts/en/solve_loop/response_agent.yaml +90 -0
- src/agents/solve/prompts/en/solve_loop/solve_agent.yaml +75 -0
- src/agents/solve/prompts/en/solve_loop/tool_agent.yaml +38 -0
- src/agents/solve/prompts/zh/analysis_loop/investigate_agent.yaml +53 -0
- src/agents/solve/prompts/zh/analysis_loop/note_agent.yaml +54 -0
- src/agents/solve/prompts/zh/solve_loop/manager_agent.yaml +66 -0
- src/agents/solve/prompts/zh/solve_loop/precision_answer_agent.yaml +62 -0
- src/agents/solve/prompts/zh/solve_loop/response_agent.yaml +90 -0
- src/agents/solve/prompts/zh/solve_loop/solve_agent.yaml +76 -0
- src/agents/solve/prompts/zh/solve_loop/tool_agent.yaml +41 -0
- src/agents/solve/solve_loop/__init__.py +22 -0
- src/agents/solve/solve_loop/citation_manager.py +74 -0
- src/agents/solve/solve_loop/manager_agent.py +274 -0
- src/agents/solve/solve_loop/precision_answer_agent.py +96 -0
- src/agents/solve/solve_loop/response_agent.py +301 -0
- src/agents/solve/solve_loop/solve_agent.py +325 -0
- src/agents/solve/solve_loop/tool_agent.py +470 -0
- src/agents/solve/utils/__init__.py +64 -0
- src/agents/solve/utils/config_validator.py +313 -0
- src/agents/solve/utils/display_manager.py +223 -0
- src/agents/solve/utils/error_handler.py +363 -0
- src/agents/solve/utils/json_utils.py +98 -0
- src/agents/solve/utils/performance_monitor.py +407 -0
- src/agents/solve/utils/token_tracker.py +541 -0
- src/api/__init__.py +0 -0
- src/api/main.py +240 -0
- src/api/routers/__init__.py +1 -0
- src/api/routers/agent_config.py +69 -0
- src/api/routers/chat.py +296 -0
- src/api/routers/co_writer.py +337 -0
- src/api/routers/config.py +627 -0
- src/api/routers/dashboard.py +18 -0
- src/api/routers/guide.py +337 -0
- src/api/routers/ideagen.py +436 -0
- src/api/routers/knowledge.py +821 -0
- src/api/routers/notebook.py +247 -0
- src/api/routers/question.py +537 -0
- src/api/routers/research.py +394 -0
- src/api/routers/settings.py +164 -0
- src/api/routers/solve.py +305 -0
- src/api/routers/system.py +252 -0
- src/api/run_server.py +61 -0
- src/api/utils/history.py +172 -0
- src/api/utils/log_interceptor.py +21 -0
- src/api/utils/notebook_manager.py +415 -0
- src/api/utils/progress_broadcaster.py +72 -0
- src/api/utils/task_id_manager.py +100 -0
- src/config/__init__.py +0 -0
- src/config/accessors.py +18 -0
- src/config/constants.py +34 -0
- src/config/defaults.py +18 -0
- src/config/schema.py +38 -0
- src/config/settings.py +50 -0
- src/core/errors.py +62 -0
- src/knowledge/__init__.py +23 -0
- src/knowledge/add_documents.py +606 -0
- src/knowledge/config.py +65 -0
- src/knowledge/example_add_documents.py +236 -0
- src/knowledge/extract_numbered_items.py +1039 -0
- src/knowledge/initializer.py +621 -0
- src/knowledge/kb.py +22 -0
- src/knowledge/manager.py +782 -0
- src/knowledge/progress_tracker.py +182 -0
- src/knowledge/start_kb.py +535 -0
- src/logging/__init__.py +103 -0
- src/logging/adapters/__init__.py +17 -0
- src/logging/adapters/lightrag.py +184 -0
- src/logging/adapters/llamaindex.py +141 -0
- src/logging/config.py +80 -0
- src/logging/handlers/__init__.py +20 -0
- src/logging/handlers/console.py +75 -0
- src/logging/handlers/file.py +201 -0
- src/logging/handlers/websocket.py +127 -0
- src/logging/logger.py +709 -0
- src/logging/stats/__init__.py +16 -0
- src/logging/stats/llm_stats.py +179 -0
- src/services/__init__.py +56 -0
- src/services/config/__init__.py +61 -0
- src/services/config/knowledge_base_config.py +210 -0
- src/services/config/loader.py +260 -0
- src/services/config/unified_config.py +603 -0
- src/services/embedding/__init__.py +45 -0
- src/services/embedding/adapters/__init__.py +22 -0
- src/services/embedding/adapters/base.py +106 -0
- src/services/embedding/adapters/cohere.py +127 -0
- src/services/embedding/adapters/jina.py +99 -0
- src/services/embedding/adapters/ollama.py +116 -0
- src/services/embedding/adapters/openai_compatible.py +96 -0
- src/services/embedding/client.py +159 -0
- src/services/embedding/config.py +156 -0
- src/services/embedding/provider.py +119 -0
- src/services/llm/__init__.py +152 -0
- src/services/llm/capabilities.py +313 -0
- src/services/llm/client.py +302 -0
- src/services/llm/cloud_provider.py +530 -0
- src/services/llm/config.py +200 -0
- src/services/llm/error_mapping.py +103 -0
- src/services/llm/exceptions.py +152 -0
- src/services/llm/factory.py +450 -0
- src/services/llm/local_provider.py +347 -0
- src/services/llm/providers/anthropic.py +95 -0
- src/services/llm/providers/base_provider.py +93 -0
- src/services/llm/providers/open_ai.py +83 -0
- src/services/llm/registry.py +71 -0
- src/services/llm/telemetry.py +40 -0
- src/services/llm/types.py +27 -0
- src/services/llm/utils.py +333 -0
- src/services/prompt/__init__.py +25 -0
- src/services/prompt/manager.py +206 -0
- src/services/rag/__init__.py +64 -0
- src/services/rag/components/__init__.py +29 -0
- src/services/rag/components/base.py +59 -0
- src/services/rag/components/chunkers/__init__.py +18 -0
- src/services/rag/components/chunkers/base.py +34 -0
- src/services/rag/components/chunkers/fixed.py +71 -0
- src/services/rag/components/chunkers/numbered_item.py +94 -0
- src/services/rag/components/chunkers/semantic.py +97 -0
- src/services/rag/components/embedders/__init__.py +14 -0
- src/services/rag/components/embedders/base.py +32 -0
- src/services/rag/components/embedders/openai.py +63 -0
- src/services/rag/components/indexers/__init__.py +18 -0
- src/services/rag/components/indexers/base.py +35 -0
- src/services/rag/components/indexers/graph.py +172 -0
- src/services/rag/components/indexers/lightrag.py +156 -0
- src/services/rag/components/indexers/vector.py +146 -0
- src/services/rag/components/parsers/__init__.py +18 -0
- src/services/rag/components/parsers/base.py +35 -0
- src/services/rag/components/parsers/markdown.py +52 -0
- src/services/rag/components/parsers/pdf.py +115 -0
- src/services/rag/components/parsers/text.py +86 -0
- src/services/rag/components/retrievers/__init__.py +18 -0
- src/services/rag/components/retrievers/base.py +34 -0
- src/services/rag/components/retrievers/dense.py +200 -0
- src/services/rag/components/retrievers/hybrid.py +164 -0
- src/services/rag/components/retrievers/lightrag.py +169 -0
- src/services/rag/components/routing.py +286 -0
- src/services/rag/factory.py +234 -0
- src/services/rag/pipeline.py +215 -0
- src/services/rag/pipelines/__init__.py +32 -0
- src/services/rag/pipelines/academic.py +44 -0
- src/services/rag/pipelines/lightrag.py +43 -0
- src/services/rag/pipelines/llamaindex.py +313 -0
- src/services/rag/pipelines/raganything.py +384 -0
- src/services/rag/service.py +244 -0
- src/services/rag/types.py +73 -0
- src/services/search/__init__.py +284 -0
- src/services/search/base.py +87 -0
- src/services/search/consolidation.py +398 -0
- src/services/search/providers/__init__.py +128 -0
- src/services/search/providers/baidu.py +188 -0
- src/services/search/providers/exa.py +194 -0
- src/services/search/providers/jina.py +161 -0
- src/services/search/providers/perplexity.py +153 -0
- src/services/search/providers/serper.py +209 -0
- src/services/search/providers/tavily.py +161 -0
- src/services/search/types.py +114 -0
- src/services/setup/__init__.py +34 -0
- src/services/setup/init.py +285 -0
- src/services/tts/__init__.py +16 -0
- src/services/tts/config.py +99 -0
- src/tools/__init__.py +91 -0
- src/tools/code_executor.py +536 -0
- src/tools/paper_search_tool.py +171 -0
- src/tools/query_item_tool.py +310 -0
- src/tools/question/__init__.py +15 -0
- src/tools/question/exam_mimic.py +616 -0
- src/tools/question/pdf_parser.py +211 -0
- src/tools/question/question_extractor.py +397 -0
- src/tools/rag_tool.py +173 -0
- src/tools/tex_chunker.py +339 -0
- src/tools/tex_downloader.py +253 -0
- src/tools/web_search.py +71 -0
- src/utils/config_manager.py +206 -0
- src/utils/document_validator.py +168 -0
- src/utils/error_rate_tracker.py +111 -0
- src/utils/error_utils.py +82 -0
- src/utils/json_parser.py +110 -0
- 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"]
|