阿里妹导读
文章通过一个模拟侦探游戏的例子展示了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 会觉醒出自己的意识呢?到时它们会如何思考自己的生存与延续?
import { 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);