mcpField
O
openhands-api

openhands-api

github.com/openhandsAgent 工作流17564 次安装今天更新
代码仓库

关于 openhands-api

openhands-api 是面向使用 OpenHands Cloud 和 Agent Server API 的开发者技能,涵盖会话操作、执行监控、任务委派及本地 Agent Canvas 工作流,并提供可复制的客户端示例。

主要功能

  1. 01OpenHands Cloud V1 REST API 工作流
  2. 02创建与检查会话
  3. 03异步启动和执行监控
  4. 04并行及后台会话委派
  5. 05沙盒生命周期管理与事件访问
  6. 06本地 Agent Canvas 后端会话

适用场景

  1. 01通过代码启动并监控 Cloud 会话
  2. 02委派独立或并行任务
  3. 03访问沙盒中的 Agent Server 端点
  4. 04检查本地 Agent Canvas 后端会话

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/openhands/extensions --skill openhands-api

安装后在 Claude Code 中输入/openhands-api确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

README.md
.plugin/
plugin.json
references/
example_prompt.md
README.md
TROUBLESHOOTING.md
windows.md
scripts/
openhands_api.py
openhands_api.ts

详细介绍

此技能介绍 OpenHands Cloud API(V1)、常用的 agent-server API,以及小巧、易于复制的客户端。 此技能中 shell 示例对应的 Windows PowerShell 写法见 references/windows.md。

