sycommon-python-lib 0.2.1b9__py3-none-any.whl → 0.2.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.
- command/cli.py +24 -3
- command/core/models.py +15 -0
- command/core/project.py +18 -7
- command/templates/cli/README.md.tpl +54 -0
- command/templates/cli/__init__.py +1 -0
- command/templates/cli/app.py.tpl +45 -0
- command/templates/cli/commands/__init__.py.tpl +1 -0
- command/templates/cli/commands/hello.py.tpl +6 -0
- command/templates/cli/pyproject.toml.tpl +14 -0
- command/templates/cli/tools/__init__.py.tpl +3 -0
- nexus/__init__.py +40 -0
- sycli/agents/prompts.py +88 -0
- sycli/commands/resume_cmd.py +107 -0
- sycli/mode/create.py +4 -4
- sycli/models/config_models.py +8 -0
- sycli/rl/baseline_tests.py +245 -0
- sycli/rl/checkpoint.py +497 -0
- sycli/rl/convergence.py +83 -12
- sycli/rl/diagnosis.py +14 -7
- sycli/rl/engine.py +407 -31
- sycli/rl/environment.py +252 -0
- sycli/rl/experience.py +27 -0
- sycli/rl/optimization_logger.py +264 -0
- sycli/rl/reward.py +39 -13
- sycli/rl/strategy_bandit.py +80 -7
- sycli/rl/strategy_generator.py +17 -0
- sycli/rl/strategy_prompts.py +4 -0
- sycommon/agent/deep_agent.py +11 -2
- sycommon/agent/multi_agent_team.py +8 -3
- sycommon/agent/sandbox/__init__.py +4 -0
- sycommon/agent/sandbox/file_ops.py +57 -91
- sycommon/agent/sandbox/http_sandbox_backend.py +373 -222
- sycommon/agent/sandbox/minio_sync.py +500 -0
- sycommon/agent/summarization_utils.py +68 -0
- sycommon/database/async_database_service.py +4 -2
- sycommon/database/token_usage_db_service.py +3 -2
- sycommon/llm/get_llm.py +11 -3
- sycommon/llm/struct_token.py +4 -6
- sycommon/llm/token_usage_mysql_service.py +44 -47
- sycommon/middleware/sandbox.py +674 -359
- sycommon/middleware/tool_result_truncation.py +136 -0
- sycommon/middleware/traceid.py +20 -2
- sycommon/models/sandbox.py +93 -2
- sycommon/notice/__init__.py +5 -0
- sycommon/notice/wecom_message.py +328 -0
- sycommon/services.py +18 -5
- {sycommon_python_lib-0.2.1b9.dist-info → sycommon_python_lib-0.2.2.dist-info}/METADATA +13 -11
- {sycommon_python_lib-0.2.1b9.dist-info → sycommon_python_lib-0.2.2.dist-info}/RECORD +51 -37
- {sycommon_python_lib-0.2.1b9.dist-info → sycommon_python_lib-0.2.2.dist-info}/entry_points.txt +1 -0
- {sycommon_python_lib-0.2.1b9.dist-info → sycommon_python_lib-0.2.2.dist-info}/top_level.txt +1 -0
- command/templates/__pycache__/__init__.cpython-311.pyc +0 -0
- {sycommon_python_lib-0.2.1b9.dist-info → sycommon_python_lib-0.2.2.dist-info}/WHEEL +0 -0
command/cli.py
CHANGED
|
@@ -49,6 +49,12 @@ def create_init_parser(subparsers) -> None:
|
|
|
49
49
|
web - 标准 FastAPI Web 服务,适合 RESTful API 开发
|
|
50
50
|
agent - AI Agent 服务,基于 langgraph 实现状态图
|
|
51
51
|
|
|
52
|
+
CLI 项目结构:
|
|
53
|
+
commands/ - 子命令目录
|
|
54
|
+
model/ - 数据模型
|
|
55
|
+
client/ - 外部服务客户端
|
|
56
|
+
app.py - CLI 入口 (argparse)
|
|
57
|
+
|
|
52
58
|
Agent 项目结构:
|
|
53
59
|
agent/ - Agent 核心 (main_agent, nodes, states, enums)
|
|
54
60
|
api/sse/ - SSE 流式接口
|
|
@@ -79,7 +85,7 @@ Web 项目结构:
|
|
|
79
85
|
# 位置参数
|
|
80
86
|
init_parser.add_argument(
|
|
81
87
|
"project_type",
|
|
82
|
-
choices=["web", "agent"],
|
|
88
|
+
choices=["web", "agent", "cli"],
|
|
83
89
|
help="项目类型:web / agent"
|
|
84
90
|
)
|
|
85
91
|
init_parser.add_argument(
|
|
@@ -221,6 +227,8 @@ def _detect_project_type(project_path) -> str | None:
|
|
|
221
227
|
if (project_path / "agent" / "main_agent.py").exists() or \
|
|
222
228
|
(project_path / "agent" / "enums").is_dir():
|
|
223
229
|
return "agent"
|
|
230
|
+
if (project_path / "commands").is_dir() and (project_path / "app.py").exists():
|
|
231
|
+
return "cli"
|
|
224
232
|
if (project_path / "api").is_dir():
|
|
225
233
|
return "web"
|
|
226
234
|
return None
|
|
@@ -247,13 +255,19 @@ def handle_doctor_command(args) -> bool:
|
|
|
247
255
|
warnings.append("无法识别项目类型 (非 web / agent 标准结构)")
|
|
248
256
|
|
|
249
257
|
# 1. 检查必要文件
|
|
250
|
-
|
|
258
|
+
if project_type == "cli":
|
|
259
|
+
required_files = ["app.py", "pyproject.toml"]
|
|
260
|
+
else:
|
|
261
|
+
required_files = ["app.py", "requirements.txt"]
|
|
251
262
|
for file in required_files:
|
|
252
263
|
if not (project_path / file).exists():
|
|
253
264
|
issues.append(f"缺少必要文件: {file}")
|
|
254
265
|
|
|
255
266
|
# 2. 检查配置文件
|
|
256
|
-
|
|
267
|
+
if project_type == "cli":
|
|
268
|
+
config_files = [".env"]
|
|
269
|
+
else:
|
|
270
|
+
config_files = ["app.yaml", ".env"]
|
|
257
271
|
for file in config_files:
|
|
258
272
|
if not (project_path / file).exists():
|
|
259
273
|
warnings.append(f"缺少配置文件: {file}")
|
|
@@ -276,6 +290,8 @@ def handle_doctor_command(args) -> bool:
|
|
|
276
290
|
issues.append(f"缺少 Agent 核心文件: {file}")
|
|
277
291
|
elif project_type == "web":
|
|
278
292
|
required_dirs = ["api", "model", "tools", "client"]
|
|
293
|
+
elif project_type == "cli":
|
|
294
|
+
required_dirs = ["commands", "model", "tools"]
|
|
279
295
|
else:
|
|
280
296
|
required_dirs = ["api", "model", "tools"]
|
|
281
297
|
|
|
@@ -351,6 +367,11 @@ def handle_list_command(args) -> bool:
|
|
|
351
367
|
"api/sse/", "api/query.py", "tools/", "model/", "db/"],
|
|
352
368
|
"features": "nacos, kafka_log, llm, sentry (默认启用)",
|
|
353
369
|
},
|
|
370
|
+
"cli": {
|
|
371
|
+
"desc": "Python CLI 命令行工具 (argparse)",
|
|
372
|
+
"dirs": ["commands/", "model/", "client/"],
|
|
373
|
+
"features": "无默认依赖 (精简模板)",
|
|
374
|
+
},
|
|
354
375
|
}
|
|
355
376
|
|
|
356
377
|
for name, info in templates.items():
|
command/core/models.py
CHANGED
|
@@ -13,6 +13,19 @@ AGENT_DEFAULTS = {
|
|
|
13
13
|
"llm": True,
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
# CLI 项目默认关闭所有服务依赖
|
|
17
|
+
CLI_DEFAULTS = {
|
|
18
|
+
"database": False,
|
|
19
|
+
"rabbitmq": False,
|
|
20
|
+
"nacos": False,
|
|
21
|
+
"kafka_log": False,
|
|
22
|
+
"sse": False,
|
|
23
|
+
"llm": False,
|
|
24
|
+
"redis": False,
|
|
25
|
+
"elasticsearch": False,
|
|
26
|
+
"sentry": False,
|
|
27
|
+
}
|
|
28
|
+
|
|
16
29
|
|
|
17
30
|
@dataclass
|
|
18
31
|
class FeatureOptions:
|
|
@@ -32,6 +45,8 @@ class FeatureOptions:
|
|
|
32
45
|
"""根据项目类型返回推荐默认配置"""
|
|
33
46
|
if project_type == "agent":
|
|
34
47
|
return cls(**AGENT_DEFAULTS)
|
|
48
|
+
if project_type == "cli":
|
|
49
|
+
return cls(**CLI_DEFAULTS)
|
|
35
50
|
return cls()
|
|
36
51
|
|
|
37
52
|
def to_template_vars(self) -> dict:
|
command/core/project.py
CHANGED
|
@@ -153,13 +153,19 @@ def init_project(
|
|
|
153
153
|
'db/', 'db/service.py', 'db/model/', 'db/model/entity.py',
|
|
154
154
|
],
|
|
155
155
|
}
|
|
156
|
+
# CLI 项目不需要的服务端文件
|
|
157
|
+
cli_skip_files = {'app.yaml', 'app.yaml.tpl', 'requirements.txt', 'requirements.txt.tpl', 'Dockerfile', 'Dockerfile.tpl'}
|
|
156
158
|
disabled_features = {
|
|
157
159
|
key for key, flag_val in features.to_template_vars().items()
|
|
158
160
|
if flag_val == 'false' and key in feature_map
|
|
159
161
|
}
|
|
160
162
|
|
|
161
163
|
def _should_skip(rel_path: str) -> bool:
|
|
162
|
-
"""
|
|
164
|
+
"""检查文件是否因功能关闭或项目类型而应跳过"""
|
|
165
|
+
# CLI 项目跳过不需要的服务端文件
|
|
166
|
+
if project_type == "cli":
|
|
167
|
+
if rel_path in cli_skip_files or rel_path.replace('.tpl', '') in cli_skip_files:
|
|
168
|
+
return True
|
|
163
169
|
for feat_key in disabled_features:
|
|
164
170
|
for pattern in feature_map[feat_key]:
|
|
165
171
|
if pattern in rel_path:
|
|
@@ -191,7 +197,7 @@ def init_project(
|
|
|
191
197
|
copied_files = _render_files(file_mappings, context, project_path, verbose)
|
|
192
198
|
|
|
193
199
|
if copied_files > 0:
|
|
194
|
-
_print_success_message(project_path, project_name, copied_files)
|
|
200
|
+
_print_success_message(project_path, project_name, copied_files, project_type)
|
|
195
201
|
return True
|
|
196
202
|
else:
|
|
197
203
|
print_warning("未创建任何文件,可能是缺少模板文件或模板路径配置错误")
|
|
@@ -284,7 +290,8 @@ def _render_files(
|
|
|
284
290
|
def _print_success_message(
|
|
285
291
|
project_path: Path,
|
|
286
292
|
project_name: str,
|
|
287
|
-
copied_files: int
|
|
293
|
+
copied_files: int,
|
|
294
|
+
project_type: str = "web",
|
|
288
295
|
) -> None:
|
|
289
296
|
"""打印成功信息"""
|
|
290
297
|
print_success(f"模板工程 {project_name} 创建完成!")
|
|
@@ -293,7 +300,11 @@ def _print_success_message(
|
|
|
293
300
|
print()
|
|
294
301
|
print_info("下一步操作:")
|
|
295
302
|
print(f" cd {project_name}")
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
303
|
+
if project_type == "cli":
|
|
304
|
+
print(f" uv sync")
|
|
305
|
+
print(f" uv run python app.py")
|
|
306
|
+
else:
|
|
307
|
+
print(f" # 建议先创建虚拟环境")
|
|
308
|
+
print(f" python -m venv .venv && source .venv/bin/activate")
|
|
309
|
+
print(f" pip install -r requirements.txt")
|
|
310
|
+
print(f" python app.py")
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# {{ project_name }}
|
|
2
|
+
|
|
3
|
+
{{ project_name }} - Python CLI 命令行工具
|
|
4
|
+
|
|
5
|
+
## 项目结构
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
{{ project_name }}/
|
|
9
|
+
├── commands/ # 子命令目录
|
|
10
|
+
│ ├── __init__.py
|
|
11
|
+
│ └── hello.py # 示例子命令
|
|
12
|
+
├── tools/ # 工具函数
|
|
13
|
+
├── model/ # 数据模型
|
|
14
|
+
├── client/ # 外部服务客户端
|
|
15
|
+
├── app.py # CLI 入口
|
|
16
|
+
├── pyproject.toml # uv 项目配置
|
|
17
|
+
└── README.md
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## 快速开始
|
|
21
|
+
|
|
22
|
+
### 安装依赖
|
|
23
|
+
```bash
|
|
24
|
+
uv sync
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### 使用
|
|
28
|
+
```bash
|
|
29
|
+
# 查看帮助
|
|
30
|
+
uv run python app.py -h
|
|
31
|
+
|
|
32
|
+
# hello 命令
|
|
33
|
+
uv run python app.py hello --name "World"
|
|
34
|
+
|
|
35
|
+
# 查看版本
|
|
36
|
+
uv run python app.py version
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### 添加新子命令
|
|
40
|
+
|
|
41
|
+
1. 在 `commands/` 目录下创建新模块,例如 `commands/xxx.py`
|
|
42
|
+
2. 实现 `handle_xxx(args)` 函数
|
|
43
|
+
3. 在 `app.py` 中注册子命令:
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from commands.xxx import handle_xxx
|
|
47
|
+
|
|
48
|
+
# 在 create_parser() 中添加:
|
|
49
|
+
xxx_parser = subparsers.add_parser("xxx", help="xxx 说明")
|
|
50
|
+
|
|
51
|
+
# 在 main() 中添加:
|
|
52
|
+
elif args.command == "xxx":
|
|
53
|
+
handle_xxx(args)
|
|
54
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""CLI 项目模板"""
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""
|
|
2
|
+
{{ project_name }} - CLI 工具入口
|
|
3
|
+
创建时间: {{ create_time }}
|
|
4
|
+
作者: {{ author }}
|
|
5
|
+
"""
|
|
6
|
+
import argparse
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
from commands.hello import handle_hello
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def create_parser() -> argparse.ArgumentParser:
|
|
13
|
+
"""创建命令行解析器"""
|
|
14
|
+
parser = argparse.ArgumentParser(
|
|
15
|
+
prog="{{ project_name }}",
|
|
16
|
+
description="{{ project_name }} - CLI 命令行工具",
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
subparsers = parser.add_subparsers(dest="command", help="子命令")
|
|
20
|
+
|
|
21
|
+
# hello 子命令
|
|
22
|
+
hello_parser = subparsers.add_parser("hello", help="打招呼")
|
|
23
|
+
hello_parser.add_argument("--name", default="World", help="名字")
|
|
24
|
+
|
|
25
|
+
# version 子命令
|
|
26
|
+
subparsers.add_parser("version", help="显示版本")
|
|
27
|
+
|
|
28
|
+
return parser
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def main() -> None:
|
|
32
|
+
"""CLI 主入口"""
|
|
33
|
+
parser = create_parser()
|
|
34
|
+
args = parser.parse_args()
|
|
35
|
+
|
|
36
|
+
if args.command == "hello":
|
|
37
|
+
handle_hello(args)
|
|
38
|
+
elif args.command == "version":
|
|
39
|
+
print("{{ project_name }} 1.0.0")
|
|
40
|
+
else:
|
|
41
|
+
parser.print_help()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
if __name__ == "__main__":
|
|
45
|
+
main()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""子命令模块"""
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "{{ project_name }}"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "{{ project_name }} - CLI 命令行工具"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.11"
|
|
7
|
+
dependencies = []
|
|
8
|
+
|
|
9
|
+
[project.scripts]
|
|
10
|
+
{{ short_project_name }} = "app:main"
|
|
11
|
+
|
|
12
|
+
[build-system]
|
|
13
|
+
requires = ["hatchling"]
|
|
14
|
+
build-backend = "hatchling.build"
|
nexus/__init__.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import sys
|
|
3
|
+
import os
|
|
4
|
+
import glob
|
|
5
|
+
import argparse
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
NEXUS_URL = "http://192.168.2.174:8081"
|
|
9
|
+
USERNAME = "Osulcode.xiao"
|
|
10
|
+
PASSWORD = "Osulcode.xiao@123"
|
|
11
|
+
REPOSITORY = "shengye"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def main():
|
|
15
|
+
parser = argparse.ArgumentParser(prog="sypublish", description="上传 whl 到 Nexus")
|
|
16
|
+
parser.add_argument("file", nargs="?", help="whl 文件路径 (默认: dist/*.whl 中最新的)")
|
|
17
|
+
parser.add_argument("--url", default=NEXUS_URL, help=f"Nexus 地址 (默认: {NEXUS_URL})")
|
|
18
|
+
parser.add_argument("-r", "--repository", default=REPOSITORY, help=f"仓库名称 (默认: {REPOSITORY})")
|
|
19
|
+
args = parser.parse_args()
|
|
20
|
+
|
|
21
|
+
if args.file:
|
|
22
|
+
file_path = args.file
|
|
23
|
+
else:
|
|
24
|
+
dist_files = sorted(glob.glob("dist/*.whl"))
|
|
25
|
+
if not dist_files:
|
|
26
|
+
print("No .whl file found in dist/")
|
|
27
|
+
sys.exit(1)
|
|
28
|
+
file_path = dist_files[-1]
|
|
29
|
+
|
|
30
|
+
if not os.path.exists(file_path):
|
|
31
|
+
print(f"File not found: {file_path}")
|
|
32
|
+
sys.exit(1)
|
|
33
|
+
|
|
34
|
+
repo_url = f"{args.url}/repository/{args.repository}/"
|
|
35
|
+
cmd = ["twine", "upload", "--repository-url", repo_url, file_path, "-u", USERNAME, "-p", PASSWORD]
|
|
36
|
+
sys.exit(subprocess.call(cmd))
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
if __name__ == "__main__":
|
|
40
|
+
main()
|
sycli/agents/prompts.py
CHANGED
|
@@ -379,3 +379,91 @@ SERVICE_REPORT:
|
|
|
379
379
|
notes: {any relevant notes about the service state}
|
|
380
380
|
```
|
|
381
381
|
"""
|
|
382
|
+
|
|
383
|
+
# ── Direction Correction (E2) ────────────────────────────────────
|
|
384
|
+
|
|
385
|
+
DIRECTION_CORRECTION_PROMPT = """\
|
|
386
|
+
你是一个架构纠偏专家。RL 优化 agent 已经在错误方向上持续了多轮。
|
|
387
|
+
|
|
388
|
+
## 失败轨迹
|
|
389
|
+
{failure_trajectory}
|
|
390
|
+
|
|
391
|
+
## 当前架构信息
|
|
392
|
+
- 框架: {framework}
|
|
393
|
+
- 架构模式: {architecture_pattern}
|
|
394
|
+
- 启动方式: {startup_method}
|
|
395
|
+
- 关键模块: {key_modules}
|
|
396
|
+
|
|
397
|
+
## 分析要求
|
|
398
|
+
1. 指出当前方向的**根本问题**(不是表面症状)
|
|
399
|
+
2. 分析为什么 LLM 一直在错误方向上尝试(可能的认知偏差)
|
|
400
|
+
3. 建议一个**完全不同**的架构/实现方向
|
|
401
|
+
4. 列出需要**避免**的策略类型
|
|
402
|
+
5. 列出**应该尝试**的策略类型
|
|
403
|
+
|
|
404
|
+
## 输出格式(严格遵守)
|
|
405
|
+
|
|
406
|
+
CORRECTION_ANALYSIS:
|
|
407
|
+
root_cause: |
|
|
408
|
+
(根本原因分析)
|
|
409
|
+
wrong_directions:
|
|
410
|
+
- (错误方向1)
|
|
411
|
+
- (错误方向2)
|
|
412
|
+
suggested_direction: |
|
|
413
|
+
(建议的新方向)
|
|
414
|
+
constraints:
|
|
415
|
+
- (必须遵守的约束1)
|
|
416
|
+
- (必须遵守的约束2)
|
|
417
|
+
avoid_strategies:
|
|
418
|
+
- (应避免的策略类型1)
|
|
419
|
+
- (应避免的策略类型2)
|
|
420
|
+
recommended_strategies:
|
|
421
|
+
- name: (策略名称)
|
|
422
|
+
approach: (具体方法)
|
|
423
|
+
focus: (聚焦领域)
|
|
424
|
+
"""
|
|
425
|
+
|
|
426
|
+
# ── Pre-Analysis (E5) ────────────────────────────────────────────
|
|
427
|
+
|
|
428
|
+
PRE_ANALYSIS_PROMPT = """\
|
|
429
|
+
你是一个项目分析专家。在开始 RL 优化前,请对项目进行深度预分析。
|
|
430
|
+
|
|
431
|
+
## 项目环境信息
|
|
432
|
+
{environment_info}
|
|
433
|
+
|
|
434
|
+
## 预分析目标
|
|
435
|
+
1. **启动方式分析**: 项目如何启动?有哪些可选的启动方式?
|
|
436
|
+
2. **架构模式识别**: 项目采用了什么架构模式?各模块职责是什么?
|
|
437
|
+
3. **方案优劣评估**: 当前架构方案的优势和不足是什么?
|
|
438
|
+
4. **风险识别**: 优化过程中可能遇到的主要风险
|
|
439
|
+
5. **优化建议**: 建议的优化方向和优先级
|
|
440
|
+
|
|
441
|
+
## 输出格式(严格遵守)
|
|
442
|
+
|
|
443
|
+
PRE_ANALYSIS:
|
|
444
|
+
startup_analysis:
|
|
445
|
+
primary_method: (主要启动方式)
|
|
446
|
+
alternative_methods: (其他可选方式)
|
|
447
|
+
configuration: (关键配置)
|
|
448
|
+
architecture_analysis:
|
|
449
|
+
pattern: (架构模式名称)
|
|
450
|
+
description: (模式描述)
|
|
451
|
+
modules:
|
|
452
|
+
- name: (模块名)
|
|
453
|
+
role: (职责描述)
|
|
454
|
+
dependencies: (依赖的其他模块)
|
|
455
|
+
data_flow: (数据流向描述)
|
|
456
|
+
strengths:
|
|
457
|
+
- (优势1)
|
|
458
|
+
- (优势2)
|
|
459
|
+
weaknesses:
|
|
460
|
+
- (不足1)
|
|
461
|
+
- (不足2)
|
|
462
|
+
risks:
|
|
463
|
+
- risk: (风险描述)
|
|
464
|
+
mitigation: (缓解措施)
|
|
465
|
+
optimization_priorities:
|
|
466
|
+
- priority: HIGH|MEDIUM|LOW
|
|
467
|
+
area: (优化领域)
|
|
468
|
+
reason: (原因)
|
|
469
|
+
"""
|
sycli/commands/resume_cmd.py
CHANGED
|
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
6
|
import json
|
|
7
|
+
import subprocess
|
|
7
8
|
from pathlib import Path
|
|
8
9
|
|
|
9
10
|
from sycli.core.backend import create_backend
|
|
@@ -14,6 +15,99 @@ from sycli.mode.create import run_create
|
|
|
14
15
|
from sycli.mode.optimize import run_optimize
|
|
15
16
|
|
|
16
17
|
|
|
18
|
+
def _show_resume_summary(sycli_dir: Path, state: dict) -> None:
|
|
19
|
+
"""显示恢复前的进展摘要。"""
|
|
20
|
+
# 尝试从 optimization_log.jsonl 读取最近事件
|
|
21
|
+
log_file = sycli_dir / "optimization_log.jsonl"
|
|
22
|
+
if log_file.exists():
|
|
23
|
+
try:
|
|
24
|
+
recent_events: list[dict] = []
|
|
25
|
+
with open(log_file, encoding="utf-8") as f:
|
|
26
|
+
for line in f:
|
|
27
|
+
line = line.strip()
|
|
28
|
+
if line:
|
|
29
|
+
recent_events.append(json.loads(line))
|
|
30
|
+
# 只保留 round_end 事件
|
|
31
|
+
round_ends = [e for e in recent_events if e.get("event") == "round_end"]
|
|
32
|
+
if round_ends:
|
|
33
|
+
info("最近轮次:")
|
|
34
|
+
for ev in round_ends[-5:]:
|
|
35
|
+
data = ev.get("data", {})
|
|
36
|
+
r = ev.get("round", "?")
|
|
37
|
+
score = data.get("score", 0)
|
|
38
|
+
decision = data.get("decision", "?")
|
|
39
|
+
strategy = data.get("strategy", "")
|
|
40
|
+
info(f" R{r}: score={score:.3f}, decision={decision}, strategy={strategy}")
|
|
41
|
+
except Exception:
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
# 从 rounds.json 读取最佳得分
|
|
45
|
+
rounds_file = sycli_dir / "rl" / "rounds.json"
|
|
46
|
+
if rounds_file.exists():
|
|
47
|
+
try:
|
|
48
|
+
with open(rounds_file) as f:
|
|
49
|
+
rounds = json.load(f)
|
|
50
|
+
if rounds:
|
|
51
|
+
best = max(rounds, key=lambda r: r.get("score", 0))
|
|
52
|
+
info(f"最佳轮次: R{best.get('round_number', '?')}, score={best.get('score', 0):.3f}")
|
|
53
|
+
total_rounds = len(rounds)
|
|
54
|
+
info(f"已完成轮次: {total_rounds}")
|
|
55
|
+
except Exception:
|
|
56
|
+
pass
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _verify_git_state(project_root: Path, state: dict) -> tuple[bool, str]:
|
|
60
|
+
"""验证 git 状态与记录一致。
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
(is_consistent, message)
|
|
64
|
+
"""
|
|
65
|
+
last_commit = state.get("last_git_commit", "")
|
|
66
|
+
if not last_commit:
|
|
67
|
+
return True, "无 git commit 记录(跳过校验)"
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
result = subprocess.run(
|
|
71
|
+
["git", "rev-parse", "HEAD"],
|
|
72
|
+
capture_output=True, text=True, cwd=str(project_root), timeout=10,
|
|
73
|
+
)
|
|
74
|
+
if result.returncode != 0:
|
|
75
|
+
return True, "非 git 仓库(跳过校验)"
|
|
76
|
+
|
|
77
|
+
current = result.stdout.strip()
|
|
78
|
+
if current == last_commit:
|
|
79
|
+
return True, f"git HEAD 一致 ({current[:8]})"
|
|
80
|
+
|
|
81
|
+
# 检查是否有未提交的修改
|
|
82
|
+
status_result = subprocess.run(
|
|
83
|
+
["git", "status", "--porcelain"],
|
|
84
|
+
capture_output=True, text=True, cwd=str(project_root), timeout=10,
|
|
85
|
+
)
|
|
86
|
+
dirty = bool(status_result.stdout.strip())
|
|
87
|
+
|
|
88
|
+
msg = f"git HEAD 不一致: 记录={last_commit[:8]}, 当前={current[:8]}"
|
|
89
|
+
if dirty:
|
|
90
|
+
msg += "(且有未提交修改)"
|
|
91
|
+
return False, msg
|
|
92
|
+
|
|
93
|
+
except (FileNotFoundError, subprocess.TimeoutExpired):
|
|
94
|
+
return True, "git 不可用(跳过校验)"
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _get_git_head(project_root: Path) -> str:
|
|
98
|
+
"""获取当前 git HEAD commit hash。"""
|
|
99
|
+
try:
|
|
100
|
+
result = subprocess.run(
|
|
101
|
+
["git", "rev-parse", "HEAD"],
|
|
102
|
+
capture_output=True, text=True, cwd=str(project_root), timeout=10,
|
|
103
|
+
)
|
|
104
|
+
if result.returncode == 0:
|
|
105
|
+
return result.stdout.strip()
|
|
106
|
+
except Exception:
|
|
107
|
+
pass
|
|
108
|
+
return ""
|
|
109
|
+
|
|
110
|
+
|
|
17
111
|
def handle_resume(args) -> int:
|
|
18
112
|
"""Handle `sycli resume` command - resume an interrupted optimization."""
|
|
19
113
|
project_root = Path(args.project_root or ".").resolve()
|
|
@@ -63,6 +157,19 @@ def handle_resume(args) -> int:
|
|
|
63
157
|
info(f"Status was: {status}")
|
|
64
158
|
info(f"Task: {task}")
|
|
65
159
|
|
|
160
|
+
# ── E4: 进展摘要 ──
|
|
161
|
+
info("")
|
|
162
|
+
info("── 进展摘要 ──")
|
|
163
|
+
_show_resume_summary(sycli_dir, state)
|
|
164
|
+
|
|
165
|
+
# ── E4: Git 状态校验 ──
|
|
166
|
+
info("")
|
|
167
|
+
info("── Git 状态校验 ──")
|
|
168
|
+
is_consistent, git_msg = _verify_git_state(project_root, state)
|
|
169
|
+
info(f" {git_msg}")
|
|
170
|
+
if not is_consistent:
|
|
171
|
+
warning("代码可能已被外部修改,优化结果可能不可预测。")
|
|
172
|
+
|
|
66
173
|
# Initialize components
|
|
67
174
|
try:
|
|
68
175
|
llm = create_llm(config.llm, streaming=True)
|
sycli/mode/create.py
CHANGED
|
@@ -12,7 +12,7 @@ from typing import Any
|
|
|
12
12
|
from deepagents import create_deep_agent
|
|
13
13
|
from deepagents.backends.local_shell import LocalShellBackend
|
|
14
14
|
from langchain_core.language_models import BaseChatModel
|
|
15
|
-
from
|
|
15
|
+
from sycli.rl.checkpoint import FileCheckpointer
|
|
16
16
|
|
|
17
17
|
from sycli.agents.factory import build_subagents, _memory_middleware, _inject_project_root
|
|
18
18
|
from sycli.agents.prompts import ARCHITECT_PROMPT, DATA_ANALYST_PROMPT, ORCHESTRATOR_PROMPT
|
|
@@ -119,7 +119,7 @@ async def run_create(
|
|
|
119
119
|
header("Phase 3: Build & Iterate")
|
|
120
120
|
|
|
121
121
|
subagents = build_subagents(config, llm, backend, project_name, mode="create", project_root=str(sycli_dir.parent))
|
|
122
|
-
checkpointer =
|
|
122
|
+
checkpointer = FileCheckpointer(sycli_dir / "checkpoints")
|
|
123
123
|
thread_id = f"sycli-create-{project_name}-{uuid.uuid4().hex[:8]}"
|
|
124
124
|
|
|
125
125
|
agent = create_deep_agent(
|
|
@@ -355,7 +355,7 @@ async def _run_architect_phase(
|
|
|
355
355
|
system_prompt=_inject_project_root(ARCHITECT_PROMPT, str(backend.cwd)),
|
|
356
356
|
subagents=[researcher_subagent],
|
|
357
357
|
memory=[],
|
|
358
|
-
checkpointer=
|
|
358
|
+
checkpointer=FileCheckpointer(sycli_dir / "checkpoints"),
|
|
359
359
|
debug=False,
|
|
360
360
|
name="sycli-architect",
|
|
361
361
|
)
|
|
@@ -422,7 +422,7 @@ async def _run_data_analyst_phase(
|
|
|
422
422
|
system_prompt=_inject_project_root(DATA_ANALYST_PROMPT, str(backend.cwd)),
|
|
423
423
|
subagents=[researcher_subagent],
|
|
424
424
|
memory=[],
|
|
425
|
-
checkpointer=
|
|
425
|
+
checkpointer=FileCheckpointer(sycli_dir / "checkpoints"),
|
|
426
426
|
debug=False,
|
|
427
427
|
name="sycli-data-analyst",
|
|
428
428
|
)
|
sycli/models/config_models.py
CHANGED
|
@@ -76,6 +76,7 @@ class RLSettings(BaseModel):
|
|
|
76
76
|
convergence_strategy: str = "window" # window | ema | patience
|
|
77
77
|
convergence_threshold: float = 0.05
|
|
78
78
|
convergence_window_size: int = 10
|
|
79
|
+
convergence_near_target_margin: float = 0.02 # 接近目标时的容差(#8)
|
|
79
80
|
regression_penalty_weight: float = 0.3
|
|
80
81
|
auto_fix_compilation: bool = True
|
|
81
82
|
auto_fix_max_retries: int = 5
|
|
@@ -84,6 +85,13 @@ class RLSettings(BaseModel):
|
|
|
84
85
|
exploration_epsilon_start: float = 0.3
|
|
85
86
|
exploration_epsilon_end: float = 0.05
|
|
86
87
|
exploration_epsilon_decay: float = 0.95
|
|
88
|
+
accept_threshold: float = 0.8 # ACCEPT 决策的准确率阈值,默认 0.8(#4)
|
|
89
|
+
experience_keep_rounds: int = 30 # 经验存储保留的最近轮数(#15)
|
|
90
|
+
perturbation_restart: bool = True # 停滞但未达标时是否扰动重启(#10)
|
|
91
|
+
max_bandit_arms: int = 20 # Bandit 最大臂数量,超过时淘汰低效臂(#6)
|
|
92
|
+
direction_correction_enabled: bool = True # 是否启用方向纠偏(E2)
|
|
93
|
+
direction_correction_rollback_threshold: int = 3 # 连续 ROLLBACK 触发数
|
|
94
|
+
direction_correction_decline_rounds: int = 5 # 持续下降触发轮数
|
|
87
95
|
|
|
88
96
|
|
|
89
97
|
class MemorySettings(BaseModel):
|