API 实用示例
Halo API 实用示例:和数字人对话、为每位用户分配线程、回答数字人的提问、交换文件、让数字人互相调用,以及用 Webhook 触发运行。
这里的每个示例都完整地做完一件事,只用到本参考收录的操作。复制命令,替换其中的 <占位符>,再按你用的语言改写即可。
开始之前
- 按快速上手设置好
HALO_URL和HALO_TOKEN,并准备好其中的watch.mjs脚本:它能流式显示任何会话或线程。 - 使用 Halo 桌面版时,操作期间保持 设置 › 远程访问 开启:API 和 Webhook 只在开启时提供。
- 大多数示例需要数字人的
id和spaceId,先列出来:
curl -s "$HALO_URL/api/apps" -H "Authorization: Bearer $HALO_TOKEN"输出(节选):
{"success":true,"data":[{"id":"<APP_ID>","spaceId":"<SPACE_ID>","spec":{"name":"订单助手"},"status":"active"}]}响应里可能有密钥
每一项都带着数字人配置里保存的值,包括填在那里的密钥。不要把输出写进日志或工单。
向数字人提问并流式接收回答
在一个终端里监听数字人的主线程,它的标识是
app-chat:<APP_ID>:bashnode watch.mjs 'app-chat:<APP_ID>'在另一个终端里发送消息:
bashcurl -sX POST "$HALO_URL/api/apps/<APP_ID>/chat/send" \ -H "Authorization: Bearer $HALO_TOKEN" \ -H "Content-Type: application/json" \ -d '{"spaceId":"<SPACE_ID>","message":"还有哪些订单在等待付款?"}'输出:
json{"success":true,"data":{"conversationId":"app-chat:<APP_ID>"}}
消息被接收后调用立即返回。回复会流式出现在第一个终端里,以 [done] 结束。
不用 WebSocket 时,轮询 GET /api/apps/:appId/chat/status,直到 isGenerating 为 false:先等 2 秒,之后每次等待时间加倍。然后从 GET /api/apps/:appId/chat/messages 读取最后一条 assistant 消息。
同一线程还在回复时发来的消息不会另起一个处理过程:它会在当前步骤之后并入正在进行的回复,或等这一轮结束后合并处理。请一次发一条,等回复完成再发下一条,这样每条回复对应一条消息。
为每位用户分配线程
你的产品把用户转给数字人时,给每位用户一个单独的线程,他们的上下文就不会混在一起。用你那边的用户 ID 命名线程:
app-chat:<APP_ID>:http:direct:<USER_KEY><USER_KEY> 只能包含字母、数字、_ 和 -,最长 128 个字符,例如 user-12345。把这个标识作为 conversationId 传给 chat/send、chat/status、chat/messages,以及 WebSocket 的 subscribe:
curl -sX POST "$HALO_URL/api/apps/<APP_ID>/chat/send" \
-H "Authorization: Bearer $HALO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"spaceId":"<SPACE_ID>","conversationId":"app-chat:<APP_ID>:http:direct:user-12345","message":"我的订单 A1001 到哪了?"}'用户下次再来时,用同一个标识就能接着之前的对话。每位数字人的线程列表最多保留 500 个这样的线程,闲置 30 天的线程会从列表中移除,在 Halo 里改过名的除外。
其他形式的标识会返回 400;属于 IM 聊天的线程不能通过 API 写入。完整做法和错误处理见集成指南。
回答数字人的提问
运行期间(定时、Webhook 或手动启动),数字人可能停下来请人做决定。运行会一直等到有人回答,在 Halo 里或通过 API 都可以。
发现提问。 每个 WebSocket 连接都会收到
app:escalation:new,带有appId、entryId、question和choices。不用 WebSocket 时,列出最近的提问:bashcurl -s "$HALO_URL/api/apps/<APP_ID>/activity?type=escalation&limit=5" \ -H "Authorization: Bearer $HALO_TOKEN"输出(节选):
json{"success":true,"data":[{"id":"<ENTRY_ID>","type":"escalation","content":{"summary":"是否给订单 A1001 退款?","choices":["退款","保留订单"]}}]}条目里没有
userResponse,说明这个提问还没有人回答。有content.questions时,这一条同时问了几件事,每件都有自己的question和choices。按对方的决定回答:
bashcurl -sX POST "$HALO_URL/api/apps/<APP_ID>/escalation/<ENTRY_ID>/respond" \ -H "Authorization: Bearer $HALO_TOKEN" \ -H "Content-Type: application/json" \ -d '{"choice":"退款","text":"客户已经退回了商品"}'
choice必须与给出的某个choices完全一致。对方用自己的话回答时,只发text。- 条目带有
content.questions时,发送answers:按顺序每个问题一个{"choice":…}或{"text":…},每个问题都要回答。 - 第一个回答生效,不论来自 Halo 还是 API,而且无法撤回。已经回答过或已过期的提问会返回
"success": false和原因。
只发送真人做出的决定
运行会按你发送的内容继续。不要让脚本替别人挑选答案。
把文件交给工作区并读取结果
数字人在它的工作区文件夹里工作。你可以把文本文件放进去,再读取它产出的文件:
写入输入文件,放在工作区根目录(同名文件会被覆盖):
bashcurl -sX POST "$HALO_URL/api/spaces/<SPACE_ID>/artifacts/file" \ -H "Authorization: Bearer $HALO_TOKEN" \ -H "Content-Type: application/json" \ -d '{"parentPath":"","name":"orders.csv","content":"id,amount\nA1001,120\nA1002,80\n"}'请数字人处理,做法同第一个示例,比如「把 orders.csv 汇总成 report.md」。
找到并读取结果。 列出工作区的文件,取出目标文件的
path,再读取:bashcurl -s "$HALO_URL/api/spaces/<SPACE_ID>/artifacts" -H "Authorization: Bearer $HALO_TOKEN" curl -s -G "$HALO_URL/api/artifacts/content" --data-urlencode "path=<FILE_PATH>" \ -H "Authorization: Bearer $HALO_TOKEN"文本在
data.content里返回。图片、PDF 等二进制文件以 base64 编码返回,并带"encoding":"base64"。超过 10 MB 的文本文件或 50 MB 的二进制文件会被拒绝。
这些操作只能写入文本。要交付二进制文件,请直接放进运行 Halo 的电脑上的工作区文件夹。路径超出允许的工作区会返回 403。
让一个数字人向另一个提问
有时一个数字人需要另一个的答案,比如「周报」数字人要向「销售数据」数字人要数字。
在同一个工作区里,给提问的数字人打开 对话协作(数字人 › 选中它 › 能力与设置 › 模型与能力)。打开后它可以读取并给本工作区的其他会话发消息,包括其他数字人,并最多等待 5 分钟的回复。不需要调用 API:在它的指令里写清楚找谁、什么时候问。
跨工作区,或想让它通过 API 调用时,在同一个地方为提问的数字人打开 操作 Halo,或在它的 spec 里声明 permissions: ["halo-api-ref"]。之后它的每次会话都会得到 HALO_API_URL、HALO_API_TOKEN 和 HALO_SPACE_ID,以及它可以调用的操作说明。在它的系统提示词里加上类似这样的指令:
需要本周销售数据时,向「销售数据」数字人提问:
1. POST $HALO_API_URL/api/apps/<OTHER_APP_ID>/chat/send,请求头
"Authorization: Bearer $HALO_API_TOKEN",JSON 请求体
{"spaceId":"<OTHER_SPACE_ID>","conversationId":"app-chat:<OTHER_APP_ID>:http:direct:weekly-report","message":"<你的问题>"}
2. 每隔几秒轮询 GET $HALO_API_URL/api/apps/<OTHER_APP_ID>/chat/status?conversationId=<同一个标识>,
直到 isGenerating 为 false。
3. 从 GET $HALO_API_URL/api/apps/<OTHER_APP_ID>/chat/messages?conversationId=<同一个标识>
读取最后一条 assistant 消息。
一次只问一个问题。HALO_API_TOKEN是 Halo 按工作区签发的令牌,只在这台电脑上有效,也只能调用助手可用的操作。它不是访问密码。- 这两项权限默认都关闭,因为它们让数字人能做的事远超它自己的任务。只给你信任的数字人打开。
不要把访问密码放进数字人
不要把访问密码存进数字人的配置、指令或文件。能读到它们的人就能完全控制这台 Halo,而且每次导出数字人,密码都会跟着一起带走。
让其他系统启动数字人(Webhook)
其他系统推送事件时,数字人可以随之运行,比如商城来了新订单,或者代码平台推送了提交。
在数字人的 spec 里声明 Webhook。 选一个其他数字人没用过的小写路径,再生成一个随机密钥,例如用
openssl rand -hex 32:yamlsubscriptions: - source: type: webhook config: path: new-order secret: <WEBHOOK_SECRET>创建数字人时就写进去,或者之后用
PATCH /api/apps/:appId/spec加上,发送完整的subscriptions列表。每个事件都会作为事件数据交给运行,包含path、body和query;在系统提示词里引用请求体的字段即可,例如「订单号在body.orderId」。把事件发到
<HALO_URL>/hooks/<path>。这个路径不使用访问密码,而是对每个请求签名:签名是用密钥对原始请求体计算的 HMAC-SHA256 十六进制值,放在X-Hub-Signature-256: sha256=<hex>中发送:bashBODY='{"orderId":"A1001","dedupKey":"order-A1001"}' SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$WEBHOOK_SECRET" | sed 's/^.* //') curl -sX POST "$HALO_URL/hooks/new-order" \ -H "Content-Type: application/json" \ -H "X-Hub-Signature-256: sha256=$SIG" \ --data-binary "$BODY"输出:
json{"ok":true,"received":true}Halo 也接受放在
X-Signature-256或X-Webhook-Signature中的十六进制值(不带前缀)。要对实际发送的字节签名:重新序列化后的 JSON 对不上签名。
| 响应 | 含义 |
|---|---|
200 {"ok":true,"received":true} | 已接收,运行在后台开始 |
401 Invalid webhook signature | 签名缺失或错误 |
413 | 请求体超过 256 KB |
415 | 不是 application/json |
404 Webhook ingress not available | Halo 的数字人运行环境尚未就绪,例如刚刚启动时 |
- 重复事件:60 秒内
dedupKey相同的事件只运行一次。没有dedupKey时,发到同一路径的相同请求体视为同一个事件。请给每个不同的事件一个自己的dedupKey。 - 数字人正忙:同一个数字人不会同时运行两次。运行期间到达的事件会交给这次运行,或者并入下一次运行。
- 能否访问到:调用方必须能访问
HALO_URL:同一网络内用局域网地址,任何地方都能用 设置 › 远程访问 中的 互联网访问 地址。
没有密钥的 Webhook 谁都能调用
不设置 secret 时,任何能访问到这个地址的人都能用任意内容启动数字人。务必设置密钥。密钥保存在数字人的 spec 里,导出或发布数字人之前请先删掉。
相关页面
- 快速上手:设置变量、发出第一个请求、
watch.mjs - WebSocket 事件:全部事件和拼接回复的规则
- API:数字人:上面用到的全部操作
- Halo Server 集成指南:把数字人作为你的产品的 AI 后端