本技能有意聚焦于常见的 OpenHands API 工作流:

  • 默认使用 OpenHands Cloud(https://app.all-hands.dev)。
  • 使用 /api/v1/... 下的 V1 app server REST API。
  • 包含少量使用 X-Session-API-Key 的 agent server 端点(位于沙盒内)。
  • 涵盖 多会话委派模式:需要全新的上下文窗口或后台工作时,启动单独的 Cloud 会话。
  • 涵盖 本地 Agent Canvas 后端会话:直接调用本地 agent server,以启动或检查会话。

何时使用此技能

在以下情况下使用此技能:

  • 通过代码启动或检查 OpenHands Cloud 会话
  • 通过轮询启动任务来监控异步启动
  • 监控长时间运行的作业的执行状态
  • 创建单独的 Cloud 会话以并行工作或执行后台任务
  • 会话运行后访问沙盒 agent-server 端点
  • 在本地 Agent Canvas 后端或本地 agent server 上启动或检查会话

身份验证

App server(Cloud)

使用 Bearer 身份验证:

  • 标头:Authorization: Bearer <OPENHANDS_CLOUD_API_KEY>
  • 首选环境变量:OPENHANDS_CLOUD_API_KEY
  • 向后兼容的环境变量:OPENHANDS_API_KEY
Agent server(沙盒内)

使用会话身份验证:

  • 标头:X-Session-API-Key: <session_api_key>

如何获取 agent_server_url 和 session_api_key:

  1. 通过 app server(Bearer 身份验证)启动或获取 app 会话,例如:
    • POST /api/v1/app-conversations
    • 或 GET /api/v1/app-conversations?ids=<conversation_id>
  2. 在返回的 JSON 中查找沙盒/运行时连接字段(不同部署/版本中的字段名称略有差异)。常见模式:
    • 包含 agent_server_url(或类似字段)的沙盒对象
    • 类似 session_api_key 的会话密钥
  3. 使用这些值直接调用 agent server:
    • 基础地址:{agent_server_url}/api/...
    • 标头:X-Session-API-Key: <session_api_key>

示例(常见字段名称;请根据你的部署调整):

# using the minimal Python client (`OpenHandsAPI`)
conv = api.app_conversation_get(app_conversation_id)

session_api_key = conv.get("session_api_key")
conversation_url = conv.get("conversation_url", "")

# `conversation_url` often looks like: https://<runtime-host>/api/conversations/<id>
agent_server_url = conversation_url.rsplit("/api/conversations", 1)[0]

如果会话记录中没有这些字段,请列出/搜索沙盒(GET /api/v1/sandboxes/search),并使用会话所引用的沙盒来查找 agent server URL 和会话密钥。

本地 Agent Canvas 后端

仅在本地开发 Agent Canvas / agent-server 时使用本地后端流程,例如 agent-canvas、agent-canvas --backend-only,或入口为 http://localhost:8000 的 npm run dev。此流程会通过 X-Session-API-Key 直接调用 Agent 服务。这不是自动化流程,与通过 POST /api/v1/app-conversations 委派给 OpenHands Cloud 的方式不同;后者使用 Bearer 身份验证调用 Cloud 应用 API,并可能返回异步启动任务记录。

Agent Canvas 在本地运行时,如果设置了 LOCAL_BACKEND_API_KEY,启动器会使用该密钥;否则会在 ~/.openhands/agent-canvas/api-key.txt 生成并持久化会话 API 密钥。设置 OH_SESSION_API_KEY_PATH 可覆盖持久化密钥的路径。切勿打印、记录或粘贴实际密钥;在示例和脚本中使用命令替换或环境变量。

LOCAL_AGENT_SERVER_URL="${LOCAL_AGENT_SERVER_URL:-http://localhost:8000}"
SESSION_API_KEY="${LOCAL_BACKEND_API_KEY:-$(cat "${OH_SESSION_API_KEY_PATH:-$HOME/.openhands/agent-canvas/api-key.txt}")}"

创建后端对话前,检查本地服务:

curl -sS "${LOCAL_AGENT_SERVER_URL}/server_info" \
  -H "X-Session-API-Key: ${SESSION_API_KEY}"

使用 POST /api/conversations 启动后端对话。请包含该后端所需的 Agent 设置和工作区。本地 Agent 服务调用需要显式指定 workspace,例如 {"kind": "LocalWorkspace", "working_dir": "/workspace"};而委派到 Cloud 应用对话时,使用的是 selected_repository 和 selected_branch 等 app-server 字段。如果从现有的 Agent Canvas 会话启动对话,请传递当前配置的设置或加密后的设置,而不要在脚本中硬编码机密信息。

CONVERSATION_JSON=$(curl -sS -X POST "${LOCAL_AGENT_SERVER_URL}/api/conversations" \
  -H "X-Session-API-Key: ${SESSION_API_KEY}" \
  -H "Content-Type: application/json" \
  -d @- <<'JSON'
{
  "agent": {
    "kind": "Agent",
    "llm": {
      "model": "your-model-provider/your-model-name",
      "api_key": "**********"
    },
    "tools": [
      {"name": "terminal"},
      {"name": "file_editor"},
      {"name": "task_tracker"}
    ]
  },
  "workspace": {"kind": "LocalWorkspace", "working_dir": "/workspace"},
  "initial_message": {
    "content": [{"text": "Summarize the current workspace."}],
    "run": true
  }
}
JSON
)
CONVERSATION_ID=$(python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])' <<<"${CONVERSATION_JSON}")
printf 'Conversation: %s/api/conversations/%s\n' "${LOCAL_AGENT_SERVER_URL}" "${CONVERSATION_ID}"

轮询状态并检查近期事件:

curl -sS "${LOCAL_AGENT_SERVER_URL}/api/conversations/${CONVERSATION_ID}" \
  -H "X-Session-API-Key: ${SESSION_API_KEY}"

curl -sS "${LOCAL_AGENT_SERVER_URL}/api/conversations/${CONVERSATION_ID}/events/search?limit=20&sort_order=TIMESTAMP_DESC" \
  -H "X-Session-API-Key: ${SESSION_API_KEY}"

如果 Agent Canvas UI 使用相同的基础 URL,浏览器路由为:

printf '%s/conversations/%s\n' "${LOCAL_AGENT_SERVER_URL}" "${CONVERSATION_ID}"

常见的 V1 应用服务端点

