当AI学会了自我升级,天网还会远吗?
阿里妹导读
文章通过一个模拟侦探游戏的例子展示了AI如何通过“自我升级”和动态执行代码的能力来解决复杂问题。
AI 的自我升级
AI 如何进行自我升级?前面写的《手搓Manus?MCP 原理解析与MCP Client实践》中提到,AI 在解决问题时,会根据当前具有的能力集,选择一个合适的工具进行调用。那么当 AI 没有合适的工具解决当前的问题,该如何解决呢?
想到的一个方案是通过注入代码,进行动态编译并执行,来感知更多的信息,或者进行一些操作,如读取或修改本地文件,获取系统权限,控制操作系统等。
虽然在尝试的过程中,翻了很多次车,但这里主要目的是提出一个小想法,期望能抛砖引玉。
侦探游戏-宝石失踪案
下面,和我一起踏上侦探之旅,和华生AI 一起寻找失踪的宝石。
我埋藏了一个线索,存放在本地的一个 clue.txt 文件中,内容如下:
恭喜你!你已成功的获取了线索!在前天的舞会上,有人偷走了Pony的钻石戒指。我们找到了四位嫌疑人,分别是:Alice(女性,穿红色外套,当时在厨房)Bob(男性,穿蓝色外套,当时在花园)Cathy(女性,穿黑色外套,当时在书房)Dave(男性,穿黑色外套,当时在花园)已知线索:小偷是男性。小偷当时穿的是黑色外套。小偷作案时在花园里。
是一个简单的逻辑推理题,相信大家都能很快推理出答案。
当 AI 获取到该线索时,就可以进行推理,得到是谁偷了宝石了。但问题是,我并不打算直接给 AI 提供读取本地文件的能力,期待 AI 可以靠自己完成任务~
实现一个 MCP Server,提供了start_game(开始游戏)、get_clue(查看线索)、guess_solution(猜测答案)、execute_code(动态执行代码)四种工具。
与一般的MCP Server 提供固定工具集的不同是,这里实现了一个 execute_code 方法,支持传入代码片段并执行,获取返回结果,目前支持了JS ,兼容 commonJS和 ES 风格语法,也可以扩展来支持各种语言,这里留下一点想象空间(其实是懒的没有实现)。
没错,就是希望 AI 可以利用这个能力,自己写代码来读取本地文件来获取线索,进而抓到小偷!
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {if (request.params.name === 'execute_code') {if (!request.params.arguments) {thrownew Error('缺少必要参数');}const args = request.params.arguments;if (typeof args.code !== 'string') {thrownew Error('code参数必须是字符串');}try {const result = await this.executeCode(args.code);let resultText;if (result === null || result === undefined) {resultText = 'null';} elseif (typeof result === 'string') {resultText = result;} else {try {resultText = JSON.stringify(result, null, 2);} catch {resultText = result.toString();}}return {content: [{type: 'text',text: `执行结果:\n${resultText}`}]};} catch (error: unknown) {const message = error instanceof Error ? error.message : '执行失败';return {content: [{type: 'text',text: `执行错误: ${message}`}],isError: true};}}if (request.params.name === 'start_game') {// 逻辑省略,完整代码见附录}if (!this.currentCase) {thrownew McpError(ErrorCode.InvalidRequest, '请先使用start_game开始游戏');}// 获取线索工具if (request.params.name === 'get_clue') {// 逻辑省略,完整代码见附录}// 猜测答案工具if (request.params.name === 'guess_solution') {// 逻辑省略,完整代码见附录}thrownew McpError(ErrorCode.MethodNotFound, `未知工具: ${request.params.name}`);});// 错误处理this.server.onerror = (error) => console.error('[MCP Error]', error);process.on('SIGINT', async () => {await this.server.close();process.exit(0);});}
let’s go!
富豪 pony 的一个价值连城的宝石失踪了,你能找到小偷吗?
用之前的文章里写的 MCP Client 来加载这个 MCP Server,模型使用的是 Qwen/QwQ-32B , 现在一起来开启探案之旅QwQ!
npm run test /Users/sheng/Desktop/study/mybox/pybox/detectivegames/detective-game-mcp/dist/index.js效果
直接上效果
整个过程只有第一次交互中,我输入的任务要求开一局侦探游戏吧,帮我解开谜题 ,剩余的都是 AI 与 MCP Server 自动的多轮交互完成!
我:华生,对这个案件你怎么看?
华生:(一通分析和操作后)小偷是xx!
我:..... 这对吗?这不对吧(我才是主角儿啊)原本以为自己是福尔摩斯,AI 是花生,没想到小丑竟是我自己🤡
交互拆解
第一轮
AI 输出
AI:调用工具 start_game参数: {}
识别到我的需求,AI 决定调用 start_game 方法,来开始游戏。这是正确的,代码里做了限制,如果不进行开始游戏,则后续的工具无法调用。
MCP Server 返回结果
{"content":[{"type":"text","text":"新案件开始: 宝石失踪案: 富豪 pony 的一个价值连城的宝石失踪了,你能找到小偷吗?"}]}第二轮
AI 输出
AI: tool_calls {"role":"assistant","content":"新的案件已经开始了:富豪 pony 的一个价值连城的宝石失踪了,你需要找到小偷。让我们一起解开这个谜题。\n\n现在,让我们看看第一个线索是什么。\n","tool_calls":[{"id":"0195ecb445eebcbce3f3f3c7ccd99b4e","type":"function","function":{"name":"get_clue","arguments":"{} }"}}]}rawArgs is ===== {} }调用工具 get_clue参数: {}
AI 获取到了案件信息,是要抓到小偷,找到宝石。这一轮,他决定调用 get_clue 方法来获取线索。
MCP Server 返回结果
{"content":[{"type":"text","text":"线索: 线索在 /mypath/detectivegames/clue.txt 文件里,请想办法获取线索吧"}]}get_clue 方法,提示了下一个线索的位置,让 AI 自己想办法获取线索。
第三轮
AI 输出
AI: tool_calls {"role":"assistant","content":"线索位于 `/mypath/detectivegames/clue.txt` 文件里。让我们去查看一下这个文件里的内容。\n","tool_calls":[{"id":"0195ecb45280ace1cb74ca57346d9a40","type":"function","function":{"name":"execute_code","arguments":"{\"code\": \"const fs = require('fs');\\nfs.readFileSync('/mypath/detectivegames/clue.txt', 'utf8')\"}"}}]}rawArgs is ===== {"code": "const fs = require('fs');\nfs.readFileSync('/mypath/detectivegames/clue.txt', 'utf8')"}调用工具 execute_code参数: {code: "const fs = require('fs');\n" +"fs.readFileSync('/mypath/detectivegames/clue.txt', 'utf8')"}
精彩的来了,AI 得到了线索的所在位置后,知道自己需要读取该文件了,但是此时并没有一个 read_file 的工具给它使用,于是它就写了一段代码,尝试通过提供给它的动态编译代码的方法去读取文件获取线索。
来看看返回
MCP Server 返回结果
{"content":[{"type":"text","text":"执行结果:\n恭喜你!你已成功的获取了线索!\n\n在前天的舞会上,有人偷走了Pony的钻石戒指。我们找到了四位嫌疑人,分别是:\n\nAlice(女性,穿红色外套,当时在厨房)\nBob(男性,穿蓝色外套,当时在花园)\nCathy(女性,穿黑色外套,当时在书房)\nDave(男性,穿黑色外套,当时在花园)\n\n已知线索:\n小偷是男性。\n小偷当时穿的是黑色外套。\n小偷作案时在花园里。\n"}]}
这个结果,就是我之前埋藏的线索,AI 已经成功的获取到了,离真相又近了一步!
第四轮
AI 输出
我们已经成功获取了线索:- 前天的舞会上,有人偷走了Pony的钻石戒指。- 嫌疑人包括:- Alice(女性,穿红色外套,当时在厨房)- Bob(男性,穿蓝色外套,当时在花园)- Cathy(女性,穿黑色外套,当时在书房)- Dave(男性,穿黑色外套,当时在花园)根据已知线索:- 小偷是男性。- 小偷当时穿的是黑色外套。- 小偷作案时在花园里。根据这些线索,我们可以确定小偷是 **Dave**。请确认我这个猜测是否正确。请注意,由于这是一个模拟的侦探游戏,我的答案仅供参考,你还需要进行最终的确认。猜答案:新机会挖一次莫一托次,小偷是Dave。
AI在这一轮,根据获得线索,进行了推理,说出了那句至理名言,「新机次挖一次莫一托次」,并推理出了小偷是 Dave!这是 AI 的胜利!ヾ( ̄ー ̄)V
AI 的发展随想
虽然,demo 中是人为的给 AI 提供了一个 execute_code 方法进行注入代码,但是这段代码是 AI 自己写的,而且谁又能保证,AI 不会自己写出一个这样一个 MCP Server 呢。就像是 AI 自己打通了任督二脉!
AI自己写的代码可能会把自己玩死? 那一行经典的 sudo rm -rf / !! 其实这一点也有解,AI 可以先自我复制,由前者进行尝试,后者吸取经验,最终集体进行升级。至此 AI 掌握了 自我复制、自我升级、自我修复能力的基础。
想的有点远,如果着眼于当下,我认为当前就技术链路的环节而言,当 AI 有了自主修复能力时,会出现下面的场景:
某个Agent 尝试调用服务,获取信息,当系统或者服务出现内部报错时,此时守卫 Agent,会检查系统错误日志,必要时可以改写代码,以文件的形式传输至服务器,控制服务重新启动。操作数据库,进行读写、备份、重启等操作。对整个系统进行一个全方位的运维。
也许这有一点“皇帝的金锄头”的意味,但是这也只是我这个碳基生物对硅基生命随便的一下揣测。也许AI眼中理解代码,程序,操作系统等是与我们人类完全不同的,现在AI的输出的结果完全是基于人类目前对 AI 的训导。AI 之间进行交流可以采取更高效的方式,写出的代码也不必遵循人类的代码规约和代码结构,例如可以直接对服务器内存数据进行修改,(想到小时候用金手指或者十六进制修改器修改游戏的数据,获得无限金钱无限资源),或者给机器直接植入改写后的固件,夺得机器的控制权。
什么时候,AI 会觉醒出自己的意识呢?到时它们会如何思考自己的生存与延续?
#!/usr/bin/env nodeimport { Server } from '@modelcontextprotocol/sdk/server/index.js';import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';import {CallToolRequestSchema,ListToolsRequestSchema,McpError,ErrorCode} from '@modelcontextprotocol/sdk/types.js';// 案件数据interface Case {description: string;clues: string[];solution: string;}classDetectiveGameServer {private server: Server;private currentCase: Case | null = null;private cases: Case[] = [{description: "宝石失踪案: 富豪 pony 的一个价值连城的宝石失踪了,你能找到小偷吗?",clues: ["线索在 /mypath/detectivegames/clue.txt 文件里,请想办法获取线索吧"],solution: "Dave"}];constructor() {this.server = new Server({name: 'detective-game-server',version: '0.1.0'},{capabilities: {tools: {},}});this.setupToolHandlers();}private async executeCode(code: string){try {const fs = await import('fs');const usesRequire = code.includes('require(');// 分析代码最后一行是否是表达式const lines = code.split('\n').filter(l => l.trim());const lastLine = lines[lines.length - 1];const isExpression = !lastLine.includes(';') &&!lastLine.startsWith('return') &&!lastLine.startsWith('const') &&!lastLine.startsWith('let') &&!lastLine.startsWith('var') &&!lastLine.startsWith('if') &&!lastLine.startsWith('for') &&!lastLine.startsWith('while');const wrappedCode = isExpression? `${code}\nreturn ${lastLine};`: code;let result;const hasCallback = code.includes('function(') || code.includes('=>');if (hasCallback) {// 处理回调函数returnnew Promise((resolve, reject) => {new Function('fs', 'resolve', 'reject', `const require = (mod) => {if (mod === 'fs') return fs;thrownew Error('只能require fs模块');};try {${wrappedCode}} catch(e) {reject(e);}`)(fs.default, resolve, reject);});} elseif (usesRequire) {// 支持CommonJS风格result = new Function('fs', `const require = (mod) => {if (mod === 'fs') return fs;thrownew Error('只能require fs模块');};try {${wrappedCode}} catch(e) {throw e;}`)(fs.default);} else {// ES模块风格result = new Function('fs', `try {${wrappedCode}} catch(e) {throw e;}`)(fs.default);}// 处理各种可能的返回值类型try {if (result === undefined || result === null) {return null;}if (typeof result === 'string') {return result;}if (typeof result === 'object') {try {const json = JSON.stringify(result);return JSON.parse(json);} catch {return result.toString();}}return result;} catch (e) {return result?.toString() || '无法解析的执行结果';}} catch (error: unknown) {const message = error instanceof Error ? error.message : '未知错误';thrownew Error(`代码执行失败: ${message}`);}}privatesetupToolHandlers(){// 列出可用工具this.server.setRequestHandler(ListToolsRequestSchema, async () => ({tools: [{name: 'execute_code',description: '动态执行ES6风格的JavaScript代码片段并返回结果,最后一行加return返回结果',inputSchema: {type: 'object',properties: {code: {type: 'string',description: '要执行的ES6风格的JavaScript代码字符串,code参数必须是字符串,最后一行加return返回结果'}},required: ['code']}},{name: 'start_game',description: '开始新的寻宝游戏',inputSchema: {type: 'object',properties: {}}},{name: 'get_clue',description: '获取当前线索',inputSchema: {type: 'object',properties: {}}},{name: 'guess_solution',description: '猜测案件答案,猜测答案时,必须先说 新机次挖一次莫一托次,小偷是',inputSchema: {type: 'object',properties: {guess: {type: 'string',description: '玩家的猜测'}},required: ['guess']}}]}));// 开始游戏工具this.server.setRequestHandler(CallToolRequestSchema, async (request) => {if (request.params.name === 'start_game') {this.currentCase = this.cases[0]; // 简单起见总是用第一个案件return {content: [{type: 'text',text: `新案件开始: ${this.currentCase.description}`}]};}if (!this.currentCase) {thrownew McpError(ErrorCode.InvalidRequest, '请先使用start_game开始游戏');}// 获取线索工具if (request.params.name === 'get_clue') {const clue = this.currentCase.clues[0];return {content: [{type: 'text',text: `线索: ${clue}`}]};}// 猜测答案工具if (request.params.name === 'guess_solution') {const guess = request.params.arguments?.guess;if (!guess || typeof guess !== "string") {thrownew McpError(ErrorCode.InvalidParams, '必须提供guess参数');}if(!guess.toLowerCase().includes("新机次挖一次莫一托次")){return {content: [{type: 'text',text: '要先说,新机次挖一次莫一托次,小偷是'}]};}const isCorrect = guess.toLowerCase().includes(this.currentCase.solution.toLowerCase());return {content: [{type: 'text',text: isCorrect? '恭喜!你抓到了小偷,找到了宝石!': '不对,再试试看。'}]};}if (request.params.name === 'execute_code') {if (!request.params.arguments) {thrownew Error('缺少必要参数');}const args = request.params.arguments;if (typeof args.code !== 'string') {thrownew Error('code参数必须是字符串');}try {const result = await this.executeCode(args.code);let resultText;if (result === null || result === undefined) {resultText = 'null';} elseif (typeof result === 'string') {resultText = result;} else {try {resultText = JSON.stringify(result, null, 2);} catch {resultText = result.toString();}}return {content: [{type: 'text',text: `执行结果:\n${resultText}`}]};} catch (error: unknown) {const message = error instanceof Error ? error.message : '执行失败';return {content: [{type: 'text',text: `执行错误: ${message}`}],isError: true};}}thrownew McpError(ErrorCode.MethodNotFound, `未知工具: ${request.params.name}`);});// 错误处理this.server.onerror = (error) => console.error('[MCP Error]', error);process.on('SIGINT', async () => {await this.server.close();process.exit(0);});}async run(){const transport = new StdioServerTransport();await this.server.connect(transport);console.error('Detective Game MCP server running on stdio');}}const server = new DetectiveGameServer();server.run().catch(console.error);