jarvis-ai-assistant 0.3.20__py3-none-any.whl → 0.3.22__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 (57) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +24 -3
  3. jarvis/jarvis_agent/config_editor.py +5 -1
  4. jarvis/jarvis_agent/edit_file_handler.py +15 -9
  5. jarvis/jarvis_agent/jarvis.py +99 -3
  6. jarvis/jarvis_agent/memory_manager.py +3 -3
  7. jarvis/jarvis_agent/share_manager.py +3 -1
  8. jarvis/jarvis_agent/task_analyzer.py +0 -1
  9. jarvis/jarvis_agent/task_manager.py +15 -5
  10. jarvis/jarvis_agent/tool_executor.py +2 -2
  11. jarvis/jarvis_code_agent/code_agent.py +42 -18
  12. jarvis/jarvis_git_utils/git_commiter.py +3 -6
  13. jarvis/jarvis_mcp/sse_mcp_client.py +9 -3
  14. jarvis/jarvis_mcp/streamable_mcp_client.py +15 -5
  15. jarvis/jarvis_memory_organizer/memory_organizer.py +1 -1
  16. jarvis/jarvis_methodology/main.py +4 -4
  17. jarvis/jarvis_multi_agent/__init__.py +3 -3
  18. jarvis/jarvis_platform/base.py +10 -5
  19. jarvis/jarvis_platform/kimi.py +18 -6
  20. jarvis/jarvis_platform/tongyi.py +18 -5
  21. jarvis/jarvis_platform/yuanbao.py +10 -3
  22. jarvis/jarvis_platform_manager/main.py +21 -7
  23. jarvis/jarvis_platform_manager/service.py +4 -3
  24. jarvis/jarvis_rag/cli.py +61 -22
  25. jarvis/jarvis_rag/embedding_manager.py +10 -3
  26. jarvis/jarvis_rag/llm_interface.py +4 -1
  27. jarvis/jarvis_rag/query_rewriter.py +3 -1
  28. jarvis/jarvis_rag/rag_pipeline.py +47 -3
  29. jarvis/jarvis_rag/retriever.py +240 -2
  30. jarvis/jarvis_smart_shell/main.py +59 -18
  31. jarvis/jarvis_stats/cli.py +11 -9
  32. jarvis/jarvis_stats/stats.py +14 -8
  33. jarvis/jarvis_stats/storage.py +23 -6
  34. jarvis/jarvis_tools/cli/main.py +63 -29
  35. jarvis/jarvis_tools/edit_file.py +17 -90
  36. jarvis/jarvis_tools/file_analyzer.py +0 -1
  37. jarvis/jarvis_tools/generate_new_tool.py +3 -3
  38. jarvis/jarvis_tools/read_code.py +0 -1
  39. jarvis/jarvis_tools/read_webpage.py +14 -4
  40. jarvis/jarvis_tools/registry.py +16 -9
  41. jarvis/jarvis_tools/retrieve_memory.py +0 -1
  42. jarvis/jarvis_tools/save_memory.py +0 -1
  43. jarvis/jarvis_tools/search_web.py +0 -2
  44. jarvis/jarvis_tools/sub_agent.py +197 -0
  45. jarvis/jarvis_tools/sub_code_agent.py +194 -0
  46. jarvis/jarvis_tools/virtual_tty.py +21 -13
  47. jarvis/jarvis_utils/config.py +35 -5
  48. jarvis/jarvis_utils/input.py +297 -56
  49. jarvis/jarvis_utils/methodology.py +3 -1
  50. jarvis/jarvis_utils/output.py +5 -2
  51. jarvis/jarvis_utils/utils.py +483 -170
  52. {jarvis_ai_assistant-0.3.20.dist-info → jarvis_ai_assistant-0.3.22.dist-info}/METADATA +10 -2
  53. {jarvis_ai_assistant-0.3.20.dist-info → jarvis_ai_assistant-0.3.22.dist-info}/RECORD +57 -55
  54. {jarvis_ai_assistant-0.3.20.dist-info → jarvis_ai_assistant-0.3.22.dist-info}/WHEEL +0 -0
  55. {jarvis_ai_assistant-0.3.20.dist-info → jarvis_ai_assistant-0.3.22.dist-info}/entry_points.txt +0 -0
  56. {jarvis_ai_assistant-0.3.20.dist-info → jarvis_ai_assistant-0.3.22.dist-info}/licenses/LICENSE +0 -0
  57. {jarvis_ai_assistant-0.3.20.dist-info → jarvis_ai_assistant-0.3.22.dist-info}/top_level.txt +0 -0
