f7a720204a
- 移除 GovAI, nomifun-tauri, 算力盒子 的 submodule 引用 - 添加所有子项目的完整源代码 - 保留原始 .git 为 .git.bak 备份
150 lines
6.9 KiB
Markdown
150 lines
6.9 KiB
Markdown
# Remote 能力 API · 对接示例 cookbook
|
||
|
||
> 配套 `remote-capability-api.zh.md`。所有示例用同一枚**伙伴访问令牌**(绑定到某个具体伙伴,调用即以该伙伴身份运行);端点 = WebUI/LAN 端口(默认 `25808`)或 `nomifun-web` 的服务端口。下文用 `$HOST`/`$TOKEN` 占位。
|
||
|
||
## 0. 先决:拿到端点 + 令牌
|
||
|
||
- **端点**:`http://<你的实例IP>:25808`(开启 WebUI 远程访问后),或本机 `http://127.0.0.1:<port>`。
|
||
- **令牌(运维侧一次性发放,绑定到一个伙伴)**:
|
||
- 桌面应用:WebUI/远程访问面板为某个伙伴点「生成访问令牌」(明文只显示一次)。
|
||
- 无头服务器:启动时 `NOMIFUN_COMPANION_TOKEN=$(openssl rand -hex 32) nomifun-web --host 127.0.0.1 --port 8787`,绑定到默认伙伴,把这串 hex 当令牌。
|
||
- 本机可信上下文(桌面 webview / dev NoAuth)可 `curl -X POST http://127.0.0.1:<port>/api/webui/companions/<companion-id>/access-token`(远程/普通 curl 会 403——铸造刻意只限本地可信)。
|
||
- 拿到后:`export TOKEN=<令牌>`;所有请求带 `Authorization: Bearer $TOKEN`。
|
||
- **以伙伴身份运行**:调用继承所绑定伙伴的模型/人格/知识库;`nomi_agent_run` 不带 `model` 时用该伙伴的 profile 模型,**所以该伙伴要先配置好可用模型**(否则铸造响应里会带 `warning`)。
|
||
- **能力发现**:`GET /v1/tools`(或 `/v1/tools?profile=agent` 精瘦集)列出所有工具名 + 描述 + JSON Schema;下文工具名以此为准(`nomi_agent_run` 一定有)。
|
||
|
||
---
|
||
|
||
## 1. MCP 客户端(Claude Code / Cursor / 任意 MCP Agent)—— 旗舰
|
||
|
||
最省事:把 NomiFun 作为一个 Streamable-HTTP MCP server 配进去。Claude Code / Cursor 的 `mcpServers`:
|
||
|
||
```json
|
||
{
|
||
"mcpServers": {
|
||
"nomifun": {
|
||
"type": "streamable-http",
|
||
"url": "http://$HOST:25808/mcp-agent",
|
||
"headers": { "Authorization": "Bearer $TOKEN" }
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
- `/mcp-agent` = curated「干活」工具集(agent/browser/computer/knowledge/files);要全平台控制面用 `/mcp`。
|
||
- 连上后 `tools/list` 即见 `nomi_*` 工具,`tools/call` 驱动。委派整件事就调 `nomi_agent_run`。
|
||
|
||
Python 通用 MCP SDK:
|
||
|
||
```python
|
||
from mcp.client.streamable_http import streamablehttp_client
|
||
from mcp import ClientSession
|
||
|
||
async def main():
|
||
headers = {"Authorization": "Bearer " + TOKEN}
|
||
async with streamablehttp_client("http://%s:25808/mcp" % HOST, headers=headers) as (r, w, _):
|
||
async with ClientSession(r, w) as s:
|
||
await s.initialize()
|
||
tools = await s.list_tools()
|
||
res = await s.call_tool("nomi_agent_run", {"goal": "调研 X 并写 notes.md"})
|
||
print(res)
|
||
```
|
||
|
||
---
|
||
|
||
## 2. curl(HTTP/REST,最通用)
|
||
|
||
```bash
|
||
# 列能力(精瘦 agent 档)
|
||
curl -s "http://$HOST:25808/v1/tools?profile=agent" -H "Authorization: Bearer $TOKEN"
|
||
|
||
# 委派一个目标(一句话把活交给一个自治 nomi agent)
|
||
curl -s -X POST "http://$HOST:25808/v1/tools/nomi_agent_run" \
|
||
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
||
-d '{"goal":"调研竞品定价并写入 notes.md","timeout_secs":600}'
|
||
# => 200 {"result":{"conversation_id":123,"status":"completed","text":"..."}}
|
||
# 长任务 => {"result":{"conversation_id":123,"status":"running",...}},之后轮询:
|
||
curl -s -X POST "http://$HOST:25808/v1/tools/nomi_agent_result" \
|
||
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
||
-d '{"conversation_id":123}'
|
||
|
||
# 调任意能力(名字来自 /v1/tools)
|
||
curl -s -X POST "http://$HOST:25808/v1/tools/<tool_name>" \
|
||
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{...args...}'
|
||
|
||
# 危险操作:先返回 {"needs_confirmation":true,...}(HTTP 409) → 向用户复述后带 confirm 重试
|
||
curl -s -X POST "http://$HOST:25808/v1/tools/<tool>" \
|
||
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
||
-d '{...args..., "confirm": true}'
|
||
```
|
||
|
||
**结果信封**:成功 `200 {"result": <payload>}`;工具报错 `422 {"error":..}`;需确认 `409 {"needs_confirmation":true,..}`;未知工具 `404`;无/错令牌 `401`。
|
||
|
||
### 流式(SSE)
|
||
|
||
```bash
|
||
curl -N -X POST "http://$HOST:25808/v1/tools/nomi_agent_run/stream" \
|
||
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
||
-d '{"goal":"..."}'
|
||
# 每行一个 data: JSON 事件(agent 的 text/tool_call delta),
|
||
# 末帧 data: {"type":"__result__","data":{"result":{...终值...}}}
|
||
```
|
||
|
||
---
|
||
|
||
## 3. Python(REST + SSE)
|
||
|
||
```python
|
||
import requests, json
|
||
BASE = "http://%s:25808" % HOST
|
||
H = {"Authorization": "Bearer " + TOKEN, "Content-Type": "application/json"}
|
||
|
||
# 调用
|
||
r = requests.post(f"{BASE}/v1/tools/nomi_agent_run", headers=H, json={"goal": "..."})
|
||
print(r.json()) # {"result": {...}} / {"error":...} / {"needs_confirmation":...}
|
||
|
||
# 流式
|
||
with requests.post(f"{BASE}/v1/tools/nomi_agent_run/stream", headers=H,
|
||
json={"goal": "..."}, stream=True) as resp:
|
||
for line in resp.iter_lines():
|
||
if line and line.startswith(b"data: "):
|
||
ev = json.loads(line[6:])
|
||
if ev.get("type") == "__result__":
|
||
print("FINAL:", ev["data"]); break
|
||
print("delta:", ev)
|
||
```
|
||
|
||
---
|
||
|
||
## 4. nomicore CLI(人/脚本)
|
||
|
||
```bash
|
||
export NOMIFUN_URL=http://$HOST:25808
|
||
export NOMIFUN_COMPANION_TOKEN=$TOKEN
|
||
|
||
nomicore tools # 离线列出 Remote 能力(无需运行实例)
|
||
nomicore call nomi_agent_run '{"goal":"..."}'
|
||
nomicore agent "调研竞品定价并总结" # nomi_agent_run 的便捷包装
|
||
```
|
||
|
||
---
|
||
|
||
## 5. 任意 HTTP 自动化(n8n / Zapier / Make / shell 脚本)
|
||
|
||
把一个 HTTP 节点指向 `POST http://$HOST:25808/v1/tools/{name}`,Header `Authorization: Bearer $TOKEN`,Body = 该工具的 JSON 参数。零 SDK。
|
||
|
||
## 6. 从 OpenAPI 生成客户端
|
||
|
||
`GET http://$HOST:25808/v1/openapi.json[?profile=agent]` 是 OpenAPI 3.1 契约 —— 喂给 `openapi-generator` 生成任意语言的 typed client,或导入 Postman/Insomnia/Bruno。
|
||
|
||
## 7. 接进别的 LLM agent 框架(LangChain / OpenAI tool-calling / 自研)
|
||
|
||
`GET /v1/tools` 每个工具自带 `name` + `description` + `input_schema`(标准 JSON Schema)。把它们直接注册成你框架的工具列表;模型决定调用某工具时,转一发 `POST /v1/tools/{name}`(带 `confirm` 处理 409)。等于让 NomiFun 全平台能力即插即用地成为你 agent 的工具集。
|
||
|
||
---
|
||
|
||
## 备注
|
||
|
||
- **安全**:持令牌即全权(≈授予 RCE 等价能力);只发给可信客户端,公网前置 TLS 反代,令牌可吊销(`DELETE /api/webui/companions/{id}/access-token`,只影响对应伙伴)。
|
||
- **MCP vs REST 选择**:agent/MCP 客户端用 `/mcp`(-agent);脚本/自动化/其它语言用 `/v1`;要实时进度用 `/v1/tools/{name}/stream`(SSE) 或(MCP 端长任务)`nomi_agent_run` 的 `{status:running}` 句柄 + `nomi_agent_result` 轮询。
|