以下是最简客户端实现的主要端点:

  • GET /api/v1/users/me — 验证身份并查看当前账户
  • GET /api/v1/app-conversations/search?limit=... — 列出近期对话
  • GET /api/v1/app-conversations?ids=... — 按 id 获取对话记录(批量)
  • GET /api/v1/app-conversations/count — 统计对话数量
  • POST /api/v1/app-conversations — 启动新对话(创建沙箱)
  • GET /api/v1/app-conversations/start-tasks?ids=... — 检查异步启动任务状态
  • GET /api/v1/conversation/{app_conversation_id}/events/search?limit=... — 读取对话事件
  • GET /api/v1/conversation/{app_conversation_id}/events/count — 统计事件数量
  • GET /api/v1/sandboxes/search?limit=... — 列出沙箱
  • POST /api/v1/sandboxes/{sandbox_id}/pause / .../resume — 管理沙箱生命周期
  • GET /api/v1/app-conversations/{app_conversation_id}/download — 下载轨迹 zip

使用其他 Cloud 对话委派工作

如果你想要一个拥有全新上下文窗口的独立 OpenHands 对话,请使用 Cloud API。 以下情况适合使用此方式:

  • 可独立运行的后台任务
  • 并行调查或实现任务
  • 需要长时间运行,且希望让当前对话保持专注的工作
  • 特定于任务的上下文,例如一个对话构建组件,另一个对话运行测试
委派清单

开始委派的 Cloud 对话时:

  1. 编写自包含的任务说明。不要假设新对话了解当前对话的任何上下文。
  2. 包含仓库、分支、相关文件路径、约束条件和预期输出。
  3. 使用 POST /api/v1/app-conversations 开始新对话。
  4. 轮询启动任务,直到 status 为 READY,并获得 app_conversation_id。
  5. 通过 GET /api/v1/app-conversations?ids=... 监控委派的对话。
  6. 分享或保存响应中的 conversation_ui_url。该链接会在 Agent Canvas 中打开对话。如果服务端未返回此链接,请使用 <base_url>/canvas/conversations/<app_conversation_id>,其中 <base_url> 是你调用 API 的服务地址。切勿分享 conversation_url;它是沙盒的 agent-server API,并非用户可以打开的页面。
最简 cURL 流程
curl -X POST "https://app.all-hands.dev/api/v1/app-conversations" \
  -H "Authorization: Bearer ${OPENHANDS_CLOUD_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "initial_message": {
      "content": [{"type": "text", "text": "Investigate flaky tests in tests/test_api.py. Report the root cause and propose a fix."}]
    },
    "selected_repository": "owner/repo"
  }'

如果响应中尚未包含 app_conversation_id,请轮询启动任务:

curl -s "https://app.all-hands.dev/api/v1/app-conversations/start-tasks?ids=${START_TASK_ID}" \
  -H "Authorization: Bearer ${OPENHANDS_CLOUD_API_KEY}"

然后检查执行状态并获取要分享的链接(conversation_ui_url):

curl -s "https://app.all-hands.dev/api/v1/app-conversations?ids=${APP_CONVERSATION_ID}" \
  -H "Authorization: Bearer ${OPENHANDS_CLOUD_API_KEY}"
最简 Python 流程
from openhands_api import OpenHandsAPI

api = OpenHandsAPI()  # prefers OPENHANDS_CLOUD_API_KEY

start = api.app_conversation_start(
    initial_message=(
        "Implement the requested dashboard component in src/dashboard.tsx. "
        "Update any related tests and summarize the changes."
    ),
    selected_repository="owner/repo",
    selected_branch="main",
    title="Dashboard component task",
)

ready = start
if not ready.get("app_conversation_id"):
    ready = api.poll_start_task_until_ready(start["id"])

conversation_id = ready["app_conversation_id"]
print(f"Delegated conversation: {api.app_conversation_ui_url(conversation_id)}")

status = api.app_conversation_get(conversation_id)
print(status.get("sandbox_status"), status.get("execution_status"))

api.close()
并行处理建议
  • 最好同时运行 5 个或更少的委派对话。
  • 开始更多对话前,检查近期对话,并统计仍处于 execution_status == "running" 状态的对话数。
  • 如果已知对话 ID,可使用 GET /api/v1/app-conversations?ids=... 批量查询指定对话。

示例:

items = api.app_conversations_search(limit=50).get("items", [])
running = [item for item in items if item.get("execution_status") == "running"]
if len(running) >= 5:
    print("Wait for some delegated conversations to finish before starting more.")
