@huajiwuyan/hello 3.0.0

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 (117) hide show
  1. package/README.md +68 -0
  2. package/dist/index.d.ts +2 -0
  3. package/dist/index.js +448 -0
  4. package/package.json +38 -0
  5. package/templates/claude/commands/hello.md +760 -0
  6. package/templates/claude/skills/SKILL.md +90 -0
  7. package/templates/claude/skills/SKILL.toml +7 -0
  8. package/templates/claude/skills/assets/icon-large.svg +12 -0
  9. package/templates/claude/skills/assets/icon-small-400px.svg +12 -0
  10. package/templates/claude/skills/assets/templates/CHANGELOG.md +24 -0
  11. package/templates/claude/skills/assets/templates/CHANGELOG_{YYYY}.md +25 -0
  12. package/templates/claude/skills/assets/templates/INDEX.md +36 -0
  13. package/templates/claude/skills/assets/templates/archive/_index.md +22 -0
  14. package/templates/claude/skills/assets/templates/context.md +82 -0
  15. package/templates/claude/skills/assets/templates/modules/_index.md +22 -0
  16. package/templates/claude/skills/assets/templates/modules/module.md +35 -0
  17. package/templates/claude/skills/assets/templates/plan/proposal.md +104 -0
  18. package/templates/claude/skills/assets/templates/plan/tasks.md +49 -0
  19. package/templates/claude/skills/references/functions/auto.md +217 -0
  20. package/templates/claude/skills/references/functions/clean.md +167 -0
  21. package/templates/claude/skills/references/functions/commit.md +374 -0
  22. package/templates/claude/skills/references/functions/exec.md +178 -0
  23. package/templates/claude/skills/references/functions/help.md +105 -0
  24. package/templates/claude/skills/references/functions/init.md +228 -0
  25. package/templates/claude/skills/references/functions/plan.md +219 -0
  26. package/templates/claude/skills/references/functions/review.md +146 -0
  27. package/templates/claude/skills/references/functions/rollback.md +208 -0
  28. package/templates/claude/skills/references/functions/test.md +153 -0
  29. package/templates/claude/skills/references/functions/upgrade.md +371 -0
  30. package/templates/claude/skills/references/functions/validate.md +147 -0
  31. package/templates/claude/skills/references/rules/package.md +212 -0
  32. package/templates/claude/skills/references/rules/scaling.md +150 -0
  33. package/templates/claude/skills/references/rules/state.md +318 -0
  34. package/templates/claude/skills/references/rules/tools.md +371 -0
  35. package/templates/claude/skills/references/services/knowledge.md +408 -0
  36. package/templates/claude/skills/references/services/templates.md +344 -0
  37. package/templates/claude/skills/references/stages/analyze.md +201 -0
  38. package/templates/claude/skills/references/stages/design.md +379 -0
  39. package/templates/claude/skills/references/stages/develop.md +497 -0
  40. package/templates/claude/skills/references/stages/evaluate.md +286 -0
  41. package/templates/claude/skills/references/stages/tweak.md +244 -0
  42. package/templates/claude/skills/scripts/create_package.py +260 -0
  43. package/templates/claude/skills/scripts/list_packages.py +145 -0
  44. package/templates/claude/skills/scripts/migrate_package.py +399 -0
  45. package/templates/claude/skills/scripts/project_stats.py +438 -0
  46. package/templates/claude/skills/scripts/upgradewiki.py +321 -0
  47. package/templates/claude/skills/scripts/utils.py +596 -0
  48. package/templates/claude/skills/scripts/validate_package.py +309 -0
  49. package/templates/codex/prompts/hello.md +757 -0
  50. package/templates/codex/skills/SKILL.md +74 -0
  51. package/templates/codex/skills/SKILL.toml +7 -0
  52. package/templates/codex/skills/assets/icon-large.svg +12 -0
  53. package/templates/codex/skills/assets/icon-small-400px.svg +12 -0
  54. package/templates/codex/skills/assets/templates/CHANGELOG.md +24 -0
  55. package/templates/codex/skills/assets/templates/CHANGELOG_{YYYY}.md +25 -0
  56. package/templates/codex/skills/assets/templates/INDEX.md +36 -0
  57. package/templates/codex/skills/assets/templates/archive/_index.md +22 -0
  58. package/templates/codex/skills/assets/templates/context.md +82 -0
  59. package/templates/codex/skills/assets/templates/modules/_index.md +22 -0
  60. package/templates/codex/skills/assets/templates/modules/module.md +35 -0
  61. package/templates/codex/skills/assets/templates/plan/proposal.md +104 -0
  62. package/templates/codex/skills/assets/templates/plan/tasks.md +29 -0
  63. package/templates/codex/skills/references/functions/auto.md +181 -0
  64. package/templates/codex/skills/references/functions/brain.md +275 -0
  65. package/templates/codex/skills/references/functions/clean.md +154 -0
  66. package/templates/codex/skills/references/functions/commit.md +265 -0
  67. package/templates/codex/skills/references/functions/debug/condition-based-waiting.md +151 -0
  68. package/templates/codex/skills/references/functions/debug/defense-in-depth.md +147 -0
  69. package/templates/codex/skills/references/functions/debug/root-cause-tracing.md +168 -0
  70. package/templates/codex/skills/references/functions/debug.md +389 -0
  71. package/templates/codex/skills/references/functions/exec.md +153 -0
  72. package/templates/codex/skills/references/functions/help.md +101 -0
  73. package/templates/codex/skills/references/functions/init.md +221 -0
  74. package/templates/codex/skills/references/functions/plan.md +178 -0
  75. package/templates/codex/skills/references/functions/review.md +135 -0
  76. package/templates/codex/skills/references/functions/rlm.md +864 -0
  77. package/templates/codex/skills/references/functions/rollback.md +190 -0
  78. package/templates/codex/skills/references/functions/test.md +140 -0
  79. package/templates/codex/skills/references/functions/upgrade.md +363 -0
  80. package/templates/codex/skills/references/functions/validate.md +135 -0
  81. package/templates/codex/skills/references/rules/cache.md +136 -0
  82. package/templates/codex/skills/references/rules/scaling.md +124 -0
  83. package/templates/codex/skills/references/rules/state.md +201 -0
  84. package/templates/codex/skills/references/rules/tools.md +301 -0
  85. package/templates/codex/skills/references/services/attention.md +53 -0
  86. package/templates/codex/skills/references/services/knowledge.md +559 -0
  87. package/templates/codex/skills/references/services/package.md +383 -0
  88. package/templates/codex/skills/references/services/templates.md +390 -0
  89. package/templates/codex/skills/references/stages/analyze.md +191 -0
  90. package/templates/codex/skills/references/stages/design.md +355 -0
  91. package/templates/codex/skills/references/stages/develop.md +520 -0
  92. package/templates/codex/skills/references/stages/tweak.md +239 -0
  93. package/templates/codex/skills/rlm/__init__.py +39 -0
  94. package/templates/codex/skills/rlm/agent_orchestrator.py +422 -0
  95. package/templates/codex/skills/rlm/context_manager.py +366 -0
  96. package/templates/codex/skills/rlm/engine.py +915 -0
  97. package/templates/codex/skills/rlm/folding.py +391 -0
  98. package/templates/codex/skills/rlm/repl.py +452 -0
  99. package/templates/codex/skills/rlm/roles/analyzer.md +66 -0
  100. package/templates/codex/skills/rlm/roles/designer.md +94 -0
  101. package/templates/codex/skills/rlm/roles/explorer.md +43 -0
  102. package/templates/codex/skills/rlm/roles/implementer.md +62 -0
  103. package/templates/codex/skills/rlm/roles/kb_keeper.md +138 -0
  104. package/templates/codex/skills/rlm/roles/pkg_keeper.md +163 -0
  105. package/templates/codex/skills/rlm/roles/reviewer.md +74 -0
  106. package/templates/codex/skills/rlm/roles/synthesizer.md +90 -0
  107. package/templates/codex/skills/rlm/roles/tester.md +83 -0
  108. package/templates/codex/skills/rlm/schemas/agent_result.json +174 -0
  109. package/templates/codex/skills/rlm/session.py +376 -0
  110. package/templates/codex/skills/rlm/shared_tasks.py +370 -0
  111. package/templates/codex/skills/scripts/create_package.py +260 -0
  112. package/templates/codex/skills/scripts/list_packages.py +145 -0
  113. package/templates/codex/skills/scripts/migrate_package.py +399 -0
  114. package/templates/codex/skills/scripts/project_stats.py +438 -0
  115. package/templates/codex/skills/scripts/upgradewiki.py +321 -0
  116. package/templates/codex/skills/scripts/utils.py +596 -0
  117. package/templates/codex/skills/scripts/validate_package.py +309 -0
