文章

LLM神秘小技巧

LLM神秘小技巧

工具调用

用工具调用优化提示词

当在制作一个多用途的agent时,系统提示词往往需要很长,覆盖所有可能的用途。 这无疑会增加模型的负担,增加模型的困惑度,模型无法正确遵循指令。 考虑到这种大量主要用于指导agent在不同场景下不同的行为,因此效率低下。 在某一实际场景下与当前场景无关的提示词将影响模型的判断,且token数增加使花费增加。

因此,这里提出了一个优化方案:

  • 一些针对特殊场景的提示词,可以作为工具调用的返回结果,以role=tool的身份出现。

例如,在已有的实践场景中,我为agent提供了“绘制数学函数”的工具,但绘制数学函数有复杂的格式要求。 这些格式若作为系统提示词的一部分,在大多数情况下,无需使用该工具,将浪费token,并反倒降低模型对其他指令的遵循。 因此,除提供正常的“绘制数学函数”工具外,额外提供了“绘制数学函数的格式说明”工具。 并在系统提示词中,要求模型在绘制数学函数前首先调用“绘制数学函数的格式说明”工具。 这样,在系统提示词中的长段的格式说明仅变为了一句话和一个工具,而当需要使用该工具时,模型会自行学习格式,最终调用“绘制数学函数”工具。

强制工具调用相对顺序

在上一节中,我们提出了一个优化方案,即一些针对特殊场景的提示词,可以作为工具调用的返回结果,以role=tool的身份出现。 但在实践中,由于模型的不确定性,模型在调用工具时,无法保证工具调用的相对顺序。 例如在上面的案例中,模型并没有按照要求调用“绘制数学函数的格式说明”工具,而是直接调用了“绘制数学函数”工具。导致模型无法正确绘制数学函数。

为了解决这个问题,我找到了一个强制工具调用相对顺序的方案。 在“绘制数学函数”工具的params中,添加一个key字段,该字段的值为一串随机字符串。 并提示模型,key必须从“绘制数学函数的格式说明”工具的返回值中获取。 这样,由于模型需要调用“绘制数学函数”时,无法给出key的值,将迫使模型调用先调用“绘制数学函数的格式说明”工具,以获取key的值。

该方法可以与上一节中的优化方案结合使用,减少提示词,并保证模型必须学习了“绘制数学函数的格式说明”,再调用目标工具。

该方法亦可拓展,通过该方法可以建立工具之间的拓扑关系,从而引导模型按照一定的顺序调用工具。

其他

与大模型沟通json vs yaml

与大模型沟通时,常需要大模型给出格式化的数据再进行解析。目前在市面上主流的格式为json。 多数的工具调用等,其内部均使用json格式,但在实际应用中,让大模型给出yaml,效果往往优于json。

转义

yaml一大优势是很大程度上避免了转义。 例如,双引号在json中需要表示为:

0
1
2
{
  "text": "\""
}

而在yaml中,可以表示为:

0
text: '"'

在“text”不是一个普通的string,而是例如一段程序代码时,会有更明显的区别。 例如考虑如下代码:

0
1
2
3
4
5
6
#include <iostream>
int main()
{
    std::cout << "Hello, World!" << std::endl;
    std::cout << "Some special characters: \" \' \t \\" << std::endl;
    return 0;
}

在json中,需要表示为:

0
1
2
{
    "text": "#include <iostream>\nint main()\n{\n    std::cout << \"Hello, World!\" << std::endl;\n    std::cout << \"Some special characters: \\\" \\' \\t \\\\\" << std::endl;\n    return 0;\n}"
}

而在yaml中,可以表示为:

0
1
2
3
4
5
6
7
text: |
    #include <iostream>
    int main()
    {
        std::cout << "Hello, World!" << std::endl;
        std::cout << "Some special characters: \" \' \t \\" << std::endl;
        return 0;
    }

显然,yaml可以使代码几乎无需转义,原样输出,对大模型更友好,避免了错误的转义造成的问题。 当程序被替换为json时同理,json嵌套导致更复杂的转义问题,例如双引号在一次转义后变为\",在二次转义后变为\\\"

若出现代码包含转义的双引号(如果需要大模型写代码,这很常见),然后这段代码被作为一个json字段存储,而这个json字段由给到一个大模型(例如上下文压缩时), 那么这种次转义造成反斜杠翻倍,让大模型需要处理的转义,也很容易导致大模型输出的json不符合json规范,然而yaml则可以避免这个问题。

所以当文本内容本身为json/代码等时,使用yaml无疑更优。 并且yaml允许没有引号的字段,这可以大大减轻格式要求对大模型的负担。

嵌套

当出现复杂的json对象,例如

0
1
2
3
4
5
6
{
  "obj": {
    "obj": {
      "text": "Hello, World!" 
    }
  }
}

大模型在末尾要生成连续的},如果模型生成了正确的缩进,这可能有利于模型理解当前所在的层级,而若没有,这种反括号的连续生成,将使得模型生成正确json的概率大大降低。

而yaml则没有这个问题,yaml将迫使大模型缩进,这些缩进有利于模型理解当前所在的层级,当离开一个层级时,也无需像json那样生成},仅需改变缩进即可。

本文由作者按照 CC BY-NC 4.0 进行授权, 未经许可不得转载。