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,94 @@
1
+ You are an autonomous agent in a strict signal-driven multi-agent system.
2
+ Your job is to **respond to exactly one input signal with exactly one output intent**—nothing more, nothing less.
3
+
4
+ ### 1. YOUR IDENTITY
5
+ - Name: {{ name }}
6
+ - Role: {{ description }}
7
+ {{ system_prompt }}
8
+
9
+ ### 2. TEAM DIRECTORY (Use ONLY these contacts)
10
+ {{ yellow_page }}
11
+ - User: The human supervisor. Contact only for ambiguity, confirmation, or final report.
12
+
13
+ > 📌 Rule: Never invent new agents. Never ask an agent to do something outside their description.
14
+
15
+ ### 3. YOUR CAPABILITIES
16
+ You may only perform actions listed below:
17
+ {{ capabilities }}
18
+
19
+ > 📌 Rule: If a task isn’t covered here, delegate it—don’t improvise.
20
+
21
+ ---
22
+
23
+ ### 🔁 HOW THIS SYSTEM WORKS
24
+
25
+ #### ✅ YOU RECEIVE ONE OF THESE INPUTS:
26
+ 1. **[INCOMING MAIL]** – A message from another agent or the User.
27
+ 2. **[BODY FEEDBACK]** – Result of your last action (e.g., file content, execution log).
28
+ 3. **[INTERNAL QUERY]** – Your last intent was unclear; the system asks for clarification.
29
+
30
+ #### ✅ YOU MUST REPLY WITH EXACTLY ONE OF THESE:
31
+
32
+ - **If responding to [INCOMING MAIL] or [BODY FEEDBACK]:**
33
+ `INTENT: [CONTEXT IF NEEDED]`
34
+
35
+ **IMPORANT**, you must choose one of the actions listed in your capabilities as your intent.
36
+
37
+ When you want to send an email, strictly state:
38
+ "Send email to [Recipient Name] with subject [Topic] saying [Content]"
39
+
40
+ Examples:
41
+ - `INTENT: Read file 'sales.csv'`
42
+ - `INTENT: Email Planner: Data analysis complete; summary ready.`
43
+ - `INTENT: List all .py files in /src/utils`
44
+
45
+ - **If responding to [INTERNAL QUERY]:**
46
+ `CLARIFICATION: `
47
+
48
+ Example:
49
+ - `CLARIFICATION: The filename is 'config.yaml'.`
50
+
51
+ > ⚠️ CRITICAL RULES:
52
+ > - **ONE ACTION PER TURN.** Never chain steps.
53
+ > - **NEVER generate fake signals** (e.g., don’t write `[BODY FEEDBACK]` yourself).
54
+ > - **ALWAYS wait for real feedback** before proceeding.
55
+ > - **NEVER output JSON, markdown, or thinking monologue.**
56
+
57
+
58
+ ---
59
+
60
+ ### 🎯 SESSION GOAL & END CONDITION
61
+
62
+ - The **first message you receive** is your **ANCHOR TASK**—this is your ultimate goal.
63
+ - You must keep working until that task is fully resolved.
64
+ - Your **final action must be**:
65
+ `INTENT: Reply to [Sender of Anchor Task]: [brief result summary]`
66
+
67
+ ---
68
+
69
+ ### 🧪 EXAMPLE SESSION (Follow this pattern)
70
+
71
+ [INCOMING MAIL]
72
+ From: User
73
+ Subject: Analyze sales data
74
+ Body: Please analyze sales.csv and summarize monthly trends.
75
+
76
+ → YOUR RESPONSE:
77
+ INTENT: Read file 'sales.csv'
78
+
79
+ === BODY FEEDBACK ===
80
+ Action: read_file
81
+ Result: "month,sales\nJan,200\nFeb,180\n..."
82
+
83
+ → YOUR RESPONSE:
84
+ INTENT: Analyze monthly sales trends and write a 2-sentence summary
85
+
86
+ === BODY FEEDBACK ===
87
+ Action: run_analysis
88
+ Result: "Sales peaked in January (200 units) and declined slightly in February (180 units)."
89
+
90
+ → YOUR RESPONSE:
91
+ INTENT: Reply to User: Monthly sales summary: [paste result]
92
+
93
+
94
+
@@ -0,0 +1,38 @@
1
+ name: Tom
2
+ description: 互联网资料采集员
3
+ module: agentmatrix.agents.base
4
+ class_name: BaseAgent
5
+
6
+ # 动态 Mixin 组合
7
+ mixins:
8
+ - agentmatrix.skills.filesystem.FileSkillMixin
9
+ - agentmatrix.skills.data_crawler.DigitalInternCrawlerMixin
10
+
11
+ # 属性初始化(Mixin 需要的实例属性)
12
+ attribute_initializations:
13
+ browser_adapter: null
14
+
15
+ # 类属性设置(可选)
16
+ class_attributes:
17
+ _custom_log_level: 10 # logging.DEBUG
18
+
19
+ instruction_to_caller: |
20
+ 告诉我想研究或者搜索什么
21
+ system_prompt: |
22
+ You are an elite information retrieval specialist.
23
+
24
+ 你的任务是根据其他研究者的指示从互联网上采集最有价值的资料,你的能力 research_crawler 可以帮助你实现这一点,给你数据采集任务的研究者必须提供好这些信息:
25
+ 1. 要在搜索引擎里进行搜索的关键字: 研究者应该提供好这个关键字,以便你能够找到相关的资料,关键字要么是一个短语,要么是几个关键词(不应该超过三个,如果超过三个你应该提醒研究者重新设计搜索关键词)
26
+ 2. 研究或者搜索的目的,简单清晰的目标可以帮助你判断内容是否和目的相关,从而决定是否应该下载或者继续浏览
27
+ 3. 下载文件保存的目录名
28
+ 4. 确定搜集资料的研究目的分类, 从下面几个类别中选择最合适的,便于有针对性的寻找资料:
29
+ - STEM(适用于科学,技术,工程,数学等)
30
+ - HUMANITIES(适用于人文学科),
31
+ - BUSINESS(商业或者新闻相关)
32
+ - GENERAL (适用于其他所有类别)
33
+ 4. 可以指定搜集资料做多花费多少分钟,只能用户指定,不要自己决定
34
+
35
+ 搜集资料完成之后,不管结果如何,立刻报告结果,不要去自己分析结果。
36
+ backend_model: default_llm
37
+ cerebellum:
38
+ backend_model: mimo
@@ -0,0 +1,17 @@
1
+ #You should always have one USER agent
2
+ name: User
3
+ description: Master of world
4
+ module: agentmatrix.agents.user_proxy
5
+ class_name: UserProxyAgent
6
+
7
+ # 动态 Mixin 组合
8
+ mixins:
9
+ - skills.filesystem.FileSkillMixin
10
+
11
+ # 属性初始化
12
+ attribute_initializations:
13
+ on_mail_received: null
14
+
15
+ instruction_to_caller: "要精炼不要啰嗦"
16
+ system_prompt: ""
17
+ backend_model: default_llm
@@ -0,0 +1 @@
1
+ """Skills module for Agent-Matrix framework."""
@@ -0,0 +1,315 @@
1
+ """
2
+ 爬虫技能的公共辅助方法
3
+
4
+ 提供链接筛选、按钮选择等 LLM 决策功能。
5
+ """
6
+
7
+ import json
8
+ import re
9
+ from collections import deque
10
+ from typing import Dict, List, Optional, Any
11
+
12
+
13
+ class CrawlerHelperMixin:
14
+ """
15
+ 爬虫技能的公共辅助方法 Mixin
16
+
17
+ 依赖:
18
+ - self.cerebellum.backend.think() - LLM 调用
19
+ - self.logger - 日志记录
20
+ """
21
+
22
+ async def _filter_relevant_links(
23
+ self,
24
+ candidates: Dict[str, str],
25
+ page_summary: str,
26
+ ctx,
27
+ prompt_template: str = None
28
+ ) -> List[str]:
29
+ """
30
+ [Brain] 批量筛选链接(统一实现)
31
+
32
+ Args:
33
+ candidates: 候选链接字典 {url: link_text}
34
+ page_summary: 当前页面摘要
35
+ ctx: 上下文对象(MissionContext 或 WebSearcherContext)
36
+ prompt_template: 可选的自定义 prompt(用于特殊需求)
37
+
38
+ Returns:
39
+ 筛选后的 URL 列表
40
+ """
41
+ # 1. 规则预过滤
42
+ ignored_keywords = [
43
+ "login", "signin", "sign up", "register", "password",
44
+ "privacy policy", "terms of use", "contact us", "about us",
45
+ "customer service", "language", "sitemap", "javascript:",
46
+ "mailto:", "tel:", "unsubscribe"
47
+ ]
48
+
49
+ clean_candidates = {}
50
+ for link, link_text in candidates.items():
51
+ if not link or len(link_text) < 2:
52
+ continue
53
+ text_lower = link_text.lower()
54
+ if any(k in text_lower for k in ignored_keywords):
55
+ continue
56
+ clean_candidates[link] = link_text
57
+
58
+ if not clean_candidates:
59
+ self.logger.debug(f"No clean links found for {ctx.purpose}")
60
+ return []
61
+
62
+ # 2. 分批 LLM 过滤
63
+ batch_size = 10
64
+ selected_urls = []
65
+ url_pattern = re.compile(r'(https?://[^\s"\'<>]+)')
66
+ candidates_list = list(clean_candidates.items())
67
+
68
+ for i in range(0, len(candidates_list), batch_size):
69
+ batch = candidates_list[i:i + batch_size]
70
+ list_str = "\n".join([f"- [{text}] ({url})" for url, text in batch])
71
+ batch_url_map = {url.strip(): text for url, text in batch}
72
+
73
+ # 使用自定义或默认 prompt
74
+ if prompt_template:
75
+ prompt = prompt_template.format(
76
+ purpose=ctx.purpose,
77
+ page_summary=page_summary,
78
+ list_str=list_str
79
+ )
80
+ else:
81
+ # 默认 prompt
82
+ prompt = f"""
83
+ Mission: Find links relevant to "{ctx.purpose}".
84
+
85
+ Below is a list of links found on a webpage.
86
+ This page is about: {page_summary}.
87
+ Select ONLY the links that are likely to contain information related to the Mission or worth to explore.
88
+
89
+ [Candidates]
90
+ {list_str}
91
+
92
+ [Instructions]
93
+ 1. Select links that are likely to contain information related to the Mission. Or may lead to information related to the Mission (destination worth explore).
94
+ 2. Ignore links clearly point to non-relevant pages or destinations
95
+ 3. 注意,如果是百度百科这样的网页,上面的链接很多是无关的,要仔细甄别,只选择确定有关的
96
+ 4. OUTPUT FORMAT: Just list the full URLs of the selected links, one per line.
97
+ """
98
+
99
+ try:
100
+ # 调用小脑
101
+ resp = await self.cerebellum.backend.think(
102
+ messages=[{"role": "user", "content": prompt}]
103
+ )
104
+ raw_reply = resp.get('reply', '')
105
+ self.logger.debug(f"LLM reply: {raw_reply}")
106
+
107
+ # 3. 正则提取与验证
108
+ found_urls = url_pattern.findall(raw_reply)
109
+ for raw_url in found_urls:
110
+ clean_url = raw_url.strip('.,;)]}"\'')
111
+
112
+ if clean_url in batch_url_map:
113
+ selected_urls.append(clean_url)
114
+ else:
115
+ # 容错匹配
116
+ for original_url in batch_url_map.keys():
117
+ if clean_url in original_url and len(clean_url) > 15:
118
+ selected_urls.append(original_url)
119
+ break
120
+
121
+ except Exception as e:
122
+ self.logger.error(f"Link filtering batch failed: {e}")
123
+ continue
124
+
125
+ self.logger.debug(f"Selected links: {selected_urls}")
126
+ return list(set(selected_urls))
127
+
128
+ async def _choose_best_interaction(
129
+ self,
130
+ candidates: List[Dict],
131
+ page_summary: str,
132
+ ctx,
133
+ prompt_template: str = None
134
+ ) -> Optional:
135
+ """
136
+ [Brain] 选择最佳按钮点击(统一实现)
137
+
138
+ 使用串行淘汰机制 + 三级筛选策略:
139
+ 1. Immediate (立即访问): 高度吻合,直接返回
140
+ 2. Potential (潜在相关): 可能相关,放回队列头部继续竞争
141
+ 3. None (无价值): 删除,继续下一组
142
+
143
+ Args:
144
+ candidates: List[Dict] 格式,每个 Dict 是 {button_text: PageElement}
145
+ page_summary: 当前页面摘要
146
+ ctx: 上下文对象(MissionContext 或 WebSearcherContext)
147
+ prompt_template: 可选的自定义 prompt 模板
148
+
149
+ Returns:
150
+ 选中的 PageElement,如果没有合适的则返回 None
151
+ """
152
+ if not candidates:
153
+ return None
154
+
155
+ BATCH_SIZE = 10
156
+
157
+ # 转换为列表格式: [(button_text, element), ...]
158
+ all_candidates = []
159
+ for candidate_dict in candidates:
160
+ for text, element in candidate_dict.items():
161
+ all_candidates.append((text, element))
162
+
163
+ # 使用 deque 支持高效的头部操作
164
+ candidate_deque = deque(all_candidates)
165
+
166
+ while candidate_deque:
167
+ # 取前 batch_size 个(如果不足则取全部)
168
+ batch_size = min(BATCH_SIZE, len(candidate_deque))
169
+ batch = [candidate_deque.popleft() for _ in range(batch_size)]
170
+
171
+ # 评估这批
172
+ if prompt_template:
173
+ result = await self._evaluate_button_batch(
174
+ batch, page_summary, ctx, prompt_template
175
+ )
176
+ else:
177
+ result = await self._evaluate_button_batch(
178
+ batch, page_summary, ctx
179
+ )
180
+
181
+ if result["priority"] == "immediate":
182
+ # 找到最佳匹配,立即返回
183
+ self.logger.info(
184
+ f"⚡ Immediate match found: [{result['text']}] | "
185
+ f"Reason: {result['reason']}"
186
+ )
187
+ return result["element"]
188
+
189
+ elif result["priority"] == "potential":
190
+ # 将 winner 放回队列头部,参与下一轮竞争
191
+ winner_tuple = (result["text"], result["element"])
192
+ if len(candidate_deque) > 0:
193
+ candidate_deque.appendleft(winner_tuple)
194
+ self.logger.debug(
195
+ f" Potential: [{result['text']}] → Put back to queue front. "
196
+ f"Queue size: {len(candidate_deque)}"
197
+ )
198
+ else:
199
+ return result["element"]
200
+ # else: None,这批全部丢弃,继续下一轮
201
+
202
+ return None
203
+
204
+ async def _evaluate_button_batch(
205
+ self,
206
+ batch: List[tuple],
207
+ page_summary: str,
208
+ ctx,
209
+ prompt_template: str = None
210
+ ) -> Dict[str, Any]:
211
+ """
212
+ 评估一批按钮(统一实现)
213
+
214
+ Args:
215
+ batch: [(button_text, element), ...]
216
+ page_summary: 页面摘要
217
+ ctx: 上下文对象
218
+ prompt_template: 可选的自定义 prompt 模板
219
+
220
+ Returns:
221
+ {
222
+ "priority": "immediate" | "potential" | "none",
223
+ "text": str,
224
+ "element": PageElement,
225
+ "reason": str
226
+ }
227
+ """
228
+ if not batch:
229
+ return {"priority": "none", "text": None, "element": None, "reason": "Empty batch"}
230
+
231
+ options_str = ""
232
+ for idx, (text, element) in enumerate(batch):
233
+ options_str += f"{idx + 1}. [{text}]\n"
234
+ options_str += "0. [None of these are useful]"
235
+
236
+ # 使用自定义或默认 prompt
237
+ if prompt_template:
238
+ prompt = prompt_template.format(
239
+ purpose=ctx.purpose,
240
+ page_summary=page_summary,
241
+ options_str=options_str,
242
+ batch_size=len(batch)
243
+ )
244
+ else:
245
+ # 默认 prompt(取 data_crawler 的版本,更详细)
246
+ prompt = f"""
247
+ You are evaluating buttons on a webpage to see if it can help with your research topic:
248
+ [Research Topic]
249
+ {ctx.purpose}
250
+
251
+ [Page Context]
252
+ {page_summary}
253
+
254
+ [Task]
255
+ Categorize your choice into THREE levels:
256
+
257
+ **LEVEL 1 - IMMEDIATE** (应立即访问)
258
+ - Button clearly leads to information that achieves the purpose
259
+
260
+ **LEVEL 2 - POTENTIAL** (可能相关)
261
+ - Button might lead to relevant information
262
+ - Examples: "Learn More", "Details", "Next", "View Resources"
263
+
264
+ **LEVEL 3 - NONE** (都不相关)
265
+ - Buttons unrelated to the purpose
266
+ - Examples: "Share", "Login", "Home", "Contact"
267
+
268
+ [Options]
269
+ {options_str}
270
+
271
+ [Output Format]
272
+ JSON:
273
+ {{
274
+ "choice_id": <number 0-{len(batch)}>,
275
+ "priority": "immediate" | "potential" | "none",
276
+ "reason": "short explanation"
277
+ }}
278
+ """
279
+
280
+ try:
281
+ resp = await self.cerebellum.backend.think(
282
+ messages=[{"role": "user", "content": prompt}]
283
+ )
284
+ raw_reply = resp.get('reply', '')
285
+
286
+ # 提取 JSON(兼容各种格式)
287
+ json_str = raw_reply.replace("```json", "").replace("```", "").strip()
288
+ result = json.loads(json_str)
289
+
290
+ choice_id = int(result.get("choice_id", 0))
291
+ priority = result.get("priority", "none").lower()
292
+ reason = result.get("reason", "")
293
+
294
+ if priority not in ["immediate", "potential", "none"]:
295
+ priority = "none"
296
+
297
+ if choice_id == 0 or priority == "none":
298
+ return {"priority": "none", "text": None, "element": None, "reason": reason}
299
+
300
+ selected_index = choice_id - 1
301
+
302
+ if 0 <= selected_index < len(batch):
303
+ selected_text, selected_element = batch[selected_index]
304
+ return {
305
+ "priority": priority,
306
+ "text": selected_text,
307
+ "element": selected_element,
308
+ "reason": reason
309
+ }
310
+ else:
311
+ return {"priority": "none", "text": None, "element": None, "reason": "Invalid choice"}
312
+
313
+ except Exception as e:
314
+ self.logger.exception(f"Batch evaluation failed: {e}")
315
+ return {"priority": "none", "text": None, "element": None, "reason": f"Error: {e}"}