shell-and-makefile-only-workflow-calling-llm-with-curl · ZH · 2026-10-10

不用任何 SDK:只用 Bash 脚本和 Makefile 调用聚合站

面向运维和数据人员,介绍如何仅用 Bash 脚本和 Makefile 配合 curl 调用聚合站 API。涵盖请求体构造、密钥安全存放、管道串联多次调用,无需安装 Python/Node。

为什么用 curl 就够了

聚合站提供标准的 HTTP API,任何能发请求的工具都可以调用。如果你日常在终端工作,不想为了跑一次模型调用而安装 Python 或 Node,curl 加上一点 shell 脚本就能完成全部事情。

本文以运维和数据人员熟悉的场景为例,说明如何用 Bash 和 Makefile 调用聚合站,包括:

  • 请求体怎么写
  • 如何安全存放 API key
  • 如何用管道把多个调用串起来

用 curl 发一个基础请求

聚合站的接口兼容 OpenAI 风格的 /v1/chat/completions。一个最小的请求如下:

curl -sS https://your-aggregator.example/v1/chat/completions \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-3-5-sonnet",
    "messages": [
      {"role": "user", "content": "用一句话解释什么是 HTTP"}
    ]
  }'

要点:

  • -sS:静默模式但保留错误输出,适合脚本。
  • -H:设置认证和内容类型。
  • -d:POST 数据,必须是合法 JSON。

注意:模型名称请以聚合站文档公布的标识符为准,不同聚合站可能略有差异。

请求体是纯 JSON,不要用单引号搞乱它

在 shell 里拼 JSON 最容易出错的是引号。推荐做法:

  • 把 JSON 写进独立文件,用 -d @file.json 读取。
  • 或者用 jq -n 生成 JSON,避免手动转义。

例如,创建一个 payload.json:

{
  "model": "gpt-4o-mini",
  "messages": [
    {"role": "user", "content": "把下面这句话翻译成英文:今天天气不错"}
  ]
}

然后调用:

curl -sS https://your-aggregator.example/v1/chat/completions \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d @payload.json

如果消息内容需要动态变化,可以用 jq 构造:

jq -n --arg msg "$USER_INPUT" '{
  model: "deepseek-chat",
  messages: [{role: "user", content: $msg}]
}' > payload.json

这样无论输入包含什么字符,都不会破坏 JSON 结构。

把 API key 放进环境文件,别写进脚本

直接把 key 硬编码在脚本里有泄露风险,尤其是在共享仓库中。推荐使用环境文件:

  1. 创建 ~/.aggregator.env,内容:
export API_KEY="sk-你的密钥"
export BASE_URL="https://your-aggregator.example/v1"
  1. 设置权限,仅自己可读:
chmod 600 ~/.aggregator.env
  1. 在脚本开头加载:
source ~/.aggregator.env

如果不想每次都手动 source,可以在 ~/.bashrc 里加一行 [ -f ~/.aggregator.env ] && source ~/.aggregator.env。注意不要把这个文件提交到 Git。

用管道串联多次调用

shell 管道可以把上一个调用的输出直接交给下一个调用。假设你想先让模型生成一句英文,再翻译成中文:

curl -sS "$BASE_URL/chat/completions" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-3-5-sonnet",
    "messages": [{"role": "user", "content": "写一句关于秋天的英文短句"}]
  }' \
| jq -r '.choices[0].message.content' \
| while read -r line; do
    jq -n --arg text "$line" '{
      model: "qwen-plus",
      messages: [{role: "user", content: ("翻译成中文:" + $text)}]
    }' \
    | curl -sS "$BASE_URL/chat/completions" \
        -H "Authorization: Bearer $API_KEY" \
        -H "Content-Type: application/json" \
        -d @- \
    | jq -r '.choices[0].message.content'
done

这里的关键点:

  • jq -r 提取纯文本,去掉 JSON 包装。
  • curl -d @- 从标准输入读取请求体。
  • while read 逐行处理,避免一次传入多行导致 JSON 混乱。

这种模式适合批量处理、链式调用,但要注意每次调用都会产生费用,请控制循环次数。

用 Makefile 管理常用调用

如果某些调用你经常重复,可以写一个简单的 Makefile:

BASE_URL ?= https://your-aggregator.example/v1
API_KEY ?= $(shell cat ~/.aggregator_key 2>/dev/null)

chat:
	@curl -sS $(BASE_URL)/chat/completions \
		-H "Authorization: Bearer $(API_KEY)" \
		-H "Content-Type: application/json" \
		-d @payload.json | jq -r '.choices[0].message.content'

translate:
	@jq -n --arg text "$(TEXT)" '{
		model: "gpt-4o-mini",
		messages: [{role: "user", content: ("翻译成英文:" + $text)}]
	}' | curl -sS $(BASE_URL)/chat/completions \
		-H "Authorization: Bearer $(API_KEY)" \
		-H "Content-Type: application/json" \
		-d @- | jq -r '.choices[0].message.content'

使用方式:

  • make chat 读取 payload.json 并输出回复。
  • make translate TEXT="你好" 直接翻译指定文本。

Makefile 的好处是把长命令封装成短任务,同时变量可以集中管理,方便切换模型或环境。

几个实用技巧

  • 检查 HTTP 状态码:加 -w "\n%{http_code}\n" 可以输出状态码,便于判断是否成功。
  • 超时与重试:--max-time 30 --retry 2 避免脚本卡死。
  • 流式输出:在请求体里加 "stream": true,然后用 jq 逐行解析 data: 行。不过流式处理在 shell 里稍复杂,非必要可跳过。
  • 错误处理:用 jq -e 检查 JSON 是否包含预期字段,否则退出非零状态。

费用提醒

聚合站按官方价 ×1.3 扣费,贡献 key 按官方价 ×1.1(优质 ×1.2)返 USDC。所有调用都会计入用量,写循环或批量脚本时建议先用小规模测试,确认逻辑后再放大。

用 Bash + curl + Makefile 调用聚合站,不需要任何 SDK,也不需要运行时环境。对于习惯命令行的运维和数据人员,这是最轻量的集成方式。