- AI Agent
- 人工智能
- 大模型
- AI 应用
- 工具调用
- 本地部署
- MCP Clients
- Agent 记忆
【免费下载链接】Operit
The most powerful AI agent and AI chat software on Android/Operit是一款Android上能力最为强大、发展最久的AI Agent
本指南以 docs/doc-src/feature-protocol/external_http_chat.md 为核心,完整讲解 Operit 内置的局域网 HTTP 聊天接口:如何在应用内启用服务、如何配置 Bearer Token 鉴权、如何用curl完成同步调用、SSE 流式调用与异步回调三种模式,并深入其底层实现源码,说明每个参数的语义与生效条件。读完本文,你可以在同一局域网内的电脑、脚本或另一台设备上,以标准 HTTP/JSON 方式向 Operit 发送消息并取回 AI 回复,为自动化集成、本地工作流或外部控制台提供统一的调用入口。
1. 接口定位:HTTP 版的外部聊天入口
External HTTP Chat API 是 Operit 新增的局域网 HTTP 聊天接口。它与现有的EXTERNAL_CHATIntent 广播接口(协议说明见 external_intent_chat.md)语义完全一致——复用相同的请求字段与行为规则,只是入口从 Android 广播换成了 HTTP 端点。也就是说,凡是广播接口能做的事(发消息、新建对话、启动浮窗、过滤工具状态等),HTTP 接口都能以更通用的方式做到,便于任何支持 HTTP 的语言与平台接入。
从源码结构看,整个能力由三部分组成:
- 服务器实现:ExternalChatHttpServer.kt,基于 NanoHTTPD 监听端口、分发路由;
- 请求模型与执行器:ExternalChatModels.kt、ExternalChatRequestExecutor.kt,负责解析 JSON 请求并调度聊天;
- 配置存储:ExternalHttpApiPreferences.kt,通过 DataStore 持久化开关、端口与 Token。
2. 启用方式与监听配置
在应用内依次进入设置 → 数据和权限 → 外部 HTTP 调用,然后:
- 打开"启用"开关;
- 记录页面展示的监听地址与 Bearer Token(页面会根据当前设备的局域网 IPv4 地址自动生成形如
http://192.168.x.x:8094的访问地址列表,见 ExternalHttpChatSettingsScreen.kt); - 如有需要,修改端口并保存。
默认端口为8094。在源码中该默认值定义于 ExternalHttpApiPreferences.kt(DEFAULT_PORT = 8094),端口合法范围校验为1..65535(isValidPort)。服务器监听地址为0.0.0.0(见ExternalChatHttpServer.kt的LISTEN_HOST),因此同一局域网内的其他设备均可访问。
启用后服务器以NanoHTTPD启动,路由分发逻辑位于serve()方法:
GET /api/health→ 健康检查;POST /api/external-chat→ 聊天调用;OPTIONS→ CORS 预检;- 其余
/api/*未知路径 →404(API endpoint not found); - 此外同一端口还承载了 Web 聊天静态页面与
api/web/*接口,以及 A2A(/a2a与/.well-known/agent-card.json,见 external_a2a_server.md)。
3. 鉴权:Bearer Token
除OPTIONS预检请求外,所有请求都必须携带:
Authorization: Bearer YOUR_TOKENBearer Token 在首次启用时自动生成,也可以在设置页里手动重置。源码中的ensureBearerToken()/resetBearerToken()使用UUID.randomUUID().toString().replace("-", "")生成 32 位十六进制串,经 DataStore 持久化(external_http_api_preferences,键external_http_api_bearer_token)。
服务端的校验逻辑位于requireBearerToken():Token 为空时返回401 Bearer token not configured;请求头非Bearer前缀或 Token 不匹配时返回401 Unauthorized。Authorization头解析时对大小写不敏感(ignoreCase = true)。
注意:该接口为局域网明文 HTTP,Token 仅用于避免局域网内随意调用,并不提供传输加密。如涉及敏感数据,建议仅在可信网络中使用。
4. 接口总览
| 接口 | 方法 | 说明 |
|---|---|---|
/api/health | GET | 检查服务联通性与鉴权是否正常 |
/api/external-chat | POST | 发送聊天请求(同步 / SSE 流式 / 异步回调) |
4.1 健康检查GET /api/health
用于验证服务是否可达、鉴权是否配置正确。对应实现返回ExternalChatHealthResponse,其中enabled取自定义配置、service_running固定为true(服务器在运行才可能响应)、port为当前监听端口、version_name来自BuildConfig.VERSION_NAME。
curl -H "Authorization: Bearer YOUR_TOKEN" "http://DEVICE_IP:8094/api/health"返回示例:
{ "status": "ok", "enabled": true, "service_running": true, "port": 8094, "version_name": "1.10.0+1" }4.2 请求体字段(POST /api/external-chat)
请求体为 JSON,字段复用现有 Intent 接口语义(与 external_intent_chat.md 的 extras 一一对应),完整字段见ExternalChatHttpRequest:
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
request_id | String | 自动生成 UUID | 业务侧请求 ID,原样回传,便于关联请求/响应 |
message | String | 必填 | 要发送给 AI 的文本;为空则返回400 Missing extra: message |
group | String | - | create_new_chat=true时用于新对话分组 |
create_new_chat | Boolean | false | 是否强制创建新对话再发送消息 |
chat_id | String | - | 指定发送到某个对话(仅create_new_chat=false时生效) |
create_if_none | Boolean | true | 未指定chat_id且当前没有对话时是否自动创建;false且无对话则失败 |
show_floating | Boolean | false | 是否启动/显示悬浮窗服务(FloatingChatService) |
return_tool_status | Boolean | true | 是否返回工具状态相关内容;false时移除<tool*>、<tool_result*>、<status>辅助内容 |
initial_mode | String | 沿用上次/WINDOW | 浮窗初始模式,仅show_floating=true时有意义 |
auto_exit_after_ms | Long | -1 | show_floating=true时自动退出/关闭浮窗的超时毫秒数 |
timeout_ms | Long | -1 | 聊天超时毫秒数(HTTP 接口新增) |
stop_after | Boolean | false | 本次请求结束后是否停止聊天服务 |
HTTP 接口新增字段:
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
stream | Boolean | false | true时改为按 SSE 分块返回 |
response_mode | String | sync | sync或async_callback,解析不区分大小写 |
callback_url | String | - | response_mode=async_callback时必填,且必须为http/https |
补充说明(均有源码支撑):
return_tool_status默认为true;设为false时,外部返回中的<tool*>、<tool_result*>、<status>会被过滤,减小ai_response与 SSEdelta的传输体积。实现位于 ExternalChatResponseSanitizer.kt,通过 XML 流切分识别标签名并剔除上述三类,同时压缩多余空行;initial_mode仅在show_floating=true时有意义,可选值:WINDOW、BALL、VOICE_BALL、FULLSCREEN、RESULT_DISPLAY、SCREEN_OCR;- 如果
show_floating=true且未传initial_mode,则沿用当前/上次保存的浮窗模式;首次默认WINDOW; stream=true与response_mode=async_callback不能同时使用,同时出现返回400 Bad Request(错误文案Invalid parameter: stream=true is not compatible with async_callback);response_mode非法值返回400 Invalid parameter: response_mode must be sync/async_callback;async_callback缺少callback_url返回400 callback_url is required for async_callback;非http/https返回400 callback_url must be http/https;message缺失返回400 Missing extra: message。
5. 同步调用(response_mode=sync)
同步模式会阻塞请求直到聊天完成,一次性返回完整 JSON。示例:
curl -X POST "http://DEVICE_IP:8094/api/external-chat" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json; charset=utf-8" \ -d '{ "message": "你好,帮我总结今天的待办", "response_mode": "sync", "show_floating": true, "return_tool_status": false, "initial_mode": "WINDOW" }'返回示例:
{ "request_id": "f0fdde0c-3f68-43c1-ae43-9d7736d6fd7d", "success": true, "chat_id": "1742558116153", "ai_response": "这是今天的待办总结……" }如果请求本身格式正确,但聊天执行失败,也会返回同结构 JSON,只是success = false,且error携带失败原因。从源码看,服务端通过runBlocking调用ExternalChatRequestExecutor.execute(),后者内部先做prepareRequest(校验消息、按需启动浮窗、按需建会话),再调用StandardChatManagerTool.sendMessageToAI,最后经ExternalChatResponseSanitizer处理返回。
6. SSE 流式返回(stream=true)
当stream=true时,接口返回text/event-stream,每个分块都是标准 SSE 格式,便于实时展示增量输出。示例:
curl -N -X POST "http://DEVICE_IP:8094/api/external-chat" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Accept: text/event-stream" \ -H "Content-Type: application/json; charset=utf-8" \ -d '{ "message": "请一步步解释这个问题", "stream": true, "show_floating": true, "return_tool_status": false, "initial_mode": "WINDOW" }'返回事件类型:
start:已接受请求并拿到chat_id;delta:本次增量文本;done:全部完成,ai_response为完整结果;error:处理失败。
返回示例:
event: start data: {"event":"start","request_id":"req-001","chat_id":"1742558116153"} event: delta data: {"event":"delta","request_id":"req-001","chat_id":"1742558116153","delta":"你好,"} event: delta data: {"event":"delta","request_id":"req-001","chat_id":"1742558116153","delta":"下面我来解释。"} event: done data: {"event":"done","request_id":"req-001","chat_id":"1742558116153","success":true,"ai_response":"你好,下面我来解释。"}错误示例:
event: error data: {"event":"error","request_id":"req-001","success":false,"error":"Invalid parameter: stream=true is not compatible with async_callback"}注意事项:
- SSE 模式下建议显式发送
Accept: text/event-stream; - 连接关闭后,服务端会尝试取消这次 AI 响应。这在源码中有明确实现:SSE 响应使用
PipedInputStream/PipedOutputStream管道流,通过FilterInputStream.close()在客户端断开时调用streamJob.cancel()并取消底层responseStreamSession,避免后台继续空跑消耗算力; - 服务端对
text/event-stream响应禁用 gzip(useGzipWhenAccepted),并附带Cache-Control: no-cache、Connection: keep-alive、X-Accel-Buffering: no头,保证流式实时性; - 响应为 chunked 编码,
start事件在executor.startStreaming()成功返回Started后立即下发,delta逐块透传(每条data:行内的换行会被拆分为多个data:行以符合 SSE 规范),done在流结束后携带完整ai_response。
7. 异步回调(response_mode=async_callback)
异步模式立即返回"已接受",AI 完成后 Operit 主动向callback_url推送结果,适合不希望长期占用 HTTP 连接的业务场景。示例:
curl -X POST "http://DEVICE_IP:8094/api/external-chat" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json; charset=utf-8" \ -d '{ "message": "继续刚才的话题", "response_mode": "async_callback", "callback_url": "http://YOUR_PC:8080/callback" }'立即返回:
{ "request_id": "dca1a2e0-8f7e-4bf8-9523-a4b7bdf2fd13", "accepted": true, "status": "accepted" }AI 完成后,Operit 会向callback_url发送一次POST application/json回调,请求体仍然是:
{ "request_id": "dca1a2e0-8f7e-4bf8-9523-a4b7bdf2fd13", "success": true, "chat_id": "1742558116153", "ai_response": "……" }注意:
- JSON 请求体默认按 UTF-8 处理,建议显式发送
Content-Type: application/json; charset=utf-8。源码中resolveRequestCharset()会从Content-Type中解析charset,解析失败或缺失时回退到 UTF-8(application/json规范默认 UTF-8); - v1 不做重试;
- callback 非 2xx 或网络失败只记日志,不自动补发。实现见
postCallback():使用 OkHttpClient(retryOnConnectionFailure(false),即不自动重连)执行一次POST,非成功响应或异常仅写入AppLogger警告/错误日志。
8. 行为语义总表(与 Intent 接口保持一致)
| 条件 | 行为 |
|---|---|
show_floating=true | 尝试启动FloatingChatService(Manifest 中注册于app/src/main/AndroidManifest.xml的.services.FloatingChatService) |
show_floating=true+initial_mode | 按该模式启动浮窗(WINDOW/BALL/VOICE_BALL/FULLSCREEN/RESULT_DISPLAY/SCREEN_OCR) |
show_floating=true未传initial_mode | 沿用当前/已保存模式;首次默认WINDOW |
create_new_chat=true | 先创建新对话(可带group)再发送;此时忽略chat_id |
chat_id | 仅在create_new_chat=false时生效(发送时作为chat_id参数传入) |
create_if_none=false且当前无对话 | 返回失败(错误No current chat and create_if_none=false) |
stop_after=true | 请求结束后尝试停止聊天服务(执行器 cleanup 阶段调用stop_chat_service) |
return_tool_status=false | 过滤工具状态相关 XML(tool/tool_result/status),减小ai_response/SSEdelta体积 |
stream=true | 响应改为 SSE,不再返回单个固定 JSON 响应体 |
stream=true+response_mode=async_callback | 返回400 Bad Request |
这些语义与现有EXTERNAL_CHATIntent 接口保持一致,可对照 external_intent_chat.md 中的参数表交叉验证。执行器源码prepareRequest()中的先后顺序为:校验message→(show_floating时)启动聊天服务并注入initial_mode/timeout_ms→(create_if_none=false且无chat_id时)检查当前会话存在性 →(create_new_chat时)创建新对话 → 组装send_message_to_ai工具参数(message、可选chat_id、可选timeout_ms)→ 执行 →(stop_after时)停止服务。
9. 设置页内置的快捷示例
设置页 ExternalHttpChatSettingsScreen.kt 会根据当前设备 IP 与端口动态生成可直接复制的curl示例(syncCurl、asyncCurl、healthCurl),并展示 Web 入口(/)、Web API(/api/)与 A2A Agent Card(/.well-known/agent-card.json)地址,方便在启用服务后立即本地调试:
- 同步示例:
curl -X POST "http://<ip>:8094/api/external-chat" -H "Authorization: Bearer <token>" ... -d '{"message":"你好","response_mode":"sync","show_floating":true,"initial_mode":"WINDOW","return_tool_status":false}' - 异步示例:
... -d '{"message":"你好","response_mode":"async_callback","callback_url":"http://YOUR_PC:8080/callback"}' - 健康检查:
curl -H "Authorization: Bearer <token>" "http://<ip>:8094/api/health"
另外,服务端为所有 API 响应都附加了 CORS 头(Access-Control-Allow-Origin: *、Access-Control-Allow-Methods: GET, POST, PATCH, DELETE, OPTIONS、Access-Control-Allow-Headers: Authorization, Content-Type, Accept、Access-Control-Max-Age: 3600),因此浏览器端脚本(例如自建的 Web 调试页)也可以直接跨域调用该接口。
10. 常见问题排查
401 Unauthorized:确认Authorization头格式为Bearer <token>,且 Token 与设置页一致;Token 可在设置页重置后更新调用方。400 Missing extra: message:请求体缺少或为空message。400 Invalid parameter: response_mode must be sync/async_callback:response_mode拼写错误或使用了未支持的值。400 Invalid parameter: callback_url is required...:async_callback模式下漏传callback_url。400 Invalid parameter: stream=true is not compatible with async_callback:stream与异步回调不能共存。404 API endpoint not found:路径写错(正确路径为/api/health与/api/external-chat)。- 连接超时:确认调用方与手机处于同一局域网,且设置了正确端口;服务器监听
0.0.0.0,无需额外内网穿透。 Content-Length缺失导致请求体读取失败:HTTP 客户端需正确设置Content-Length(curl -d会自动处理),服务端据此读取请求体(见readRequestBody())。
本文涉及的协议文档与实现源码均位于当前仓库:external_http_chat.md、external_intent_chat.md、ExternalChatHttpServer.kt、ExternalChatModels.kt、ExternalChatRequestExecutor.kt、ExternalChatResponseSanitizer.kt、ExternalHttpApiPreferences.kt。
- AI Agent
- 人工智能
- 大模型
- AI 应用
- 工具调用
- 本地部署
- MCP Clients
- Agent 记忆
【免费下载链接】Operit
The most powerful AI agent and AI chat software on Android/Operit是一款Android上能力最为强大、发展最久的AI Agent
相关推荐
Cog HTTP API 实战指南:同步/异步预测、SSE 流式输出、Webhook 回调与文件上传
Cog HTTP API 实战指南:同步/异步预测、SSE 流式输出、Webhook 回调与文件上传 Cog 构建的 Docker 镜像在启动后会内置一个完整的
MLOps容器模型推理服务开发工具CubeSandbox 鉴权配置指南:Cube API Server 回调式鉴权与密钥鉴权实战
CubeSandbox 鉴权配置指南:Cube API Server 回调式鉴权与密钥鉴权实战 导读 本文以 CubeSandbox 项目中 Cube API
Agent 沙箱虚拟化云原生人工智能后端容器运行时SOFARPC调用方式完全掌握:同步、异步、回调、泛化调用实战
SOFARPC调用方式完全掌握:同步、异步、回调、泛化调用实战 SOFARPC是一款高性能、高扩展性的生产级Java RPC框架,提供了丰富的服务调用方式,包括
后端RPC框架微服务服务注册发现负载均衡
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考