├── .gitignore ├── imgs ├── logo.webp └── solve.png ├── main.py ├── readme.md ├── readme_en.md ├── requirements.txt └── src └── action ├── Calculate.py └── Programmer.py /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .DS_Store 3 | tmp/ 4 | data/ 5 | pdf/ 6 | .idea/ 7 | logs/ 8 | 9 | *.jsonl 10 | *.json 11 | *.txt 12 | localFile/ 13 | # ./generate_data/*.josnl 14 | # ./generate_data/*/*/*.josnl 15 | 16 | # Byte-compiled / optimized / DLL files 17 | __pycache__/ 18 | *.py[cod] 19 | *$py.class 20 | 21 | # C extensions 22 | *.so 23 | 24 | # merged_weights 25 | hf_merge/ 26 | 27 | # Distribution / packaging 28 | .Python 29 | build/ 30 | develop-eggs/ 31 | dist/ 32 | downloads/ 33 | eggs/ 34 | .eggs/ 35 | lib/ 36 | lib64/ 37 | parts/ 38 | sdist/ 39 | var/ 40 | wheels/ 41 | share/python-wheels/ 42 | *.egg-info/ 43 | .installed.cfg 44 | *.egg 45 | MANIFEST 46 | 47 | # PyInstaller 48 | # Usually these files are written by a python script from a template 49 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 50 | *.manifest 51 | *.spec 52 | 53 | # Installer logs 54 | pip-log.txt 55 | pip-delete-this-directory.txt 56 | 57 | # Unit test / coverage reports 58 | htmlcov/ 59 | .tox/ 60 | .nox/ 61 | .coverage 62 | .coverage.* 63 | .cache 64 | nosetests.xml 65 | coverage.xml 66 | *.cover 67 | *.py,cover 68 | .hypothesis/ 69 | .pytest_cache/ 70 | cover/ 71 | 72 | # Translations 73 | *.mo 74 | *.pot 75 | 76 | # Django stuff: 77 | *.log 78 | local_settings.py 79 | db.sqlite3 80 | db.sqlite3-journal 81 | 82 | # Flask stuff: 83 | instance/ 84 | .webassets-cache 85 | 86 | # Scrapy stuff: 87 | .scrapy 88 | 89 | # Sphinx documentation 90 | docs/_build/ 91 | 92 | # PyBuilder 93 | .pybuilder/ 94 | target/ 95 | 96 | # Jupyter Notebook 97 | .ipynb_checkpoints 98 | 99 | # IPython 100 | profile_default/ 101 | ipython_config.py 102 | 103 | # pyenv 104 | # For a library or package, you might want to ignore these files since the code is 105 | # intended to run in multiple environments; otherwise, check them in: 106 | # .python-version 107 | 108 | # pipenv 109 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 110 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 111 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 112 | # install all needed dependencies. 113 | #Pipfile.lock 114 | 115 | # poetry 116 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 117 | # This is especially recommended for binary packages to ensure reproducibility, and is more 118 | # commonly ignored for libraries. 119 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 120 | #poetry.lock 121 | 122 | # pdm 123 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 124 | #pdm.lock 125 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 126 | # in version control. 127 | # https://pdm.fming.dev/#use-with-ide 128 | .pdm.toml 129 | 130 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 131 | __pypackages__/ 132 | 133 | # Celery stuff 134 | celerybeat-schedule 135 | celerybeat.pid 136 | 137 | # SageMath parsed files 138 | *.sage.py 139 | 140 | # Environments 141 | .env 142 | .venv 143 | env/ 144 | venv/ 145 | ENV/ 146 | env.bak/ 147 | venv.bak/ 148 | 149 | # Spyder project settings 150 | .spyderproject 151 | .spyproject 152 | 153 | # Rope project settings 154 | .ropeproject 155 | 156 | # mkdocs documentation 157 | /site 158 | 159 | # mypy 160 | .mypy_cache/ 161 | .dmypy.json 162 | dmypy.json 163 | 164 | # Pyre type checker 165 | .pyre/ 166 | 167 | # pytype static type analyzer 168 | .pytype/ 169 | 170 | # Cython debug symbols 171 | cython_debug/ 172 | 173 | # PyCharm 174 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 175 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 176 | # and can be added to the global gitignore or merged into this file. For a more nuclear 177 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 178 | #.idea/ 179 | 180 | -------------------------------------------------------------------------------- /imgs/logo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangJinyu/Math-Multi-Agent/07c44240f490b3d7414d2087d809aa0998523d12/imgs/logo.webp -------------------------------------------------------------------------------- /imgs/solve.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XiangJinyu/Math-Multi-Agent/07c44240f490b3d7414d2087d809aa0998523d12/imgs/solve.png -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from openai import OpenAI 3 | from src.action.Calculate import WolframAlphaQuery, simple_calculate 4 | from src.action import Programmer 5 | print('ok') 6 | import openai 7 | import os 8 | import re 9 | import ast 10 | from datetime import datetime 11 | import time 12 | 13 | api_key_1 = "输入GPT密钥" 14 | base_url_1 = "输入转接地址" 15 | 16 | api_key_2 = "输入Claude密钥" 17 | base_url_2 = "输入转接地址" 18 | 19 | GPT_client = openai.Client(api_key=api_key_1, base_url=base_url_1) 20 | 21 | Claude_client = OpenAI(api_key=api_key_2, base_url=base_url_2) 22 | 23 | question = """ 24 | 3、A 与 B 二人进行 “ 抽鬼牌 ”游戏 。游戏开始时, A 手中有n张两两不同的牌 。 B 手上有n+1张牌,其中n张牌与 A 手中的牌相同,另一张为“鬼牌 ”,与其他所有牌都不同。游戏规则为: 25 | 26 | i) 双方交替从对方手中抽取一张牌, A 先从 B 手中抽取。 27 | 28 | ii) 若某位玩家抽到对方的牌与自己手中的某张牌一致,则将两张牌丢弃。 29 | 30 | iii) 最后剩一张牌(鬼牌)时,持有鬼牌的玩家为输家。 31 | 32 | 假设每一次抽牌从对方手上抽到任一张牌的概率都相同,请问下列n中哪个n使 A 的胜率最大?(单选题) 33 | 34 | 35 | A.n = 31 36 | 37 | 38 | B.n = 32 39 | 40 | 41 | C.n = 999 42 | 43 | 44 | D.n = 1000 45 | 46 | E.对所有的n,A 的胜率都一样 47 | """ 48 | 49 | 50 | def askLLM(messages, max_retries=10, delay=2): 51 | """ 52 | 参数: 53 | - messages: 发送到模型的消息 54 | - max_retries: 最大重试次数 55 | - delay: 重试之间的等待时间(秒) 56 | 57 | 返回: 58 | - 模型的响应内容,或在重试次数耗尽后返回None 59 | """ 60 | MODEL = "gpt-4-turbo-2024-04-09" 61 | attempt = 0 62 | 63 | while attempt < max_retries: 64 | try: 65 | response = GPT_client.chat.completions.create( 66 | model=MODEL, messages=messages, temperature=0.7, max_tokens=3000 67 | ) 68 | return response.choices[0].message.content 69 | except Exception as e: 70 | print(f"尝试 {attempt + 1}/{max_retries} 失败: {e}") 71 | time.sleep(delay) 72 | attempt += 1 73 | 74 | print("重试次数已耗尽,未能成功获取响应。") 75 | return None 76 | 77 | 78 | def actLLM(messages, the_model, max_retries=10, delay=2): 79 | attempt = 0 80 | 81 | while attempt < max_retries: 82 | try: 83 | 84 | if the_model == "GPT": 85 | MODEL = "gpt-4-turbo-2024-04-09" 86 | response = GPT_client.chat.completions.create( 87 | model=MODEL, messages=messages, temperature=0.7, max_tokens=3000 88 | ) 89 | return response.choices[0].message.content 90 | 91 | if the_model == "Claude": 92 | MODEL = "claude-3-opus-20240229" 93 | response = Claude_client.chat.completions.create( 94 | model=MODEL, messages=messages, temperature=0.7, max_tokens=3000 95 | ) 96 | return response.choices[0].message.content 97 | 98 | except Exception as e: 99 | print(f"尝试 {attempt + 1}/{max_retries} 失败: {e}") 100 | time.sleep(delay) 101 | attempt += 1 102 | 103 | print("重试次数已耗尽,未能成功获取响应。") 104 | return None 105 | 106 | 107 | def extract_code_block(code_block): 108 | match = re.search(r'```python(.*?)```', code_block, re.DOTALL) 109 | if match: 110 | code = match.group(1) 111 | try: 112 | code = code.encode('utf-8', 'ignore').decode('utf-8') 113 | except UnicodeEncodeError: 114 | code = code.encode('utf-8', 'ignore').decode('utf-8') 115 | return code 116 | else: 117 | return 'No code' 118 | 119 | 120 | def choose_action(list, message, question, the_model): 121 | action = list[0] 122 | query = list[1] 123 | 124 | if action == "wolfram_alpha": 125 | app_id = '输入你的wolfram_alpha key' 126 | Wolfram = WolframAlphaQuery(app_id) 127 | response_data = Wolfram.send_query(query) 128 | if response_data: 129 | answer = Wolfram.process_response(response_data) 130 | print(answer) 131 | return answer 132 | else: 133 | return "高级计算出错" 134 | 135 | if action == "simple_calculate": 136 | answer = simple_calculate(query) 137 | print("Result:", answer) 138 | return answer 139 | 140 | if action == "deep_thinking": 141 | think_message = [{"role": "system", 142 | "content": f"""你是一名数学指导,think step by step, 143 | 根据目前的对话信息及问题,进行深度分析,提出建设性的建议和问题。分析当前亟待解决的问题以及可能解决问题的一些路径,并尝试解决它们。不需要提出接下来需要调用的方法。尽可能多地使用LateX公式进行推导。 144 | """}, 145 | {"role": "user", "content": "历史对话内容如下:" + str(message) + "\n当前疑问:" + query}] 146 | answer = actLLM(think_message, the_model) 147 | return answer 148 | 149 | if action == "deduction": 150 | derivation_message = [{"role": "system", 151 | "content": f"""你是一名数学推导大师,think step by step, 152 | 根据目前的对话信息及问题,进行深度分析,严谨地使用LaTeX格式进行推导。 153 | 请先列出已知信息,然后再开始严谨的推导。 154 | 尽可能多地使用LateX公式进行推导。 155 | 不需要提出接下来需要调用的方法。 156 | """}, 157 | {"role": "user", "content": "历史对话内容如下:" + str(message) + "\n当前疑问:" + query}] 158 | answer = actLLM(derivation_message, the_model) 159 | return answer 160 | if action == "programmer": 161 | programmer = Programmer.Python_Programmer(f"问题:{question}\n目的:{query}") 162 | answer = programmer.solve_problem() 163 | for ans in answer: 164 | print(ans) 165 | return str(answer) 166 | 167 | if action == "Resolve": 168 | return True 169 | 170 | else: 171 | return "调用出错" 172 | 173 | 174 | def main(): 175 | # 获取当前时间 176 | start_time = datetime.now() 177 | print("开始时间:", start_time) 178 | # m为总共有几名角色进行解题 179 | m = 5 180 | # n为每个角色解题总共最多几轮 181 | n = 15 182 | # 生成m个思路,为不同角色提供不同的解题路径 183 | process_message = [{"role": "system", 184 | "content": rf""" 185 | 你是一名数学家,思考{m}个不同的解题方向和思路,使用List的格式输出。(输出格式为:[str,str,...]) 186 | 除了输出Python格式的List之外,不要输出任何其它的内容,你的回复将直接用于python程序的解析。 187 | 不要给出答案,而是给出不同解题的方向和思路。使用中文。 188 | """}, 189 | {"role": "user", "content": question}] 190 | way = askLLM(process_message) 191 | 192 | # 将m个思路解析成列表 193 | way_list = ast.literal_eval(way) 194 | print(way) 195 | 196 | total_summary = "" 197 | # 开启进程循环,这里的process遍历一个为解题一轮 198 | for process in range(m): 199 | # 选择方法Prompt 200 | choose_template = [{"role": "system", 201 | "content": r"""你将对对话解析,认为接下来需要调用的方法及输入的内容是什么?使用List的格式输出。(输出格式为:str,str) 202 | 可以调用的方法如下: 203 | ``` 204 | 1.wolfram_alpha : 该方法可以对复杂的方程或函数进行求解。Output Format:["wolfram_alpha","LaTeX格式表示的公式(只有公式,没有任何其它解释)"]. Example:["wolfram_alpha","\int_{0}^{5} \frac{x^{3}}{5+3x} \cdot \frac{x+5}{x+2}"] 205 | 2.simple_calculate : 该方法可以对简单的方程或函数进行求解。Output Format:["simple_calculate","LaTeX格式表示的公式(只有公式,没有任何其它解释)"]. Example:["simple_calculate","\int_{0}^{5} \frac{x^{3}}{5+3x} dx"] 206 | 3.deep_thinking : 该方法会对当前已知信息进行深度分析,并给出接下来可能的探索方向。Output Format:["deep_thinking","目前的困惑或疑问"]. Example:["deep_thinking","如何找到一条路径以最大化右转数量,并最小化总等待时间?"] 207 | 4.deduction :该方法启动链式思考,分析需要得到的结论,进行逐步推导.Output Format:["deduction","LaTeX格式表示需要推导的公式或用自然语言描述需要推导的问题"]. Example:["deduction","对已知的方程进行推导"] 208 | 5.programmer :调用程序员,为你撰写Python代码求解出所需要的问题的数值解。调用该方法时,请完整精确说出需求(包括优化的方法等),因为程序员只能看到题目,无法看到你之前的推导过程。Output Format:["programmer","LaTeX格式表示需要推导的公式或用自然语言描述需要推导的问题,涉及的公式或问题及参数必须详细准确,不能有信息缺失"]. Example:["programmer","用蒙特卡洛算法,写一个python代码,求解一个a=3,b=4,高为5的椭球的体积"] 209 | 6.Resolve : 该方法代表你已经得出了最终答案,将输出最终答案并结束这个问题的解答。Output Format:["Resolve","最终答案/结论或求解结果"]. Example:["Resolve","通过上述推导步骤,我给出该题最终结果为18"] 210 | ``` 211 | 只能调用一个方法,不要输出其它任何解释,只输出List. 212 | LaTeX格式表示的公式禁止使用\[,\],\(以及\)。请统一使用$$。 213 | Latex表达时,也请使用\来调用特殊格式,而不是双斜杠。因为你的公式直接被读取,不用担心被解析为转义字符。 214 | """}] 215 | # 主进程Prompt 216 | message = [{"role": "system", 217 | "content": r"""你是一名数学学家,请先拆解问题,然后think step by step, 218 | 可以使用Latex表达式,进行公式推导,分析当前已知信息,然后根据目前的推导,决策接下来需要进行的步骤。 219 | 注意,LaTeX格式表示的公式禁止使用\[,\],\(以及\)。请统一使用$$。 220 | 你的推导尽可能严谨,因为这个题目是一个全球数学竞赛题(允许进行编程求解)将你的每个推导结论转为疑问,来进行进一步思考和自问。 221 | 在最后的结尾,必须加上你认为下一步可以做的行动,可以调用以下行为: 222 | ``` 223 | 1.调用wolfram_alpha : 该方法可以对复杂的方程或函数进行求解,在说明使用该方法时,请一并使用LaTeX格式给出方程/公式 224 | 2.调用simple_calculate : 该方法可以对简单的方程或函数进行求解,如初等函数或简单积分微分,在说明使用该方法时,请一并使用LaTeX格式给出方程/公式 225 | 3.调用deep_thinking : 该方法会对当前已知信息进行深度分析,并给出接下来可能的探索方向 226 | 4.使用deduction : 该方法启动链式思考,分析需要得到的结论,进行逐步推导 227 | 5.使用programmer : 该方法调用程序员,是一个较通用的方法,为你撰写Python代码求解出所需要的问题的数值解,该方法应该作为优先级较高的方法。可以进行大规模计算以及解决复杂的数值解问题,也可在推导/证明等过程中方便试错和推测最终答案。你几乎可以在任何情况下使用这个方法。但如果较复杂的程序会导致超时(运行大于10分钟),这时可以考虑测试特殊情况或将范围缩小进行代码运行后参考,或者更换其它解决方式。 228 | 6.Resolve : 该方法代表你已经得出了最终答案,将输出最终答案并结束这个问题的解答。注意,这个方法不会帮助你解决问题,仅仅是在你确定最终答案后进行汇报的方式。 229 | ``` 230 | 每次只能选择一个行动,并使用着重符号标明需要调用的行为。 231 | 始终使用中文输出回答。 232 | """}, {"role": "user", "content": question}, {"role": "user", "content": way_list[process]}] 233 | 234 | answer_article = "" 235 | 236 | # 不同角色交替使用GPT和Claude来作为action的Model,增加答案可能性 237 | if process % 2 == 0: 238 | the_model = "Claude" 239 | else: 240 | the_model = "GPT" 241 | 242 | # 单进程循环,这里i遍历一个代表思考一轮(进行一轮行动) 243 | for i in range(n): 244 | # 每一轮开始时,都对当前形势进行思考,作为answer 245 | answer = askLLM(message) 246 | answer_article += answer + "\n" 247 | print(answer) 248 | message.append({"role": "assistant", "content": answer}) 249 | 250 | # 分析完当前形势后,挑选下一步的行动 251 | choose_message = choose_template 252 | choose_message.append( 253 | {"role": "user", "content": str(message[-1])}) 254 | choice = askLLM(choose_message) 255 | answer_article += choice + "\n" 256 | print(choice) 257 | 258 | # 将选择行动的str解析为list 259 | choice_list = ast.literal_eval(choice) 260 | 261 | # 将选择行动的list传入选择行动函数,调用对应的方法,返回结果 262 | answer = choose_action(choice_list, message, question, the_model) 263 | answer_article += str(answer) + "\n" 264 | print(answer) 265 | 266 | # 当得出角色认为的正确答案,则退出该角色的解题循环 267 | if answer is True: 268 | final_answer = choice_list[1] 269 | answer_article += "本循环最终答案:" + final_answer + "\n" 270 | print(final_answer) 271 | break 272 | 273 | # 将调用行动得到的答案加入到主进程中 274 | message.append({"role": "user", "content": answer}) 275 | 276 | # 当角色进程来到最后一轮时,强制给出目前认为最可能的答案 277 | if i == n - 1: 278 | message.append({"role": "user", 279 | "content": "现在,根据之前所有的推导和证明,给出你认为最可能的答案或结果,哪怕这个答案你还不够确定,并且不需要给出下一步行动。"}) 280 | answer = askLLM(message) 281 | answer_article += answer + "\n" 282 | print(answer) 283 | 284 | # 对刚刚角色的整个做题流程进行总结 285 | summary_message = [{"role": "system", 286 | "content": rf""" 287 | 根据题目及完整解题流程,给出一个精简的解题流程总结,step by step,给出每一步的解题的说明。你的总结应该尽可能专业,并包含涉及到的公式。也需要明确给出解题过程中最终得出的结论(如果严谨得出,信心程度为强,如果只是推测或其它方式,则为一般或弱)。 288 | output format: 289 | ``` 290 | 1. 291 | 2. 292 | 3. 293 | ... 294 | 最终结论:xxx 295 | 信心程度:强/一般/弱 296 | ``` 297 | """}, 298 | {"role": "user", "content": f"\n原问题:{question}\n\n解题过程:{answer_article}"}] 299 | 300 | model = "Claude" 301 | summary = actLLM(summary_message, model) 302 | 303 | summary = f"关键解题步骤摘要及结论:\n\n{summary}\n\n" 304 | 305 | # 将每一轮的结果都封装到一起 306 | total_summary += f"# 第{process + 1}名角色解题关键过程及结果为:\n\n{summary}" 307 | 308 | # 将关键结果总结和完整解题步骤整理在一起,方便查看 309 | answer_article = summary + answer_article 310 | 311 | # 将完整结果存入txt文件中 312 | with open(f"answer_article_{process + 1}.txt", "w", encoding="utf-8") as file: 313 | file.write(answer_article) 314 | 315 | # 评审角色对每一轮(即每个角色)的结果进行整理分析,评选出最可能的结果 316 | review_message = [{"role": "system", 317 | "content": rf""" 318 | 根据问题及多个角色的推理过程及得到的答案,先给出你的总结和分析,step by step,最终给出你认为最可能的正确答案。 319 | 数值解,理论推导,解析解等方法都可以作为最终答案,主要关注给出每个答案的人数及过程是否正确。 320 | """}, 321 | {"role": "user", "content": f"\n问题:{question}\n\n解题结果:{total_summary}"}] 322 | print(total_summary) 323 | review = askLLM(review_message) 324 | 325 | print(review) 326 | 327 | # 获取当前时间 328 | end_time = datetime.now() 329 | print("开始时间:", start_time) 330 | print("结束时间:", end_time) 331 | 332 | # 计算耗时 333 | time_difference = end_time - start_time 334 | total_seconds = time_difference.total_seconds() 335 | # 将总秒数转换为分钟 336 | total_minutes = total_seconds / 60 337 | print("耗时分钟:", total_minutes) 338 | 339 | # 将最终评审结果整理到txt文件中 340 | with open(f"Final_answer.txt", "w", encoding="utf-8") as file: 341 | file.write(review) 342 | 343 | 344 | if __name__ == "__main__": 345 | main() -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Math-Multi-Agent 4 | 5 | > 2024年阿里全球数学竞赛AI赛道全球第2名项目(特工宇宙)解决方案。 6 | 7 | 8 | 9 | [![Forks][forks-shield]][forks-url] 10 | [![Stargazers][stars-shield]][stars-url] 11 | [![Issues][issues-shield]][issues-url] 12 | 13 | 14 | 15 | 16 |
17 | 18 |

