wisruntime — 企业级 Agent Runtime SDK
版本: v0.2.0 (Adapter Layer) | 日期: 2026-07 | 团队: WIS Team
一、项目概述
1.1 定位
wisruntime 是一个企业级 Agent Runtime SDK(Python),以 pip install 方式提供给 AI 应用工程师使用。
它的核心使命:让 Agent 工程师专注 Agent 能力开发,无需关心底层基础设施。
1.2 一句话描述
为 Agent 开发者提供轻量、可扩展、多框架适配的统一运行时 SDK——用框架原生 API 构建 Agent 后,数行代码即可获得完整的生产级服务端点,并自动接入 WIS Hub 平台能力(MCP / Skill / Sandbox)。
1.3 核心价值
没有 wisruntime 有了 wisruntime
══════════════ ══════════════
AI 工程师 → 写 FastAPI 路由 AI 工程师 → 写 invoke_agent 函数
→ 处理 SSE 流式协议 → create_server(invoke_agent).start(8000)
→ 对接 MCP 协议 → ✅ Dify SSE 服务就绪
→ 对接 Skill API → ✅ 平台能力自动接入
→ 管理 Sandbox → ✅ API-KEY 统一鉴权
→ 对接鉴权系统 → ✅ 开箱即用
→ 每个应用都重复一遍
1.4 对标参考
本架构深度借鉴了阿里云 AgentRun Python SDK 的分层设计思想,在其基础上针对 WIS Hub 平台的差异化特点(统一 API-KEY 鉴权、Dify SSE 协议、DeepAgents 框架、平台能力托管)进行了重新设计。
二、整体架构
2.1 分层架构图
┌──────────────────────────────────────────────────────────────────────┐
│ WIS Hub 平台 (AI 中台) │
│ │
│ MCP 服务 │ Skill 服务 │ Sandbox 服务 │ Checkpoint 服务 │
│ (REST) │ (REST) │ (REST) │ (REST) │
│ │
│ 统一 API-KEY 鉴权: 创建应用 → 勾选能力 → 生成 Key │
└────────────────────────────┬─────────────────────────────────────────┘
│ HTTPS + Bearer <API-KEY>
│
┌────────────────────────────┴─────────────────────────────────────────┐
│ wisruntime SDK │
│ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ Infrastructure 层 │ │
│ │ Config (API-KEY / 端点 / 超时) │ │
│ │ CredentialContext (contextvars: 请求级 API-KEY 注入) │ │
│ │ BaseModel / 异常定义 / 日志 │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ Platform Client 层 (框架无关,只写一次) │ │
│ │ │ │
│ │ mcp/MCPClient skill/SkillClient │ │
│ │ sandbox/SandboxClient memory/CheckpointClient │ │
│ │ │ │
│ │ 每个 Client: 自动读 API-KEY, 统一错误处理, 异步 HTTP (httpx) │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ Capability Bridge 层 (每个框架一份) │ │
│ │ │ │
│ │ adapters/deepagents/ │ │
│ │ ├── mcp.py MCPClient → DeepAgents BaseTool │ │
│ │ ├── skill.py SkillClient → DeepAgents BaseTool │ │
│ │ └── sandbox.py SandboxClient → DeepAgents BaseTool │ │
│ │ │ │
│ │ adapters/agentscope/ (未来) │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ Adapter 层 (用户可选工具: Agent 对象 → invoke_agent 函数) │ │
│ │ │ │
│ │ 提供两种使用方式: │ │
│ │ ① wrap_agent(agent) → invoke_agent 函数 (便捷包装) │ │
│ │ ② 用户在 invoke_agent 内部手动做事件转换 │ │
│ │ │ │
│ │ adapters/deepagents/ (DeepAgents 事件 → CanonicalEvent) │ │
│ │ adapters/agentscope/ (未来) │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ Server 层 (框架无关,只认函数) │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌───────────────────┐ │ │
│ │ │ DifyHandler │ │AGUIHandler │ │ AgentInvoker │ │ │
│ │ │ SSE+Blocking │ │ (规划中) │ │ 函数归一化引擎 │ │ │
│ │ └──────┬───────┘ └──────┬───────┘ └─────────┬─────────┘ │ │
│ │ └─────────────────┴────────────────────┘ │ │
│ │ │ │ │
│ │ CanonicalEvent │ │
│ │ ProtocolRegistry │ │
│ └────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
2.2 分层职责
| 层 | 职责 | 关键组件 |
|---|---|---|
| Infrastructure | 全局配置、凭证注入、基础模型 | Config, CredentialContext, BaseModel |
| Platform Client | 封装 WIS Hub REST API 调用,统一鉴权 | MCPClient, SkillClient, SandboxClient, CheckpointClient |
| Capability Bridge | 平台客户端 → 框架原生 Tool 对象转换 | DeepAgentsMCPTool, DeepAgentsSkillTool |
| Adapter | (用户可选) Agent 对象 → invoke_agent 函数包装,及框架事件 → CanonicalEvent 转换 | wrap_agent(), DeepAgentsAdapter |
| Server | HTTP 服务承载、协议路由、请求校验、响应序列化。只接收 invoke_agent 函数 | DifyHandler, AgentInvoker, ProtocolRegistry |
2.3 关键设计决策
| 决策 | 说明 | 参考来源 |
|---|---|---|
| API-KEY 唯一鉴权 | 不引入 AK/SK/STS 签名体系,整个应用只用一个 API-KEY | WIS Hub 平台特征 |
| SDK 不管理资源生命周期 | 资源由 WIS Hub 平台管理,SDK 只做运行时调用(零 CRUD) | 与 AgentRun 的核心差异 |
| Platform Client 框架无关 | MCP/Skill/Sandbox 客户端只写一次,所有框架共享 | AgentRun Resource 层思想 |
| Capability Bridge 每个框架一份 | 将平台客户端转为框架原生 Tool 对象 | AgentRun Integration 层思想 |
| CanonicalEvent 13 种类型 | 综合 LangChain v2/v3、Dify/Wisknow、AgentRun 四套事件体系 | 见第三节 |
| Server 只接收 invoke_agent 函数 | Server 不知道框架的存在,用户函数 = 唯一入口。与 AgentRun 完全一致 | AgentRun AgentRunServer |
| AgentInvoker 单层归一化 | 用户函数 yield 的 str/AgentEvent → CanonicalEvent 流 | AgentRun AgentInvoker 模式 |
| 协议处理器双向负责 | Handler 负责请求解析 + 响应序列化 | AgentRun Protocol Handler 模式 |
| contextvars 注入 API-KEY | Server 模式下从请求 Bearer 头提取 → 注入请求上下文 | AgentRun CredentialContext 模式 |
三、CanonicalEvent 设计
CanonicalEvent 是 wisruntime 的核心抽象——框架事件和协议事件之间唯一的稳定契约。所有 Adapter 产出 CanonicalEvent,所有 ProtocolHandler 消费 CanonicalEvent。
3.1 13 种 CanonicalEvent 类型
┌──────────────────────────────────────────────────────────────────────┐
│ CanonicalEvent 类型体系 (13 种) │
├──────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ 内容流事件 │ │
│ │ TextDelta 文本增量 (token 级) │ │
│ │ ReasoningDelta 推理增量 (CoT 思维链 / extended thinking) │ │
│ │ FileOutput 文件输出 (图片/音频/视频/文件) │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ 工具调用生命周期 │ │
│ │ ToolCallStart 工具调用开始 (name + args_delta 分块流式) │ │
│ │ ToolCallEnd 工具调用结束 (result 或 error) │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ 子Agent 生命周期 (DeepAgents 子智能体委派) │ │
│ │ SubAgentStart 子Agent 开始运行 (agent_name, run_id, task) │ │
│ │ SubAgentEnd 子Agent 运行结束 (status: completed/failed) │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ 图执行生命周期 (🆕 v0.2) │ │
│ │ NodeLifecycleEvent LangGraph 节点生命周期 │ │
│ │ (node_name, node_type, phase=start/end, run_id, metadata) │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ 人机协作 (🆕 v0.2) │ │
│ │ InterruptEvent Agent 暂停等待人工输入/审批 │ │
│ │ (interrupt_id, node_name, message, data) │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ 元事件 │ │
│ │ Usage Token 用量统计 (prompt/completion/total) │ │
│ │ Error 错误事件 (message, code, recoverable) │ │
│ │ Custom 自定义事件 (name + payload 扩展通道) │ │
│ │ Done 流结束标记 (由 AgentInvoker 自动插入) │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────┘
3.2 LangGraph → CanonicalEvent → Dify SSE 完整事件映射表
下表展示从 LangGraph astream_events v2 事件,经 LangGraphEventConverter 转换为 CanonicalEvent,再经 DifyHandler 序列化为 Dify SSE 格式的完整链路。
LangGraph astream_events v2 CanonicalEvent Dify SSE
══════════════════════════ ════════════════ ════════════
on_chat_model_stream
chunk.content (str) TextDeltaEvent agent_message
delta=<content> { event, task_id, id<message_id>,
answer }
chunk.tool_call_chunks[] ToolCallStartEvent agent_thought
first chunk: {id, name, args} tool_call_id=<id> { event, task_id,
subsequent: {index, args} name=<name> id<tool_call_id>,
args_delta=<args> message_id, position,
tool, tool_input }
chunk.reasoning_content ReasoningDeltaEvent reasoning_chunk
(DeepSeek/Claude thinking) delta=<reasoning> { event, task_id,
reasoning }
on_chat_model_end
output.token_usage UsageEvent (累积到 message_end
prompt/completion/total metadata.usage)
on_tool_start
name="task" SubAgentStartEvent subagent_start
input.subagent_type agent_name=<type> { agent_name, run_id,
input.description task_description=<desc> task_description }
name≠"task" ToolCallStartEvent agent_thought
(if not via stream) (去重,避免重复发送) (同上)
on_tool_end
name="task" SubAgentEndEvent subagent_end
output status="completed" { agent_name, run_id, status }
name≠"task" ToolCallEndEvent agent_thought
output=<result> result=<formatted> { observation }
on_tool_error ErrorEvent error
code="TOOL_ERROR" { message, code, status=500 }
on_chain_start NodeLifecycleEvent node_started
name="model" / "tools" / ... phase="start" { node_id<run_id>,
node_type=<inferred> node_type, title }
on_chain_end NodeLifecycleEvent node_finished
phase="end" { node_id, node_type, title }
on_custom_event CustomEvent { event: <name>,
(from get_stream_writer) name=<event_name> ...payload }
payload=<data>
on_chain_error / ErrorEvent error
on_llm_error / code="CHAIN_ERROR" / { message, code, status=500 }
on_retriever_error "LLM_ERROR" / ...
interrupt() / interrupt_on InterruptEvent workflow_paused
(LangGraph interrupt) interrupt_id, node_name, { message, node_name }
message, data
(user fn 正常结束) DoneEvent message_end
(AgentInvoker 自动插入) metadata { task_id, id<message_id>,
metadata: { usage,
conversation_id },
files: [] }
3.3 数据流
┌─── invoke_agent 函数 (用户编写) ───┐
│ │
│ 用户可以在函数内部: │
│ · 直接 yield str / CanonicalEvent │
│ · 使用 wrap_agent(agent) 自动转换 │
│ · 手写 LangGraphEventConverter │
│ · 调用 MCPClient / SkillClient │
│ │
└────────────┬───────────────────────┘
│
▼
┌─────────────────────────┐
│ AgentInvoker │ ← 归一化:
│ │ str → TextDelta
│ AsyncGenerator[ │ chunk → TextDelta+ReasoningDelta
│ CanonicalEvent] │ 异常 → Error, 透传 CanonicalEvent
│ │ 自动 Done, 同步生成器支持
└────────────┬────────────┘
│
▼
┌─────────────────────────┐
│ ProtocolHandler │ ← 序列化: CanonicalEvent → SSE/JSON
│ │
│ DifyHandler → Dify SSE │ 生成 message_id
│ (未来) AGUIHandler │ 回传 conversation_id
└─────────────────────────┘
Adapter 层: wrap_agent → LangGraphEventConverter → CanonicalEvent 流 → yield
四、分层详解
4.1 Infrastructure 层
┌────────────────────────────────────────────────────┐
│ Config │
│ ├── api_key: str 统一 API-KEY │
│ ├── hub_endpoint: str WIS Hub 地址 │
│ ├── timeout: int = 600 请求超时(秒) │
│ └── 获取优先级: │
│ 1. 显式传入 Config(api_key="...") │
│ 2. 环境变量 WIS_HUB_API_KEY │
│ 3. 请求级 contextvars overlay (Server 模式) │
│ │
│ CredentialContext (contextvars) │
│ ├── get_request_api_key() → Optional[str] │
│ ├── set_request_api_key(key) 上下文管理器 │
│ └── 用途: Server 模式从 Bearer 头注入 → 全局可用 │
│ │
│ ApiKeyMiddleware (纯 ASGI) │
│ ├── 每次请求从 Authorization: Bearer <KEY> 解析 │
│ ├── 注入 contextvars overlay │
│ └── 请求结束时自动复位 │
└────────────────────────────────────────────────────┘
与 AgentRun 的差异: AgentRun 使用 AK/SK + STS + RAM 签名三级体系,wisruntime 只使用 API-KEY,结构大幅简化。但 contextvars 注入模式完全复用。
4.2 Platform Client 层
核心原则: 框架无关,只写一次。只做运行时调用,不做资源 CRUD。
WIS Hub 平台负责资源的创建和管理,SDK 不需要 create() / delete() 操作。每个 Client 只需要:
# 统一模式 (以 MCP 为例)
class MCPClient:
def __init__(self, base_url=None, api_key=None, timeout=600):
self._base_url = base_url or f"{Config.current().hub_endpoint}/mcp"
self._api_key = api_key
self._timeout = timeout
@property
def api_key(self) -> str:
"""三级获取: 显式传入 → contextvars → 环境变量"""
return self._api_key or CredentialContext.get_request_api_key()
async def call(self, method: str, params: dict) -> dict:
"""调用 MCP 工具"""
...
async def list_tools(self) -> list[dict]:
"""列出可用工具"""
...
各 Client 职责:
| Client | 职责 | 端点示例 |
|---|---|---|
MCPClient |
调用 WIS Hub 托管的 MCP 服务 | POST /mcp/invoke |
SkillClient |
调用 WIS Hub 托管的 Skill | POST /skill/{name}/invoke |
SandboxClient |
代码/浏览器沙箱执行 | POST /sandbox/execute |
CheckpointClient |
会话检查点读写 | GET/POST /checkpoint/{conversation_id} |
4.3 Capability Bridge 层
核心原则: 将平台客户端转换为框架原生的 Tool 对象。每个框架写一份。
# 以 DeepAgents 为例
from wisruntime.adapters.deepagents.mcp import DeepAgentsMCPTool
# MCPClient → DeepAgents BaseTool
tool = DeepAgentsMCPTool(
client=MCPClient(),
name="company_search",
description="搜索公司内部知识库"
)
agent = create_deep_agent(tools=[tool])
为什么需要这一层? DeepAgents 的 BaseTool 有特定的接口要求(如 ainvoke、schema、name)。MCPClient 返回的是原始 HTTP 响应。这层 Bridge 负责把 REST API 调用适配为框架期望的 Tool 协议。
4.4 Adapter 层
定位: 用户可选工具层。Server 不调用 Adapter,Adapter 是用户在 invoke_agent 中使用的辅助工具。
与 AgentRun 的设计一致:AgentRun 的 AgentRunConverter(仅 LangGraph 需要)是用户在 invoke_agent 内部自行使用的,不是 Server 的依赖。
两种使用方式:
方式 A: 用 wrap_agent 把 Agent 对象包装成 invoke_agent 函数 (便捷)
════════════════════════════════════════════════════════════════════
from wisruntime.adapters.deepagents import wrap_agent
agent = create_deep_agent(tools=[...])
create_server(wrap_agent(agent)).start(8000)
# wrap_agent 内部: Agent.astream_events → CanonicalEvent 流 → invoke_agent 函数
方式 B: 在 invoke_agent 内部手动做事件转换 (完全控制)
════════════════════════════════════════════════════════════════════
from wisruntime.adapters.deepagents import DeepAgentsAdapter
async def my_invoke_agent(request: AgentRequest):
agent = create_deep_agent(tools=[...])
adapter = DeepAgentsAdapter()
async for event in agent.astream_events(request.messages):
canonical = adapter.convert(event) # 框架事件 → CanonicalEvent
if canonical:
yield canonical
create_server(my_invoke_agent).start(8000)
Adapter 内部需要处理的核心问题 (参考 AgentRun AgentRunConverter):
| 问题 | 解决方案 |
|---|---|
| 流式工具调用 ID 追踪 | 维护 tool_call_index → id 映射,ToolCallStart 时携带 id |
| 推理/文本分离 | 检测 reasoning_content 或 reasoning-delta → ReasoningDelta |
| 结束检测 | v2: on_chain_end + name="LangGraph"; v3: message-finish |
| 错误降级 | on_tool_error → ToolCallEnd(error=...),不升级为全局 Error |
4.5 Server 层
核心原则: 与 AgentRun 完全一致——只接收 invoke_agent 函数,不感知框架。
┌──────────────────────────────────────────────────────────────┐
│ WisRuntimeServer │
│ │
│ 构造: create_server(invoke_agent, protocols=[DifyHandler()]) │
│ │
│ 初始化流程: │
│ 1. 创建 FastAPI app │
│ 2. 注入 ApiKeyMiddleware → contextvars │
│ 3. (可选) checkpoint 自动持久化包装 │
│ 4. 创建 AgentInvoker(invoke_agent) │
│ 5. 注册 /health 路由 │
│ 6. 挂载 ProtocolHandler 路由 │
│ │
│ 两种使用模式: │
│ │
│ 模式 1: 独立服务 (开发/调试) │
│ create_server(invoke_agent).start(port=8000) │
│ │
│ 模式 2: FastAPI 子应用 (生产推荐) │
│ app.mount("/agent", create_server(invoke_agent).as_fastapi_app())│
└──────────────────────────────────────────────────────────────┘
AgentInvoker — 唯一的归一化入口
invoke_agent 函数 → AgentInvoker.invoke(fn, request) → CanonicalEvent 流
自动检测:
├── async generator → 直接迭代
├── async function → await 得到返回值,单次 yield
├── sync function → loop.run_in_executor(线程池) + contextvars 传播
├── str → CanonicalEvent(type=TextDelta, data={delta: str})
├── CanonicalEvent → 透传
├── None → 跳过
└── Exception → CanonicalEvent(type=Error, ...)
设计决策:
├── Done 事件: AgentInvoker 在 generator 耗尽时自动插入
│ (Dify 协议必须有 message_end,AgentRun 不需要)
├── str 语义: 用户 yield 的 str 就是模型的文本输出 → TextDelta
│ 高级类型 (Reasoning/ToolCall) 用户通过直接 yield CanonicalEvent 表达
└── 函数签名: 不强约束,运行时 inspect 检测 (与 AgentRun 一致)
用户函数接收 AgentRequest 作为第一个位置参数
AgentRequest — 归一化请求模型
class Message(BaseModel):
role: Literal["system", "user", "assistant", "tool"]
content: str | list[dict] # 文本或多模态
name: str | None = None
class AgentRequest(BaseModel):
messages: list[Message] # 归一化消息列表
stream: bool = True # 是否流式
conversation_id: str | None = None # 会话ID
user_id: str | None = None # 用户ID
headers: dict[str, str] # 关键 header 副本 (可跨线程)
raw_request: Request | None = None # 原始 Starlette Request (本地可用)
protocol_extensions: dict = {} # 协议特有字段 (如 Dify 的 inputs/files)
ProtocolHandler — 响应序列化
┌─────────────────────────────────────────────────────────┐
│ DifyHandler │
│ │
│ 端点: │
│ POST /dify/v1/chat-messages SSE Streaming │
│ POST /dify/v1/chat-messages Blocking (stream=false) │
│ POST /dify/v1/chat-messages/{task_id}/stop (Phase 3) │
│ │
│ CanonicalEvent → Dify SSE 映射: │
│ TextDelta → agent_message │
│ { task_id, id<message_id>, answer }│
│ ReasoningDelta → reasoning_chunk │
│ { task_id, reasoning } │
│ ToolCallStart → agent_thought │
│ { task_id, id<tool_call_id>, │
│ message_id, position, │
│ tool, tool_input } │
│ ToolCallEnd → agent_thought │
│ { task_id, id<tool_call_id>, │
│ message_id, position, │
│ observation } │
│ SubAgentStart → subagent_start │
│ SubAgentEnd → subagent_end │
│ FileOutput → message_file │
│ NodeLifecycleEvent → node_started / node_finished 🆕 │
│ InterruptEvent → workflow_paused 🆕 │
│ Usage → (累积到 message_end.metadata.usage) │
│ Error → error { message, code, status } │
│ Custom → { event: name, ...payload } │
│ Done → message_end │
│ { task_id, id<message_id>, │
│ metadata: { usage, │
│ conversation_id }, │
│ files: [] } │
│ │
│ DifyHandler 关键 ID 管理 (🆕 v0.2): │
│ ├── task_id: 请求级 UUID,所有 SSE 事件共享 │
│ ├── message_id: 助手消息 UUID,贯穿 agent_message、 │
│ │ agent_thought、message_end │
│ ├── conversation_id: 请求传入 → message_end.metadata 回传 │
│ ├── agent_thought.id: 使用 ToolCallStart.tool_call_id │
│ │ (不再新生成 UUID,确保 start/end 配对一致) │
│ └── 心跳: 每 10 秒自动发送 data: {"event": "ping"}\n\n │
│ (asyncio background task) │
│ │
│ AGUIHandler (规划中) │
│ CanonicalEvent → AG-UI SSE 格式 │
└─────────────────────────────────────────────────────────┘
WisRuntimeServer — 组装
class ServerConfig(BaseModel):
cors_origins: list[str] = ["*"]
health_path: str = "/health"
class WisRuntimeServer:
def __init__(self, invoker: AgentInvoker, protocols: list[ProtocolHandler], config: ServerConfig):
# 1. 创建 FastAPI app
# 2. (Phase 2) 注入 ApiKeyMiddleware
# 3. for handler in protocols: handler.install(app, invoker)
# 4. 注册 health 路由
# 5. 配置 CORS
def start(self, host="0.0.0.0", port=8000):
"""模式 1: 独立服务 (内部调 uvicorn.run)"""
def as_fastapi_app(self) -> FastAPI:
"""模式 2: 导出为 FastAPI 子应用,供 app.mount()"""
# 模式 3 (as_router) 不做。用户如需 include_router 级别的控制,
# 可以手动创建 AgentInvoker + DifyHandler 自行组装。
def create_server(
invoke_agent: Callable,
*,
protocols: list[ProtocolHandler] | None = None, # 默认 [DifyHandler()]
config: ServerConfig | None = None,
checkpoint_enabled: bool = False, # Phase 3
) -> WisRuntimeServer:
...
五、用户故事与实施蓝图
5.1 用户故事总览
故事 1 ──────────────────────────────────────────────────────────────
│ 用户故事: 作为 AI 工程师,我只需写一个 invoke_agent 函数, │
│ 就能获得一个 Dify 兼容的 SSE 服务端点 │
│ │
│ 验收标准: │
│ from wisruntime import create_server │
│ from wisruntime.model import AgentRequest │
│ │
│ async def my_invoke_agent(request: AgentRequest): │
│ # 用户可以自由使用任何框架、任意逻辑 │
│ async for chunk in llm.astream(request.messages): │
│ yield chunk.content # yield str 即可 │
│ │
│ create_server(my_invoke_agent).start(port=8000) │
│ # → curl -X POST http://localhost:8000/dify/v1/chat-messages │
│ # -d '{"query": "你好", "response_mode": "streaming"}' │
│ # → SSE 流式响应,包含 agent_message / agent_thought / message_end│
└─────────────────────────────────────────────────────────────────────┘
故事 2 ──────────────────────────────────────────────────────────────
│ 用户故事: 作为 AI 工程师,我接入 WIS Hub MCP 服务时, │
│ 只需创建一个 MCPClient,它能自动处理鉴权和协议转换 │
│ │
│ 验收标准: │
│ from wisruntime.mcp import MCPClient │
│ from wisruntime.adapters.deepagents.mcp import DeepAgentsMCPTool │
│ from wisruntime.adapters.deepagents import wrap_agent │
│ from deepagents import create_deep_agent │
│ │
│ client = MCPClient() # 自动读 WIS_HUB_API_KEY │
│ tool = DeepAgentsMCPTool(client, name="search") │
│ agent = create_deep_agent(tools=[tool]) │
│ create_server(wrap_agent(agent)).start(8000) │
│ # → Agent 调用工具时,MCPClient 自动携带 Bearer 头请求 WIS Hub │
└─────────────────────────────────────────────────────────────────────┘
故事 3 ──────────────────────────────────────────────────────────────
│ 用户故事: 作为 AI 工程师,我可以将 wisruntime 服务嵌入到已有 │
│ FastAPI 应用中,作为子路由挂载 │
│ │
│ 验收标准: │
│ from fastapi import FastAPI │
│ app = FastAPI() │
│ app.mount("/agent", create_server(my_invoke_agent).as_fastapi_app())│
│ # → /agent/dify/v1/chat-messages 就绪 │
│ # → 复用主应用的 middleware 和 lifecycle │
└─────────────────────────────────────────────────────────────────────┘
故事 4 ──────────────────────────────────────────────────────────────
│ 用户故事: 作为 AI 工程师,我用 wrap_agent 包装 deepagents Agent, │
│ 一行代码就生成 invoke_agent 函数 │
│ │
│ 验收标准: │
│ from deepagents import create_deep_agent │
│ from wisruntime.adapters.deepagents import wrap_agent │
│ │
│ agent = create_deep_agent(tools=[...]) │
│ create_server(wrap_agent(agent)).start(8000) │
│ # → wrap_agent 内部: astream_events → CanonicalEvent → yield │
└─────────────────────────────────────────────────────────────────────┘
故事 5 ──────────────────────────────────────────────────────────────
│ 用户故事: 作为 AI 工程师,我的 Agent 需要调用 WIS Hub 的 Skill 和 │
│ Sandbox 能力,使用与 MCP 相同的简洁 API │
│ │
│ 验收标准: │
│ from wisruntime.skill import SkillClient │
│ from wisruntime.sandbox import SandboxClient │
│ from wisruntime.adapters.deepagents.skill import DeepAgentsSkillTool│
│ from wisruntime.adapters.deepagents.sandbox import DeepAgentsSandboxTool│
│ agent = create_deep_agent(tools=[ │
│ DeepAgentsSkillTool(SkillClient(), name="data_analysis"), │
│ DeepAgentsSandboxTool(SandboxClient(), name="code_executor"), │
│ ]) │
│ create_server(wrap_agent(agent)).start(8000) │
└─────────────────────────────────────────────────────────────────────┘
故事 6 ──────────────────────────────────────────────────────────────
│ 用户故事: 作为 AI 工程师,我的 Agent 对话历史可以自动持久化到 │
│ WIS Hub Checkpoint 服务,支持断点续传 │
│ │
│ 验收标准: │
│ create_server(my_invoke_agent, checkpoint_enabled=True).start(8000) │
│ # → 每次对话自动保存到 WIS Hub │
│ # → 下次请求带有相同 conversation_id 时恢复上下文 │
└─────────────────────────────────────────────────────────────────────┘
5.2 实施阶段
Phase 0: 项目骨架 (已完成)
══════════════════════════
✅ 清理旧代码,确定新的架构方向
✅ 分析 AgentRun、LangChain、Dify 参考架构
✅ 确定 CanonicalEvent 11 种类型
✅ 确定分层架构
Phase 1: 核心基础设施 + MVP Server (✅ 已完成)
═══════════════════════════════════════════
目标: 用户故事 1 — 写 invoke_agent 函数,获得 Dify SSE 端点
✅ CanonicalEvent 13 种类型定义 (v0.2 扩至 13 种)
✅ AgentRequest + Message 模型
✅ Config + CredentialContext (contextvars 定义)
✅ AgentInvoker (async/sync/iterator 检测, str→TextDelta,
模型 chunk 智能提取, 同步生成器支持, 自动 Done)
✅ DifyHandler (parse_request + _format_stream + 心跳 + Blocking)
- message_id 生成与全链路透传
- agent_thought.id 使用 tool_call_id (start/end 配对一致)
- conversation_id 回传
✅ WisRuntimeServer (start + as_fastapi_app)
✅ ProtocolRegistry + create_server 顶层 API
✅ create_server 自动检测 CompiledStateGraph → wrap_agent
Phase 2: LangGraph 适配器层 (✅ 已完成 — v0.2)
═══════════════════════════════════
目标: 用户故事 4 — wrap_agent、LangGraphEventConverter
✅ LangGraphEventConverter (astream_events v2 → CanonicalEvent)
- 工具调用 ID 追踪 (tool_call_id_map)
- 子智能体检测 (task 工具 → SubAgentStart/End)
- 节点生命周期 (on_chain_start/end → NodeLifecycleEvent)
- CustomEvent、ErrorEvent、UsageEvent 映射
- updates/values 兼容模式
✅ wrap_agent(agent_graph) 工厂函数
- 支持 astream_events (默认)、updates、values 三种模式
✅ AgentInvoker 模型 chunk 智能提取
- content → TextDeltaEvent
- reasoning_content → ReasoningDeltaEvent
✅ CanonicalEvent 扩展: +InterruptEvent, +NodeLifecycleEvent
✅ DifyHandler SSE 格式合规修复
- message_id, conversation_id, agent_thought id 一致性
Phase 3: 平台能力接入 (规划中)
══════════════════════════
目标: 用户故事 2 + 5 — MCP/Skill/Sandbox 接入
□ MCPClient + MCP 数据模型
□ SkillClient + Skill 数据模型
□ SandboxClient + Sandbox 数据模型
□ DeepAgentsMCPTool (MCPClient → BaseTool)
□ DeepAgentsSkillTool (SkillClient → BaseTool)
□ DeepAgentsSandboxTool (SandboxClient → BaseTool)
Phase 4: 高级特性
════════════════
目标: 用户故事 6 — Checkpoint、多协议
□ CheckpointClient + 自动持久化
□ AGUIHandler
□ Stop endpoint
□ ApiKeyMiddleware
Phase 4: 多框架 + 多协议
══════════════════════
目标: 验证可扩展性
□ AgentScope Adapter
□ AGUI Handler
□ 内部 PyPI 发布
□ 性能基准测试
5.3 目录结构 (目标态)
wisruntime/
├── __init__.py # 顶层导出: create_server, Config
├── config.py # Config, CredentialContext
├── middleware.py # ApiKeyMiddleware (纯 ASGI)
├── model.py # CanonicalEvent, AgentRequest 等核心模型
├── exceptions.py # 自定义异常
│
├── mcp/ # Platform Client: MCP
│ ├── __init__.py
│ ├── client.py # MCPClient
│ └── model.py # MCP 请求/响应模型
│
├── skill/ # Platform Client: Skill
│ ├── __init__.py
│ ├── client.py # SkillClient
│ └── model.py
│
├── sandbox/ # Platform Client: Sandbox
│ ├── __init__.py
│ ├── client.py # SandboxClient
│ └── model.py
│
├── memory/ # Platform Client: Checkpoint
│ ├── __init__.py
│ ├── client.py # CheckpointClient
│ └── model.py
│
├── adapters/ # Adapter + Capability Bridge (用户可选工具)
│ ├── __init__.py # 包说明
│ └── langgraph/ # LangGraph/LangChain/DeepAgents 框架 (✅ v0.2)
│ ├── __init__.py # 导出 LangGraphEventConverter, wrap_agent
│ ├── converter.py # LangGraphEventConverter (事件转换器)
│ └── wrap_agent.py # wrap_agent() 糖函数
│ # (未来) llamaindex/ # LlamaIndex 框架适配
│ # (未来) crewai/ # CrewAI 框架适配
│
├── server/ # Server 层
│ ├── __init__.py
│ ├── server.py # WisRuntimeServer (FastAPI 组装)
│ ├── invoker.py # AgentInvoker (函数归一化)
│ ├── model.py # AgentRequest, ProtocolRegistry
│ ├── dify_handler.py # DifyHandler (SSE + Blocking)
│ └── agui_handler.py # AGUIHandler (规划中)
│
└── integration/ # 会话持久化 + 框架 Memory 适配
├── __init__.py
└── deepagents/
└── memory.py # WISCheckpointMemory (CheckpointClient → DeepAgents Memory)
六、技术规格
6.1 运行时依赖
| 包 | 版本 | 用途 |
|---|---|---|
fastapi |
≥ 0.110 | Web 框架 |
uvicorn[standard] |
≥ 0.27 | ASGI 服务器 |
sse-starlette |
≥ 2.0 | SSE 流式响应 |
pydantic |
≥ 2.0 | 数据校验与序列化 |
httpx |
≥ 0.27 | 异步 HTTP 客户端 |
6.2 环境变量
| 变量 | 用途 | 必填 |
|---|---|---|
WIS_HUB_API_KEY |
WIS Hub 统一 API-KEY | 使用 MCP/Skill/Sandbox/Checkpoint 时必填 |
WIS_HUB_ENDPOINT |
WIS Hub 地址 | 可选,默认读取 SDK 内置配置 |
6.3 Python 版本
≥ 3.11
七、参考资源
- AgentRun Python SDK 架构文档 — 阿里云 AgentRun SDK 完整架构
- LangChain 流式事件详解 — v2/v3 事件体系
- Dify vs 灵知 流式事件差异分析 — 协议兼容性分析
- PRD.md — 原始产品需求文档
Metadata
Release files for wisruntime 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| wisruntime-0.1.1.tar.gz | 58.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| wisruntime-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 99.6 kB
Release files / wisruntime-0.1.1.tar.gz
| Download URL | wisruntime-0.1.1.tar.gz |
|---|---|
| Size | 58.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
cbfb3d1e2d21a08a65b65154cb4a988109d21b756f88060c4a480813c98dccac
|
|
BLAKE2b-256 checksum How to use checksums |
59adea651f7dd87bfdbfffef7d8d94709d12bbf1846cc07493390cb51bd56aff
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|
Release files / wisruntime-0.1.1-py3-none-any.whl
| Download URL | wisruntime-0.1.1-py3-none-any.whl |
|---|---|
| Size | 41.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2e3d3f46cf833f211519a2c814fd2fcb1f8d2d232971a150c126146684372c42
|
|
BLAKE2b-256 checksum How to use checksums |
3e5ed17ae1d1a08ec1df91ac4f921e729c7236afb42b316cedd9d7ac5a297c32
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|