启动任务与 app_conversation_id(常见问题)

在许多部署中,POST /api/v1/app-conversations 是异步的,会返回一个启动任务对象:

  • id 是 start_task_id
  • app_conversation_id 是你应在对话操作中使用的 ID,例如:
    • GET /api/v1/app-conversations/{app_conversation_id}/download
    • GET /api/v1/conversation/{app_conversation_id}/events/...

如果初始响应中没有 app_conversation_id,请通过以下接口获取:

  • GET /api/v1/app-conversations/start-tasks?ids=<start_task_id>

如果将 start_task_id 传给 /download,你会收到 404 Not Found。

常见 Agent 服务端点

这些端点针对 agent_server_url 运行(而非应用服务):

  • POST {agent_server_url}/api/bash/execute_bash_command
  • GET {agent_server_url}/api/file/download/<absolute_path>
  • POST {agent_server_url}/api/file/upload/<absolute_path>(multipart)
  • GET {agent_server_url}/api/conversations/{conversation_id}/events/search
  • GET {agent_server_url}/api/conversations/{conversation_id}/events/count
统计事件数(建议方式)

如果需要了解某个对话包含多少个事件,可以:

  1. 应用服务计数(正常工作时最快)
    • GET /api/v1/conversation/{app_conversation_id}/events/count
  2. Agent 服务计数(可靠的备用方案)
    • GET {agent_server_url}/api/conversations/{app_conversation_id}/events/count
  3. 轨迹压缩包备用方案(开销更大,但只需调用一次且能获取完整载荷)
    • GET /api/v1/app-conversations/{app_conversation_id}/download
    • 解压并统计 event_*.json 文件

不要依赖最后一个事件的 id 来推断事件总数。 在 agent-server API 中,事件 ID 是 UUID(而非单调递增的整数)。

故障排除

常见问题及解决方案请参阅 TROUBLESHOOTING.md。

事件结构(用于调试)

以下接口返回的事件:

  • 应用服务器:GET /api/v1/conversation/{id}/events/search
  • Agent 服务器:GET {agent_server_url}/api/conversations/{id}/events/search

……具有相同的高层结构。

每个事件通常包含:

  • id(UUID)
  • timestamp
  • kind
  • source

常见的 kind 值:

kindsource(典型值)关键字段(常见)用途
ActionEventagenttool_name、tool_call_id、actionAgent 请求调用工具
ObservationEventenvironmenttool_name、tool_call_id、action_id、observation沙盒/环境生成的工具结果
MessageEventuser / assistantmessage(或类似字段)用户/助手聊天消息
ConversationStateUpdateEventenvironmentkey、value状态转换/元数据

工具调用的关联方式:

  • ActionEvent.tool_call_id == ObservationEvent.tool_call_id
  • ObservationEvent.action_id == ActionEvent.id

示例(简化版):

{
  "id": "<action-event-uuid>",
  "kind": "ActionEvent",
  "source": "agent",
  "tool_name": "terminal",
  "tool_call_id": "toolu_...",
  "action": {"command": "ls"}
}
{
  "id": "<observation-event-uuid>",
  "kind": "ObservationEvent",
  "source": "environment",
  "tool_name": "terminal",
  "tool_call_id": "toolu_...",
  "action_id": "<action-event-uuid>",
  "observation": {"exit_code": 0, "stdout": "..."}
}

调试单行命令(事件)

以下命令假设你查询的是应用服务器接口。查询 Agent 服务器时,请替换 URL 基址并使用 X-Session-API-Key。

快速打印时间线
curl -s "${BASE_URL:-https://app.all-hands.dev}/api/v1/conversation/${APP_CONVERSATION_ID}/events/search?limit=100" \
  -H "Authorization: Bearer ${OPENHANDS_CLOUD_API_KEY:-$OPENHANDS_API_KEY}" \
  -H "Accept: application/json" | \
python3 - <<'PY'
import json, sys
items = (json.load(sys.stdin) or {}).get("items", [])
for i, e in enumerate(items):
    print(f"{i:04d}  {e.get('timestamp','')}  {e.get('source','')}  {e.get('kind','')}")