19 | 20 | Logo 21 | 22 | 23 |

Math-Multi-Agent

24 |

25 | 简体中文 | English 26 |
27 | 报告Bug 28 | · 29 | 提出新特性 30 |

31 | 32 |

33 | 34 | ## 🎯News 35 | - [2024.6] 🎉🎉我们开源了我们的所有代码! 36 | 37 | ## 目录 38 | 39 | - [文件目录说明](#🌴文件目录说明) 40 | - [如何运行](#🎨如何运行) 41 | - [项目原理](#🛠项目原理) 42 | - [项目成员](#🤗成员) 43 | - [鸣谢](#鸣谢) 44 | 45 | ### 🌴文件目录说明 46 | ``` 47 | filetree 48 | ├── imgs/:存放图像资源 49 | ├── LICENSE.txt 50 | ├── README.md 51 | ├── /src/:核心代码 52 | │ ├── /action/ 53 | │ │ ├── Calculate.py 54 | │ │ └── Programmer.py 55 | ├── main.py:主函数 56 | ├── requirements.txt: 列出了项目所需的Python库及其版本。 57 | ``` 58 | 59 | ### 🎨如何运行 60 | 首先克隆仓库: 61 | ```sh 62 | git clone https://github.com/isaacJinyu/Math-Multi-Agent.git 63 | ``` 64 | 65 | 然后依次执行如下步骤: 66 | 1. 确保已安装所有列出在 `requirements.txt`中的Python库。 67 | ``` 68 | pip install -r requirements.txt 69 | ``` 70 | 2. 在`main.py`及`action/Programmer.py`中填入你的密钥和中转地址,需要填写OpenAI和Anthropic两家公司的密钥。 71 | ``` 72 | api_key = "输入对应公司的密钥" 73 | base_url = "输入中转地址(若直连,则不使用base_url)" 74 | ``` 75 | 3. 在`main.py`下的`choose_action`的`wolfram_alpha`这个action处的app_id输入你的Wolfram|Alpha API密钥 76 | ``` 77 | app_id = '输入Wolfram|Alpha API密钥' 78 | ``` 79 | 4. 在`main.py`下的`question`中填入你的数学问题 80 | 5. 在命令行中运行 `python main.py`来启动项目。 81 | 6. 查看输出的 `Final_answer.txt`文件获取最终答案。 82 | 7. 查看各个角色的解题过程,可以在当前目录下找到以角色编号命名的 `.txt`文件。 83 | 84 | 85 | ### 🛠项目原理 86 |

