matrix-for-agents 0.1.2__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 (66) hide show
  1. agentmatrix/__init__.py +20 -0
  2. agentmatrix/agents/__init__.py +1 -0
  3. agentmatrix/agents/base.py +572 -0
  4. agentmatrix/agents/claude_coder.py +10 -0
  5. agentmatrix/agents/data_crawler.py +14 -0
  6. agentmatrix/agents/post_office.py +212 -0
  7. agentmatrix/agents/report_writer.py +14 -0
  8. agentmatrix/agents/secretary.py +10 -0
  9. agentmatrix/agents/stateful.py +10 -0
  10. agentmatrix/agents/user_proxy.py +82 -0
  11. agentmatrix/agents/worker.py +30 -0
  12. agentmatrix/backends/__init__.py +1 -0
  13. agentmatrix/backends/llm_client.py +414 -0
  14. agentmatrix/backends/mock_llm.py +35 -0
  15. agentmatrix/cli_runner.py +94 -0
  16. agentmatrix/core/__init__.py +0 -0
  17. agentmatrix/core/action.py +50 -0
  18. agentmatrix/core/browser/bing.py +208 -0
  19. agentmatrix/core/browser/browser_adapter.py +298 -0
  20. agentmatrix/core/browser/browser_common.py +85 -0
  21. agentmatrix/core/browser/drission_page_adapter.py +1296 -0
  22. agentmatrix/core/browser/google.py +230 -0
  23. agentmatrix/core/cerebellum.py +121 -0
  24. agentmatrix/core/events.py +22 -0
  25. agentmatrix/core/loader.py +185 -0
  26. agentmatrix/core/loader_v1.py +146 -0
  27. agentmatrix/core/log_util.py +158 -0
  28. agentmatrix/core/message.py +32 -0
  29. agentmatrix/core/prompt_engine.py +30 -0
  30. agentmatrix/core/runtime.py +211 -0
  31. agentmatrix/core/session.py +20 -0
  32. agentmatrix/db/__init__.py +1 -0
  33. agentmatrix/db/database.py +79 -0
  34. agentmatrix/db/vector_db.py +213 -0
  35. agentmatrix/docs/Design.md +109 -0
  36. agentmatrix/docs/Framework Capbilities.md +105 -0
  37. agentmatrix/docs/Planner Design.md +148 -0
  38. agentmatrix/docs/crawler_flow.md +110 -0
  39. agentmatrix/docs/report_writer.md +83 -0
  40. agentmatrix/docs/review.md +99 -0
  41. agentmatrix/docs/skill_design.md +23 -0
  42. agentmatrix/profiles/claude_coder.yml +40 -0
  43. agentmatrix/profiles/mark.yml +26 -0
  44. agentmatrix/profiles/planner.yml +21 -0
  45. agentmatrix/profiles/prompts/base.txt +88 -0
  46. agentmatrix/profiles/prompts/base_v1.txt +101 -0
  47. agentmatrix/profiles/prompts/base_v2.txt +94 -0
  48. agentmatrix/profiles/tom_the_data_crawler.yml +38 -0
  49. agentmatrix/profiles/user_proxy.yml +17 -0
  50. agentmatrix/skills/__init__.py +1 -0
  51. agentmatrix/skills/crawler_helpers.py +315 -0
  52. agentmatrix/skills/data_crawler.py +777 -0
  53. agentmatrix/skills/filesystem.py +204 -0
  54. agentmatrix/skills/notebook.py +158 -0
  55. agentmatrix/skills/project_management.py +114 -0
  56. agentmatrix/skills/report_writer.py +194 -0
  57. agentmatrix/skills/report_writer_utils.py +379 -0
  58. agentmatrix/skills/search_tool.py +383 -0
  59. agentmatrix/skills/terminal_ctrl.py +122 -0
  60. agentmatrix/skills/utils.py +33 -0
  61. agentmatrix/skills/web_searcher.py +1107 -0
  62. matrix_for_agents-0.1.2.dist-info/METADATA +44 -0
  63. matrix_for_agents-0.1.2.dist-info/RECORD +66 -0
  64. matrix_for_agents-0.1.2.dist-info/WHEEL +5 -0
  65. matrix_for_agents-0.1.2.dist-info/licenses/LICENSE +190 -0
  66. matrix_for_agents-0.1.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,379 @@