@@ -23,12 +23,20 @@ def list_tools(
23
23
 
24
24
  if as_json:
25
25
  if detailed:
26
- PrettyOutput.print(json.dumps(tools, indent=2, ensure_ascii=False), OutputType.CODE, lang="json")
26
+ PrettyOutput.print(
27
+ json.dumps(tools, indent=2, ensure_ascii=False),
28
+ OutputType.CODE,
29
+ lang="json",
30
+ )
27
31
  else:
28
32
  simple_tools = [
29
33
  {"name": t["name"], "description": t["description"]} for t in tools
30
34
  ]
31
- PrettyOutput.print(json.dumps(simple_tools, indent=2, ensure_ascii=False), OutputType.CODE, lang="json")
35
+ PrettyOutput.print(
36
+ json.dumps(simple_tools, indent=2, ensure_ascii=False),
37
+ OutputType.CODE,
38
+ lang="json",
39
+ )
32
40
  else:
33
41
  PrettyOutput.section("可用工具列表", OutputType.SYSTEM)
34
42
  for tool in tools:
@@ -37,18 +45,27 @@ def list_tools(
37
45
  if detailed:
38
46
  PrettyOutput.print(" 参数:", OutputType.INFO)
39
47
  import json as _json # local import to ensure available
40
- PrettyOutput.print(_json.dumps(tool["parameters"], ensure_ascii=False, indent=2), OutputType.CODE, lang="json")
48
+
49
+ PrettyOutput.print(
50
+ _json.dumps(tool["parameters"], ensure_ascii=False, indent=2),
51
+ OutputType.CODE,
52
+ lang="json",
53
+ )
41
54
 
42
55
 
43
56
  @app.command("stat")
44
57
  def stat_tools(
45
58
  as_json: bool = typer.Option(False, "--json", help="以JSON格式输出"),
46
- last_days: Optional[int] = typer.Option(None, "--days", help="显示最近N天的统计(默认显示所有历史数据)"),
47
- format: str = typer.Option("table", "--format", help="显示格式: table, chart, summary")
59
+ last_days: Optional[int] = typer.Option(
60
+ None, "--days", help="显示最近N天的统计(默认显示所有历史数据)"
61
+ ),
62
+ format: str = typer.Option(
63
+ "table", "--format", help="显示格式: table, chart, summary"
64
+ ),
48
65
  ):
49
66
  """显示工具调用统计信息"""
50
67
  from jarvis.jarvis_stats.stats import StatsManager
51
-
68
+
52
69
  if format == "table":
53
70
  registry = ToolRegistry()
54
71
  stats = registry._get_tool_stats()
@@ -64,13 +81,24 @@ def stat_tools(
64
81
  table_data.sort(key=lambda x: x[1], reverse=True)
65
82
 
66
83
  if as_json:
67
- PrettyOutput.print(json.dumps(dict(table_data), indent=2), OutputType.CODE, lang="json")
84
+ PrettyOutput.print(
85
+ json.dumps(dict(table_data), indent=2), OutputType.CODE, lang="json"
86
+ )
68
87
  else:
69
88
  time_desc = f"最近{last_days}天" if last_days else "所有历史"
70
89
  PrettyOutput.section(f"工具调用统计 ({time_desc})", OutputType.SYSTEM)
71
90
  if table_data:
72
- PrettyOutput.print(tabulate(table_data, headers=["工具名称", "调用次数"], tablefmt="grid"), OutputType.CODE, lang="text")
73
- PrettyOutput.print(f"\n总计: {len(table_data)} 个工具被使用,共 {sum(x[1] for x in table_data)} 次调用", OutputType.INFO)
91
+ PrettyOutput.print(
92
+ tabulate(
93
+ table_data, headers=["工具名称", "调用次数"], tablefmt="grid"
94
+ ),
95
+ OutputType.CODE,
96
+ lang="text",
97
+ )
98
+ PrettyOutput.print(
99
+ f"\n总计: {len(table_data)} 个工具被使用,共 {sum(x[1] for x in table_data)} 次调用",
100
+ OutputType.INFO,
101
+ )
74
102
  else:
75
103
  PrettyOutput.print("暂无工具调用记录", OutputType.INFO)
76
104
  else:
@@ -79,51 +107,59 @@ def stat_tools(
79
107
  # 显示所有标记为 tool 组的指标
80
108
  metrics = StatsManager.list_metrics()
81
109
  tool_metrics = []
82
-
110
+
83
111
  for metric in metrics:
84
112
  # 检查是否是工具组的指标
85
113
  if last_days:
86
114
  stats_data = StatsManager.get_stats(
87
- metric_name=metric,
88
- last_days=last_days,
89
- tags={"group": "tool"}
115
+ metric_name=metric, last_days=last_days, tags={"group": "tool"}
90
116
  )
91
117
  else:
92
118
  # 获取所有历史数据
93
119
  from datetime import datetime
120
+
94
121
  stats_data = StatsManager.get_stats(
95
122
  metric_name=metric,
96
123
  start_time=datetime(2000, 1, 1),
97
124
  end_time=datetime.now(),
98
- tags={"group": "tool"}
125
+ tags={"group": "tool"},
99
126
  )
100
127
  if stats_data and stats_data.get("records"):
101
128
  tool_metrics.append(metric)
102
-
129
+
103
130
  if tool_metrics:
104
131
  for metric in tool_metrics:
105
132
  if format == "chart":
106
133
  if last_days:
107
- StatsManager.plot(metric, last_days=last_days, tags={"group": "tool"})
134
+ StatsManager.plot(
135
+ metric, last_days=last_days, tags={"group": "tool"}
136
+ )
108
137
  else:
109
138
  from datetime import datetime
139
+
110
140
  StatsManager.plot(
111
- metric,
112
- start_time=datetime(2000, 1, 1),
113
- end_time=datetime.now(),
114
- tags={"group": "tool"}
141
+ metric,
142
+ start_time=datetime(2000, 1, 1),
143
+ end_time=datetime.now(),
144
+ tags={"group": "tool"},
115
145
  )
116
146
  elif format == "summary":
117
147
  if last_days:
118
- StatsManager.show(metric, last_days=last_days, format="summary", tags={"group": "tool"})
148
+ StatsManager.show(
149
+ metric,
150
+ last_days=last_days,
151
+ format="summary",
152
+ tags={"group": "tool"},
153
+ )
119
154
  else:
120
155
  from datetime import datetime
156
+
121
157
  StatsManager.show(
122
- metric,
123
- start_time=datetime(2000, 1, 1),
124
- end_time=datetime.now(),
125
- format="summary",
126
- tags={"group": "tool"}
158
+ metric,
159
+ start_time=datetime(2000, 1, 1),
160
+ end_time=datetime.now(),
161
+ format="summary",
162
+ tags={"group": "tool"},
127
163
  )
128
164
  else:
129
165
  PrettyOutput.print("暂无工具调用记录", OutputType.INFO)
@@ -159,9 +195,7 @@ def call_tool(
159
195
  with open(args_file, "r", encoding="utf-8") as f:
160
196
  tool_args = json.load(f)
161
197
  except (json.JSONDecodeError, FileNotFoundError) as e:
162
- PrettyOutput.print(
163
- f"错误: 无法从文件加载参数: {str(e)}", OutputType.ERROR
164
- )
198
+ PrettyOutput.print(f"错误: 无法从文件加载参数: {str(e)}", OutputType.ERROR)
165
199
  raise typer.Exit(code=1)
166
200
 
167
201
  required_params = tool_obj.parameters.get("required", [])
@@ -31,7 +31,6 @@ class FileSearchReplaceTool:
31
31
  - reason: 修改原因描述
32
32
  - SEARCH: 需要查找的原始代码(必须包含足够上下文)
33
33
  - REPLACE: 替换后的新代码
34
- 3. 工具会自动选择最适合的编辑模式
35
34
 
36
35
  ## 核心原则
37
36
  1. **精准修改**: 只修改必要的代码部分,保持其他部分不变
@@ -132,107 +131,35 @@ class FileSearchReplaceTool:
132
131
  file_path = os.path.abspath(file_info["path"])
133
132
  changes = file_info["changes"]
134
133
 
135
- # 创建已处理文件变量,用于失败时回滚
136
- original_content = None
137
- processed = False
138
- file_success = True
139
-
140
134
  try:
141
- file_exists = os.path.exists(file_path)
142
- content = ""
143
-
144
- try:
145
- # 如果文件存在,则读取内容
146
- if file_exists:
147
- with open(file_path, "r", encoding="utf-8") as f:
148
- content = f.read()
149
- original_content = content
150
-
151
-
152
- success, temp_content = EditFileHandler._fast_edit(
153
- file_path, changes
135
+ success, result = EditFileHandler._fast_edit(file_path, changes)
136
+ if success:
137
+ stdout_message = f"文件 {file_path} 修改完成"
138
+ stdout_messages.append(stdout_message)
139
+ overall_success = True
140
+ file_results.append(
141
+ {
142
+ "file": file_path,
143
+ "success": True,
144
+ "stdout": stdout_message,
145
+ "stderr": "",
146
+ }
147
+ )
148
+ else:
149
+ PrettyOutput.print(
150
+ f"文件 {file_path} 处理失败", OutputType.ERROR
154
151
  )
155
- if not success:
156
- PrettyOutput.print(f"文件 {file_path} 处理失败", OutputType.ERROR)
157
- file_results.append(
158
- {
159
- "file": file_path,
160
- "success": False,
161
- "stdout": "",
162
- "stderr": temp_content,
163
- }
164
- )
165
- continue
166
-
167
-
168
-
169
- # 只有当所有替换操作都成功时,才写回文件
170
- if success and (
171
- temp_content != original_content or not file_exists
172
- ):
173
- # 确保目录存在
174
- os.makedirs(
175
- os.path.dirname(os.path.abspath(file_path)), exist_ok=True
176
- )
177
-
178
- with open(file_path, "w", encoding="utf-8") as f:
179
- f.write(temp_content)
180
-
181
- processed = True
182
-
183
- action = "创建并写入" if not file_exists else "成功修改"
184
- stdout_message = f"文件 {file_path} {action} 完成"
185
- stdout_messages.append(stdout_message)
186
-
187
- overall_success = True
188
-
189
- file_results.append(
190
- {
191
- "file": file_path,
192
- "success": True,
193
- "stdout": stdout_message,
194
- "stderr": "",
195
- }
196
- )
197
-
198
- except Exception as e:
199
- stderr_message = f"处理文件 {file_path} 时出错: {str(e)}"
200
- stderr_messages.append(stderr_message)
201
- PrettyOutput.print(stderr_message, OutputType.WARNING)
202
- file_success = False
203
152
  file_results.append(
204
153
  {
205
154
  "file": file_path,
206
155
  "success": False,
207
156
  "stdout": "",
208
- "stderr": stderr_message,
157
+ "stderr": result,
209
158
  }
210
159
  )
211
-
212
160
  except Exception as e:
213
161
  error_msg = f"文件搜索替换操作失败: {str(e)}"
214
162
  PrettyOutput.print(error_msg, OutputType.WARNING)
215
-
216
- # 如果有已修改的文件,尝试回滚
217
- if processed:
218
- rollback_message = "操作失败,正在回滚修改..."
219
- stderr_messages.append(rollback_message)
220
- PrettyOutput.print(rollback_message, OutputType.WARNING)
221
-
222
- try:
223
- if original_content is None:
224
- # 如果是新创建的文件,则删除
225
- if os.path.exists(file_path):
226
- os.remove(file_path)
227
- stderr_messages.append(f"已删除新创建的文件: {file_path}")
228
- else:
229
- # 如果是修改的文件,则恢复原内容
230
- with open(file_path, "w", encoding="utf-8") as f:
231
- f.write(original_content)
232
- stderr_messages.append(f"已回滚文件: {file_path}")
233
- except:
234
- stderr_messages.append(f"回滚文件失败: {file_path}")
235
-
236
163
  file_results.append(
237
164
  {
238
165
  "file": file_path,
@@ -105,7 +105,6 @@ class FileAnalyzerTool:
105
105
 
106
106
  analysis_result = platform.chat_until_success(analysis_request)
107
107
 
108
-
109
108
  # 清理会话
110
109
  platform.delete_chat()
111
110
 
@@ -80,7 +80,9 @@ class generate_new_tool:
80
80
  # 验证工具代码中的名称是否与tool_name一致
81
81
  import re
82
82
 
83
- match = re.search(r"^\s*name\s*=\s*[\"'](.+?)[\"']", tool_code, re.MULTILINE)
83
+ match = re.search(
84
+ r"^\s*name\s*=\s*[\"'](.+?)[\"']", tool_code, re.MULTILINE
85
+ )
84
86
  if not match:
85
87
  return {
86
88
  "success": False,
@@ -146,8 +148,6 @@ class generate_new_tool:
146
148
  )
147
149
  success_message += f"\n注册到当前会话失败,可能需要重新启动Jarvis"
148
150
 
149
-
150
-
151
151
  # 检查并安装缺失的依赖
152
152
  try:
153
153
  required_packages = set()
@@ -115,7 +115,6 @@ class ReadCodeTool:
115
115
  f"{numbered_content}\n\n"
116
116
  )
117
117
 
118
-
119
118
  if agent:
120
119
  files = agent.get_user_data("files")
121
120
  if files:
@@ -81,15 +81,25 @@ class WebpageTool:
81
81
  resp = http_get(url, timeout=10.0, allow_redirects=True)
82
82
  content_md = md(resp.text, strip=["script", "style"])
83
83
  except requests.exceptions.HTTPError as e:
84
- PrettyOutput.print(f"⚠️ HTTP错误 {e.response.status_code} 访问 {url}", OutputType.WARNING)
85
- return {"success": False, "stdout": "", "stderr": f"HTTP错误:{e.response.status_code}"}
84
+ PrettyOutput.print(
85
+ f"⚠️ HTTP错误 {e.response.status_code} 访问 {url}",
86
+ OutputType.WARNING,
87
+ )
88
+ return {
89
+ "success": False,
90
+ "stdout": "",
91
+ "stderr": f"HTTP错误:{e.response.status_code}",
92
+ }
86
93
  except requests.exceptions.RequestException as e:
87
94
  PrettyOutput.print(f"⚠️ 请求错误: {e}", OutputType.WARNING)
88
95
  return {"success": False, "stdout": "", "stderr": f"请求错误:{e}"}
89
96
 
90
97
  if not content_md or not content_md.strip():
91
- return {"success": False, "stdout": "", "stderr": "无法从网页抓取有效内容。"}
92
-
98
+ return {
99
+ "success": False,
100
+ "stdout": "",
101
+ "stderr": "无法从网页抓取有效内容。",
102
+ }
93
103
 
94
104
  summary_prompt = f"""以下是网页 {url} 的内容(已转换为Markdown):
95
105
  ----------------
@@ -172,10 +172,14 @@ class ToolRegistry(OutputHandlerProtocol):
172
172
 
173
173
  def handle(self, response: str, agent_: Any) -> Tuple[bool, Any]:
174
174
  try:
175
- tool_call, err_msg = self._extract_tool_calls(response)
175
+ tool_call, err_msg, auto_completed = self._extract_tool_calls(response)
176
176
  if err_msg:
177
177
  return False, err_msg
178
- return False, self.handle_tool_calls(tool_call, agent_)
178
+ result = self.handle_tool_calls(tool_call, agent_)
179
+ if auto_completed:
180
+ # 如果自动补全了结束标签,在结果中添加说明信息
181
+ result = f"检测到工具调用缺少结束标签,已自动补全{ct('TOOL_CALL')}。请确保后续工具调用包含完整的开始和结束标签。\n\n{result}"
182
+ return False, result
179
183
  except Exception as e:
180
184
  PrettyOutput.print(f"工具调用处理失败: {str(e)}", OutputType.ERROR)
181
185
  from jarvis.jarvis_agent import Agent
@@ -288,7 +292,6 @@ class ToolRegistry(OutputHandlerProtocol):
288
292
  if tool_name in self.tools:
289
293
  del self.tools[tool_name]
290
294
 
291
-
292
295
  def _load_mcp_tools(self) -> None:
293
296
  """加载MCP工具,优先从配置获取,其次从目录扫描"""
294
297
  from jarvis.jarvis_utils.config import get_mcp_config
@@ -351,7 +354,6 @@ class ToolRegistry(OutputHandlerProtocol):
351
354
  try:
352
355
  import subprocess
353
356
 
354
-
355
357
  subprocess.run(
356
358
  ["git", "clone", central_repo, central_repo_path], check=True
357
359
  )
@@ -611,16 +613,17 @@ class ToolRegistry(OutputHandlerProtocol):
611
613
  )
612
614
 
613
615
  @staticmethod
614
- def _extract_tool_calls(content: str) -> Tuple[Dict[str, Dict[str, Any]], str]:
616
+ def _extract_tool_calls(content: str) -> Tuple[Dict[str, Dict[str, Any]], str, bool]:
615
617
  """从内容中提取工具调用。
616
618
 
617
619
  参数:
618
620
  content: 包含工具调用的内容
619
621
 
620
622
  返回:
621
- Tuple[Dict[str, Dict[str, Any]], str]:
623
+ Tuple[Dict[str, Dict[str, Any]], str, bool]:
622
624
  - 第一个元素是提取的工具调用字典
623
625
  - 第二个元素是错误消息字符串(成功时为"")
626
+ - 第三个元素是是否自动补全了结束标签
624
627
 
625
628
  异常:
626
629
  Exception: 如果工具调用缺少必要字段
@@ -629,6 +632,7 @@ class ToolRegistry(OutputHandlerProtocol):
629
632
  data = re.findall(
630
633
  ot("TOOL_CALL") + r"(.*?)" + ct("TOOL_CALL"), content, re.DOTALL
631
634
  )
635
+ auto_completed = False
632
636
  if not data:
633
637
  # can_handle 确保 ot("TOOL_CALL") 在内容中。
634
638
  # 如果数据为空,则表示 ct("TOOL_CALL") 可能丢失。
@@ -649,8 +653,8 @@ class ToolRegistry(OutputHandlerProtocol):
649
653
 
650
654
  # Ask user for confirmation
651
655
 
652
-
653
656
  data = temp_data
657
+ auto_completed = True
654
658
  except (yaml.YAMLError, EOFError, KeyboardInterrupt):
655
659
  # Even after fixing, it's not valid YAML, or user cancelled.
656
660
  # Fall through to the original error.
@@ -660,6 +664,7 @@ class ToolRegistry(OutputHandlerProtocol):
660
664
  return (
661
665
  {},
662
666
  f"只有{ot('TOOL_CALL')}标签,未找到{ct('TOOL_CALL')}标签,调用格式错误,请检查工具调用格式。\n{tool_call_help}",
667
+ False,
663
668
  )
664
669
  ret = []
665
670
  for item in data:
@@ -672,6 +677,7 @@ class ToolRegistry(OutputHandlerProtocol):
672
677
  {e}
673
678
 
674
679
  {tool_call_help}""",
680
+ False,
675
681
  )
676
682
 
677
683
  if "name" in msg and "arguments" in msg and "want" in msg:
@@ -682,10 +688,11 @@ class ToolRegistry(OutputHandlerProtocol):
682
688
  f"""工具调用格式错误,请检查工具调用格式(缺少name、arguments、want字段)。
683
689
 
684
690
  {tool_call_help}""",
691
+ False,
685
692
  )
686
693
  if len(ret) > 1:
687
- return {}, "检测到多个工具调用,请一次只处理一个工具调用。"
688
- return ret[0] if ret else {}, ""
694
+ return {}, "检测到多个工具调用,请一次只处理一个工具调用。", False
695
+ return ret[0] if ret else {}, "", auto_completed
689
696
 
690
697
  def register_tool(
691
698
  self,
@@ -166,7 +166,6 @@ class RetrieveMemoryTool:
166
166
 
167
167
  # 打印结果摘要
168
168
 
169
-
170
169
  if tags:
171
170
  pass
172
171
 
@@ -174,7 +174,6 @@ class SaveMemoryTool:
174
174
 
175
175
  # 生成总结报告
176
176
 
177
-
178
177
  # 构建返回结果
179
178
  output = {
180
179
  "total": len(memories),
@@ -81,8 +81,6 @@ class SearchWebTool:
81
81
 
82
82
  url_list_str = "\n".join(f" - {u}" for u in visited_urls)
83
83
 
84
-
85
-
86
84
  summary_prompt = f"请为查询“{query}”总结以下内容:\n\n{full_content}"
87
85
 
88
86
  if not agent.model: