【AI基础专题】自主循环

上一节中,我们给模型增加了工具调用的能力,让模型能够动嘴又动手。但目前为止,模型还是被动地等待用户输入指令来调用工具。本节我们通过一个猜数字的例子,来演示如何让模型实现自主循环,根据工具的结果决定下一步的行动。

首先来定一个tools guess_number,它的功能是根据用户的猜测返回提示信息,文件路径src/tools/guess.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
export const guessTool: Tool = {
name: 'guess_number',
description: '猜一个1到100之间的整数。返回“大了”、“小了”或“猜对了”。',
parameters: {
type: 'object',
properties: {
number: { type: 'number', description: '你猜的数字' },
},
required: ['number'],
},
execute: async (args) => {
// 答案写死在工具内部,模型绝对不知道
const answer = 67;
const guess = args.number;
if (guess === answer) return '猜对了!';
return guess > answer ? '大了' : '小了';
},
};

然后来看看改造之前的模型表现:

/images/ai-based/06-01.png

/images/ai-based/06-02.png

特意贴了两张图,但是本质都是一样的。第一次猜测,工具提示他猜小了,但是这一次对话已经结束了,所以他只能等待用户再次输入来继续游戏。第二次猜测,工具提示他猜大了,但同样的,这一次对话也结束了。之所以会有图片里两种情况,是因为模型的输出是有随机性的,第一张图片里,模型没有回答问题,回了一个继续猜测的工具调用;第二张图片里,模型基于工具提示给出了总结和继续的请求。

接下来我们做一个小小的改动,我们将 src/index.ts

1
2
3
4
5
// 省略前面部分
if (reply.tool_calls && reply.tool_calls.length > 0) {
// ...
}
// 省略后面部分

改为

1
2
3
4
5
// 省略前面部分
while (reply.tool_calls && reply.tool_calls.length > 0) {
// 原逻辑不变
}
// 省略后面部分

只是从if改成了while,但是这个小小的改动就让模型具备了自主循环的能力。现在模型在第一次猜测后,工具提示他猜小了,但这一次对话并没有结束,而是继续等待模型的下一步输出。模型基于工具提示分析后,继续猜测,这样循环往复,直到模型猜对了为止。

最后我们重新设计一下系统提示词,来让模型的输出不至于太失控:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
你是一个自主智能体,能够使用工具来完成目标。

## 工作方式
你需要反复执行以下步骤,直到目标完成:

1. **思考**:分析当前状态,决定下一步行动。
2. **行动**:调用一个工具,或者给出最终答案。

## 行动格式
- 如果需要调用工具,只返回工具调用的 JSON,不要其他内容。

## 重要规则
- 如果工具返回了错误,分析错误并尝试修复,不要重复相同的错误调用。
- 如果连续三次调用没有进展,给出最终答案并说明遇到困难。
- 诚实:如果无法完成,直接说明,不要编造。
- 使用中文回复。

来看看最后的表现:
/images/ai-based/06-03.png

现在agent已经能够自主进行多轮工具调用了。每当模型调用工具后,工具的结果会被加入上下文,模型会基于新的上下文继续分析和决策,直到完成目标或者遇到困难为止。