Agent开发-实现一个对话循环

41 阅读1分钟

任务:

把 juejin.cn/post/769598… 的手动流程改成一个 for 循环。

三样东西缺一不可:

  • 终止条件: finish_reason === 'stop' , 或者没有 tool_calls
  • 最大步数: 必须有,否则它会反复调同一个工具,安静地烧完你的额度
  • 异常兜底: 工具执行失败时,把错误信息作为结果回填给模型
const MAX_STEPS = 5; //最大调用次数

const runAgent = async () => {
  for (let setup = 1; setup <= MAX_STEPS; setup++) {
    const response = await client.chat.completions.create({
      model: MODEL,
      messages,
      tools,
    })

    const choice = response.choices?.[0];
    if (!choice) {
      console.log(`当前模型没有返回任何结果`);
      return;
    }
    // 需要关联上下文,所以模型的每次回答都要塞进messages中
    messages.push(choice.message);

    // 对话结束时
    if (choice.finish_reason === "stop") {
      console.log(`当前模型对话结束: ${choice.message.content}`);
      dumpMessages(messages);
      return;
    }

    // 对话超出长度被截取
    if (choice.finish_reason === "length") {
      console.log(`当前对话超出最大长度, 请调大max_token`);
      return;
    }

    // 模型需要调用工具
    if (choice.finish_reason === "tool_calls" && choice.message.tool_calls) {
      for (const toolCall of choice.message.tool_calls) {
        let content: string;
        // 错误处理
        try {
          const args = JSON.parse(toolCall.function.arguments)
          const name = toolCall.function.name
          content = JSON.stringify(executeTool(name, args))
        } catch (error) {
          // 模型工具就算执行失败,也要把错误回填给大模型,不然出现问题无法定位
          content = `工具执行失败: ${error instanceof Error ? error.message : String(error)}`
        }
        messages.push({
          role: "tool",
          tool_call_id: toolCall.id,
          content,
        });
      }
      // 继续
      continue;
    }
    // 当以上所有条件都没有匹配上的时候就跳出循环
    console.log(`没有出现预期的 finish_reason: ${choice.finish_reason}`);
    return;
  }
  如果达到了最大对话次数,就说明任务还在执行中,大概率是报错了,模型一直循环
  console.log(`对话达到最高次数 ${MAX_STEPS},仍未结束`);
}