87 | Logo 88 |

89 | 90 | ### 🤗成员 91 | - 向劲宇:西南交通大学 92 | - 王训志:南开大学 93 | - 费清语:布里斯托大学毕业,目前腾讯产品经理 94 | - 张紫璆:澳门城市大学 95 | 96 | ### 鸣谢 97 | - 特工宇宙 98 | - … 99 | 100 | 101 | 102 | [your-project-path]:isaacJinyu/Math-Multi-Agent 103 | [contributors-shield]: https://img.shields.io/github/contributors/isaacJinyu/Math-Multi-Agent/graphs.svg?style=flat-square 104 | [contributors-url]: https://github.com/isaacJinyu/Math-Multi-Agent/graphs/contributors 105 | [forks-shield]: https://img.shields.io/github/forks/isaacJinyu/Math-Multi-Agent.svg?style=flat-square 106 | [forks-url]: https://github.com/isaacJinyu/Math-Multi-Agent/network/members 107 | [stars-shield]: https://img.shields.io/github/stars/isaacJinyu/Math-Multi-Agent.svg?style=flat-square 108 | [stars-url]: https://github.com/isaacJinyu/Math-Multi-Agent/stargazers 109 | [issues-shield]: https://img.shields.io/github/issues/isaacJinyu/Math-Multi-Agent.svg?style=flat-square 110 | [issues-url]: https://img.shields.io/github/issues/isaacJinyu/Math-Multi-Agent.svg 111 | [license-shield]: https://img.shields.io/github/license/isaacJinyu/Math-Multi-Agent.svg?style=flat-square 112 | [license-url]: ./LICENSE.txt 113 | 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /readme_en.md: -------------------------------------------------------------------------------- 1 | # Math-Multi-Agent 2 | 3 | > Solution for the project "Special Agent Universe," which won the 2nd place globally in the AI track at the 2024 Alibaba Global Mathematics Competition. 4 | 5 | 6 | 7 | 8 | [![Forks][forks-shield]][forks-url] 9 | [![Stargazers][stars-shield]][stars-url] 10 | [![Issues][issues-shield]][issues-url] 11 | 12 | 13 | 14 | 15 |
16 | 17 |

