HTTP 协议 qeury 参数的说明
大模型就像是一个无所不知的助手,给它的任何任务都可以快速的完成。通过收集一些和大模型交互过程中不符合预期的案例,既可以提升和大模型的协作效率,也可以对自己的知识进行检验。
最近遇到的一个案例是,需要提供一个HTTP服务的接口,同事结合大模型给出的接口定义示例是:
GET /path?a=1&b=2&a=2&a=3
这里的问题,在query参数中出现了a的多次赋值。这个接口很可能是大模型结合代码中实际使用的服务框架给出来的,服务框架中对query参数重复时,可能会返回所有的值而非第一个或者最后一个。本文章就对HTTP的query参数进行说明吧。
协议约定
HTTP的结构在rfc3986#page-16中约定如下所示,query部分,也就是URI中?之后、#之前的这部分,则是我们要关注的。但是在[rfc3968]中没有进一步的对qeury的约定。
foo://example.com:8042/over/there?name=ferret#nose
\_/ \______________/\_________/ \_________/ \__/
| | | | |
scheme authority path query fragment
| _____________________|__
/ \ / \
urn:example:animal:ferret:nose
对HTTP/1.1的约定则可以在rfc7230中找到,其中定义的HTTP协议如下的(1)所示。通过对HTTP-message继续拆解,本次我们关注的query则是在request-target部分。
(1)
HTTP-message = start-line
*( header-field CRLF )
CRLF
[ message-body ]
(2)
start-line = request-line / status-line
(3)
request-line = method SP request-target SP HTTP-version CRLF
(4)
+---------+-------------------------------------------------+-------+
| Method | Description | Sec. |
+---------+-------------------------------------------------+-------+
| GET | Transfer a current representation of the target | 4.3.1 |
| | resource. | |
| HEAD | Same as GET, but only transfer the status line | 4.3.2 |
| | and header section. | |
| POST | Perform resource-specific processing on the | 4.3.3 |
| | request payload. | |
| PUT | Replace all current representations of the | 4.3.4 |
| | target resource with the request payload. | |
| DELETE | Remove all current representations of the | 4.3.5 |
| | target resource. | |
| CONNECT | Establish a tunnel to the server identified by | 4.3.6 |
| | the target resource. | |
| OPTIONS | Describe the communication options for the | 4.3.7 |
| | target resource. | |
| TRACE | Perform a message loop-back test along the path | 4.3.8 |
| | to the target resource. | |
+---------+-------------------------------------------------+-------+
(5)
request-target = origin-form
/ absolute-form
/ authority-form
/ asterisk-form
(6)
origin-form = absolute-path [ "?" query ]
请求 http://www.example.org/where?q=now 的 HTTP-message 为:
GET /where?q=now HTTP/1.1
Host: www.example.org
对request-target部分在定义在rfc7230#5.3,这里列举了四种request-target类型。一般业务使用时填充的是origin-form,在设置了代理的情况下会使用absolute-form。
这里的问题是,HTTP原始约定中没有对query的格式进行约定。query的具体解析方式依赖于http-server的具体实现,这就会产生一些细微的差异。
http-server 实现
通过大模型的汇总,常见的处理模式如下:
| 处理方式 | 示例 | 典型框架 |
|---|---|---|
| 最后值优先 | a=1&a=2 → a=2 | PHP(默认)、Express.js |
| 第一值优先 | a=1&a=2 → a=1 | 较少见 |
| 数组/列表 | a=1&a=2 → a=[1, 2] | Python、Ruby、Java Spring |
| 合并为字符串 | a=1&a=2 → a="1,2" | 某些旧系统 |
| 报错 | 返回 400 Bad Request | 严格验证的 API |
可以看到,具体的语言、框架可能对query中重复参数的处理不同。
影响
这个重复参数的接口约定没有实际使用,我们最终采取了POST+json request body的方式来约定了一版新的接口,使用了更为确定且清晰的约定。假设使用了大模型给出的原始版本,可能客户端的大模型会按照接口的约定来设定请求、服务端的大模型大模型按照接口的约定来进行处理。假设未来对这个服务的接口进行重构,新的语言/框架对于重复参数仅取最后一个值,而客户端感知不到这一变更,这就会产生功能上的混淆。
或许在使用大模型进行编程时,至少需要对接口的约定进行详尽的沟通,或者让大模型判断下潜在的兼容性影响。可能添加一些SKILL会好一些。大模型使用带来的一个影响是代码量的膨胀、服务数量的膨胀,在宏观角度下、大模型的SKILL不完全相同,一些细微的概念或者约定的差异可能就会给系统带来影响。又或者,未来程序员需要维护大模型的统一SKILL,其他的agent则在这一共享SKILL的共识下进行协作。