Witty Service - A service for managing agents and sessions
Project description
Witty-Service 端到端测试流程
本文档给出当前 witty-service 的完整 E2E(End-to-End)测试方式,覆盖:
- 本地手工联调
- 自动化 E2E(pytest)
- 故障排查清单
当前接口基线:
- Agent 生命周期:
/agents/* - Session:
/agents/{agent_id}/sessions/* - 消息接口:
/agents/{agent_id}/sessions/{session_id}/messages、/agents/{agent_id}/sessions/{session_id}/messages/stream - 健康检查:
/healthz
1. 开发环境设置
1.1 创建虚拟环境(如果还没有)
uv venv
source .venv/bin/activate
1.2 安装依赖(包含开发依赖)
uv pip install -e ".[dev]"
1.3 启动开发服务器
uv run uvicorn witty_service.main:create_app --factory --host 0.0.0.0 --port 8000 --reload
建议准备:
curl(HTTP 请求)- WebSocket 客户端(示例里使用
websocket-client)
2. 构建与部署
2.1 构建 pip 包
uv build
构建产物会生成在 dist/ 目录下:
witty_service-0.1.0-py3-none-any.whl- Wheel 包witty_service-0.1.0.tar.gz- Source 包
2.2 安装包
uv pip install dist/witty_service-0.1.0-py3-none-any.whl
2.3 生产环境启动
uv run witty-service --host 0.0.0.0 --port 8000 --workers 4
2.4 启动参数说明
| 参数 | 说明 | 默认值 |
|---|---|---|
--host |
绑定的主机地址 | 127.0.0.1 |
--port |
绑定的端口 | 8000 |
--log-level |
日志级别 | info |
--reload |
开发模式自动重载 | False |
--workers |
工作进程数 | 1 |
健康检查:
curl -s http://127.0.0.1:8000/healthz
期望返回:
{"status":"ok"}
3. 接口模型(输入/输出)
3.1 通用错误模型
所有业务错误统一返回:
{
"error": {
"code": "ERROR_CODE",
"message": "error message",
"details": {}
}
}
字段说明:
code: 稳定错误码message: 人类可读错误信息details: 可选,结构化错误细节
3.2 接口总览
| 接口 | 方法 | 描述 |
|---|---|---|
/healthz |
GET |
服务存活检查 |
/agents |
POST |
创建 Agent |
/agents |
GET |
列出所有 Agent |
/agents/{agent_id} |
GET |
获取 Agent 详情 |
/agents/{agent_id} |
DELETE |
删除 Agent |
/agents/{agent_id}/pause |
POST |
暂停 Agent |
/agents/{agent_id}/resume |
POST |
恢复 Agent |
/agents/{agent_id}/sessions |
GET |
列出所有会话 |
/agents/{agent_id}/sessions |
POST |
创建会话 |
/agents/{agent_id}/sessions/{session_id} |
GET |
获取会话详情 |
/agents/{agent_id}/sessions/{session_id} |
DELETE |
删除会话 |
/agents/{agent_id}/sessions/{session_id}/messages |
POST |
发送消息 |
/agents/{agent_id}/sessions/{session_id}/messages/stream |
POST |
发送消息并以 SSE 流返回 |
/models |
POST |
添加大模型配置 |
/models |
GET |
获取大模型列表 |
/models/{model_id} |
DELETE |
删除大模型配置 |
/agents/{agent_id}/sessions/{session_id}/events |
GET |
查询会话事件回放 |
/agents/{agent_id}/sessions/{session_id}/abort |
POST |
中止会话:中止正在运行的会话 |
/agents/{agent_id}/conversations |
GET |
列出会话摘要:查询本地数据库,返回会话列表及最新消息摘要 |
/agents/{agent_id}/conversations/{session_id} |
GET |
获取会话详情:查询本地数据库,支持消息分页(limit/before) |
/agents/{agent_id}/conversations/{session_id} |
PATCH |
更新会话元数据:修改标题、置顶状态,仅更新本地数据库 |
/agents/{agent_id}/sessions/{session_id}/messages/stream/reconnect |
POST |
SSE 流重连:重新连接到已有消息流,通过 WebSocket 接收事件 |
/agents/{agent_id}/skills/installed |
GET |
查询已安装技能:查询本地数据库,返回 agent 已安装的技能列表 |
说明:
GET /agents/{agent_id}/sessionsPOST /agents/{agent_id}/sessionsGET /agents/{agent_id}/sessions/{session_id}DELETE /agents/{agent_id}/sessions/{session_id}GET /agents/{agent_id}/sessions/{session_id}/events
以上接口均支持可选 query 参数 runtime_agent_id,用于指定远端 witty-agent-server 中的 runtime agent(例如 OpenClaw subagent)。
未显式传入时,witty-service 会调用远端 /agent/list,优先使用 defaultId,若不存在则回退到 default=true 的条目。
Agent 接口与 runtime_agent_id 的关系:
witty-service自己的agent_id表示一个沙箱内的witty-agent-server进程实例- 远端
runtime_agent_id表示该进程内的 runtime agent / OpenClaw subagent POST /agents、GET /agents、GET /agents/{agent_id}、DELETE /agents/{agent_id}、POST /agents/{agent_id}/pause、POST /agents/{agent_id}/resume这些 Agent 生命周期接口不直接接收runtime_agent_idruntime_agent_id只影响 session 相关接口的远端路由选择,不改变witty-service自己的 agent 主键语义
3.3 Agent 生命周期接口
1. POST /agents
- 接口描述:创建新 Agent
- 说明:
- 该接口不接收
runtime_agent_id - 创建完成后,
witty-service会先调用远端/agent/start - 然后使用
/agent/start返回的远端 runtime agent id 创建默认 session - 因此
default_session_id对应的远端归属由witty-agent-server当前解析出的默认/启动目标 agent 决定
- 该接口不接收
- 输入(CreateAgentRequest):
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
name |
string | 是 | Agent 名称,最小长度 1 |
description |
string | 否 | Agent 描述,默认空字符串 |
sandbox_type |
string | 是 | 沙箱类型:docker、local_process、e2b |
adapter_type |
string | 是 | 适配器类型:如 openclaw |
idle_timeout_seconds |
integer | 是 | 空闲超时时间(秒),必须大于 0 |
sandbox_id |
string | 否 | 沙箱 ID |
has_scheduled_tasks |
boolean | 否 | 是否有定时任务,默认 false |
- 输出
201(AgentResponse):
{
"id": "agent-uuid",
"name": "my-agent",
"description": "这是一个测试智能体",
"sandbox_type": "docker",
"adapter_type": "openclaw",
"status": "running",
"sandbox_id": null,
"workspace_path": "/path/to/workspace",
"idle_timeout_seconds": 3600,
"has_scheduled_tasks": false,
"created_at": "2026-04-10T12:00:00",
"updated_at": "2026-04-10T12:00:00",
"default_session_id": "session-uuid"
}
| 字段 | 类型 | 说明 |
|---|---|---|
id |
string | Agent 唯一标识 |
name |
string | Agent 名称 |
description |
string | Agent 描述 |
sandbox_type |
string | 沙箱类型 |
adapter_type |
string | 适配器类型 |
status |
string | Agent 状态:running、paused、stopped |
sandbox_id |
string | null | 沙箱 ID |
workspace_path |
string | 工作区路径 |
idle_timeout_seconds |
integer | 空闲超时时间 |
has_scheduled_tasks |
boolean | 是否有定时任务 |
created_at |
datetime | 创建时间 |
updated_at |
datetime | 更新时间 |
default_session_id |
string | null | 默认会话 ID |
补充说明:
default_session_id是 Agent 创建成功后立即创建的默认 session- 这个默认 session 的远端
runtime_agent_id不是通过POST /agents入参传入,而是由内部/agent/start结果决定 - 如果你需要把后续 session 显式路由到某个 remote runtime agent(例如
dev),应在POST /agents/{agent_id}/sessions?runtime_agent_id=dev时传入
2. GET /agents
- 接口描述:列出所有 Agent
- 输入:无
- 输出
200:list[AgentResponse]
[
{
"id": "agent-uuid-1",
"name": "my-agent",
"description": "这是一个测试智能体",
"sandbox_type": "docker",
"adapter_type": "openclaw",
"status": "running",
"sandbox_id": null,
"workspace_path": "/path/to/workspace",
"idle_timeout_seconds": 3600,
"has_scheduled_tasks": false,
"created_at": "2026-04-10T12:00:00",
"updated_at": "2026-04-10T12:00:00",
"default_session_id": "session-uuid"
}
]
3. GET /agents/{agent_id}
- 接口描述:获取 Agent 详情
- 输入:
| 字段 | 类型 | 位置 | 说明 |
|---|---|---|---|
agent_id |
string | path | Agent 唯一标识 |
- 输出
200:AgentResponse
{
"id": "agent-uuid",
"name": "my-agent",
"description": "这是一个测试智能体",
"sandbox_type": "docker",
"adapter_type": "openclaw",
"status": "running",
"sandbox_id": null,
"workspace_path": "/path/to/workspace",
"idle_timeout_seconds": 3600,
"has_scheduled_tasks": false,
"created_at": "2026-04-10T12:00:00",
"updated_at": "2026-04-10T12:00:00",
"default_session_id": "session-uuid"
}
4. DELETE /agents/{agent_id}
- 接口描述:删除 Agent - 备份运行时 → 停止运行时 → 清理沙箱 → 删除本地记录
- 输入:
| 字段 | 类型 | 位置 | 说明 |
|---|---|---|---|
agent_id |
string | path | Agent 唯一标识 |
-
输出
204:无返回内容 -
说明:
- 备份运行时文件到
~/witty-service/{agent_id}/runtime_backup/ - 调用 witty-agent-server
/agent/stop - 清理沙箱(docker stop/rm 或 kill process)
- 更新 agent 状态为 deleted
- 保留 workspace 目录(不清除,用于后续 resume)
- 备份运行时文件到
5. POST /agents/{agent_id}/pause
- 接口描述:暂停 Agent,保留沙箱状态和所有资源
- 输入:
| 字段 | 类型 | 位置 | 说明 |
|---|---|---|---|
agent_id |
string | path | Agent 唯一标识 |
- 输出
200:AgentResponse(status 变为paused)
{
"id": "agent-uuid",
"name": "my-agent",
"sandbox_type": "docker",
"adapter_type": "openclaw",
"status": "paused",
"sandbox_id": "container-id",
"workspace_path": "/path/to/workspace",
"idle_timeout_seconds": 3600,
"has_scheduled_tasks": false,
"created_at": "2026-04-10T12:00:00",
"updated_at": "2026-04-10T12:30:00",
"default_session_id": "session-uuid"
}
6. POST /agents/{agent_id}/resume
- 接口描述:恢复已暂停的 Agent,根据状态分支处理
- 输入:
| 字段 | 类型 | 位置 | 说明 |
|---|---|---|---|
agent_id |
string | path | Agent 唯一标识 |
-
输出
200:AgentResponse(status 变为running) -
分支逻辑:
| 当前状态 | 恢复流程 |
|---|---|
paused |
直接调用 /agent/start |
deleted / stopped |
1. 恢复运行时备份 2. 重新启动沙箱 3. 调用 /agent/start |
{
"id": "agent-uuid",
"name": "my-agent",
"sandbox_type": "docker",
"adapter_type": "openclaw",
"status": "running",
"sandbox_id": "container-id",
"workspace_path": "/path/to/workspace",
"idle_timeout_seconds": 3600,
"has_scheduled_tasks": false,
"created_at": "2026-04-10T12:00:00",
"updated_at": "2026-04-10T12:35:00",
"default_session_id": "session-uuid"
}
3.4 Session 接口
1. GET /agents/{agent_id}/sessions
- 接口描述:列出 Agent 的所有会话(以 witty-agent-server 为主,刷新本地缓存)
- 输出
200:list[SessionResponse]
2. POST /agents/{agent_id}/sessions
- 接口描述:创建新会话(透传到 witty-agent-server)
- 输入:空对象
{} - 输出
201(SessionResponse):
{
"id": "session-uuid",
"agent_id": "agent-uuid",
"status": "idle",
"context_initialized": true,
"runtime_type": "openclaw",
"created_at": "2026-04-10T12:00:00",
"updated_at": "2026-04-10T12:00:00"
}
| 字段 | 类型 | 说明 |
|---|---|---|
id |
string | 会话唯一标识 |
agent_id |
string | 所属 Agent ID |
status |
string | 会话运行态:running、idle、error |
context_initialized |
boolean | witty-agent-server 是否已完成上下文初始化 |
runtime_type |
string | 运行时类型:如 openclaw |
created_at |
datetime | 创建时间 |
updated_at |
datetime | 更新时间 |
- 额外说明:
runtime_agent_id是创建前的路由参数。- 创建成功后,
witty-service会在本地固化remote_runtime_agent_id,后续该 session 的查询、删除、事件回放、消息发送和 WS 连接都会优先使用这个固化值。 - session 生命周期不再通过
deleted、closed之类的状态表达,而是通过“记录是否存在 + 接口行为结果”体现。
3. GET /agents/{agent_id}/sessions/{session_id}
- 接口描述:获取会话详情(优先本地,透传 witty-agent-server)
- 输出
200:SessionResponse
4. DELETE /agents/{agent_id}/sessions/{session_id}
- 接口描述:删除会话(透传到 witty-agent-server,删除本地记录)
- 输出
204:无返回内容 - 说明:内部会调用远端
POST /agents/{remote_runtime_agent_id}/sessions/{session_id}/delete
5. GET /agents/{agent_id}/sessions/{session_id}/events
- 接口描述:查询会话事件回放(透传到 witty-agent-server)
- 输入:
| 字段 | 类型 | 位置 | 说明 |
|---|---|---|---|
session_id |
string | path | 会话 ID |
offset |
integer | query | 分页偏移,默认 0 |
limit |
integer | query | 每页数量,默认 50 |
- 输出
200(SessionEventPage):
{
"items": [
{
"id": "event-id",
"session_id": "session-id",
"type": "message.delta",
"source": "assistant",
"payload": {},
"timestamp": "2026-03-31T12:34:56.000000Z"
}
],
"pagination": {
"offset": 0,
"limit": 50,
"total": 1
}
}
3.5 消息接口
POST /agents/{agent_id}/sessions/{session_id}/messages
- 接口描述:witty-service 对外通过 REST 发送消息并返回非流式聚合结果;内部到
witty-agent-server的消息通道仍是 WebSocket - 输入(SendMessageRequest):
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
content |
string | 是 | 消息内容,最小长度 1 |
{
"content": "帮我查一下最近的错误日志"
}
- 输出
200(MessageEventsResponse):
{
"sandbox_type": "local_process",
"events": [
{
"type": "message.delta",
"session_id": "session-id",
"event_id": "uuid",
"ts_ms": 1775650000123,
"runtime_type": "openclaw",
"payload": {"delta": "当"}
},
{
"type": "message.delta",
"session_id": "session-id",
"event_id": "uuid",
"ts_ms": 1775650000123,
"runtime_type": "openclaw",
"payload": {"delta": "前环境"}
},
{
"type": "message.completed",
"session_id": "session-id",
"event_id": "uuid",
"ts_ms": 1775650000123,
"runtime_type": "openclaw",
"payload": {"text": "当前工作环境..."}
}
]
}
| 字段 | 类型 | 说明 |
|---|---|---|
sandbox_type |
string | Agent 的沙箱类型,取值来自 agent 配置的 sandbox backend,例如 docker、local_process、e2b |
events |
array | 事件数组,每个事件包含 type、session_id、event_id、ts_ms、runtime_type、payload |
POST /agents/{agent_id}/sessions/{session_id}/messages/stream
- 接口描述:witty-service 对外通过 REST 提供 SSE 流式返回;内部到
witty-agent-server的消息通道仍是 WebSocket - 输入(SendMessageRequest):
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
content |
string | 是 | 消息内容,最小长度 1 |
- 响应类型:
text/event-stream - 每条 SSE
data:的格式:
data: {"sandbox_type":"local_process","event":{"type":"message.delta","session_id":"session-id","event_id":"uuid","ts_ms":1775650000123,"runtime_type":"openclaw","payload":{"delta":"当"}}}
- 说明:
- SSE 中每条
data:对应一个上游事件。 sandbox_type取自 Agent 的沙箱类型。event的内容保持上游 envelope 结构,包含来自上游 runtime 的runtime_type,不额外嵌套sandbox_type。- session 状态同步规则为:收到运行中事件时更新为
running,收到message.completed后收敛为idle,收到client.error/stream.error或 WS 异常时更新为error。
- SSE 中每条
3.6 事件类型
| type | 含义 | payload 关键字段 |
|---|---|---|
message.delta |
assistant 增量输出 | delta |
message.completed |
assistant 输出完成 | text |
tool.call.started |
工具调用开始 | tool_name, tool_call_id, arguments, stage |
tool.call.response |
工具调用结果/过程输出 | tool_name, tool_call_id, content, is_error, stage |
usage.updated |
用量更新 | input_tokens, output_tokens, total_cost |
session.runtime.changed |
runtime session 标识变化 | runtime 原始字段 |
stream.error |
运行时流异常 | code, message |
client.error |
客户端事件错误 | code, message, details |
3.6 大模型配置接口
1. POST /models
- 接口描述:添加新的大模型配置
- 输入(CreateModelRequest):
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
name |
string | 是 | 模型名称,如 gpt-4o、claude-3-opus-20240229 |
provider |
string | 是 | 模型提供商:openai、anthropic、google、ollama、azure、deepseek、glm、minimax、kimi、custom |
api_key |
string | 是 | API 密钥 |
api_base_url |
string | 否 | API 基础地址,默认根据 provider 自动设置 |
description |
string | 否 | 模型描述,默认空字符串 |
enabled |
boolean | 否 | 是否启用,默认 true |
max_tokens |
integer | 否 | 最大输出 tokens 数,默认 4096 |
temperature |
number | 否 | 生成温度,范围 0-2,默认 0.7 |
is_default |
boolean | 否 | 是否设为默认模型,默认 false |
- 输出
201(ModelResponse):
{
"id": "model-uuid",
"name": "gpt-4o",
"provider": "openai",
"api_base_url": "https://api.openai.com/v1",
"description": "OpenAI GPT-4 Omni 模型",
"enabled": true,
"max_tokens": 4096,
"temperature": 0.7,
"is_default": true,
"created_at": "2026-04-13T10:00:00",
"updated_at": "2026-04-13T10:00:00"
}
| 字段 | 类型 | 说明 |
|---|---|---|
id |
string | 模型配置唯一标识 |
name |
string | 模型名称 |
provider |
string | 模型提供商 |
api_base_url |
string | API 基础地址 |
description |
string | 模型描述 |
enabled |
boolean | 是否启用 |
max_tokens |
integer | 最大输出 tokens 数 |
temperature |
number | 生成温度 |
is_default |
boolean | 是否为默认模型 |
created_at |
datetime | 创建时间 |
updated_at |
datetime | 更新时间 |
2. GET /models
- 接口描述:获取所有大模型配置列表
- 输出
200:list[ModelResponse]
[
{
"id": "model-uuid-1",
"name": "gpt-4o",
"provider": "openai",
"api_base_url": "https://api.openai.com/v1",
"description": "OpenAI GPT-4 Omni 模型",
"enabled": true,
"max_tokens": 4096,
"temperature": 0.7,
"is_default": true,
"created_at": "2026-04-13T10:00:00",
"updated_at": "2026-04-13T10:00:00"
},
{
"id": "model-uuid-2",
"name": "claude-3-opus-20240229",
"provider": "anthropic",
"api_base_url": "https://api.anthropic.com/v1",
"description": "Anthropic Claude 3 Opus 模型",
"enabled": true,
"max_tokens": 4096,
"temperature": 0.7,
"is_default": false,
"created_at": "2026-04-13T11:00:00",
"updated_at": "2026-04-13T11:00:00"
}
]
3. DELETE /models/{model_id}
- 接口描述:删除指定的大模型配置
- 输入:
| 字段 | 类型 | 位置 | 说明 |
|---|---|---|---|
model_id |
string | path | 模型配置唯一标识 |
- 输出
204:无返回内容
支持的模型提供商及默认 API 地址:
| Provider | 默认 API Base URL |
|---|---|
openai |
https://api.openai.com/v1 |
anthropic |
https://api.anthropic.com/v1 |
google |
https://generativelanguage.googleapis.com/v1beta |
ollama |
http://localhost:11434/v1 |
azure |
https://{resource}.openai.azure.com |
deepseek |
https://api.deepseek.com/v1 |
glm |
https://open.bigmodel.cn/api/paas/v4 |
minimax |
https://api.minimax.chat/v1 |
kimi |
https://api.moonshot.cn/v1 |
custom |
用户自定义,需通过 api_base_url 指定 |
常见错误码:
| code | HTTP 状态码 | 说明 |
|---|---|---|
MODEL_NOT_FOUND |
404 | 模型配置不存在 |
MODEL_DUPLICATE |
409 | 模型配置已存在 |
MODEL_CREATE_FAILED |
500 | 模型配置创建失败 |
MODEL_DELETE_FAILED |
500 | 模型配置删除失败 |
3.7 常见错误码
| code | HTTP 状态码 | 说明 |
|---|---|---|
INVALID_AGENT_TRANSITION |
409 | Agent 状态转换不合法 |
AGENT_NOT_FOUND |
404 | Agent 不存在 |
SESSION_NOT_FOUND |
404 | 会话不存在(两边都查不到) |
SESSION_CREATE_FAILED |
500 | 透传创建会话失败 |
SESSION_DELETE_FAILED |
500 | 透传删除会话失败 |
SESSION_LIST_FAILED |
500 | 透传列出会话失败 |
SESSION_AGENT_MISMATCH |
400 | Session 与 Agent 不匹配 |
AGENT_NOT_RUNNING |
409 | Agent 未运行(可能处于 paused 或 stopped 状态) |
SANDBOX_STATE_NOT_FOUND |
404 | 沙箱状态不存在 |
AGENT_CREATE_FAILED |
500 | Agent 创建失败 |
AGENT_PAUSE_FAILED |
500 | Agent 暂停失败 |
AGENT_RESUME_FAILED |
500 | Agent 恢复失败 |
AGENT_DELETE_FAILED |
500 | Agent 删除失败 |
RUNTIME_BACKUP_NOT_FOUND |
404 | 运行时备份不存在 |
RUNTIME_BACKUP_RESTORE_FAILED |
500 | 恢复备份失败 |
RUNTIME_START_FAILED |
500 | 启动运行时失败 |
HTTP 状态码映射规则:
- 以
_NOT_FOUND结尾 →404 - 以
_NOT_SUPPORTED或_MISMATCH结尾 →400 - 以
INVALID_开头 →409 - 以
_FAILED结尾 →500 - 其他 →
400
4. 手工 E2E 流程(按沙箱场景)
4.1 Docker 场景
创建 Agent(sandbox_type=docker):
AGENT_ID=$(
curl -s -X POST http://127.0.0.1:8000/agents \
-H 'content-type: application/json' \
-H 'authorization: Bearer YOUR_TOKEN' \
-d '{
"name": "e2e-docker-agent",
"description": "Docker 场景测试智能体",
"sandbox_type": "docker",
"adapter_type": "openclaw",
"idle_timeout_seconds": 3600
}' | jq -r '.id'
)
创建 Session:
SESSION_ID=$(
curl -s -X POST "http://127.0.0.1:8000/agents/${AGENT_ID}/sessions?runtime_agent_id=dev" \
-H 'content-type: application/json' \
-H 'authorization: Bearer YOUR_TOKEN' \
-d '{}' | jq -r '.id'
)
说明:
- 若不传
runtime_agent_id,会使用远端默认 runtime agent。 - 若希望显式打到某个 subagent(例如
dev),请通过 query 参数传入。
非流式消息接口(/messages):
curl -s -X POST "http://127.0.0.1:8000/agents/${AGENT_ID}/sessions/${SESSION_ID}/messages" \
-H 'content-type: application/json' \
-H 'authorization: Bearer YOUR_TOKEN' \
-d '{"content": "say hi from docker"}' | jq
流式消息接口(/messages/stream):
curl -N -X POST "http://127.0.0.1:8000/agents/${AGENT_ID}/sessions/${SESSION_ID}/messages/stream" \
-H 'content-type: application/json' \
-H 'authorization: Bearer YOUR_TOKEN' \
-d '{"content": "stream hi from docker"}'
清理:
curl -s -X DELETE "http://127.0.0.1:8000/agents/${AGENT_ID}" \
-H 'authorization: Bearer YOUR_TOKEN'
4.2 Local Process 场景
先配置本地 witty-agent-server 代码目录:
export WITTY_AGENT_SERVER_APP_DIR=/path/to/witty-agent-server
创建 Agent(sandbox_type=local_process):
AGENT_ID=$(
curl -s -X POST http://127.0.0.1:8000/agents \
-H 'content-type: application/json' \
-H 'authorization: Bearer YOUR_TOKEN' \
-d '{
"name": "e2e-local-agent",
"description": "Local Process 场景测试智能体",
"sandbox_type": "local_process",
"adapter_type": "openclaw",
"idle_timeout_seconds": 3600
}' | jq -r '.id'
)
创建 Session:
SESSION_ID=$(
curl -s -X POST "http://127.0.0.1:8000/agents/${AGENT_ID}/sessions" \
-H 'content-type: application/json' \
-H 'authorization: Bearer YOUR_TOKEN' \
-d '{}' | jq -r '.id'
)
非流式消息接口(/messages):
curl -s -X POST "http://127.0.0.1:8000/agents/${AGENT_ID}/sessions/${SESSION_ID}/messages" \
-H 'content-type: application/json' \
-H 'authorization: Bearer YOUR_TOKEN' \
-d '{"content": "say hi from local"}' | jq
流式消息接口(/messages/stream):
curl -N -X POST "http://127.0.0.1:8000/agents/${AGENT_ID}/sessions/${SESSION_ID}/messages/stream" \
-H 'content-type: application/json' \
-H 'authorization: Bearer YOUR_TOKEN' \
-d '{"content": "stream hi from local"}'
可选状态操作:
curl -s -X POST "http://127.0.0.1:8000/agents/${AGENT_ID}/pause" \
-H 'authorization: Bearer YOUR_TOKEN'
curl -s -X POST "http://127.0.0.1:8000/agents/${AGENT_ID}/resume" \
-H 'authorization: Bearer YOUR_TOKEN'
清理:
curl -s -X DELETE "http://127.0.0.1:8000/agents/${AGENT_ID}" \
-H 'authorization: Bearer YOUR_TOKEN'
5. Sandbox 类型与配置
5.1 docker
在 Docker 容器中运行 adaptor service(witty-agent-server)。
启动行为:
- 在随机
host_port启动容器 - 将本地
workspace_path挂载到容器的/witty-workspace - 容器内端口固定为
8080
环境变量:
| 变量名 | 说明 | 默认值 |
|---|---|---|
WITTY_DOCKER_HOST |
Docker 服务监听地址 | 127.0.0.1 |
WITTY_DOCKER_IMAGE |
镜像名(不含 tag) | witty-agent-server |
WITTY_DOCKER_IMAGE_TAG |
镜像 tag | latest |
WITTY_DOCKER_CONTAINER_PORT |
容器内服务端口 | 8080 |
WITTY_DOCKER_CONTAINER_WORKSPACE_PATH |
容器内工作区路径 | /witty-workspace |
WITTY_DOCKER_STOP_TIMEOUT |
容器停止超时(秒) | 10 |
注意事项:
workspace_path必须为绝对路径- 工作区目录必须存在
5.2 local_process
在本地进程中直接启动 witty-agent-server(通过 uvicorn)。
环境变量:
| 变量名 | 说明 | 默认值 |
|---|---|---|
WITTY_AGENT_SERVER_APP_DIR |
witty-agent-server 代码目录(必填) |
空 |
注意事项:
WITTY_AGENT_SERVER_APP_DIR必须指向有效的witty-agent-server代码目录- local process 场景下,每个
witty-serviceagent 都会拉起一个独立的witty-agent-server进程,并占用一个随机端口 witty-service.agent_id表示这个本地子进程实例;远端runtime_agent_id表示该进程内的 OpenClaw subagent,两者不是同一个维度
5.3 e2b
E2B 云沙箱运行时。当前未实现,调用会返回 SANDBOX_NOT_SUPPORTED 错误。
5.4 运行时 endpoint
所有 sandbox 启动后,会生成 AdapterEndpoint:
{
"base_url": "http://127.0.0.1:随机端口",
"health_url": "http://127.0.0.1:随机端口/ping"
}
witty-service 通过 WebSocket 连接至 base_url 与 adaptor service 通信。
6. 认证
所有 /agents/* 接口需要 Bearer Token 认证:
-H 'authorization: Bearer YOUR_TOKEN'
环境变量:
| 变量名 | 说明 |
|---|---|
AUTH_TOKEN |
API 认证 token |
7. 自动化测试
uv run pytest tests/unit/ -q
uv run pytest tests/e2e/ -q
全量测试:
uv run pytest tests/ -q
8. 故障排查
AGENT_NOT_FOUND:Agent 不存在,检查 agent_id 是否正确SESSION_NOT_FOUND:会话不存在(两边都查不到),检查 session_id 是否正确RUNTIME_AGENT_DEFAULT_NOT_FOUND:未显式传runtime_agent_id,且远端/agent/list没有可用默认 agentRUNTIME_AGENT_ID_MISSING:本地 session 未固化远端 runtime agent id,无法继续路由SESSION_CREATE_FAILED:创建会话失败,透传到 witty-agent-server 失败,检查 witty-agent-server 是否正常运行SESSION_DELETE_FAILED:删除会话失败,透传到 witty-agent-server 失败SESSION_LIST_FAILED:列出会话失败,透传到 witty-agent-server 失败SESSION_AGENT_MISMATCH:Session 与 Agent 不匹配,确认 session 属于正确的 AgentAGENT_NOT_RUNNING:Agent 未运行(可能处于 paused 或 stopped 状态),先调用/resumeRUNTIME_BACKUP_NOT_FOUND:运行时备份不存在,可能该 Agent 未执行过 delete 操作RUNTIME_BACKUP_RESTORE_FAILED:恢复备份失败,检查备份文件是否完整- 消息发送无响应:确认 witty-agent-server 的 WebSocket 连接正常
- local_process 创建 Agent 返回 500:优先检查
WITTY_AGENT_SERVER_APP_DIR是否正确,以及子进程里的witty-agent-server是否能正常响应/ping
9. 环境变量
| 变量名 | 说明 | 默认值 |
|---|---|---|
AUTH_TOKEN |
API 认证 token | 空 |
WITTY_DOCKER_HOST |
Docker 主机地址 | 127.0.0.1 |
WITTY_DOCKER_IMAGE |
Docker 镜像名 | witty-agent-server |
WITTY_DOCKER_IMAGE_TAG |
Docker 镜像标签 | latest |
WITTY_DOCKER_CONTAINER_PORT |
容器端口 | 8080 |
WITTY_AGENT_SERVER_APP_DIR |
本地进程模式 app 目录 | 空 |
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file witty_service-0.1.0.tar.gz.
File metadata
- Download URL: witty_service-0.1.0.tar.gz
- Upload date:
- Size: 147.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cdbdf961c37d0bfb5484a180fcb4c7cf93a81348ffe9fa5f1f5c6e12a0ce4867
|
|
| MD5 |
f395caec89444627aa42e520be8d3413
|
|
| BLAKE2b-256 |
9eedb6e514a4f3ab404b3efa160435f6ddf62f876abc6f1c61bba7588adb8d08
|
File details
Details for the file witty_service-0.1.0-py3-none-any.whl.
File metadata
- Download URL: witty_service-0.1.0-py3-none-any.whl
- Upload date:
- Size: 179.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
079c6edbe1e64b21495077bea75f672cc8dee342d5d161969ecaa2d6fd7a65b8
|
|
| MD5 |
14cc865d10df955a06b70218abb3d9c7
|
|
| BLAKE2b-256 |
c102749d35ca25c4196090d1d79f987e49953f1970e61cea1383e279f40934ac
|