18 | 19 | Logo 20 | 21 | 22 |

Math-Multi-Agent

23 |

24 | English | 简体中文 25 |
26 | Report Bug 27 | · 28 | Request Feature 29 |

30 | 31 |

32 | 33 | ## 🎯News 34 | - [2024.6] 🎉🎉 We have open-sourced all our code! 35 | 36 | ## Table of Contents 37 | 38 | - [Directory Structure](#🌴directory-structure) 39 | - [How to Run](#🎨how-to-run) 40 | - [Project Principle](#🛠project-principle) 41 | - [Team Members](#🤗team-members) 42 | - [Acknowledgements](#acknowledgements) 43 | 44 | ### 🌴Directory Structure 45 | ``` 46 | filetree 47 | ├── imgs/: Image resources 48 | ├── LICENSE.txt 49 | ├── README.md 50 | ├── /src/: Core code 51 | │ ├── /action/ 52 | │ │ ├── Calculate.py 53 | │ │ └── Programmer.py 54 | ├── main.py: Main function 55 | ├── requirements.txt: Lists the required Python libraries and their versions. 56 | ``` 57 | 58 | ### 🎨How to Run 59 | First, clone the repository: 60 | ```sh 61 | git clone https://github.com/isaacJinyu/Math-Multi-Agent.git 62 | ``` 63 | Then follow these steps: 64 | 1. Ensure all libraries listed in `requirements.txt` are installed. 65 | ``` 66 | pip install -r requirements.txt 67 | ``` 68 | 2. Enter your keys and proxy address in `main.py` and `action/Programmer.py`, including keys for OpenAI and Anthropic. 69 | ``` 70 | api_key = "Enter the respective company's key" 71 | base_url = "Enter the proxy address (if not connecting directly, do not use base_url)" 72 | ``` 73 | 3. Enter your Wolfram|Alpha API key in the wolfram_alpha action in `main.py` 74 | ``` 75 | app_id = 'Enter Wolfram|Alpha API key' 76 | ``` 77 | 4. Enter your math question in question in `main.py`. 78 | 5. Run `python main.py` in the command line to start the project. 79 | 6. Check the `Final_answer.txt` file for the final answer. 80 | 7. To see the problem-solving process of each agent, find ``.txt` files named after the agent numbers in the current directory. 81 | 82 | ### 🛠Project Principle 83 |