@@ -0,0 +1,915 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ HelloAGENTS-RLM Engine
5
+ 核心引擎实现
6
+
7
+ 基于:
8
+ - MIT RLM: Python REPL + 递归自调用
9
+ - Context Folding: 子轨迹分支 + 折叠
10
+ - Codex Native: spawn_agent + role preset
11
+ """
12
+
13
+ import asyncio
14
+ import json
15
+ import subprocess
16
+ import sys
17
+ from dataclasses import dataclass, field
18
+ from datetime import datetime
19
+ from enum import IntEnum
20
+ from pathlib import Path
21
+ from typing import Any, Callable, Dict, List, Optional, Union
22
+
23
+ # 添加父目录到路径以导入utils
24
+ sys.path.insert(0, str(Path(__file__).parent.parent))
25
+ from scripts.utils import setup_encoding, ExecutionReport
26
+
27
+ setup_encoding()
28
+
29
+
30
+ class RLMMode(IntEnum):
31
+ """RLM运行模式"""
32
+ DISABLED = 0 # 禁用RLM
33
+ PASSIVE = 1 # 被动模式: 仅在context溢出时启用折叠
34
+ ACTIVE = 2 # 主动模式: 预判性分支和折叠 (默认)
35
+ AGGRESSIVE = 3 # 激进模式: 所有子任务都spawn独立agent
36
+
37
+
38
+ # 多 CLI 后端配置
39
+ CLI_BACKENDS = {
40
+ "codex": {
41
+ "exec_cmd": ["codex", "exec"],
42
+ "model_flag": "--model",
43
+ "sandbox_flag": "--sandbox",
44
+ "json_flag": "--json",
45
+ "skip_git_flag": "--skip-git-repo-check",
46
+ "default_model": "gpt-5.2-codex",
47
+ },
48
+ "gemini": {
49
+ "exec_cmd": ["gemini"], # Gemini CLI 子代理通过内置机制
50
+ "model_flag": "--model",
51
+ "sandbox_flag": None, # Gemini 使用不同的沙箱机制
52
+ "json_flag": "--json",
53
+ "skip_git_flag": None,
54
+ "default_model": "gemini-3",
55
+ },
56
+ "qwen": {
57
+ "exec_cmd": ["qwen-code"], # Qwen Code 基于 Gemini CLI fork
58
+ "model_flag": "--model",
59
+ "sandbox_flag": None,
60
+ "json_flag": "--json",
61
+ "skip_git_flag": None,
62
+ "default_model": "qwen3-coder",
63
+ },
64
+ "claude": {
65
+ "exec_cmd": None, # Claude Code 使用 Task 工具,不使用命令行
66
+ "use_task_tool": True,
67
+ "default_model": "claude-sonnet-4",
68
+ },
69
+ "grok": {
70
+ "exec_cmd": ["grok", "agent"], # Grok CLI (开发中)
71
+ "model_flag": "--model",
72
+ "sandbox_flag": None,
73
+ "json_flag": "--json",
74
+ "skip_git_flag": None,
75
+ "default_model": "grok-code-fast-1",
76
+ },
77
+ }
78
+
79
+
80
+ def detect_cli_backend() -> str:
81
+ """
82
+ 检测当前运行的 CLI 环境
83
+
84
+ Returns:
85
+ CLI 后端名称: codex, gemini, qwen, claude, grok
86
+ """
87
+ import os
88
+ import shutil
89
+
90
+ # 检测环境变量
91
+ if os.environ.get("CODEX_CLI"):
92
+ return "codex"
93
+ if os.environ.get("GEMINI_CLI"):
94
+ return "gemini"
95
+ if os.environ.get("QWEN_CODE"):
96
+ return "qwen"
97
+ if os.environ.get("CLAUDE_CODE"):
98
+ return "claude"
99
+ if os.environ.get("GROK_CLI"):
100
+ return "grok"
101
+
102
+ # 检测可用的 CLI 命令
103
+ if shutil.which("codex"):
104
+ return "codex"
105
+ if shutil.which("gemini"):
106
+ return "gemini"
107
+ if shutil.which("qwen-code"):
108
+ return "qwen"
109
+ if shutil.which("grok"):
110
+ return "grok"
111
+
112
+ # 默认回退到 codex
113
+ return "codex"
114
+
115
+
116
+ @dataclass
117
+ class AgentResult:
118
+ """子Agent执行结果"""
119
+ thread_id: str
120
+ status: str # completed | failed | partial
121
+ key_findings: List[str] = field(default_factory=list)
122
+ changes_made: List[Dict[str, Any]] = field(default_factory=list)
123
+ issues_found: List[Dict[str, Any]] = field(default_factory=list)
124
+ recommendations: List[str] = field(default_factory=list)
125
+ needs_followup: bool = False
126
+ followup_context: str = ""
127
+ raw_output: str = ""
128
+ execution_time: float = 0.0
129
+
130
+ def to_dict(self) -> Dict[str, Any]:
131
+ """转换为字典"""
132
+ return {
133
+ "thread_id": self.thread_id,
134
+ "status": self.status,
135
+ "key_findings": self.key_findings,
136
+ "changes_made": self.changes_made,
137
+ "issues_found": self.issues_found,
138
+ "recommendations": self.recommendations,
139
+ "needs_followup": self.needs_followup,
140
+ "followup_context": self.followup_context,
141
+ "execution_time": self.execution_time,
142
+ }
143
+
144
+ def to_summary(self) -> str:
145
+ """生成简洁摘要"""
146
+ parts = [f"[{self.status.upper()}]"]
147
+ if self.key_findings:
148
+ parts.append(f"发现: {'; '.join(self.key_findings[:3])}")
149
+ if self.changes_made:
150
+ parts.append(f"变更: {len(self.changes_made)}个文件")
151
+ if self.issues_found:
152
+ parts.append(f"问题: {len(self.issues_found)}个")
153
+ return " | ".join(parts)
154
+
155
+
156
+ @dataclass
157
+ class FoldedTrajectory:
158
+ """折叠后的轨迹"""
159
+ trajectory_id: str
160
+ original_length: int
161
+ summary: str
162
+ key_artifacts: List[str] = field(default_factory=list)
163
+ timestamp: str = ""
164
+ fold_reason: str = ""
165
+
166
+ def __post_init__(self):
167
+ if not self.timestamp:
168
+ self.timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
169
+
170
+
171
+ class RLMEngine:
172
+ """
173
+ RLM核心引擎
174
+
175
+ 特点:
176
+ 1. 利用Codex原生spawn_agent (零MCP预加载开销)
177
+ 2. 实现MIT RLM风格的REPL环境
178
+ 3. Context Folding主动管理上下文
179
+ 4. 三层上下文架构
180
+ """
181
+
182
+ # 角色预设映射
183
+ ROLE_PRESETS = {
184
+ "explorer": {
185
+ "description": "探索代码库,收集上下文",
186
+ "sandbox": "read-only",
187
+ "tools": ["read", "grep", "glob"],
188
+ },
189
+ "analyzer": {
190
+ "description": "深度分析代码/需求/架构",
191
+ "sandbox": "read-only",
192
+ "tools": ["read", "grep", "glob", "test"],
193
+ },
194
+ "implementer": {
195
+ "description": "编写/修改代码",
196
+ "sandbox": "workspace-write",
197
+ "tools": ["read", "write", "edit", "bash"],
198
+ },
199
+ "reviewer": {
200
+ "description": "代码审查,质量检查",
201
+ "sandbox": "read-only",
202
+ "tools": ["read", "grep", "glob", "test"],
203
+ },
204
+ "tester": {
205
+ "description": "编写/运行测试",
206
+ "sandbox": "workspace-write",
207
+ "tools": ["read", "write", "edit", "bash", "test"],
208
+ },
209
+ "synthesizer": {
210
+ "description": "综合多个agent的输出",
211
+ "sandbox": "read-only",
212
+ "tools": ["read"],
213
+ },
214
+ }
215
+
216
+ def __init__(
217
+ self,
218
+ mode: RLMMode = RLMMode.ACTIVE,
219
+ max_depth: int = 5,
220
+ max_parallel: int = 24,
221
+ working_context_limit: int = 8000,
222
+ codex_model: str = "gpt-5.2-codex",
223
+ backend: Optional[str] = None,
224
+ ):
225
+ """
226
+ 初始化RLM引擎
227
+
228
+ Args:
229
+ mode: RLM运行模式
230
+ max_depth: 最大递归深度
231
+ max_parallel: 最大并行子Agent数
232
+ working_context_limit: 工作上下文token限制
233
+ codex_model: Codex模型名称(兼容参数,优先使用后端默认模型)
234
+ backend: CLI 后端名称(codex/gemini/qwen/claude/grok),None 时自动检测
235
+ """
236
+ self.mode = mode
237
+ self.max_depth = max_depth
238
+ self.max_parallel = max_parallel
239
+ self.working_context_limit = working_context_limit
240
+
241
+ # 后端检测和配置
242
+ self.backend = backend or detect_cli_backend()
243
+ self.backend_config = CLI_BACKENDS.get(self.backend, CLI_BACKENDS["codex"])
244
+ self.codex_model = codex_model if codex_model != "gpt-5.2-codex" else self.backend_config.get("default_model", codex_model)
245
+
246
+ # 状态变量
247
+ self.current_depth = 0
248
+ self.active_agents: Dict[str, asyncio.Task] = {}
249
+ self.folded_summaries: Dict[str, FoldedTrajectory] = {}
250
+ self.trajectory_counter = 0
251
+
252
+ # 三层上下文
253
+ self.working_context: List[Dict[str, Any]] = []
254
+ self.session_events: List[Dict[str, Any]] = []
255
+ self.memory_refs: Dict[str, str] = {} # key -> file path or memory://key
256
+
257
+ # 执行报告
258
+ self.report = ExecutionReport("rlm_engine")
259
+
260
+ # ==================== REPL核心函数 ====================
261
+
262
+ def peek(self, data_key: str, start: int = 0, end: int = 1000) -> str:
263
+ """
264
+ 查看外部存储的数据片段
265
+ 不将全部数据加载到context
266
+
267
+ Args:
268
+ data_key: 数据键名
269
+ start: 起始位置
270
+ end: 结束位置
271
+
272
+ Returns:
273
+ 数据片段
274
+ """
275
+ if data_key not in self.memory_refs:
276
+ return f"[ERROR] Data key '{data_key}' not found in memory_refs"
277
+
278
+ ref = self.memory_refs[data_key]
279
+
280
+ if ref.startswith("memory://"):
281
+ # 内存数据 - 实际应用中这里会有内存存储
282
+ return f"[MEMORY] {data_key}[{start}:{end}]"
283
+
284
+ # 文件数据
285
+ try:
286
+ file_path = Path(ref)
287
+ if not file_path.exists():
288
+ return f"[ERROR] File not found: {ref}"
289
+
290
+ content = file_path.read_text(encoding='utf-8')
291
+ return content[start:end]
292
+ except Exception as e:
293
+ return f"[ERROR] Failed to read '{data_key}': {e}"
294
+
295
+ def store(self, key: str, data: str, persist: bool = False) -> bool:
296
+ """
297
+ 存储数据到外部存储
298
+
299
+ Args:
300
+ key: 数据键名
301
+ data: 数据内容
302
+ persist: 是否持久化到文件系统
303
+
304
+ Returns:
305
+ 是否成功
306
+ """
307
+ try:
308
+ if persist:
309
+ # 写入helloagents知识库缓存目录
310
+ cache_dir = Path.cwd() / "helloagents" / ".rlm_cache"
311
+ cache_dir.mkdir(parents=True, exist_ok=True)
312
+
313
+ file_path = cache_dir / f"{key}.txt"
314
+ file_path.write_text(data, encoding='utf-8')
315
+ self.memory_refs[key] = str(file_path)
316
+ else:
317
+ # 仅存储引用标记
318
+ self.memory_refs[key] = f"memory://{key}"
319
+
320
+ self.session_events.append({
321
+ "type": "store",
322
+ "key": key,
323
+ "persist": persist,
324
+ "size": len(data),
325
+ "timestamp": self._get_timestamp(),
326
+ })
327
+ return True
328
+ except Exception as e:
329
+ self.report.mark_failed("store", [f"存储数据 {key}"], str(e))
330
+ return False
331
+
332
+ def fold(
333
+ self,
334
+ trajectory: str,
335
+ summary_prompt: str = "保留关键信息,生成简洁摘要",
336
+ fold_reason: str = "manual",
337
+ ) -> FoldedTrajectory:
338
+ """
339
+ 折叠子轨迹
340
+ 使用LLM生成摘要,替换原内容
341
+
342
+ Args:
343
+ trajectory: 要折叠的轨迹内容
344
+ summary_prompt: 摘要生成提示
345
+ fold_reason: 折叠原因
346
+
347
+ Returns:
348
+ 折叠后的轨迹对象
349
+ """
350
+ self.trajectory_counter += 1
351
+ trajectory_id = f"traj_{self.trajectory_counter}_{self._get_timestamp()}"
352
+
353
+ # 生成摘要 (同步方式,利用当前LLM)
354
+ summary = self._generate_summary(trajectory, summary_prompt)
355
+
356
+ # 提取关键产物
357
+ artifacts = self._extract_artifacts(trajectory)
358
+
359
+ folded = FoldedTrajectory(
360
+ trajectory_id=trajectory_id,
361
+ original_length=len(trajectory),
362
+ summary=summary,
363
+ key_artifacts=artifacts,
364
+ fold_reason=fold_reason,
365
+ )
366
+
367
+ # 原内容存入session events (可用于回溯)
368
+ self.session_events.append({
369
+ "type": "folded_trajectory",
370
+ "trajectory_id": trajectory_id,
371
+ "original": trajectory,
372
+ "summary": summary,
373
+ "artifacts": artifacts,
374
+ "timestamp": folded.timestamp,
375
+ })
376
+
377
+ # 缓存折叠结果
378
+ self.folded_summaries[trajectory_id] = folded
379
+
380
+ return folded
381
+
382
+ async def spawn_agent(
383
+ self,
384
+ role: str,
385
+ task: str,
386
+ context_hint: Optional[List[str]] = None,
387
+ timeout: int = 120,
388
+ ) -> AgentResult:
389
+ """
390
+ 创建独立context的子agent
391
+ 使用Codex原生spawn_agent + role preset
392
+
393
+ Args:
394
+ role: 角色名称 (explorer/analyzer/implementer/reviewer/tester/synthesizer)
395
+ task: 任务描述
396
+ context_hint: 相关文件/目录提示
397
+ timeout: 超时时间(秒)
398
+
399
+ Returns:
400
+ Agent执行结果
401
+ """
402
+ # 检查递归深度
403
+ if self.current_depth >= self.max_depth:
404
+ return AgentResult(
405
+ thread_id="",
406
+ status="failed",
407
+ key_findings=[f"达到最大递归深度 {self.max_depth}"],
408
+ )
409
+
410
+ # 验证角色
411
+ if role not in self.ROLE_PRESETS:
412
+ return AgentResult(
413
+ thread_id="",
414
+ status="failed",
415
+ key_findings=[f"未知角色: {role},可用角色: {list(self.ROLE_PRESETS.keys())}"],
416
+ )
417
+
418
+ self.current_depth += 1
419
+ start_time = datetime.now()
420
+
421
+ try:
422
+ # 构建子agent提示
423
+ prompt = self._build_agent_prompt(role, task, context_hint or [])
424
+
425
+ # 获取角色配置
426
+ role_config = self.ROLE_PRESETS[role]
427
+
428
+ # 记录事件
429
+ self.session_events.append({
430
+ "type": "spawn_agent",
431
+ "role": role,
432
+ "task": task,
433
+ "context_hint": context_hint,
434
+ "timestamp": self._get_timestamp(),
435
+ })
436
+
437
+ # 执行Codex agent
438
+ result = await self._execute_codex_agent(
439
+ prompt=prompt,
440
+ sandbox=role_config["sandbox"],
441
+ timeout=timeout,
442
+ )
443
+
444
+ result.execution_time = (datetime.now() - start_time).total_seconds()
445
+ return result
446
+
447
+ except asyncio.TimeoutError:
448
+ return AgentResult(
449
+ thread_id="",
450
+ status="failed",
451
+ key_findings=[f"执行超时 ({timeout}秒)"],
452
+ execution_time=(datetime.now() - start_time).total_seconds(),
453
+ )
454
+ except Exception as e:
455
+ return AgentResult(
456
+ thread_id="",
457
+ status="failed",
458
+ key_findings=[f"执行错误: {e}"],
459
+ execution_time=(datetime.now() - start_time).total_seconds(),
460
+ )
461
+ finally:
462
+ self.current_depth -= 1
463
+
464
+ async def batch(
465
+ self,
466
+ tasks: List[Dict[str, Any]],
467
+ ) -> List[AgentResult]:
468
+ """
469
+ 并行执行多个子agent任务
470
+
471
+ Args:
472
+ tasks: 任务列表,每个任务包含 role, task, context_hint
473
+
474
+ Returns:
475
+ 结果列表
476
+ """
477
+ if not tasks:
478
+ return []
479
+
480
+ # 分批处理 (最大24并行)
481
+ results = []
482
+ for i in range(0, len(tasks), self.max_parallel):
483
+ batch = tasks[i:i + self.max_parallel]
484
+
485
+ # 并行执行当前批次
486
+ batch_results = await asyncio.gather(*[
487
+ self.spawn_agent(
488
+ role=t.get("role", "explorer"),
489
+ task=t.get("task", ""),
490
+ context_hint=t.get("context_hint"),
491
+ timeout=t.get("timeout", 120),
492
+ )
493
+ for t in batch
494
+ ], return_exceptions=True)
495
+
496
+ # 处理异常
497
+ for j, r in enumerate(batch_results):
498
+ if isinstance(r, Exception):
499
+ results.append(AgentResult(
500
+ thread_id="",
501
+ status="failed",
502
+ key_findings=[f"批量执行异常: {r}"],
503
+ ))
504
+ else:
505
+ results.append(r)
506
+
507
+ return results
508
+
509
+ async def wait(self, thread_ids: List[str]) -> List[AgentResult]:
510
+ """
511
+ 等待多个agent完成
512
+
513
+ Args:
514
+ thread_ids: 要等待的thread ID列表
515
+
516
+ Returns:
517
+ 结果列表
518
+ """
519
+ results = []
520
+ for tid in thread_ids:
521
+ if tid in self.active_agents:
522
+ try:
523
+ result = await self.active_agents[tid]
524
+ results.append(result)
525
+ except Exception as e:
526
+ results.append(AgentResult(
527
+ thread_id=tid,
528
+ status="failed",
529
+ key_findings=[f"等待失败: {e}"],
530
+ ))
531
+ return results
532
+
533
+ def merge(
534
+ self,
535
+ results: List[AgentResult],
536
+ strategy: str = "synthesize",
537
+ ) -> str:
538
+ """
539
+ 合并多个agent的输出
540
+
541
+ Args:
542
+ results: Agent结果列表
543
+ strategy: 合并策略 (concat | synthesize | vote)
544
+
545
+ Returns:
546
+ 合并后的输出
547
+ """
548
+ if not results:
549
+ return ""
550
+
551
+ if strategy == "concat":
552
+ # 简单拼接
553
+ parts = []
554
+ for i, r in enumerate(results):
555
+ parts.append(f"## Agent {i+1} ({r.status})")
556
+ parts.append(r.raw_output or r.to_summary())
557
+ return "\n\n---\n\n".join(parts)
558
+
559
+ elif strategy == "synthesize":
560
+ # 综合所有发现
561
+ all_findings = []
562
+ all_recommendations = []
563
+ all_issues = []
564
+
565
+ for r in results:
566
+ all_findings.extend(r.key_findings)
567
+ all_recommendations.extend(r.recommendations)
568
+ all_issues.extend(r.issues_found)
569
+
570
+ # 去重
571
+ unique_findings = list(dict.fromkeys(all_findings))
572
+ unique_recommendations = list(dict.fromkeys(all_recommendations))
573
+
574
+ synthesis = []
575
+ if unique_findings:
576
+ synthesis.append("### 关键发现")
577
+ synthesis.extend([f"- {f}" for f in unique_findings[:10]])
578
+ if unique_recommendations:
579
+ synthesis.append("\n### 建议")
580
+ synthesis.extend([f"- {r}" for r in unique_recommendations[:10]])
581
+ if all_issues:
582
+ synthesis.append(f"\n### 问题 ({len(all_issues)}个)")
583
+ for issue in all_issues[:5]:
584
+ synthesis.append(f"- [{issue.get('severity', 'medium')}] {issue.get('description', '')}")
585
+
586
+ return "\n".join(synthesis)
587
+
588
+ elif strategy == "vote":
589
+ # 投票 (取众数)
590
+ from collections import Counter
591
+ all_recommendations = []
592
+ for r in results:
593
+ all_recommendations.extend(r.recommendations)
594
+
595
+ if not all_recommendations:
596
+ return "无共识建议"
597
+
598
+ counter = Counter(all_recommendations)
599
+ return "\n".join([f"- {item} ({count}票)" for item, count in counter.most_common(5)])
600
+
601
+ return ""
602
+
603
+ # ==================== 内部方法 ====================
604
+
605
+ def _build_agent_prompt(
606
+ self,
607
+ role: str,
608
+ task: str,
609
+ context_hint: List[str],
610
+ ) -> str:
611
+ """构建子agent提示"""
612
+ role_config = self.ROLE_PRESETS.get(role, {})
613
+ role_desc = role_config.get("description", "执行任务")
614
+
615
+ # 加载角色预设文件 (如果存在)
616
+ role_file = Path(__file__).parent / "roles" / f"{role}.md"
617
+ role_instructions = ""
618
+ if role_file.exists():
619
+ role_instructions = role_file.read_text(encoding='utf-8')
620
+
621
+ context_str = ""
622
+ if context_hint:
623
+ context_str = f"\n\n## 相关上下文\n文件/目录: {', '.join(context_hint)}"
624
+
625
+ return f"""# 角色: {role}
626
+ {role_desc}
627
+
628
+ {role_instructions}
629
+
630
+ ## 任务
631
+ {task}
632
+ {context_str}
633
+
634
+ ## 输出要求
635
+ 请以JSON格式返回结果:
636
+ ```json
637
+ {{
638
+ "status": "completed|failed|partial",
639
+ "key_findings": ["发现1", "发现2"],
640
+ "changes_made": [{{"file": "path", "type": "modify|create|delete"}}],
641
+ "issues_found": [{{"severity": "high|medium|low", "description": "..."}}],
642
+ "recommendations": ["建议1", "建议2"],
643
+ "needs_followup": false,
644
+ "followup_context": ""
645
+ }}
646
+ ```
647
+ """
648
+
649
+ async def _execute_codex_agent(
650
+ self,
651
+ prompt: str,
652
+ sandbox: str,
653
+ timeout: int,
654
+ ) -> AgentResult:
655
+ """执行子代理(支持多 CLI 后端)"""
656
+ # Claude Code 使用 Task 工具,返回提示信息
657
+ if self.backend_config.get("use_task_tool"):
658
+ return AgentResult(
659
+ thread_id="",
660
+ status="info",
661
+ key_findings=["Claude Code 环境: 请使用 Task 工具启动子代理"],
662
+ recommendations=[f"Task(subagent_type='{sandbox}', prompt='{prompt[:100]}...')"],
663
+ )
664
+
665
+ exec_cmd = self.backend_config.get("exec_cmd")
666
+ if not exec_cmd:
667
+ return AgentResult(
668
+ thread_id="",
669
+ status="failed",
670
+ key_findings=[f"后端 {self.backend} 不支持命令行执行"],
671
+ )
672
+
673
+ try:
674
+ # 动态构建命令
675
+ cmd = list(exec_cmd) # 复制基础命令
676
+
677
+ # 添加 JSON 输出标志
678
+ if self.backend_config.get("json_flag"):
679
+ cmd.append(self.backend_config["json_flag"])
680
+
681
+ # 添加跳过 Git 检查标志
682
+ if self.backend_config.get("skip_git_flag"):
683
+ cmd.append(self.backend_config["skip_git_flag"])
684
+
685
+ # 添加沙箱模式
686
+ if self.backend_config.get("sandbox_flag"):
687
+ cmd.extend([self.backend_config["sandbox_flag"], sandbox])
688
+
689
+ # 添加模型参数
690
+ if self.backend_config.get("model_flag"):
691
+ cmd.extend([self.backend_config["model_flag"], self.codex_model])
692
+
693
+ # 添加提示词
694
+ cmd.append(prompt)
695
+
696
+ # 异步执行
697
+ proc = await asyncio.create_subprocess_exec(
698
+ *cmd,
699
+ stdout=asyncio.subprocess.PIPE,
700
+ stderr=asyncio.subprocess.PIPE,
701
+ cwd=str(Path.cwd()),
702
+ )
703
+
704
+ stdout, stderr = await asyncio.wait_for(
705
+ proc.communicate(),
706
+ timeout=timeout,
707
+ )
708
+
709
+ output = stdout.decode('utf-8', errors='replace')
710
+ error = stderr.decode('utf-8', errors='replace')
711
+
712
+ if proc.returncode != 0 and error:
713
+ return AgentResult(
714
+ thread_id="",
715
+ status="failed",
716
+ key_findings=[f"Codex错误: {error[:500]}"],
717
+ raw_output=output,
718
+ )
719
+
720
+ return self._parse_agent_output(output)
721
+
722
+ except FileNotFoundError:
723
+ return AgentResult(
724
+ thread_id="",
725
+ status="failed",
726
+ key_findings=["Codex CLI未安装或不在PATH中"],
727
+ )
728
+
729
+ def _parse_agent_output(self, output: str) -> AgentResult:
730
+ """解析agent输出"""
731
+ # 尝试从输出中提取JSON
732
+ thread_id = ""
733
+
734
+ try:
735
+ for line in output.split('\n'):
736
+ line = line.strip()
737
+ if not line:
738
+ continue
739
+
740
+ # 尝试解析JSON
741
+ if line.startswith('{'):
742
+ try:
743
+ data = json.loads(line)
744
+
745
+ # 检查是否是结果JSON
746
+ if "status" in data:
747
+ return AgentResult(
748
+ thread_id=data.get("threadId", thread_id),
749
+ status=data.get("status", "completed"),
750
+ key_findings=data.get("key_findings", []),
751
+ changes_made=data.get("changes_made", []),
752
+ issues_found=data.get("issues_found", []),
753
+ recommendations=data.get("recommendations", []),
754
+ needs_followup=data.get("needs_followup", False),
755
+ followup_context=data.get("followup_context", ""),
756
+ raw_output=output,
757
+ )
758
+
759
+ # 提取threadId
760
+ if "threadId" in data:
761
+ thread_id = data["threadId"]
762
+
763
+ except json.JSONDecodeError:
764
+ continue
765
+
766
+ except Exception:
767
+ pass
768
+
769
+ # 无法解析JSON,返回原始输出
770
+ return AgentResult(
771
+ thread_id=thread_id,
772
+ status="completed",
773
+ key_findings=[output[:500] if output else "无输出"],
774
+ raw_output=output,
775
+ )
776
+
777
+ def _generate_summary(self, content: str, prompt: str) -> str:
778
+ """
779
+ 生成摘要
780
+
781
+ 注意: 这里是同步实现,实际使用时会利用当前LLM上下文
782
+ """
783
+ # 简化实现: 提取关键行
784
+ lines = content.split('\n')
785
+ key_lines = []
786
+
787
+ for line in lines:
788
+ line = line.strip()
789
+ if not line:
790
+ continue
791
+ # 保留标题、错误、重要标记
792
+ if any(marker in line.lower() for marker in
793
+ ['#', 'error', 'warning', 'success', 'failed', 'completed', '✅', '❌', '⚠️']):
794
+ key_lines.append(line)
795
+ # 保留JSON结构
796
+ elif line.startswith('{') or line.startswith('['):
797
+ key_lines.append(line[:200])
798
+
799
+ if key_lines:
800
+ return '\n'.join(key_lines[:20])
801
+
802
+ # 如果没有关键行,返回截断内容
803
+ return content[:500] + "..." if len(content) > 500 else content
804
+
805
+ def _extract_artifacts(self, trajectory: str) -> List[str]:
806
+ """提取关键产物 (文件路径、变量名等)"""
807
+ import re
808
+ artifacts = []
809
+
810
+ # 提取文件路径
811
+ paths = re.findall(r'[\w./\\-]+\.\w{1,10}', trajectory)
812
+ artifacts.extend(paths[:10])
813
+
814
+ # 提取函数/类名
815
+ funcs = re.findall(r'(?:def|class|function)\s+(\w+)', trajectory)
816
+ artifacts.extend(funcs[:5])
817
+
818
+ return list(dict.fromkeys(artifacts)) # 去重
819
+
820
+ def _get_timestamp(self) -> str:
821
+ """获取时间戳"""
822
+ return datetime.now().strftime("%Y%m%d%H%M%S")
823
+
824
+ # ==================== 状态管理 ====================
825
+
826
+ def get_status(self) -> Dict[str, Any]:
827
+ """获取当前RLM状态"""
828
+ return {
829
+ "mode": self.mode.name,
830
+ "current_depth": self.current_depth,
831
+ "max_depth": self.max_depth,
832
+ "active_agents": len(self.active_agents),
833
+ "folded_count": len(self.folded_summaries),
834
+ "session_events_count": len(self.session_events),
835
+ "memory_refs_count": len(self.memory_refs),
836
+ }
837
+
838
+ def reset(self):
839
+ """重置RLM状态"""
840
+ self.current_depth = 0
841
+ self.active_agents.clear()
842
+ self.folded_summaries.clear()
843
+ self.trajectory_counter = 0
844
+ self.working_context.clear()
845
+ self.session_events.clear()
846
+ self.memory_refs.clear()
847
+
848
+
849
+ # ==================== 便捷函数 ====================
850
+
851
+ def create_engine(
852
+ mode: Union[RLMMode, int] = RLMMode.ACTIVE,
853
+ **kwargs,
854
+ ) -> RLMEngine:
855
+ """创建RLM引擎实例"""
856
+ if isinstance(mode, int):
857
+ mode = RLMMode(mode)
858
+ return RLMEngine(mode=mode, **kwargs)
859
+
860
+
861
+ async def quick_analyze(
862
+ target: str,
863
+ engine: Optional[RLMEngine] = None,
864
+ ) -> AgentResult:
865
+ """快速分析目标"""
866
+ if engine is None:
867
+ engine = create_engine()
868
+
869
+ return await engine.spawn_agent(
870
+ role="analyzer",
871
+ task=f"分析以下目标: {target}",
872
+ context_hint=[target] if Path(target).exists() else None,
873
+ )
874
+
875
+
876
+ async def quick_implement(
877
+ spec: str,
878
+ engine: Optional[RLMEngine] = None,
879
+ ) -> AgentResult:
880
+ """快速实现规格"""
881
+ if engine is None:
882
+ engine = create_engine()
883
+
884
+ return await engine.spawn_agent(
885
+ role="implementer",
886
+ task=spec,
887
+ )
888
+
889
+
890
+ # ==================== CLI入口 ====================
891
+
892
+ if __name__ == "__main__":
893
+ import argparse
894
+
895
+ parser = argparse.ArgumentParser(description="HelloAGENTS RLM Engine")
896
+ parser.add_argument("--mode", type=int, default=2, help="RLM模式 (0-3)")
897
+ parser.add_argument("--status", action="store_true", help="显示状态")
898
+ parser.add_argument("--analyze", type=str, help="分析目标")
899
+ parser.add_argument("--implement", type=str, help="实现规格")
900
+
901
+ args = parser.parse_args()
902
+
903
+ engine = create_engine(mode=args.mode)
904
+
905
+ if args.status:
906
+ print(json.dumps(engine.get_status(), ensure_ascii=False, indent=2))
907
+ elif args.analyze:
908
+ result = asyncio.run(quick_analyze(args.analyze, engine))
909
+ print(json.dumps(result.to_dict(), ensure_ascii=False, indent=2))
910
+ elif args.implement:
911
+ result = asyncio.run(quick_implement(args.implement, engine))
912
+ print(json.dumps(result.to_dict(), ensure_ascii=False, indent=2))
913
+ else:
914
+ print("HelloAGENTS RLM Engine v1.0.0")
915
+ print("使用 --help 查看帮助")