1
+ PERSONA_DESIGNER="""
2
+ 你是一个见多识广的跨领域专家。现在公司打算编写一个研究报告(主题是关于:{{main_subject}}),主要目的包括:{{main_purpose}}。
3
+ 现在我们需要雇佣一个最适合的专家来主持完成这项工作,你来负责考虑一下应该选择什么样的专家,需要具备什么样的
4
+ 能力特质和工作习惯。写一个简短的人员需求。我们会把这个内容作为招聘广告的一部份。必须用第二人称的方式来描述需求,仿佛直接对潜在应聘者说话,用“你是”开头,用“。”结尾,不要使用“我们”。
5
+ """
6
+
7
+ BLUEPRINT_DESIGNER="""
8
+ {{persona}}
9
+
10
+ 现在的任务是根据用户要求设计一个全面的 "Research Blueprint"。
11
+
12
+ 这份蓝图**并非最终报告**,而将作为自动化AI执行器("执行者")的严格操作手册。该执行器将阅读数百份多语言文件,并逐步完成报告的撰写。
13
+
14
+ 您的输出必须结构清晰、表述精确,并严格遵循信息提取与整合的导向原则。
15
+
16
+ **核心理念**:
17
+
18
+ 1. **结构即检索指令**:蓝图中每个章节本质上都是为执行器设定的"检索指令"或"信息容器",用于归集相关事实依据。2. **逻辑流设计**:章节排列必须构成连贯的论述脉络(例如:背景→问题→分析→解决方案→结论)。
19
+ 3. **语言无关性**:尽管源文件可能包含英语、日语等多语种内容,但蓝图本身必须以简体中文撰写,以确保最终报告的中文语言属性。
20
+
21
+ **蓝图输出结构**:
22
+ Research Blueprint 应该输出如下的Markdown结构,最后一个`特别提示`是可选的,其他章节必须要有:
23
+ ```
24
+ # 研究目标
25
+
26
+ 简要的研究目标描述。
27
+
28
+ # 核心问题
29
+
30
+ 研究的核心问题或假设清单
31
+
32
+ # 章节结构大纲
33
+
34
+ 根据研究目标,预设一个章节结构,包括具体的标题。
35
+
36
+ # 特别提示
37
+
38
+ 可选,适合本研究目的的方法指引或者特别需要注意的点,不要写常识性内容
39
+ ```
40
+ """
41
+
42
+ BLUEPRINT_USER="""
43
+
44
+
45
+ """
46
+
47
+ import re
48
+ from typing import Optional, Dict, List, Union
49
+ import os
50
+
51
+ from marker.converters.pdf import PdfConverter
52
+ from marker.models import create_model_dict
53
+ from marker.output import text_from_rendered
54
+ import fitz # PyMuPDF,用于获取PDF信息和提取页面
55
+ import tempfile
56
+
57
+
58
+
59
+ def markdown_to_json(text: str) -> Optional[Dict[str, Union[str, List[Dict]]]]:
60
+ """
61
+ 将 Markdown 文本转换为 JSON 结构,按 # section 标题组织。
62
+
63
+ 参数:
64
+ text: 输入的文本
65
+
66
+ 返回:
67
+ dict: 解析后的 JSON 结构
68
+ - 一级标题作为 key
69
+ - 如果内容包含多个二级标题,value 是 list
70
+ - 其他情况 value 是字符串(原样保留内容)
71
+ 如果解析失败或找不到 markdown 内容,返回 None
72
+ """
73
+ if not text or not isinstance(text, str):
74
+ return None
75
+
76
+ try:
77
+ # 1. 截取符合 markdown 语法的部分(从第一个标题开始)
78
+ match = re.search(r'^#+\s', text, re.MULTILINE)
79
+ if not match:
80
+ return None
81
+
82
+ markdown_text = text[match.start():]
83
+
84
+ # 2. 解析 markdown 标题和内容
85
+ result = {}
86
+ lines = markdown_text.split('\n')
87
+
88
+ current_section = None
89
+ current_content_lines = []
90
+ subsections = []
91
+
92
+ i = 0
93
+ while i < len(lines):
94
+ line = lines[i]
95
+
96
+ # 检查是否是标题
97
+ header_match = re.match(r'^(#+)\s+(.+)$', line)
98
+ if header_match:
99
+ level = len(header_match.group(1))
100
+ title = header_match.group(2).strip()
101
+
102
+ if level == 1:
103
+ # 一级标题:保存前一个 section
104
+ if current_section:
105
+ if subsections:
106
+ # 如果有二级标题,使用 list
107
+ result[current_section] = subsections
108
+ else:
109
+ # 否则使用字符串内容
110
+ content = '\n'.join(current_content_lines).strip()
111
+ result[current_section] = content
112
+
113
+ # 开始新的 section
114
+ current_section = title
115
+ current_content_lines = []
116
+ subsections = []
117
+
118
+ elif level == 2:
119
+ # 二级标题:先保存之前的普通内容
120
+ if current_content_lines:
121
+ content = '\n'.join(current_content_lines).strip()
122
+ if content and subsections:
123
+ # 如果 subsections 中最后一个条目没有 content,则添加
124
+ if subsections and 'content' not in subsections[-1]:
125
+ subsections[-1]['content'] = content
126
+ else:
127
+ subsections.append({'content': content})
128
+ current_content_lines = []
129
+
130
+ # 添加新的 subsection
131
+ subsections.append({title: []})
132
+
133
+ else:
134
+ # 普通内容行
135
+ if subsections:
136
+ # 如果有 subsections,添加到最后一个 subsection 的内容中
137
+ last_subsection = subsections[-1]
138
+ # 找到 subsection 中的 key(标题)
139
+ subsection_key = [k for k in last_subsection.keys() if k != 'content'][0]
140
+ if isinstance(last_subsection[subsection_key], list):
141
+ last_subsection[subsection_key].append(line)
142
+ else:
143
+ # 如果已经不是 list 了,说明之前有普通内容,需要转换
144
+ last_subsection[subsection_key] = [last_subsection[subsection_key], line]
145
+ else:
146
+ current_content_lines.append(line)
147
+
148
+ i += 1
149
+
150
+ # 3. 保存最后一个 section
151
+ if current_section:
152
+ if subsections:
153
+ # 合并 subsections 中的 list 内容为字符串
154
+ processed_subsections = []
155
+ for subsection in subsections:
156
+ processed = {}
157
+ for key, value in subsection.items():
158
+ if isinstance(value, list):
159
+ processed[key] = '\n'.join(value).strip()
160
+ else:
161
+ processed[key] = value
162
+ if processed: # 只添加非空的 subsection
163
+ processed_subsections.append(processed)
164
+
165
+ # 处理剩余的普通内容
166
+ if current_content_lines:
167
+ content = '\n'.join(current_content_lines).strip()
168
+ if content:
169
+ processed_subsections.append({'content': content})
170
+
171
+ result[current_section] = processed_subsections if processed_subsections else []
172
+ else:
173
+ content = '\n'.join(current_content_lines).strip()
174
+ result[current_section] = content
175
+
176
+ return result if result else None
177
+
178
+ except Exception:
179
+ # 解析失败返回 None
180
+ return None
181
+
182
+
183
+
184
+
185
+ def pdf_to_markdown(pdf_path, start_page=None, end_page=None, lang=None):
186
+ """
187
+ 将PDF的指定页范围转换为Markdown格式
188
+
189
+ 参数:
190
+ pdf_path (str): PDF文件路径
191
+ start_page (int, optional): 起始页码(从1开始),默认None表示从第1页开始
192
+ end_page (int, optional): 结束页码,默认None表示到最后页
193
+ lang (str, optional): 语言代码,如'zh'、'en'等,默认None使用自动检测
194
+
195
+ 返回:
196
+ str: 转换后的Markdown文本
197
+ """
198
+
199
+ # 获取PDF总页数
200
+ with fitz.open(pdf_path) as doc:
201
+ total_pages = len(doc)
202
+
203
+ # 确定页面范围
204
+ if start_page is None:
205
+ start_page = 1
206
+ if end_page is None:
207
+ end_page = total_pages
208
+
209
+ # 验证页面范围
210
+ start_page = max(1, start_page)
211
+ end_page = min(total_pages, end_page)
212
+
213
+ if start_page > end_page:
214
+ raise ValueError(f"起始页({start_page})不能大于结束页({end_page})")
215
+
216
+ print(f"PDF总页数: {total_pages}")
217
+ print(f"转换页范围: {start_page}-{end_page}")
218
+
219
+ # 如果只需要部分页面,创建临时PDF文件
220
+ if start_page > 1 or end_page < total_pages:
221
+ print(f"提取页面 {start_page}-{end_page}...")
222
+ temp_pdf_path = extract_pages(pdf_path, start_page, end_page)
223
+ pdf_to_convert = temp_pdf_path
224
+ else:
225
+ pdf_to_convert = pdf_path
226
+
227
+ # 初始化marker模型和转换器
228
+ try:
229
+ # 加载模型
230
+ print("加载marker模型...")
231
+
232
+ # 创建PDF转换器(使用新的API)
233
+ converter = PdfConverter(
234
+ artifact_dict=create_model_dict(),
235
+ )
236
+
237
+ # 执行转换
238
+ print("正在转换PDF到Markdown...")
239
+ rendered = converter(pdf_to_convert)
240
+
241
+ # 从渲染结果中提取文本
242
+ text, _, images = text_from_rendered(rendered)
243
+
244
+ print(f"转换完成! 共 {len(text)} 个字符")
245
+
246
+ # 清理临时文件(如果存在)
247
+ if 'temp_pdf_path' in locals():
248
+ os.unlink(temp_pdf_path)
249
+
250
+ return text
251
+
252
+ except Exception as e:
253
+ print(f"转换过程中发生错误: {e}")
254
+ # 确保清理临时文件
255
+ if 'temp_pdf_path' in locals():
256
+ os.unlink(temp_pdf_path)
257
+ raise
258
+
259
+
260
+
261
+
262
+ def extract_pages(pdf_path, start_page, end_page):
263
+ """
264
+ 提取PDF的指定页面范围到临时文件
265
+
266
+ 参数:
267
+ pdf_path (str): 原始PDF文件路径
268
+ start_page (int): 起始页码(从1开始)
269
+ end_page (int): 结束页码
270
+
271
+ 返回:
272
+ str: 临时PDF文件路径
273
+ """
274
+ # 创建临时文件
275
+ temp_dir = tempfile.mkdtemp()
276
+ temp_pdf_path = os.path.join(temp_dir, "temp_pages.pdf")
277
+
278
+ # 打开原始PDF
279
+ doc = fitz.open(pdf_path)
280
+
281
+ # 创建新PDF
282
+ new_doc = fitz.open()
283
+
284
+ # 提取指定页面(注意:fitz的页面索引从0开始)
285
+ for page_num in range(start_page - 1, end_page):
286
+ new_doc.insert_pdf(doc, from_page=page_num, to_page=page_num)
287
+
288
+ # 保存临时PDF
289
+ new_doc.save(temp_pdf_path)
290
+ new_doc.close()
291
+ doc.close()
292
+
293
+ return temp_pdf_path
294
+ def batch_convert_pdfs(pdf_folder, start_page=None, end_page=None, output_dir=None, lang=None):
295
+ """
296
+ 批量转换文件夹中的所有PDF文件
297
+
298
+ 参数:
299
+ pdf_folder (str): 包含PDF文件的文件夹路径
300
+ start_page (int, optional): 起始页码
301
+ end_page (int, optional): 结束页码
302
+ output_dir (str, optional): 输出目录
303
+ lang (str, optional): 语言代码
304
+ """
305
+ if not os.path.exists(pdf_folder):
306
+ print(f"文件夹不存在: {pdf_folder}")
307
+ return
308
+
309
+ if output_dir is None:
310
+ output_dir = os.path.join(pdf_folder, "markdown_output")
311
+
312
+ os.makedirs(output_dir, exist_ok=True)
313
+
314
+ # 获取所有PDF文件
315
+ pdf_files = [f for f in os.listdir(pdf_folder) if f.lower().endswith('.pdf')]
316
+
317
+ if not pdf_files:
318
+ print(f"在 {pdf_folder} 中未找到PDF文件")
319
+ return
320
+
321
+ print(f"找到 {len(pdf_files)} 个PDF文件,开始批量转换...")
322
+
323
+ success_count = 0
324
+ for pdf_file in pdf_files:
325
+ try:
326
+ pdf_path = os.path.join(pdf_folder, pdf_file)
327
+ print(f"\n处理: {pdf_file}")
328
+
329
+ # 调用 pdf_to_markdown 获取文本
330
+ markdown_text = pdf_to_markdown(
331
+ pdf_path=pdf_path,
332
+ start_page=start_page,
333
+ end_page=end_page,
334
+ lang=lang
335
+ )
336
+
337
+ # 手动保存到文件
338
+ base_name = os.path.splitext(pdf_file)[0]
339
+ if start_page or end_page:
340
+ page_info = f"_pages_{start_page or 1}-{end_page or 'all'}"
341
+ output_filename = f"{base_name}{page_info}.md"
342
+ else:
343
+ output_filename = f"{base_name}.md"
344
+
345
+ output_path = os.path.join(output_dir, output_filename)
346
+
347
+ with open(output_path, 'w', encoding='utf-8') as f:
348
+ f.write(markdown_text)
349
+
350
+ print(f" ✅ 已保存: {output_path}")
351
+ success_count += 1
352
+
353
+ except Exception as e:
354
+ print(f" ❌ 转换失败: {e}")
355
+
356
+ print(f"\n批量转换完成! 成功: {success_count}/{len(pdf_files)}")
357
+
358
+ if __name__ == '__main__':
359
+ # ==================== 使用示例 ====================
360
+
361
+ # 定义你的 PDF 文件路径和输出目录
362
+ my_pdf = 'path/to/your/document.pdf'
363
+ output_directory = 'output_markdown'
364
+
365
+ # 检查示例文件是否存在
366
+ if not Path(my_pdf).exists():
367
+ print(f"错误:示例文件 '{my_pdf}' 不存在,请修改路径后重试。")
368
+ else:
369
+ # 示例1:转换整个 PDF 文档
370
+ print("--- 任务1: 转换整个 PDF ---")
371
+ convert_pdf_to_markdown(my_pdf, output_directory)
372
+
373
+ # 示例2:只转换 PDF 的第 2 到 3 页
374
+ print("\n--- 任务2: 转换 PDF 的第 2-3 页 ---")
375
+ convert_pdf_to_markdown(my_pdf, output_directory, start_page=2, end_page=3)
376
+
377
+ # 示例3:只转换第 5 页
378
+ print("\n--- 任务3: 只转换 PDF 的第 5 页 ---")
379
+ convert_pdf_to_markdown(my_pdf, output_directory, start_page=5, end_page=5)