84 | Logo 85 |

86 | 87 | ### 🤗Team Members 88 | - Xiang Jinyu: Southwest Jiaotong University 89 | - Wang Xunzhi: Nankai University 90 | - Fei Qingyu: Graduate of the University of Bristol, currently a Product Manager at Tencent 91 | - Zhang Ziqiu: City University of Macau 92 | 93 | ### Acknowledgements 94 | - Special Agent Universe 95 | - ... 96 | 97 | 98 | 99 | 100 | [your-project-path]:isaacJinyu/Math-Multi-Agent 101 | [contributors-shield]: https://img.shields.io/github/contributors/isaacJinyu/Math-Multi-Agent/graphs.svg?style=flat-square 102 | [contributors-url]: https://github.com/isaacJinyu/Math-Multi-Agent/graphs/contributors 103 | [forks-shield]: https://img.shields.io/github/forks/isaacJinyu/Math-Multi-Agent.svg?style=flat-square 104 | [forks-url]: https://github.com/isaacJinyu/Math-Multi-Agent/network/members 105 | [stars-shield]: https://img.shields.io/github/stars/isaacJinyu/Math-Multi-Agent.svg?style=flat-square 106 | [stars-url]: https://github.com/isaacJinyu/Math-Multi-Agent/stargazers 107 | [issues-shield]: https://img.shields.io/github/issues/isaacJinyu/Math-Multi-Agent.svg?style=flat-square 108 | [issues-url]: https://img.shields.io/github/issues/isaacJinyu/Math-Multi-Agent.svg 109 | [license-shield]: https://img.shields.io/github/license/isaacJinyu/Math-Multi-Agent.svg?style=flat-square 110 | [license-url]: ./LICENSE.txt 111 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | openai==1.14.2 2 | numpy==1.26.4 3 | scipy==1.12.0 4 | pandas==2.2.1 5 | sympy==1.12 6 | statsmodels==0.14.1 7 | scikit-learn==1.4.1.post1 8 | -------------------------------------------------------------------------------- /src/action/Calculate.py: -------------------------------------------------------------------------------- 1 | import json 2 | import urllib.parse 3 | import requests 4 | from sympy.parsing.latex import parse_latex 5 | 6 | class WolframAlphaQuery: 7 | def __init__(self, app_id): 8 | self.app_id = app_id 9 | 10 | def clean_json(self, json_str): 11 | # 清理JSON数据中的转义字符 12 | json_str = json_str.replace('\n', '').replace('\\/', '/') 13 | json_str = json_str.replace('\\t', '').replace('\\r', '').replace('\\b', '').replace('\\f', '') 14 | return json_str 15 | 16 | def send_query(self, input_expression): 17 | # 清理输入表达式 18 | input_expression = urllib.parse.quote(input_expression) 19 | # 构建查询URL 20 | query_url = f'http://api.wolframalpha.com/v2/query?appid={self.app_id}&input={input_expression}&output=json' 21 | # 发送GET请求 22 | response = requests.get(query_url) 23 | # 检查请求是否成功 24 | if response.status_code == 200: 25 | # 解析响应内容 26 | data = response.json() 27 | return data 28 | else: 29 | print(f"Request failed with status code {response.status_code}") 30 | return None 31 | 32 | def process_response(self, data): 33 | # 初始化Markdown字符串 34 | markdown_content = "" 35 | 36 | # 提取并结构化信息 37 | if data["queryresult"]["success"] is True: 38 | for pod in data["queryresult"]["pods"]: 39 | pod_title = pod.get("title", "Untitled") 40 | subpods = pod.get("subpods", []) 41 | if subpods: 42 | subpod_plaintext = subpods[0].get("plaintext", "No plaintext available") 43 | 44 | # 将提取的信息添加到Markdown字符串 45 | markdown_content += f"## {pod_title}\n" 46 | markdown_content += f"{subpod_plaintext}\n\n" 47 | return markdown_content 48 | else: 49 | return "WolframAlpha计算失败,请尝试更换表达式或更换方法" 50 | 51 | def simple_calculate(latex_expression): 52 | # 解析 LaTeX 表达式为 SymPy 表达式 53 | expression = parse_latex(latex_expression) 54 | 55 | # 尝试计算表达式的值 56 | try: 57 | result = expression.evalf() # 否则直接计算 58 | except Exception as e: 59 | return f"Error: {e}" 60 | 61 | return result 62 | 63 | def main(): 64 | # 简单计算使用示例 65 | latex_expression = r"\int_{0}^{5} \frac{x^{3}}{5+3x} dx" 66 | 67 | # 调用 simple_calculate 函数进行计算 68 | result = simple_calculate(latex_expression) 69 | 70 | # 打印结果 71 | print("simple_calculate 函数\n") 72 | print(f"函数/方程: {latex_expression}") 73 | print(f"Result: {result}") 74 | 75 | # 复杂计算使用示例 76 | app_id = '输入Wolfram|Alpha API密钥' # Wolfram|Alpha API密钥 77 | query = WolframAlphaQuery(app_id) 78 | 79 | # 查询表达式 80 | input_expression = r"\int_{0}^{5} \frac{x^{3}}{5+3x} \cdot \frac{x+5}{x+2}" 81 | response_data = query.send_query(input_expression) 82 | 83 | print("\nWolframAlphaQuery方法\n") 84 | # 处理并打印Markdown内容 85 | if response_data: 86 | markdown_content = query.process_response(response_data) 87 | print(markdown_content) 88 | 89 | if __name__ == "__main__": 90 | main() 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /src/action/Programmer.py: -------------------------------------------------------------------------------- 1 | import time 2 | import re 3 | from subprocess import Popen, PIPE, TimeoutExpired 4 | import os 5 | from openai import OpenAI 6 | import sys 7 | 8 | # 获取当前Python解释器的路径 9 | current_python_executable = sys.executable 10 | 11 | 12 | class Python_Programmer: 13 | def __init__(self, problem): 14 | self.problem = problem 15 | self.client = OpenAI( 16 | base_url="输入中转地址", 17 | api_key="输入GPT密钥", 18 | ) 19 | 20 | def run_code(self, code, timeout=600): 21 | with open("solve_code.py", "w", encoding='utf-8') as f: 22 | f.write(code) 23 | try: 24 | # 使用当前Python解释器运行脚本 25 | process = Popen([current_python_executable, "solve_code.py"], stdout=PIPE, stderr=PIPE) 26 | try: 27 | stdout, stderr = process.communicate(timeout=timeout) 28 | if process.returncode != 0: 29 | return "Error", stderr.decode("utf-8", errors='ignore') 30 | else: 31 | return "Success", stdout.decode("utf-8", errors='ignore') 32 | except TimeoutExpired: 33 | process.terminate() # 使用 terminate 来替代 os.kill 34 | stdout, stderr = process.communicate() 35 | return "Timeout", "The code execution exceeded the timeout limit.Try to optimize the code, algorithm, or other techniques to reduce the execution time." 36 | except Exception as e: 37 | return "Error", str(e) 38 | 39 | def askLLM(self, messages, max_retries=10, delay=2): 40 | """ 41 | 参数: 42 | - messages: 发送到模型的消息 43 | - max_retries: 最大重试次数 44 | - delay: 重试之间的等待时间(秒) 45 | 46 | 返回: 47 | - 模型的响应内容,或在重试次数耗尽后返回None 48 | """ 49 | MODEL = "gpt-4-turbo-2024-04-09" 50 | attempt = 0 51 | 52 | while attempt < max_retries: 53 | try: 54 | response = self.client.chat.completions.create( 55 | model=MODEL, messages=messages, temperature=0.7, max_tokens=3000 56 | ) 57 | return response.choices[0].message.content 58 | except Exception as e: 59 | print(f"尝试 {attempt + 1}/{max_retries} 失败: {e}") 60 | time.sleep(delay) 61 | attempt += 1 62 | 63 | print("重试次数已耗尽,未能成功获取响应。") 64 | return None 65 | 66 | def extract_code_block(self, code_block): 67 | match = re.search(r'```python(.*?)```', code_block, re.DOTALL) 68 | if match: 69 | code = match.group(1) 70 | try: 71 | code = code.encode('utf-8', 'ignore').decode('utf-8') 72 | except UnicodeEncodeError: 73 | code = code.encode('utf-8', 'ignore').decode('utf-8') 74 | return code 75 | else: 76 | return 'No code' 77 | 78 | def solve_problem(self): 79 | messages = [dict(role="system", 80 | content="You are an expert Python programmer. Your task is to write Python code based on the " 81 | "user's requirements. Make sure to add appropriate explanations and your personal " 82 | "thought process to your code. Also, all of the codes should be encapsulated in " 83 | "Python code blocks. " 84 | "the package you can use : numpy,scipy,pandas,sympy,statsmodels,scikit-learn ." 85 | "If you try to import another external package and encounter an error, don't say that it" 86 | "can't be imported. Instead, try to write a new code that avoids this issue. " 87 | "Always output complete code rather than just giving advice or partially modified " 88 | "code, because your code will be run directly. " 89 | "Include test cases with your code, if they are necessary for immediate execution to " 90 | "check for possible bugs. " 91 | "In your response, only the code that needs to be run should be wrapped in multiline " 92 | "code blocks. No other multiline code blocks should appear." 93 | "Your code needs to print out the output after running." 94 | "Your code should not print error messages." 95 | "Output format:" 96 | "```python" 97 | "{Your code}" 98 | "```" 99 | "使用中文输出回答。"), 100 | dict(role="user", content=self.problem)] 101 | result = [] 102 | i = 0 103 | while i < 3: 104 | text = self.askLLM(messages) 105 | code = self.extract_code_block(text) 106 | status, output = self.run_code(code) 107 | 108 | d = {"role": "assistant", "content": text + status + output} 109 | result.append(d["content"]) 110 | messages.append(d) 111 | 112 | if status == "Success": 113 | return result 114 | 115 | time.sleep(2) 116 | # if i > 1: 117 | # del messages[-1] 118 | i += 1 119 | 120 | return result 121 | 122 | 123 | def main(): 124 | programmer = Python_Programmer("用蒙特卡洛算法,写一个python代码,求解一个a=3,b=4,高为5的椭球的体积") 125 | print(programmer.solve_problem()) 126 | 127 | 128 | if __name__ == "__main__": 129 | main() 130 | --------------------------------------------------------------------------------