diff --git a/Co-creation-projects/1zrj-DataAnalysisAgent/main.ipynb b/Co-creation-projects/1zrj-DataAnalysisAgent/main.ipynb index 9f265bcc..25d65c00 100644 --- a/Co-creation-projects/1zrj-DataAnalysisAgent/main.ipynb +++ b/Co-creation-projects/1zrj-DataAnalysisAgent/main.ipynb @@ -37,7 +37,7 @@ "\n", "# 配置LLM参数\n", "os.environ[\"LLM_MODEL_ID\"] = \"Qwen/Qwen3-8B\"\n", - "os.environ[\"LLM_API_KEY\"] = \"ms-9382e20f-96c2-456a-b609-af5c81201066\"\n", + "os.environ[\"LLM_API_KEY\"] = \"ms-2e9941ca-312d-4707-b47c-8a0e9d0a8ce7\"\n", "os.environ[\"LLM_BASE_URL\"] = \"https://api-inference.modelscope.cn/v1/\"\n", "os.environ[\"LLM_TIMEOUT\"] = \"60\"\n", "\n", @@ -53,7 +53,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -157,7 +157,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 11, "metadata": {}, "outputs": [ { @@ -244,7 +244,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 12, "metadata": {}, "outputs": [ { @@ -315,7 +315,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "metadata": {}, "outputs": [ { @@ -464,153 +464,35 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "=== 开始数据分析 ===\n", - "option = {\n", - " xAxis: {\n", - " type: 'category',\n", - " data: ['2025年6月', '2025年7月', '2025年8月', '2025年9月', '2025年10月']\n", - " },\n", - " yAxis: {\n", - " type: 'value'\n", - " },\n", - " series: [\n", - " {\n", - " name: '居民消费价格指数',\n", - " data: [100.1, 100.0, 99.6, 99.7, 100.2],\n", - " type: 'line'\n", - " },\n", - " {\n", - " name: '食品烟酒类居民消费价格指数',\n", - " data: [100.1, 99.2, 97.5, 97.4, 98.4],\n", - " type: 'line'\n", - " },\n", - " {\n", - " name: '衣着类居民消费价格指数',\n", - " data: [101.6, 101.7, 101.8, 101.7, 101.7],\n", - " type: 'line'\n", - " },\n", - " {\n", - " name: '居住类居民消费价格指数',\n", - " data: [100.1, 100.1, 100.1, 100.1, 100.1],\n", - " type: 'line'\n", - " },\n", - " {\n", - " name: '生活用品及服务类居民消费价格指数',\n", - " data: [100.7, 101.2, 101.8, 102.2, 101.9],\n", - " type: 'line'\n", - " },\n", - " {\n", - " name: '交通通信类居民消费价格指数',\n", - " data: [96.3, 96.9, 97.6, 98.0, 98.5],\n", - " type: 'line'\n", - " },\n", - " {\n", - " name: '教育文化娱乐类居民消费价格指数',\n", - " data: [101.0, 100.9, 101.0, 100.8, 100.9],\n", - " type: 'line'\n", - " },\n", - " {\n", - " name: '医疗保健类居民消费价格指数',\n", - " data: [100.4, 100.5, 100.9, 101.1, 101.4],\n", - " type: 'line'\n", - " },\n", - " {\n", - " name: '其他用品及服务类居民消费价格指数',\n", - " data: [108.1, 108.0, 108.6, 109.9, 112.8],\n", - " type: 'line'\n", - " },\n", - " {\n", - " name: '非食品居民消费价格指数',\n", - " data: [100.1, 100.3, 100.5, 100.7, 100.9],\n", - " type: 'line'\n", - " },\n", - " {\n", - " name: '消费品居民消费价格指数',\n", - " data: [99.8, 99.6, 99.0, 99.2, 99.8],\n", - " type: 'line'\n", - " },\n", - " {\n", - " name: '服务居民消费价格指数',\n", - " data: [100.5, 100.5, 100.6, 100.6, 100.8],\n", - " type: 'line'\n", - " },\n", - " {\n", - " name: '不包括食品和能源居民消费价格指数',\n", - " data: [100.7, 100.8, 100.9, 101.0, 101.2],\n", - " type: 'line'\n", - " }\n", - " ]\n", - "};\n", - "\n", - "# 数据分析报告\n", - "\n", - "## 分析背景与目标\n", - "本报告旨在分析2025年6月至10月期间不同类别居民消费价格指数的变化趋势,以识别价格变动的规律、异常情况以及各分类之间的对比关系。通过可视化图表和统计分析,为政策制定者或相关机构提供决策支持。\n", - "\n", - "## 关键的发现\n", - "1. **整体趋势**:从2025年6月至10月,居民消费价格指数(CPI)总体呈上升趋势,由100.1升至100.2。\n", - "2. **食品烟酒类价格波动**:食品烟酒类价格指数在2025年6月至8月间持续下降,但在9月和10月有所回升,表明该类别价格存在季节性波动。\n", - "3. **其他用品及服务类价格显著上涨**:该类别价格指数在2025年10月达到112.8,是所有类别中涨幅最大的,显示出价格上涨压力较大。\n", - "4. **交通通信类价格稳步上升**:该类别价格指数从96.3上升至98.5,表现出持续增长的趋势。\n", - "5. **非食品居民消费价格指数略有上升**:该指数从100.1上升至100.9,显示非食品类价格整体呈温和上升趋势。\n", - "6. **消费品居民消费价格指数波动较小**:该指数在2025年6月至10月间基本保持稳定,波动幅度不大。\n", - "7. **服务类价格指数相对稳定**:服务类价格指数在2025年6月至10月间小幅上升,但整体变化不大。\n", - "\n", - "## 统计计算与趋势识别\n", - "- **居民消费价格指数(CPI)**:\n", - " - 平均值:100.15\n", - " - 最大值:100.2(2025年10月)\n", - " - 最小值:99.6(2025年8月)\n", - " - 增长率:0.1%\n", - "\n", - "- **食品烟酒类居民消费价格指数**:\n", - " - 平均值:99.2\n", - " - 最大值:100.1(2025年6月)\n", - " - 最小值:97.4(2025年9月)\n", - " - 增长率:0.8%\n", - "\n", - "- **其他用品及服务类居民消费价格指数**:\n", - " - 平均值:108.3\n", - " - 最大值:112.8(2025年10月)\n", - " - 最小值:108.0(2025年7月)\n", - " - 增长率:4.4%\n", - "\n", - "- **交通通信类居民消费价格指数**:\n", - " - 平均值:97.4\n", - " - 最大值:98.5(2025年10月)\n", - " - 最小值:96.3(2025年6月)\n", - " - 增长率:2.3%\n", - "\n", - "- **非食品居民消费价格指数**:\n", - " - 平均值:100.5\n", - " - 最大值:100.9(2025年10月)\n", - " - 最小值:100.1(2025年6月)\n", - " - 增长率:0.8%\n", - "\n", - "- **服务居民消费价格指数**:\n", - " - 平均值:100.6\n", - " - 最大值:100.8(2025年10月)\n", - " - 最小值:100.5(2025年6月)\n", - " - 增长率:0.3%\n", - "\n", - "## 异常检测\n", - "- **其他用品及服务类价格指数**:在2025年10月达到112.8,明显高于其他月份,可能由于特定商品价格上涨或供应紧张。\n", - "- **食品烟酒类价格指数**:在2025年6月至8月间持续下降,但9月和10月有所回升,可能存在季节性因素影响。\n", - "\n", - "## 对比分析\n", - "- **与其他类别相比**,其他用品及服务类价格指数的增长幅度最大,表明该类别的价格变动对整体CPI的影响最为显著。\n", - "- **食品烟酒类**和**交通通信类**的价格指数呈现出不同的趋势,前者波动较大,后者则较为平稳。\n", - "- **非食品居民消费价格指数**和**服务居民消费价格指数**的增长趋势相似,表明非食品和服务类价格在整体CPI中的占比逐渐增加。\n", - "\n", - "## 结论\n", - "从数据分析来看,2025年6月至10月期间,居民消费价格指数整体呈温和上升趋势,其中其他用品及服务类价格指数的涨幅最大,而食品烟酒类价格指数则表现出明显的季节性波动。这些变化提示我们关注特定类别价格变动的原因,并采取相应措施以应对潜在的通胀压力。\n" + "=== 开始数据分析 ===\n" + ] + }, + { + "ename": "HelloAgentsException", + "evalue": "LLM调用失败: Error code: 400 - {'error': {'code': 'invalid_parameter_error', 'message': 'parameter.enable_thinking must be set to false for non-streaming calls', 'param': None, 'type': 'invalid_request_error'}, 'request_id': 'ab5e4c0c-8a49-49fe-a686-4e9529de6475'}", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mBadRequestError\u001b[0m Traceback (most recent call last)", + "File \u001b[0;32m/usr/local/lib/python3.10/dist-packages/hello_agents/core/llm.py:326\u001b[0m, in \u001b[0;36mHelloAgentsLLM.invoke\u001b[0;34m(self, messages, **kwargs)\u001b[0m\n\u001b[1;32m 325\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 326\u001b[0m response \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_client\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mchat\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcompletions\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcreate\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 327\u001b[0m \u001b[43m \u001b[49m\u001b[43mmodel\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmodel\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 328\u001b[0m \u001b[43m \u001b[49m\u001b[43mmessages\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mmessages\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 329\u001b[0m \u001b[43m \u001b[49m\u001b[43mtemperature\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mtemperature\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtemperature\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 330\u001b[0m \u001b[43m \u001b[49m\u001b[43mmax_tokens\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mmax_tokens\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmax_tokens\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 331\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43m{\u001b[49m\u001b[43mk\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mv\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mk\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mv\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mkwargs\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mitems\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mk\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mnot\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mtemperature\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mmax_tokens\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m}\u001b[49m\n\u001b[1;32m 332\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 333\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m response\u001b[38;5;241m.\u001b[39mchoices[\u001b[38;5;241m0\u001b[39m]\u001b[38;5;241m.\u001b[39mmessage\u001b[38;5;241m.\u001b[39mcontent\n", + "File \u001b[0;32m/usr/local/lib/python3.10/dist-packages/openai/_utils/_utils.py:286\u001b[0m, in \u001b[0;36mrequired_args..inner..wrapper\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 285\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(msg)\n\u001b[0;32m--> 286\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m/usr/local/lib/python3.10/dist-packages/openai/resources/chat/completions/completions.py:1147\u001b[0m, in \u001b[0;36mCompletions.create\u001b[0;34m(self, messages, model, audio, frequency_penalty, function_call, functions, logit_bias, logprobs, max_completion_tokens, max_tokens, metadata, modalities, n, parallel_tool_calls, prediction, presence_penalty, prompt_cache_key, reasoning_effort, response_format, safety_identifier, seed, service_tier, stop, store, stream, stream_options, temperature, tool_choice, tools, top_logprobs, top_p, user, verbosity, web_search_options, extra_headers, extra_query, extra_body, timeout)\u001b[0m\n\u001b[1;32m 1146\u001b[0m validate_response_format(response_format)\n\u001b[0;32m-> 1147\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_post\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 1148\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m/chat/completions\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1149\u001b[0m \u001b[43m \u001b[49m\u001b[43mbody\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mmaybe_transform\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 1150\u001b[0m \u001b[43m \u001b[49m\u001b[43m{\u001b[49m\n\u001b[1;32m 1151\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mmessages\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mmessages\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1152\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mmodel\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mmodel\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1153\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43maudio\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43maudio\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1154\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mfrequency_penalty\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mfrequency_penalty\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1155\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mfunction_call\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mfunction_call\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1156\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mfunctions\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mfunctions\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1157\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mlogit_bias\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mlogit_bias\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1158\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mlogprobs\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mlogprobs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1159\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mmax_completion_tokens\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mmax_completion_tokens\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1160\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mmax_tokens\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mmax_tokens\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1161\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mmetadata\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mmetadata\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1162\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mmodalities\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mmodalities\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1163\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mn\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mn\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1164\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mparallel_tool_calls\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mparallel_tool_calls\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1165\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mprediction\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mprediction\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1166\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mpresence_penalty\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mpresence_penalty\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1167\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mprompt_cache_key\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mprompt_cache_key\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1168\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mreasoning_effort\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mreasoning_effort\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1169\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mresponse_format\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mresponse_format\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1170\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43msafety_identifier\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43msafety_identifier\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1171\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mseed\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mseed\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1172\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mservice_tier\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mservice_tier\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1173\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mstop\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mstop\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1174\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mstore\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mstore\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1175\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mstream\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mstream\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1176\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mstream_options\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mstream_options\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1177\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mtemperature\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mtemperature\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1178\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mtool_choice\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mtool_choice\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1179\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mtools\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mtools\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1180\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mtop_logprobs\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mtop_logprobs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1181\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mtop_p\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mtop_p\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1182\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43muser\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43muser\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1183\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mverbosity\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mverbosity\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1184\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mweb_search_options\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mweb_search_options\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1185\u001b[0m \u001b[43m \u001b[49m\u001b[43m}\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1186\u001b[0m \u001b[43m \u001b[49m\u001b[43mcompletion_create_params\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mCompletionCreateParamsStreaming\u001b[49m\n\u001b[1;32m 1187\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mstream\u001b[49m\n\u001b[1;32m 1188\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01melse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mcompletion_create_params\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mCompletionCreateParamsNonStreaming\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1189\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1190\u001b[0m \u001b[43m \u001b[49m\u001b[43moptions\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mmake_request_options\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 1191\u001b[0m \u001b[43m \u001b[49m\u001b[43mextra_headers\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mextra_headers\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mextra_query\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mextra_query\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mextra_body\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mextra_body\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mtimeout\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mtimeout\u001b[49m\n\u001b[1;32m 1192\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1193\u001b[0m \u001b[43m \u001b[49m\u001b[43mcast_to\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mChatCompletion\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1194\u001b[0m \u001b[43m \u001b[49m\u001b[43mstream\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[1;32m 1195\u001b[0m \u001b[43m \u001b[49m\u001b[43mstream_cls\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mStream\u001b[49m\u001b[43m[\u001b[49m\u001b[43mChatCompletionChunk\u001b[49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1196\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m/usr/local/lib/python3.10/dist-packages/openai/_base_client.py:1259\u001b[0m, in \u001b[0;36mSyncAPIClient.post\u001b[0;34m(self, path, cast_to, body, options, files, stream, stream_cls)\u001b[0m\n\u001b[1;32m 1256\u001b[0m opts \u001b[38;5;241m=\u001b[39m FinalRequestOptions\u001b[38;5;241m.\u001b[39mconstruct(\n\u001b[1;32m 1257\u001b[0m method\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mpost\u001b[39m\u001b[38;5;124m\"\u001b[39m, url\u001b[38;5;241m=\u001b[39mpath, json_data\u001b[38;5;241m=\u001b[39mbody, files\u001b[38;5;241m=\u001b[39mto_httpx_files(files), \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39moptions\n\u001b[1;32m 1258\u001b[0m )\n\u001b[0;32m-> 1259\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m cast(ResponseT, \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrequest\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcast_to\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mopts\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstream\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstream_cls\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream_cls\u001b[49m\u001b[43m)\u001b[49m)\n", + "File \u001b[0;32m/usr/local/lib/python3.10/dist-packages/openai/_base_client.py:1047\u001b[0m, in \u001b[0;36mSyncAPIClient.request\u001b[0;34m(self, cast_to, options, stream, stream_cls)\u001b[0m\n\u001b[1;32m 1046\u001b[0m log\u001b[38;5;241m.\u001b[39mdebug(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mRe-raising status error\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m-> 1047\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_make_status_error_from_response(err\u001b[38;5;241m.\u001b[39mresponse) \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[1;32m 1049\u001b[0m \u001b[38;5;28;01mbreak\u001b[39;00m\n", + "\u001b[0;31mBadRequestError\u001b[0m: Error code: 400 - {'error': {'code': 'invalid_parameter_error', 'message': 'parameter.enable_thinking must be set to false for non-streaming calls', 'param': None, 'type': 'invalid_request_error'}, 'request_id': 'ab5e4c0c-8a49-49fe-a686-4e9529de6475'}", + "\nDuring handling of the above exception, another exception occurred:\n", + "\u001b[0;31mHelloAgentsException\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[9], line 3\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;66;03m# 执行数据分析\u001b[39;00m\n\u001b[1;32m 2\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m=== 开始数据分析 ===\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m----> 3\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[43magent\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43mf\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m对以下数据绘制图表和数据分析\u001b[39;49m\u001b[38;5;130;43;01m\\n\u001b[39;49;00m\u001b[38;5;130;43;01m\\n\u001b[39;49;00m\u001b[38;5;132;43;01m{\u001b[39;49;00m\u001b[43msample_data\u001b[49m\u001b[38;5;132;43;01m}\u001b[39;49;00m\u001b[38;5;130;43;01m\\n\u001b[39;49;00m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m 5\u001b[0m \u001b[38;5;28mprint\u001b[39m(result)\n", + "File \u001b[0;32m/usr/local/lib/python3.10/dist-packages/hello_agents/agents/simple_agent.py:285\u001b[0m, in \u001b[0;36mSimpleAgent.run\u001b[0;34m(self, input_text, max_tool_iterations, **kwargs)\u001b[0m\n\u001b[1;32m 281\u001b[0m final_response \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 283\u001b[0m \u001b[38;5;28;01mwhile\u001b[39;00m current_iteration \u001b[38;5;241m<\u001b[39m max_tool_iterations:\n\u001b[1;32m 284\u001b[0m \u001b[38;5;66;03m# 调用LLM\u001b[39;00m\n\u001b[0;32m--> 285\u001b[0m response \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mllm\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\u001b[43mmessages\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 287\u001b[0m \u001b[38;5;66;03m# 检查是否有工具调用\u001b[39;00m\n\u001b[1;32m 288\u001b[0m tool_calls \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_parse_tool_calls(response)\n", + "File \u001b[0;32m/usr/local/lib/python3.10/dist-packages/hello_agents/core/llm.py:335\u001b[0m, in \u001b[0;36mHelloAgentsLLM.invoke\u001b[0;34m(self, messages, **kwargs)\u001b[0m\n\u001b[1;32m 333\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m response\u001b[38;5;241m.\u001b[39mchoices[\u001b[38;5;241m0\u001b[39m]\u001b[38;5;241m.\u001b[39mmessage\u001b[38;5;241m.\u001b[39mcontent\n\u001b[1;32m 334\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[0;32m--> 335\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m HelloAgentsException(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mLLM调用失败: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mstr\u001b[39m(e)\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n", + "\u001b[0;31mHelloAgentsException\u001b[0m: LLM调用失败: Error code: 400 - {'error': {'code': 'invalid_parameter_error', 'message': 'parameter.enable_thinking must be set to false for non-streaming calls', 'param': None, 'type': 'invalid_request_error'}, 'request_id': 'ab5e4c0c-8a49-49fe-a686-4e9529de6475'}" ] } ], @@ -815,7 +697,7 @@ ], "metadata": { "kernelspec": { - "display_name": "hello_agent", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -829,7 +711,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.19" + "version": "3.10.12" } }, "nbformat": 4, diff --git a/Co-creation-projects/maligee-CodeReviewAgent/README.md b/Co-creation-projects/maligee-CodeReviewAgent/README.md new file mode 100644 index 00000000..1574c675 --- /dev/null +++ b/Co-creation-projects/maligee-CodeReviewAgent/README.md @@ -0,0 +1,145 @@ +# CodeReviewAgent - 智能代码审查助手 + +> 基于HelloAgents框架的智能代码审查工具 + +## 📝 项目简介 + +CodeReviewAgent是一个智能代码审查助手,能够自动分析Python代码的质量、发现潜在问题并提供优化建议。 + +### 核心功能 + +- ✅ 代码结构分析:统计函数、类、代码行数等 +- ✅ 风格检查:检查是否符合PEP 8规范 +- ✅ 智能建议:基于LLM提供深度分析和优化建议 +- ✅ 报告生成:生成Markdown格式的审查报告 + +## 🛠️ 技术栈 + +- HelloAgents框架(SimpleAgent) +- Python AST模块(代码解析) +- OpenAI API(智能分析) + +## 🚀 快速开始 + +### 安装依赖 + +```bash +pip install -r requirements.txt +``` + +### 配置LLM参数 + +**方式1: 使用.env文件(推荐)** + +```bash +# 复制示例文件 +cp .env.example .env + +# 编辑.env文件,填入你的配置 +# LLM_MODEL_ID=Qwen/Qwen2.5-72B-Instruct +# LLM_API_KEY=your_api_key_here +# LLM_BASE_URL=https://api-inference.modelscope.cn/v1/ +``` + +**方式2: 直接在Notebook中设置(已配置)** + +项目已在`main.ipynb`中预配置了ModelScope的API,可以直接使用。如需修改,编辑第1部分的配置代码: + +```python +os.environ["LLM_MODEL_ID"] = "your_model" +os.environ["LLM_API_KEY"] = "your_key" +os.environ["LLM_BASE_URL"] = "your_api_url" +``` + +### 运行项目 + +```bash +jupyter lab +# 打开main.ipynb并运行所有单元格 +``` + +## 📖 使用示例 + +### 快速体验 + +打开`main.ipynb`,运行「第0部分:快速演示」,即可快速了解项目功能。 + +### 完整功能 + +1. 将待审查的代码放入`data/sample_code.py` +2. 依次运行`main.ipynb`的第1-7部分 +3. 查看生成的审查报告`outputs/review_report.md` + +## 🎯 项目亮点 + +- **自动化**:无需人工逐行检查,自动发现问题 +- **智能化**:利用LLM理解代码语义,提供深度建议 +- **可扩展**:易于添加新的检查规则和工具 + +## 📂 项目结构 + +``` +jjyaoao-CodeReviewAgent/ +├── README.md # 项目说明文档 +├── requirements.txt # 依赖列表 +├── .gitignore # Git忽略文件 +├── .env.example # 环境变量示例 +├── main.ipynb # 主程序(包含快速演示和完整功能) +├── data/ +│ └── sample_code.py # 示例代码 +└── outputs/ + └── review_report.md # 审查报告 +``` + +## 🔧 技术实现 + +### 工具系统 + +1. **CodeAnalysisTool**: 使用Python AST模块解析代码结构 +2. **StyleCheckTool**: 检查PEP 8代码风格规范 + +### 智能体设计 + +使用HelloAgents的SimpleAgent,配合自定义工具实现智能代码审查。 + +## 📊 示例输出 + +```markdown +# 代码审查报告 + +## 代码结构分析 +- 函数数量: 3 +- 类数量: 1 +- 代码行数: 45 + +## 风格问题 +- 第12行:超过79个字符 +- 第25行:缩进不规范 + +## 优化建议 +1. 建议将长函数拆分为多个小函数 +2. 添加类型注解提高代码可读性 +3. 补充文档字符串 +``` + +## 🚧 未来改进 + +- [ ] 支持更多编程语言(JavaScript、Java等) +- [ ] 添加安全漏洞检测 +- [ ] 集成更多静态分析工具 +- [ ] 支持批量文件审查 +- [ ] 生成HTML格式报告 + +## 👤 作者 + +- GitHub: [@jjyaoao](https://github.com/jjyaoao) +- 项目链接:[CodeReviewAgent](https://github.com/datawhalechina/Hello-Agents/tree/main/Co-creation-projects/jjyaoao-CodeReviewAgent) + +## 🙏 致谢 + +感谢Datawhale社区和Hello-Agents项目! + +## 📄 许可证 + +本项目采用MIT许可证。 + diff --git a/Co-creation-projects/maligee-CodeReviewAgent/data/orchestrator.py b/Co-creation-projects/maligee-CodeReviewAgent/data/orchestrator.py new file mode 100644 index 00000000..ec502863 --- /dev/null +++ b/Co-creation-projects/maligee-CodeReviewAgent/data/orchestrator.py @@ -0,0 +1,453 @@ +"""使用多 Agent 模式的主系统编排逻辑""" + +from datetime import datetime +from typing import Dict, Any, List, Optional +from models import ContentNode, ContentLevel, ColumnPlan, ReviewResult +from agents import ( + PlannerAgent, + WriterAgent, + ReflectionWriterAgent, + ReviewerAgent, + RevisionAgent +) +from config import get_settings, get_word_count + + +class ColumnWriterOrchestrator: + """ + 提供多 Agent 模式的专栏写作系统 + + 架构设计: + 1. PlannerAgent → PlanAndSolveAgent(任务分解和规划) + 2. WriterAgent → ReActAgent(推理和工具调用) + 3. 评审+修改 → ReflectionAgent(自我反思优化) + """ + + def __init__(self, use_reflection_mode: bool = False): + """ + 初始化编排器 + + Args: + use_reflection_mode: 是否使用 ReflectionAgent 模式 + - True: 使用 ReflectionAgent(自动评审和优化) + - False: 使用 ReActAgent + 独立评审流程 + """ + self.settings = get_settings() + self.use_reflection_mode = use_reflection_mode + + # 创建各个 Agent + print("\n 初始化专栏写作系统...") + print(f" 模式选择: {'ReflectionAgent(自我反思)' if use_reflection_mode else 'ReActAgent(推理行动)+ 评审'}") + + # 规划 Agent - 使用 PlanAndSolveAgent + self.planner = PlannerAgent() + + # 写作 Agent - 根据模式选择 + if use_reflection_mode: + self.writer = ReflectionWriterAgent() + print(" WriterAgent: ReflectionAgent(内置评审优化)") + self.reviewer = None + self.revision_agent = None + else: + self.writer = WriterAgent(enable_search=self.settings.enable_search) + print(" WriterAgent: ReActAgent(推理-行动-搜索)") + + # 评审和修改 Agent(仅 ReAct 模式下可用) + if self.settings.enable_review: + self.reviewer = ReviewerAgent() + self.revision_agent = RevisionAgent() + print(f" ReviewerAgent: 已启用(通过阈值: {self.settings.approval_threshold})") + print(f" RevisionAgent: 已启用(最大修改次数: {self.settings.max_revisions})") + else: + self.reviewer = None + self.revision_agent = None + print(" ReviewerAgent: 已禁用") + + # 统计信息 + self.stats = { + 'total_generations': 0, + 'total_reviews': 0, + 'total_revisions': 0, + 'total_rewrites': 0, + 'approved_first_try': 0, + 'start_time': None, + 'end_time': None + } + + print("▸ 系统初始化完成\n") + + def create_column(self, main_topic: str) -> Dict[str, Any]: + """ + 创建完整专栏 + + Args: + main_topic: 专栏主题 + + Returns: + 包含专栏完整信息的字典 + """ + self.stats['start_time'] = datetime.now() + + print(f"\n{'='*70}") + print(f"▸ 开始创建专栏:{main_topic}") + print(f"{'='*70}\n") + + # Step 1: 规划专栏结构(使用 PlanAndSolveAgent) + print("▸ 第一步:规划专栏结构(PlanAndSolveAgent)") + print("-" * 70) + column_plan = self.planner.plan_column(main_topic) + print(f" 标题:{column_plan.column_title}") + print(f" 话题数:{column_plan.get_topic_count()} 个") + print(f" 目标读者:{column_plan.target_audience}\n") + + # Step 2: 为每个子话题创建内容树 + mode_name = "ReflectionAgent" if self.use_reflection_mode else "ReActAgent" + print(f"▸️ 第二步:撰写专栏文章({mode_name})") + print("-" * 70) + + content_trees = self._write_topics_sequential(column_plan) + + # Step 3: 组装完整专栏 + print("\n▸ 第三步:组装专栏内容") + print("-" * 70) + full_column = self._assemble_column(column_plan, content_trees) + + self.stats['end_time'] = datetime.now() + duration = (self.stats['end_time'] - self.stats['start_time']).total_seconds() + + print(f"\n{'='*70}") + print(f"▸ 专栏创建完成!耗时 {duration:.1f} 秒") + print(f"{'='*70}\n") + + # 添加统计信息 + full_column['creation_stats'] = self.stats + full_column['agent_modes'] = { + 'planner': 'PlanAndSolveAgent', + 'writer': 'ReflectionAgent' if self.use_reflection_mode else 'ReActAgent', + 'reviewer': 'ReviewerAgent' if (self.reviewer and not self.use_reflection_mode) else None, + 'revision': 'RevisionAgent' if (self.revision_agent and not self.use_reflection_mode) else None + } + + return full_column + + def _write_topics_sequential(self, column_plan: ColumnPlan) -> List[ContentNode]: + """顺序写作各个话题""" + content_trees = [] + + for idx, topic in enumerate(column_plan.topics, 1): + print(f"\n{'─'*70}") + print(f"▸ 正在写作第 {idx}/{column_plan.get_topic_count()} 个话题") + print(f" 话题:{topic['title']}") + print(f"{'─'*70}") + + tree = self._write_topic_tree(topic, column_plan) + content_trees.append(tree) + + # 显示进度 + progress = idx / column_plan.get_topic_count() * 100 + print(f"\n▸ 总体进度:{progress:.0f}% ({idx}/{column_plan.get_topic_count()})") + + return content_trees + + def _write_topic_tree( + self, + topic: Dict[str, Any], + column_context: ColumnPlan + ) -> ContentNode: + """递归写作话题树""" + root = ContentNode( + id=topic['id'], + title=topic['title'], + level=ContentLevel.TOPIC, + description=topic['description'] + ) + + context = { + 'column_title': column_context.column_title, + 'column_description': column_context.column_description, + 'target_audience': column_context.target_audience, + 'current_topic': topic + } + + self._recursive_write(root, context, level=1) + return root + + def _recursive_write( + self, + node: ContentNode, + context: Dict[str, Any], + level: int + ): + """递归写作核心逻辑""" + if level > self.settings.max_depth: + indent = " " * level + print(f"{indent}▸️ 达到最大深度 {self.settings.max_depth},停止展开") + return + + indent = " " * level + print(f"\n{indent}{'┈'*40}") + print(f"{indent}▸ Level {level}: {node.title}") + print(f"{indent}{'┈'*40}") + + if self.use_reflection_mode: + # 模式1: 使用 ReflectionAgent(内置评审优化) + self._write_with_reflection(node, context, level, indent) + else: + # 模式2: 使用 ReActAgent(推理-行动) + self._write_with_react(node, context, level, indent) + + def _write_with_reflection( + self, + node: ContentNode, + context: Dict[str, Any], + level: int, + indent: str + ): + """使用 ReflectionAgent 模式写作""" + print(f"{indent}▸️ 使用 ReflectionAgent 生成并优化内容...") + + content_data = self.writer.generate_and_refine_content(node, context, level) + self.stats['total_generations'] += 1 + + # ReflectionAgent 已经完成了自我评审和优化 + node.content = content_data['content'] + node.metadata = content_data.get('metadata', {}) + node.metadata['agent_mode'] = 'ReflectionAgent' + node.metadata['auto_refined'] = True + + word_count = content_data.get('word_count', len(content_data['content'])) + print(f"{indent} 字数:{word_count}") + print(f"{indent}▸ 内容已通过自我反思优化") + + # 处理子节点 + self._process_children(node, content_data, context, level, indent) + + def _write_with_react( + self, + node: ContentNode, + context: Dict[str, Any], + level: int, + indent: str + ): + """使用 ReActAgent 模式写作(可选评审)""" + print(f"{indent}▸️ 使用 ReActAgent 生成内容(推理-行动)...") + + content_data = self.writer.generate_content(node, context, level) + self.stats['total_generations'] += 1 + + current_content = content_data['content'] + word_count = content_data.get('word_count', len(current_content)) + print(f"{indent} 字数:{word_count}") + print(f"{indent}▸ ReActAgent 完成推理和行动") + + # 如果启用评审,进行评审和可能的修改 + if self.reviewer and self.settings.enable_review: + current_content, review_metadata = self._review_and_revise( + node, current_content, content_data, level, indent + ) + content_data['content'] = current_content + content_data['metadata'] = {**content_data.get('metadata', {}), **review_metadata} + + node.content = current_content + node.metadata = content_data.get('metadata', {}) + node.metadata['agent_mode'] = 'ReActAgent' + + # 处理子节点 + self._process_children(node, content_data, context, level, indent) + + def _review_and_revise( + self, + node: ContentNode, + content: str, + content_data: Dict[str, Any], + level: int, + indent: str + ) -> tuple: + """ + 评审并根据需要修改内容 + + Args: + node: 当前节点 + content: 当前内容 + content_data: 完整的内容数据 + level: 层级 + indent: 缩进 + + Returns: + (最终内容, 评审元数据) + """ + target_word_count = get_word_count(level) + key_points = content_data.get('metadata', {}).get('keywords', []) + if not key_points: + key_points = [node.title, node.description] + + revision_count = 0 + final_content = content + review_history = [] + + while revision_count <= self.settings.max_revisions: + # 评审 + print(f"{indent}▸ 开始评审(第 {revision_count + 1} 轮)...") + review_result = self.reviewer.review_content( + content=final_content, + level=level, + target_word_count=target_word_count, + key_points=key_points + ) + self.stats['total_reviews'] += 1 + + review_history.append({ + 'round': revision_count + 1, + 'score': review_result.score, + 'grade': review_result.grade, + 'needs_revision': review_result.needs_revision + }) + + print(f"{indent} 评审结果: {review_result.score}/100 ({review_result.grade})") + + # 检查是否通过评审 + if review_result.score >= self.settings.approval_threshold: + print(f"{indent}▸ 内容通过评审!") + if revision_count == 0: + self.stats['approved_first_try'] += 1 + break + + # 检查是否还能修改 + if revision_count >= self.settings.max_revisions: + print(f"{indent}▸️ 达到最大修改次数 ({self.settings.max_revisions}),使用当前版本") + break + + # 检查是否需要重写(分数太低) + if review_result.score < self.settings.revision_threshold: + print(f"{indent}▸️ 分数过低 ({review_result.score} < {self.settings.revision_threshold}),需要重写") + self.stats['total_rewrites'] += 1 + # 重新生成内容 + new_content_data = self.writer.generate_content( + node, + {'review_feedback': review_result.reviewer_notes}, + level, + additional_requirements=f"请注意避免以下问题: {review_result.reviewer_notes}" + ) + self.stats['total_generations'] += 1 + final_content = new_content_data['content'] + else: + # 修改内容 + print(f"{indent}▸ 根据评审意见修改内容...") + revised_data = self.revision_agent.revise_content( + original_content=final_content, + review_result=review_result, + target_word_count=target_word_count + ) + self.stats['total_revisions'] += 1 + final_content = revised_data.get('revised_content', final_content) + + revision_count += 1 + + # 构建评审元数据 + final_review = review_history[-1] if review_history else {} + review_metadata = { + 'review_score': final_review.get('score'), + 'review_grade': final_review.get('grade'), + 'review_rounds': len(review_history), + 'review_history': review_history, + 'reviewed': True + } + + return final_content, review_metadata + + def _process_children( + self, + node: ContentNode, + content_data: Dict[str, Any], + context: Dict[str, Any], + level: int, + indent: str + ): + """处理子节点""" + if content_data.get('needs_expansion') and level < self.settings.max_depth: + subsections = content_data.get('subsections', []) + if subsections: + print(f"{indent}▸ 需要展开 {len(subsections)} 个子节点") + + for subsection in subsections: + child = ContentNode( + id=subsection['id'], + title=subsection['title'], + level=ContentLevel(level + 1), + description=subsection['description'] + ) + node.add_child(child) + + # 递归写作子节点 + self._recursive_write(child, context, level + 1) + + def _assemble_column( + self, + plan: ColumnPlan, + trees: List[ContentNode] + ) -> Dict[str, Any]: + """组装完整专栏""" + articles = [] + + for tree in trees: + article_content = self._tree_to_markdown(tree) + + articles.append({ + 'id': tree.id, + 'title': tree.title, + 'content': article_content, + 'metadata': tree.metadata, + 'word_count': tree.count_words() + }) + + return { + 'column_info': { + 'title': plan.column_title, + 'description': plan.column_description, + 'target_audience': plan.target_audience, + 'topic_count': plan.get_topic_count() + }, + 'articles': articles, + 'statistics': self._calculate_statistics(trees) + } + + def _tree_to_markdown(self, node: ContentNode, depth: int = 0) -> str: + """将内容树转换为markdown""" + markdown = [] + + heading_level = "#" * (depth + 1) + markdown.append(f"{heading_level} {node.title}\n") + + if node.content: + markdown.append(node.content) + markdown.append("\n") + + for child in node.children: + child_md = self._tree_to_markdown(child, depth + 1) + markdown.append(child_md) + + return "\n".join(markdown) + + def _calculate_statistics(self, trees: List[ContentNode]) -> Dict[str, Any]: + """计算统计信息""" + total_words = 0 + total_nodes = 0 + + def count_tree(node: ContentNode): + nonlocal total_words, total_nodes + total_nodes += 1 + total_words += len(node.content) if node.content else 0 + + for child in node.children: + count_tree(child) + + for tree in trees: + count_tree(tree) + + return { + 'total_articles': len(trees), + 'total_nodes': total_nodes, + 'total_words': total_words, + 'avg_words_per_article': total_words // len(trees) if trees else 0 + } + diff --git a/Co-creation-projects/maligee-CodeReviewAgent/data/sample_code.py b/Co-creation-projects/maligee-CodeReviewAgent/data/sample_code.py new file mode 100644 index 00000000..c962c99a --- /dev/null +++ b/Co-creation-projects/maligee-CodeReviewAgent/data/sample_code.py @@ -0,0 +1,44 @@ +""" +示例代码:一个简单的用户管理系统 +用于演示代码审查功能 +""" + +class UserManager: + """用户管理类""" + + def __init__(self): + self.users = [] + + def add_user(self, name, age, email): + """添加用户""" + user = {"name": name, "age": age, "email": email} + self.users.append(user) + return True + + def get_user(self, name): + """获取用户信息""" + for user in self.users: + if user["name"] == name: + return user + return None + + def delete_user(self, name): + """删除用户""" + for i, user in enumerate(self.users): + if user["name"] == name: + del self.users[i] + return True + return False + +def calculate_average_age(users): + """计算平均年龄""" + total = 0 + for user in users: + total += user["age"] + return total / len(users) + +def send_email(email, message): + """发送邮件(模拟)""" + print(f"发送邮件到 {email}: {message}") + return True + diff --git a/Co-creation-projects/maligee-CodeReviewAgent/main.ipynb b/Co-creation-projects/maligee-CodeReviewAgent/main.ipynb new file mode 100644 index 00000000..6fc7372f --- /dev/null +++ b/Co-creation-projects/maligee-CodeReviewAgent/main.ipynb @@ -0,0 +1,843 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# CodeReviewAgent - 智能代码审查助手\n", + "\n", + "本项目演示如何使用HelloAgents框架构建一个智能代码审查助手。\n", + "\n", + "## 📖 使用说明\n", + "\n", + "- **快速体验**: 运行「第0部分」的快速演示\n", + "- **完整功能**: 依次运行第1-7部分,体验完整的代码审查流程" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "\n", + "## 第0部分:快速演示 ⚡\n", + "\n", + "如果你想快速了解项目功能,可以运行这个简化版本。" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "✅ 库导入和配置完成\n" + ] + } + ], + "source": [ + "# 快速演示 - 导入库和配置\n", + "from hello_agents import SimpleAgent, HelloAgentsLLM\n", + "from hello_agents.tools import Tool, ToolParameter\n", + "from typing import Dict, Any, List\n", + "import ast\n", + "import os\n", + "\n", + "# 配置LLM参数\n", + "os.environ[\"LLM_MODEL_ID\"] = \"Qwen/Qwen2.5-72B-Instruct\"\n", + "os.environ[\"LLM_API_KEY\"] = \"ms-9c264720-1dc2-4c5b-88b5-0244f39e8144\"\n", + "os.environ[\"LLM_BASE_URL\"] = \"https://api-inference.modelscope.cn/v1/\"\n", + "os.environ[\"LLM_TIMEOUT\"] = \"60\"\n", + "\n", + "print(\"✅ 库导入和配置完成\")" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "✅ 工具定义完成\n" + ] + } + ], + "source": [ + "# 快速演示 - 定义简单的代码分析工具\n", + "class QuickAnalysisTool(Tool):\n", + " def __init__(self):\n", + " super().__init__(\n", + " name=\"quick_analysis\",\n", + " description=\"快速分析Python代码结构\"\n", + " )\n", + " \n", + " def run(self, parameters: Dict[str, Any]) -> str:\n", + " code = parameters.get(\"code\", \"\")\n", + " if not code:\n", + " return \"错误:代码不能为空\"\n", + " \n", + " try:\n", + " tree = ast.parse(code)\n", + " functions = [n.name for n in ast.walk(tree) if isinstance(n, ast.FunctionDef)]\n", + " classes = [n.name for n in ast.walk(tree) if isinstance(n, ast.ClassDef)]\n", + " return f\"发现{len(classes)}个类、{len(functions)}个函数: {', '.join(functions)}\"\n", + " except Exception as e:\n", + " return f\"代码解析失败: {str(e)}\"\n", + " \n", + " def get_parameters(self) -> List[ToolParameter]:\n", + " return [\n", + " ToolParameter(\n", + " name=\"code\",\n", + " type=\"string\",\n", + " description=\"要分析的Python代码\",\n", + " required=True\n", + " )\n", + " ]\n", + "\n", + "print(\"✅ 工具定义完成\")" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "✅ 工具 'quick_analysis' 已注册。\n", + "✅ 智能体创建完成\n", + "✅ 可用工具: ['quick_analysis']\n" + ] + } + ], + "source": [ + "# 快速演示 - 创建工具注册表和智能体\n", + "from hello_agents import ToolRegistry\n", + "\n", + "# 创建工具注册表\n", + "quick_registry = ToolRegistry()\n", + "quick_registry.register_tool(QuickAnalysisTool())\n", + "\n", + "# 创建智能体\n", + "quick_agent = SimpleAgent(\n", + " name=\"快速审查助手\",\n", + " llm=HelloAgentsLLM(),\n", + " system_prompt=\"你是代码审查助手,使用工具分析代码并给出简要建议。\",\n", + " tool_registry=quick_registry\n", + ")\n", + "\n", + "print(\"✅ 智能体创建完成\")\n", + "print(f\"✅ 可用工具: {list(quick_registry._tools.keys())}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== 快速演示:分析测试代码 ===\n", + "看来快速分析工具仍然无法正确解析你的代码,但根据你提供的代码内容,它看起来是正确的 Python 代码。这里有几个可能的原因和建议:\n", + "\n", + "1. **编码问题**:确保文件是以 UTF-8 编码保存的。\n", + "2. **特殊字符**:检查代码中是否有不可见的特殊字符,例如在复制粘贴过程中可能带入的字符。\n", + "3. **行尾符**:确保行尾符是标准的换行符(LF),而不是回车换行符(CRLF)。\n", + "\n", + "### 代码审查建议\n", + "\n", + "1. **函数命名**:函数名 `hello` 和 `world` 遵循了 Python 的命名约定,使用小写字母和下划线。\n", + "2. **类命名**:类名 `Greeter` 使用了驼峰命名法(CamelCase),符合 PEP 8 规范。\n", + "3. **方法命名**:方法名 `greet` 也遵循了 Python 的命名约定。\n", + "4. **代码结构**:代码结构清晰,每个函数和类都有明确的职责。\n", + "\n", + "### 改进建议\n", + "\n", + "虽然代码本身没有明显的问题,但可以考虑以下几点:\n", + "\n", + "1. **添加文档字符串**:为函数和类添加文档字符串,以便更好地描述它们的功能。\n", + " ```python\n", + " def hello():\n", + " \"\"\"Prints 'Hello'.\"\"\"\n", + " print(\"Hello\")\n", + "\n", + " def world():\n", + " \"\"\"Prints 'World'.\"\"\"\n", + " print(\"World\")\n", + "\n", + " class Greeter:\n", + " \"\"\"A class for greeting.\"\"\"\n", + "\n", + " def greet(self):\n", + " \"\"\"Performs the greeting by calling hello() and world().\"\"\"\n", + " hello()\n", + " world()\n", + " ```\n", + "\n", + "2. **测试**:考虑为类和方法编写单元测试,以确保它们按预期工作。\n", + " ```python\n", + " import unittest\n", + "\n", + " class TestGreeter(unittest.TestCase):\n", + " def test_greet(self):\n", + " greeter = Greeter()\n", + " with self.assertLogs(level='INFO') as logs:\n", + " greeter.greet()\n", + " self.assertIn('Hello', logs.output[0])\n", + " self.assertIn('World', logs.output[1])\n", + "\n", + " if __name__ == '__main__':\n", + " unittest.main()\n", + " ```\n", + "\n", + "如果工具仍然无法解析代码,建议手动检查代码文件,确保没有隐藏的特殊字符或编码问题。希望这些建议对你有帮助!\n", + "\n", + "✅ 快速演示完成!\n", + "\n", + "💡 提示:继续运行下面的单元格,体验完整功能\n" + ] + } + ], + "source": [ + "# 快速演示 - 测试代码\n", + "test_code = \"\"\"\n", + "def hello():\n", + " print(\"Hello\")\n", + "\n", + "def world():\n", + " print(\"World\")\n", + "\n", + "class Greeter:\n", + " def greet(self):\n", + " hello()\n", + " world()\n", + "\"\"\"\n", + "\n", + "print(\"=== 快速演示:分析测试代码 ===\")\n", + "result = quick_agent.run(f\"请分析这段代码:\\n{test_code}\")\n", + "print(result)\n", + "print(\"\\n✅ 快速演示完成!\")\n", + "print(\"\\n💡 提示:继续运行下面的单元格,体验完整功能\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "\n", + "# 完整版代码审查系统\n", + "\n", + "下面是完整的代码审查系统,包含更强大的分析功能。" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 第1部分:环境配置" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "✅ 环境配置完成\n", + "✅ 使用模型: Qwen/Qwen2.5-72B-Instruct\n", + "✅ API地址: https://api-inference.modelscope.cn/v1/\n" + ] + } + ], + "source": [ + "# 导入必要的库\n", + "from hello_agents import SimpleAgent, HelloAgentsLLM\n", + "from hello_agents.tools import Tool, ToolParameter\n", + "#from trae_agent.tools.base import Tool, ToolParameter \n", + "from typing import Dict, Any, List\n", + "import ast\n", + "import os\n", + "\n", + "# 配置LLM参数\n", + "os.environ[\"LLM_MODEL_ID\"] = \"Qwen/Qwen2.5-72B-Instruct\"\n", + "os.environ[\"LLM_API_KEY\"] = \"ms-9c264720-1dc2-4c5b-88b5-0244f39e8144\"\n", + "os.environ[\"LLM_BASE_URL\"] = \"https://api-inference.modelscope.cn/v1/\"\n", + "os.environ[\"LLM_TIMEOUT\"] = \"60\"\n", + "\n", + "print(\"✅ 环境配置完成\")\n", + "print(f\"✅ 使用模型: {os.getenv('LLM_MODEL_ID')}\")\n", + "print(f\"✅ API地址: {os.getenv('LLM_BASE_URL')}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 第2部分:定义代码分析工具" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "✅ CodeAnalysisTool定义完成\n" + ] + } + ], + "source": [ + "class CodeAnalysisTool(Tool):\n", + " \"\"\"代码静态分析工具\"\"\"\n", + "\n", + " def __init__(self):\n", + " super().__init__(\n", + " name=\"code_analysis\",\n", + " description=\"分析Python代码的结构、复杂度和潜在问题\"\n", + " )\n", + "\n", + " def run(self, parameters: Dict[str, Any]) -> str:\n", + " \"\"\"分析代码并返回结果\"\"\"\n", + " code = parameters.get(\"code\", \"\")\n", + " if not code:\n", + " return \"错误:代码不能为空\"\n", + " \n", + " try:\n", + " tree = ast.parse(code)\n", + "\n", + " # 统计信息\n", + " functions = [node for node in ast.walk(tree) if isinstance(node, ast.FunctionDef)]\n", + " classes = [node for node in ast.walk(tree) if isinstance(node, ast.ClassDef)]\n", + "\n", + " result = {\n", + " \"函数数量\": len(functions),\n", + " \"类数量\": len(classes),\n", + " \"代码行数\": len(code.split('\\n')),\n", + " \"函数列表\": [f.name for f in functions],\n", + " \"类列表\": [c.name for c in classes]\n", + " }\n", + "\n", + " return str(result)\n", + " except SyntaxError as e:\n", + " return f\"语法错误:{str(e)}\"\n", + " \n", + " def get_parameters(self) -> List[ToolParameter]:\n", + " return [\n", + " ToolParameter(\n", + " name=\"code\",\n", + " type=\"string\",\n", + " description=\"要分析的Python代码\",\n", + " required=True\n", + " )\n", + " ]\n", + "\n", + "print(\"✅ CodeAnalysisTool定义完成\")" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "✅ StyleCheckTool定义完成\n" + ] + } + ], + "source": [ + "class StyleCheckTool(Tool):\n", + " \"\"\"代码风格检查工具\"\"\"\n", + "\n", + " def __init__(self):\n", + " super().__init__(\n", + " name=\"style_check\",\n", + " description=\"检查代码是否符合PEP 8规范\"\n", + " )\n", + "\n", + " def run(self, parameters: Dict[str, Any]) -> str:\n", + " \"\"\"检查代码风格\"\"\"\n", + " code = parameters.get(\"code\", \"\")\n", + " if not code:\n", + " return \"错误:代码不能为空\"\n", + " \n", + " issues = []\n", + "\n", + " lines = code.split('\\n')\n", + " for i, line in enumerate(lines, 1):\n", + " # 检查行长度\n", + " if len(line) > 79:\n", + " issues.append(f\"第{i}行:超过79个字符\")\n", + "\n", + " # 检查缩进\n", + " if line.startswith(' ') and not line.startswith(' '):\n", + " if len(line) - len(line.lstrip()) not in [0, 4, 8, 12]:\n", + " issues.append(f\"第{i}行:缩进不规范\")\n", + "\n", + " if not issues:\n", + " return \"代码风格良好,符合PEP 8规范\"\n", + " return \"发现以下问题:\\n\" + \"\\n\".join(issues)\n", + " \n", + " def get_parameters(self) -> List[ToolParameter]:\n", + " return [\n", + " ToolParameter(\n", + " name=\"code\",\n", + " type=\"string\",\n", + " description=\"要检查的Python代码\",\n", + " required=True\n", + " )\n", + " ]\n", + "\n", + "print(\"✅ StyleCheckTool定义完成\")" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "✅ SecurityCheckTool 定义完成\n" + ] + } + ], + "source": [ + "class SecurityCheckTool(Tool):\n", + " \"\"\"Python 代码安全漏洞静态检查工具(基于简单词法+关键字)\"\"\"\n", + "\n", + " def __init__(self):\n", + " super().__init__(\n", + " name=\"security_check\",\n", + " description=\"扫描 Python 代码中潜在的高危函数与危险模式,提示用户注意安全风险\"\n", + " )\n", + "\n", + " # 6 类危险模式:①命令执行 ②文件写 ③反序列化 ④SQL ⑤动态导入 ⑥通配导入\n", + " self.DANGER_MAP = {\n", + " \"os.system\": \"命令执行,建议用 subprocess 并严格过滤外部输入\",\n", + " \"os.popen\": \"命令执行,建议用 subprocess 并严格过滤外部输入\",\n", + " \"subprocess.call\": \"命令执行,建议用 subprocess.run(..., shell=False) 并过滤输入\",\n", + " \"subprocess.check_call\": \"命令执行,建议用 subprocess.run(..., shell=False) 并过滤输入\",\n", + " \"eval\": \"动态执行任意代码,极易产生 RCE,建议用 ast.literal_eval 或 JSON 解析\",\n", + " \"exec\": \"动态执行任意代码,风险极高,尽量避免\",\n", + " \"open\": \"文件写操作,请确认路径是否来自外部、是否做了校验/沙箱\",\n", + " \"pickle.load\": \"反序列化可导致任意代码执行,建议改用 json 或严格签名校验\",\n", + " \"pickle.loads\": \"反序列化可导致任意代码执行,建议改用 json 或严格签名校验\",\n", + " \"marshal.load\": \"反序列化可导致任意代码执行\",\n", + " \"yaml.load\": \"PyYAML 默认构造任意 Python 对象,请用 yaml.safe_load\",\n", + " \"ctypes.CDLL\": \"加载本地 so/dll,需确认路径可信\",\n", + " \"__import__\": \"动态导入,需确认模块名来源可信\",\n", + " \"importlib.import_module\": \"动态导入,需确认模块名来源可信\",\n", + " \"input\": \"Python2 中 input()=eval(raw_input()),Python3 虽安全但常被误用\",\n", + " \"sqlite3.execute\": \"SQL 注入入口,请使用参数化查询(? 占位符)\",\n", + " \"cursor.execute\": \"SQL 注入入口,请使用参数化查询\",\n", + " \"from os import *\": \"通配导入会污染命名空间,显式导入所需函数\",\n", + " \"import os\\n\": \"(仅提醒)若结合外部输入拼接路径,请注意路径穿越\",\n", + " }\n", + "\n", + " # ----------- 核心扫描逻辑 -----------\n", + " def run(self, parameters: Dict[str, Any]) -> str:\n", + " code: str = parameters.get(\"code\", \"\")\n", + " if not code:\n", + " return \"错误:代码不能为空\"\n", + "\n", + " issues: List[str] = []\n", + " lines = code.splitlines()\n", + "\n", + " for lineno, line in enumerate(lines, 1):\n", + " line_stripped = line.strip()\n", + " if not line_stripped or line_stripped.startswith(\"#\"):\n", + " continue\n", + "\n", + " # 1) 子串匹配危险函数\n", + " for pattern, msg in self.DANGER_MAP.items():\n", + " if pattern in line:\n", + " issues.append(f\"第 {lineno} 行:{line_stripped[:60]}... → {msg}\")\n", + "\n", + " # 2) 简单正则补充:yaml.load( 前面没 safe_\n", + " if \"yaml.load(\" in line and \"safe_load\" not in line:\n", + " issues.append(f\"第 {lineno} 行:检测到 yaml.load(),请改用 yaml.safe_load()\")\n", + "\n", + " # 3) 通配导入\n", + " if \"from \" in line and \" import *\" in line:\n", + " issues.append(f\"第 {lineno} 行:通配导入(import *)会污染命名空间,建议显式导入\")\n", + "\n", + " if not issues:\n", + " return \"✅ 未检测到常见高危函数/模式,代码看起来相对安全(仍需结合业务场景人工复核)\"\n", + " return \"⚠️ 发现潜在安全风险:\\n\" + \"\\n\".join(issues)\n", + "\n", + " # ----------- 参数描述 -----------\n", + " def get_parameters(self) -> List[ToolParameter]:\n", + " return [\n", + " ToolParameter(\n", + " name=\"code\",\n", + " type=\"string\",\n", + " description=\"待扫描的 Python 源代码\",\n", + " required=True\n", + " )\n", + " ]\n", + "\n", + "\n", + "print(\"✅ SecurityCheckTool 定义完成\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 第3部分:创建智能体" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "✅ 工具 'code_analysis' 已注册。\n", + "✅ 工具 'style_check' 已注册。\n", + "✅ 工具 'security_check' 已注册。\n", + "✅ 智能体创建完成\n", + "智能体名称: 代码审查助手\n", + "可用工具: ['code_analysis', 'style_check', 'security_check']\n" + ] + } + ], + "source": [ + "# 导入工具注册表\n", + "from hello_agents import ToolRegistry\n", + "\n", + "# 创建工具注册表\n", + "tool_registry = ToolRegistry()\n", + "tool_registry.register_tool(CodeAnalysisTool())\n", + "tool_registry.register_tool(StyleCheckTool())\n", + "tool_registry.register_tool(SecurityCheckTool())\n", + "\n", + "# 初始化LLM\n", + "llm = HelloAgentsLLM()\n", + "\n", + "# 定义系统提示词\n", + "system_prompt = \"\"\"你是一位经验丰富的代码审查专家。你的任务是:\n", + "\n", + "1. 使用code_analysis工具分析代码结构\n", + "2. 使用style_check工具检查代码风格\n", + "3. 基于分析结果,提供详细的审查报告\n", + "\n", + "审查报告应包括:\n", + "- 代码结构分析\n", + "- 风格问题\n", + "- 潜在bug\n", + "- 性能优化建议\n", + "- 最佳实践建议\n", + "\n", + "请以Markdown格式输出报告。\"\"\"\n", + "\n", + "# 创建智能体\n", + "agent = SimpleAgent(\n", + " name=\"代码审查助手\",\n", + " llm=llm,\n", + " system_prompt=system_prompt,\n", + " tool_registry=tool_registry\n", + ")\n", + "\n", + "print(\"✅ 智能体创建完成\")\n", + "print(f\"智能体名称: {agent.name}\")\n", + "print(f\"可用工具: {list(tool_registry._tools.keys())}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 第4部分:读取示例代码" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== 待审查的代码 ===\n", + "\"\"\"\n", + "示例代码:一个简单的用户管理系统\n", + "用于演示代码审查功能\n", + "\"\"\"\n", + "\n", + "class UserManager:\n", + " \"\"\"用户管理类\"\"\"\n", + " \n", + " def __init__(self):\n", + " self.users = []\n", + " \n", + " def add_user(self, name, age, email):\n", + " \"\"\"添加用户\"\"\"\n", + " user = {\"name\": name, \"age\": age, \"email\": email}\n", + " self.users.append(user)\n", + " return True\n", + " \n", + " def get_user(self, name):\n", + " \"\"\"获取用户信息\"\"\"\n", + " for user in self.users:\n", + " if user[\"name\"] == name:\n", + " return user\n", + " return None\n", + " \n", + " def delete_user(self, name):\n", + " \"\"\"删除用户\"\"\"\n", + " for i, user in enumerate(self.users):\n", + " if user[\"name\"] == name:\n", + " del self.users[i]\n", + " return True\n", + " return False\n", + "\n", + "def calculate_average_age(users):\n", + " \"\"\"计算平均年龄\"\"\"\n", + " total = 0\n", + " for user in users:\n", + " total += user[\"age\"]\n", + " return total / len(users)\n", + "\n", + "def send_email(email, message):\n", + " \"\"\"发送邮件(模拟)\"\"\"\n", + " print(f\"发送邮件到 {email}: {message}\")\n", + " return True\n", + "\n", + "\n", + "\n", + "==================================================\n", + "\n" + ] + } + ], + "source": [ + "# 读取示例代码\n", + "with open(\"data/sample_code.py\", \"r\", encoding=\"utf-8\") as f:\n", + "# with open(\"data/orchestrator.py\", \"r\", encoding=\"utf-8\") as f:\n", + " sample_code = f.read()\n", + "\n", + "print(\"=== 待审查的代码 ===\")\n", + "print(sample_code)\n", + "print(\"\\n\" + \"=\"*50 + \"\\n\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 第5部分:执行代码审查" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== 开始代码审查 ===\n", + "### 代码审查报告\n", + "\n", + "#### 代码结构分析\n", + "在进行代码结构分析时,`code_analysis`工具报告了一个语法错误:“'[' was never closed”。这可能是因为在代码中存在未闭合的方括号,或者是一个解析错误。然而,从提供的代码来看,并没有明显的语法错误。这可能是由于工具对某些特定格式的处理不当造成的。为了确保准确性,我将再次手动检查代码结构。\n", + "\n", + "#### 手动代码结构分析\n", + "- **UserManager 类**:\n", + " - `__init__` 方法初始化了一个空列表 `self.users`,用于存储用户信息。\n", + " - `add_user` 方法将新用户添加到 `self.users` 列表中。\n", + " - `get_user` 方法通过用户名查找用户信息。\n", + " - `delete_user` 方法通过用户名从 `self.users` 列表中删除用户。\n", + "\n", + "- **calculate_average_age 函数**:\n", + " - 计算给定用户列表的平均年龄。\n", + "\n", + "- **send_email 函数**:\n", + " - 模拟发送邮件的功能,打印一条消息。\n", + "\n", + "#### 风格问题\n", + "根据 `style_check` 工具的结果,代码风格良好,符合 PEP 8 规范。没有发现明显的风格问题。\n", + "\n", + "#### 潜在 bug\n", + "- **delete_user 方法**:\n", + " - 在 `delete_user` 方法中,如果用户列表中有多个同名用户,删除操作只会删除第一个匹配的用户。这可能导致意外的行为。建议在删除用户前确认唯一性或提供更详细的错误信息。\n", + "\n", + "- **calculate_average_age 函数**:\n", + " - 如果 `users` 列表为空,`calculate_average_age` 函数会抛出 `ZeroDivisionError`。建议在计算前检查列表长度,避免除零错误。\n", + "\n", + "#### 性能优化建议\n", + "- **用户查找和删除**:\n", + " - 当用户数量较多时,线性查找(`get_user` 和 `delete_user` 中的 `for` 循环)效率较低。可以考虑使用字典(`dict`)来存储用户信息,以提高查找和删除的性能。例如,可以将 `self.users` 改为 `self.users = {}`,键为用户名,值为用户信息字典。\n", + "\n", + "#### 最佳实践建议\n", + "- **异常处理**:\n", + " - 在 `calculate_average_age` 函数中,添加异常处理来捕获 `ZeroDivisionError`,并返回适当的默认值或错误信息。\n", + " \n", + " ```python\n", + " def calculate_average_age(users):\n", + " \"\"\"计算平均年龄\"\"\"\n", + " if not users:\n", + " return 0 # 或者返回其他默认值\n", + " total = sum(user[\"age\"] for user in users)\n", + " return total / len(users)\n", + " ```\n", + "\n", + "- **日志记录**:\n", + " - 在 `send_email` 函数中,使用日志记录库(如 `logging`)代替 `print`,以便更好地管理和控制日志输出。\n", + "\n", + " ```python\n", + " import logging\n", + "\n", + " def send_email(email, message):\n", + " \"\"\"发送邮件(模拟)\"\"\"\n", + " logging.info(f\"发送邮件到 {email}: {message}\")\n", + " return True\n", + " ```\n", + "\n", + "- **单元测试**:\n", + " - 编写单元测试来验证各个方法和函数的正确性。这有助于确保代码的稳定性和可靠性。\n", + "\n", + " ```python\n", + " import unittest\n", + "\n", + " class TestUserManager(unittest.TestCase):\n", + " def setUp(self):\n", + " self.manager = UserManager()\n", + " self.manager.add_user(\"Alice\", 30, \"alice@example.com\")\n", + " self.manager.add_user(\"Bob\", 25, \"bob@example.com\")\n", + "\n", + " def test_add_user(self):\n", + " self.assertEqual(len(self.manager.users), 2)\n", + "\n", + " def test_get_user(self):\n", + " user = self.manager.get_user(\"Alice\")\n", + " self.assertEqual(user[\"name\"], \"Alice\")\n", + " self.assertEqual(user[\"age\"], 30)\n", + " self.assertEqual(user[\"email\"], \"alice@example.com\")\n", + "\n", + " def test_delete_user(self):\n", + " self.assertTrue(self.manager.delete_user(\"Alice\"))\n", + " self.assertEqual(len(self.manager.users), 1)\n", + " self.assertIsNone(self.manager.get_user(\"Alice\"))\n", + "\n", + " if __name__ == \"__main__\":\n", + " unittest.main()\n", + " ```\n", + "\n", + "### 总结\n", + "总体来说,代码结构清晰,风格良好,但存在一些潜在的 bug 和性能优化空间。通过上述建议的改进,可以进一步提升代码的健壮性和性能。\n", + "代码审查完毕。。。\n" + ] + } + ], + "source": [ + "# 执行代码审查\n", + "print(\"=== 开始代码审查 ===\")\n", + "review_result = agent.run(f\"请审查以下Python代码:\\n\\n```python\\n{sample_code}\\n```\")\n", + "\n", + "print(review_result)\n", + "print(\"代码审查完毕。。。\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 第6部分:保存审查报告" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "✅ 审查报告已保存到 outputs/review_report.md\n" + ] + } + ], + "source": [ + "# 保存审查报告\n", + "with open(\"outputs/review_report.md\", \"w\", encoding=\"utf-8\") as f:\n", + " f.write(review_result)\n", + "\n", + "print(\"\\n✅ 审查报告已保存到 outputs/review_report.md\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 第7部分:总结与展望\n", + "\n", + "### 实现的功能\n", + "- ✅ 代码结构分析\n", + "- ✅ PEP 8风格检查\n", + "- ✅ 智能审查报告生成\n", + "\n", + "### 遇到的挑战\n", + "- 如何准确解析Python代码结构\n", + "- 如何设计合理的提示词让LLM生成高质量报告\n", + "\n", + "### 未来改进方向\n", + "- 支持更多编程语言\n", + "- 添加安全漏洞检测\n", + "- 集成更多静态分析工具\n", + "- 支持批量文件审查" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/Co-creation-projects/maligee-CodeReviewAgent/outputs/review_report.md b/Co-creation-projects/maligee-CodeReviewAgent/outputs/review_report.md new file mode 100644 index 00000000..e28d0438 --- /dev/null +++ b/Co-creation-projects/maligee-CodeReviewAgent/outputs/review_report.md @@ -0,0 +1,136 @@ +感谢你提供的工具执行结果。根据这些结果,我将对代码进行进一步的审查和调整。以下是详细的审查报告: + +## 代码审查报告 + +### 代码结构分析 +- **类结构**: + - `UserManager` 类实现了用户管理的基本功能,包括添加用户 (`add_user`)、获取用户信息 (`get_user`) 和删除用户 (`delete_user`)。 + - 类的方法逻辑清晰,每个方法负责一个特定的功能。 +- **函数**: + - `calculate_average_age` 函数用于计算用户的平均年龄。 + - `send_email` 函数用于模拟发送邮件。 + +### 风格问题 +- **行长度**: + - 第1行超过了79个字符。建议将多行注释或文档字符串分成多行,以符合PEP 8规范。 + - 例如: + ```python + """ + 示例代码:一个简单的用户管理系统 + 用于演示代码审查功能 + """ + ``` + +### 潜在bug +- **除零错误**: + - `calculate_average_age` 函数在用户列表为空时会引发除零错误。建议在计算前检查用户列表是否为空。 + ```python + def calculate_average_age(users): + if not users: + return 0 + total_age = 0 + for user in users: + total_age += user["age"] + return total_age / len(users) + ``` + +### 性能优化建议 +- **删除用户**: + - 在 `delete_user` 方法中,使用 `enumerate` 和 `del` 是合理的,但在删除多个用户时可能会导致性能问题。如果需要频繁删除用户,可以考虑使用列表推导式或其他数据结构(如字典)来提高效率。 + ```python + def delete_user(self, name): + self.users = [user for user in self.users if user["name"] != name] + return bool(self.users) + ``` + +### 最佳实践建议 +- **异常处理**: + - 在 `add_user` 方法中,可以考虑添加异常处理,以防止输入无效的数据类型。 + ```python + def add_user(self, name, age, email): + if not isinstance(name, str) or not isinstance(age, int) or not isinstance(email, str): + raise ValueError("Invalid input type") + user = {"name": name, "age": age, "email": email} + self.users.append(user) + return True + ``` +- **单元测试**: + - 建议编写单元测试来验证每个方法的正确性。可以使用 `unittest` 或 `pytest` 等测试框架。 + ```python + import unittest + + class TestUserManager(unittest.TestCase): + def setUp(self): + self.manager = UserManager() + self.manager.add_user("Alice", 30, "alice@example.com") + + def test_add_user(self): + self.assertTrue(self.manager.add_user("Bob", 25, "bob@example.com")) + self.assertEqual(len(self.manager.users), 2) + + def test_get_user(self): + user = self.manager.get_user("Alice") + self.assertEqual(user["name"], "Alice") + self.assertIsNone(self.manager.get_user("Charlie")) + + def test_delete_user(self): + self.assertTrue(self.manager.delete_user("Alice")) + self.assertFalse(self.manager.delete_user("Charlie")) + + def test_calculate_average_age(self): + self.manager.add_user("Bob", 25, "bob@example.com") + self.assertEqual(calculate_average_age(self.manager.users), 27.5) + + if __name__ == "__main__": + unittest.main() + ``` + +### 修正后的代码 +```python +""" +示例代码:一个简单的用户管理系统 +用于演示代码审查功能 +""" + +class UserManager: + """用户管理类""" + + def __init__(self): + self.users = [] + + def add_user(self, name, age, email): + """添加用户""" + if not isinstance(name, str) or not isinstance(age, int) or not isinstance(email, str): + raise ValueError("Invalid input type") + user = {"name": name, "age": age, "email": email} + self.users.append(user) + return True + + def get_user(self, name): + """获取用户信息""" + for user in self.users: + if user["name"] == name: + return user + return None + + def delete_user(self, name): + """删除用户""" + self.users = [user for user in self.users if user["name"] != name] + return bool(self.users) + +def calculate_average_age(users): + """计算平均年龄""" + if not users: + return 0 + total_age = 0 + for user in users: + total_age += user["age"] + return total_age / len(users) + +def send_email(email, message): + """发送邮件(模拟)""" + print(f"发送邮件到 {email}: {message}") + return True +``` + +希望这份审查报告对你有帮助!如果有任何其他问题或需要进一步的建议,请随时告诉我。 \ No newline at end of file diff --git a/Co-creation-projects/maligee-CodeReviewAgent/requirements.txt b/Co-creation-projects/maligee-CodeReviewAgent/requirements.txt new file mode 100644 index 00000000..920e4a08 --- /dev/null +++ b/Co-creation-projects/maligee-CodeReviewAgent/requirements.txt @@ -0,0 +1,13 @@ +# HelloAgents框架 +hello-agents[all]>=0.1.0 + +# Jupyter环境 +jupyter>=1.0.0 +notebook>=7.0.0 + +# 环境变量管理 +python-dotenv>=1.0.0 + +# 代码分析工具 +ast-comments>=1.0.0 +