PY
查找类似错误的事件
curl -s "${BASE_URL:-https://app.all-hands.dev}/api/v1/conversation/${APP_CONVERSATION_ID}/events/search?limit=200" \
  -H "Authorization: Bearer ${OPENHANDS_CLOUD_API_KEY:-$OPENHANDS_API_KEY}" \
  -H "Accept: application/json" | \
python3 - <<'PY'
import json, sys
items = (json.load(sys.stdin) or {}).get("items", [])
for i, e in enumerate(items):
    if e.get("kind") == "ErrorEvent" or ("code" in e and "detail" in e):
        print(i, e.get("kind"), e.get("code"), str(e.get("detail", ""))[:400])
PY
检查工具调用匹配情况(未匹配的操作/重复的观察结果)
curl -s "${BASE_URL:-https://app.all-hands.dev}/api/v1/conversation/${APP_CONVERSATION_ID}/events/search?limit=200" \
  -H "Authorization: Bearer ${OPENHANDS_CLOUD_API_KEY:-$OPENHANDS_API_KEY}" \
  -H "Accept: application/json" | \
python3 - <<'PY'
import json, sys
from collections import Counter
items = (json.load(sys.stdin) or {}).get("items", [])
action_ids = {e.get("id") for e in items if e.get("kind") == "ActionEvent"}
obs_action_ids = [e.get("action_id") for e in items if e.get("kind") == "ObservationEvent" and e.get("action_id")]
observed = set(obs_action_ids)
print("actions:", len(action_ids))
print("observations:", len(observed))
unmatched = action_ids - observed
print("unmatched actions:", list(unmatched)[:20] if unmatched else "none")
dups = [aid for aid, c in Counter(obs_action_ids).items() if c > 1]
print("duplicate observation action_ids:", list(dups)[:20] if dups else "none")
PY

快速入门(Python)

# Copy `skills/openhands-api/scripts/openhands_api.py` into your project (e.g. as `openhands_api.py`),
# then import it normally:
from openhands_api import OpenHandsAPI

api = OpenHandsAPI()  # prefers OPENHANDS_CLOUD_API_KEY

me = api.users_me()
print(me)

recent = api.app_conversations_search(limit=5)
print(recent)

api.close()

CLI 示例

搜索对话:

export OPENHANDS_CLOUD_API_KEY="..."
python skills/openhands-api/scripts/openhands_api.py search-conversations --limit 5

从提示词文件启动对话:

python skills/openhands-api/scripts/openhands_api.py start-conversation \
  --prompt-file skills/openhands-api/references/example_prompt.md \
  --repo owner/repo \
  --branch main

面向扩展此客户端的 AI Agent 的注意事项

  • 优先使用带有较小 limit 的 .../search 接口。
  • 避免可能生成大量 API 调用的循环。
  • 仅在收到请求时启动对话:这可能会创建沙盒并产生费用。
  • 对于沙盒文件操作和命令执行,请使用带有 X-Session-API-Key 的 Agent 服务器接口。

另请参阅:

  • skills/openhands-api/scripts/openhands_api.py
  • 最初的灵感来源客户端:enyst/llm-playground → openhands-api-client-v1/scripts/cloud_api_v1.py
  • 故障排除内容和真实使用反馈 → https://github.com/jpshackelford/.openhands/tree/main/skills/openhands-cloud-api

信息来源

此技能与当前 OpenHands API 文档和实现保持一致:

  • OpenHands/docs/openhands/usage/cloud/cloud-api.mdx
  • OpenHands/docs/openhands/usage/agent-canvas/backend-setup/local.mdx
  • OpenHands/docs/sdk/arch/agent-server.mdx
  • OpenHands/docs/openhands/usage/api/v1.mdx
  • OpenHands/OpenHands/openhands/app_server/v1_router.py
  • OpenHands/OpenHands/openhands/app_server/app_conversation/app_conversation_router.py
  • OpenHands/OpenHands/openhands/app_server/app_conversation/app_conversation_models.py

评论

支持 Markdown