KaiWu commited on
Commit
ab037c6
·
0 Parent(s):

initial commit

Browse files
Files changed (4) hide show
  1. .env +61 -0
  2. AGENTS.md +5 -0
  3. agent_loop.py +128 -0
  4. requirements.txt +2 -0
.env ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # API Key (required)
2
+ # Get yours at: https://console.anthropic.com/
3
+ ANTHROPIC_API_KEY=sk-9wTK68ZO58_s4w5_SUsR7w
4
+
5
+ # Model ID (required)
6
+ MODEL_ID=gemini-3-flash-thinking
7
+
8
+ # Base URL (optional, for Anthropic-compatible providers)
9
+ ANTHROPIC_BASE_URL=https://coding.qunhequnhe.com
10
+
11
+ # =============================================================================
12
+ # Anthropic-compatible providers
13
+ #
14
+ # Provider MODEL_ID SWE-bench TB2 Base URL
15
+ # --------------- -------------------- --------- ------ -------------------
16
+ # Anthropic claude-sonnet-4-6 79.6% 59.1% (default)
17
+ # MiniMax MiniMax-M2.5 80.2% - see below
18
+ # GLM (Zhipu) glm-5 77.8% - see below
19
+ # Kimi (Moonshot) kimi-k2.5 76.8% - see below
20
+ # DeepSeek deepseek-chat 73.0% - see below
21
+ # (V3.2)
22
+ #
23
+ # SWE-bench = SWE-bench Verified (Feb 2026)
24
+ # TB2 = Terminal-Bench 2.0 (Feb 2026)
25
+ # =============================================================================
26
+
27
+ # ---- International ----
28
+
29
+ # MiniMax https://www.minimax.io
30
+ # ANTHROPIC_BASE_URL=https://api.minimax.io/anthropic
31
+ # MODEL_ID=MiniMax-M2.5
32
+
33
+ # GLM (Zhipu) https://z.ai
34
+ # ANTHROPIC_BASE_URL=https://api.z.ai/api/anthropic
35
+ # MODEL_ID=glm-5
36
+
37
+ # Kimi (Moonshot) https://platform.moonshot.ai
38
+ # ANTHROPIC_BASE_URL=https://api.moonshot.ai/anthropic
39
+ # MODEL_ID=kimi-k2.5
40
+
41
+ # DeepSeek https://platform.deepseek.com
42
+ # ANTHROPIC_BASE_URL=https://api.deepseek.com/anthropic
43
+ # MODEL_ID=deepseek-chat
44
+
45
+ # ---- China mainland ----
46
+
47
+ # MiniMax https://platform.minimax.io
48
+ # ANTHROPIC_BASE_URL=https://api.minimaxi.com/anthropic
49
+ # MODEL_ID=MiniMax-M2.5
50
+
51
+ # GLM (Zhipu) https://open.bigmodel.cn
52
+ # ANTHROPIC_BASE_URL=https://open.bigmodel.cn/api/anthropic
53
+ # MODEL_ID=glm-5
54
+
55
+ # Kimi (Moonshot) https://platform.moonshot.cn
56
+ # ANTHROPIC_BASE_URL=https://api.moonshot.cn/anthropic
57
+ # MODEL_ID=kimi-k2.5
58
+
59
+ # DeepSeek (no regional split, same endpoint globally)
60
+ # ANTHROPIC_BASE_URL=https://api.deepseek.com/anthropic
61
+ # MODEL_ID=deepseek-chat
AGENTS.md ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ # 本项目致力于搭建一个基于终端交互的agent,用于生成CAD模型或3D模型
2
+ # 这是一个python项目,使用conda管理虚拟环境,虚拟环境名字是:aigc
3
+ # 开发过程中,不要过度设计,少即是多
4
+ # agent迭代方式仍会遵循敏捷迭代,也就是每个版本都独立、可交付
5
+ # 推理、设计、改动等过程中任何一环,出现拿不准的情况,不要擅自主张,可以拆解问题或向用户提问
agent_loop.py ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import subprocess
3
+ from pathlib import Path
4
+
5
+ from anthropic import Anthropic
6
+ from dotenv import load_dotenv
7
+
8
+ load_dotenv(override=True)
9
+
10
+ if os.getenv("ANTHROPIC_BASE_URL"):
11
+ os.environ.pop("ANTHROPIC_AUTH_TOKEN", None)
12
+
13
+ WORKDIR = Path.cwd()
14
+ client = Anthropic(base_url=os.getenv("ANTHROPIC_BASE_URL"))
15
+ MODEL = os.environ["MODEL_ID"]
16
+
17
+ SYSTEM = f"You are a coding agent at {WORKDIR}. Use tools to solve tasks. Act, don't explain."
18
+
19
+
20
+ def safe_path(p: str) -> Path:
21
+ path = (WORKDIR / p).resolve()
22
+ if not path.is_relative_to(WORKDIR):
23
+ raise ValueError(f"Path escapes workspace: {p}")
24
+ return path
25
+
26
+
27
+ def run_bash(command: str) -> str:
28
+ dangerous = ["rm -rf /", "sudo", "shutdown", "reboot", "> /dev/"]
29
+ if any(d in command for d in dangerous):
30
+ return "Error: Dangerous command blocked"
31
+ try:
32
+ r = subprocess.run(command, shell=True, cwd=WORKDIR,
33
+ capture_output=True, text=True, timeout=120)
34
+ out = (r.stdout + r.stderr).strip()
35
+ return out[:50000] if out else "(no output)"
36
+ except subprocess.TimeoutExpired:
37
+ return "Error: Timeout (120s)"
38
+
39
+
40
+ def run_read(path: str, limit: int = None) -> str:
41
+ try:
42
+ text = safe_path(path).read_text()
43
+ lines = text.splitlines()
44
+ if limit and limit < len(lines):
45
+ lines = lines[:limit] + [f"... ({len(lines) - limit} more lines)"]
46
+ return "\n".join(lines)[:50000]
47
+ except Exception as e:
48
+ return f"Error: {e}"
49
+
50
+
51
+ def run_write(path: str, content: str) -> str:
52
+ try:
53
+ fp = safe_path(path)
54
+ fp.parent.mkdir(parents=True, exist_ok=True)
55
+ fp.write_text(content)
56
+ return f"Wrote {len(content)} bytes to {path}"
57
+ except Exception as e:
58
+ return f"Error: {e}"
59
+
60
+
61
+ def run_edit(path: str, old_text: str, new_text: str) -> str:
62
+ try:
63
+ fp = safe_path(path)
64
+ content = fp.read_text()
65
+ if old_text not in content:
66
+ return f"Error: Text not found in {path}"
67
+ fp.write_text(content.replace(old_text, new_text, 1))
68
+ return f"Edited {path}"
69
+ except Exception as e:
70
+ return f"Error: {e}"
71
+
72
+
73
+ # -- The dispatch map: {tool_name: handler} --
74
+ TOOL_HANDLERS = {
75
+ "bash": lambda **kw: run_bash(kw["command"]),
76
+ "read_file": lambda **kw: run_read(kw["path"], kw.get("limit")),
77
+ "write_file": lambda **kw: run_write(kw["path"], kw["content"]),
78
+ "edit_file": lambda **kw: run_edit(kw["path"], kw["old_text"], kw["new_text"]),
79
+ }
80
+
81
+ TOOLS = [
82
+ {"name": "bash", "description": "Run a shell command.",
83
+ "input_schema": {"type": "object", "properties": {"command": {"type": "string"}}, "required": ["command"]}},
84
+ {"name": "read_file", "description": "Read file contents.",
85
+ "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "limit": {"type": "integer"}}, "required": ["path"]}},
86
+ {"name": "write_file", "description": "Write content to file.",
87
+ "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
88
+ {"name": "edit_file", "description": "Replace exact text in file.",
89
+ "input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "old_text": {"type": "string"}, "new_text": {"type": "string"}}, "required": ["path", "old_text", "new_text"]}},
90
+ ]
91
+
92
+
93
+ def agent_loop(messages: list):
94
+ while True:
95
+ response = client.messages.create(
96
+ model=MODEL, system=SYSTEM, messages=messages,
97
+ tools=TOOLS, max_tokens=8000,
98
+ )
99
+ messages.append({"role": "assistant", "content": response.content})
100
+ if response.stop_reason != "tool_use":
101
+ return
102
+ results = []
103
+ for block in response.content:
104
+ if block.type == "tool_use":
105
+ handler = TOOL_HANDLERS.get(block.name)
106
+ output = handler(**block.input) if handler else f"Unknown tool: {block.name}"
107
+ print(f"> {block.name}: {output[:200]}")
108
+ results.append({"type": "tool_result", "tool_use_id": block.id, "content": output})
109
+ messages.append({"role": "user", "content": results})
110
+
111
+
112
+ if __name__ == "__main__":
113
+ history = []
114
+ while True:
115
+ try:
116
+ query = input("\033[36ms02 >> \033[0m")
117
+ except (EOFError, KeyboardInterrupt):
118
+ break
119
+ if query.strip().lower() in ("q", "exit", ""):
120
+ break
121
+ history.append({"role": "user", "content": query})
122
+ agent_loop(history)
123
+ response_content = history[-1]["content"]
124
+ if isinstance(response_content, list):
125
+ for block in response_content:
126
+ if hasattr(block, "text"):
127
+ print(block.text)
128
+ print()
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ anthropic
2
+ python-dotenv