Skip to main content

wisruntime — 企业级 Agent Runtime SDK

版本: v0.1.1 (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 层 (框架无关,只写一次)                         │  │
│  │                                                                  │  │
│  │  tool/ToolClient (ABC) + MCPClient                           │  │
│  │  tool/ToolInfo, ToolResult (shared models)                │  │
│  │                                                                  │  │
│  │  原则: 不重复造轮子,直接使用标准 mcp Python 包    │  │
│  └────────────────────────────────────────────────────────────────┘  │
│                                                                      │
│  ┌────────────────────────────────────────────────────────────────┐  │
│  │  Capability Bridge 层 (每个框架一份)                             │  │
│  │                                                                  │  │
│  │  adapters/langgraph/                                           │  │
│  │    ├── tool_adapter.py  ToolClient → LangChain StructuredTool           │  │
│  │    └── builtin.py       create_model / create_mcp_tools()           │  │
│  │              │  │
│  │                                                                  │  │
│  │  adapters/agentscope/  (未来)                                    │  │
│  └────────────────────────────────────────────────────────────────┘  │
│                                                                      │
│  ┌────────────────────────────────────────────────────────────────┐  │
│  │  Adapter 层 (用户可选工具: Agent 对象 → invoke_agent 函数)       │  │
│  │                                                                  │  │
│  │  提供两种使用方式:                                                 │  │
│  │  ① wrap_agent(agent) → invoke_agent 函数 (便捷包装)              │  │
│  │  ② 用户在 invoke_agent 内部手动做事件转换                         │  │
│  │                                                                  │  │
│  │  adapters/langgraph/   (DeepAgents 事件 → CanonicalEvent)      │  │
│  │  adapters/agentscope/   (未来)                                   │  │
│  └────────────────────────────────────────────────────────────────┘  │
│                                                                      │
│  ┌────────────────────────────────────────────────────────────────┐  │
│  │  Server 层 (框架无关,只认函数)                                    │  │
│  │                                                                  │  │
│  │  ┌──────────────┐  ┌──────────────┐  ┌───────────────────┐     │  │
│  │  │ DifyHandler  │  │AGUIHandler   │  │  AgentInvoker     │     │  │
│  │  │ SSE+Blocking │  │  (规划中)     │  │  函数归一化引擎    │     │  │
│  │  └──────┬───────┘  └──────┬───────┘  └─────────┬─────────┘     │  │
│  │         └─────────────────┴────────────────────┘                │  │
│  │                           │                                      │  │
│  │                    CanonicalEvent                                │  │
│  │                    ProtocolRegistry                              │  │
│  └────────────────────────────────────────────────────────────────┘  │
└──────────────────────────────────────────────────────────────────────┘

2.2 分层职责

层 职责 关键组件
Infrastructure 全局配置、凭证注入、基础模型 Config, CredentialContext, BaseModel
Platform Client 平台工具接入层,抽象协议 + MCP 实现 ToolClient (ABC), MCPClient, ToolInfo, ToolResult
Capability Bridge ToolClient → LangChain StructuredTool 转换 LangChainToolAdapter, create_mcp_tools()
Adapter (用户可选) Agent 对象 → invoke_agent 函数包装,及框架事件 → CanonicalEvent 转换 wrap_agent(), LangGraphEventConverter
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 层 (tool/)

核心原则: 框架无关,只写一次。ToolClient 抽象协议 + MCP 实现。

WIS Hub 平台负责资源的创建和管理,SDK 不需要 create() / delete() 操作。

# ToolClient 抽象协议
class ToolClient(ABC):
    async def list_tools(self) -> list[ToolInfo]: ...
    async def call_tool(self, name: str, arguments: dict) -> ToolResult: ...
    async def close(self): ...

# MCP 协议实现 (薄封装标准 mcp 包)
class MCPClient(ToolClient):
    def __init__(self, url=None, api_key=None, timeout=600):
        self._url = url or f"{Config().hub_endpoint}/wishub-mcp/mcp"
        self._api_key = api_key or Config().api_key  # 三级获取

    async def list_tools(self) -> list[ToolInfo]:  # async 不加后缀
        ...

    async def call_tool(self, name, arguments=None) -> ToolResult:
        ...

    def list_tools_sync(self) -> list[ToolInfo]:  # sync 糖加 _sync 后缀
        ...

各 Client 职责:

Client 职责 端点示例
MCPClient (已实现) MCP Streamable HTTP 工具发现与调用 POST /wishub-mcp/mcp
SkillClient (已实现) Skill 知识包发现/详情/下载 — 本地化注入,不走 ToolClient 协议 GET /wishub-skill/openapi/v1/skills
SandboxClient (规划中) 代码/浏览器沙箱执行 —

4.3 Capability Bridge 层 (adapters/langgraph/)

核心原则: 将 ToolClient 的 ToolInfo 转换为 LangChain StructuredTool。

from wisruntime.tool import MCPClient
from wisruntime.adapters.langgraph import create_mcp_tools, LangChainToolAdapter
from deepagents import create_deep_agent

# 方式 A: 一行获取 MCP 工具并创建 Agent(推荐)
tools = await create_mcp_tools()                           # 自动读取 WIS_HUB_API_KEY
agent = create_deep_agent(model=model, tools=tools)

# 方式 B: 手动控制 — 创建 client,桥接所有工具
client = MCPClient()
tools = await LangChainToolAdapter.from_client(client)
agent = create_deep_agent(model=model, tools=tools)

# 方式 C: 过滤只接入部分工具
tools = await LangChainToolAdapter.from_client(
    client, filter_fn=lambda t: "search" in t.name
)

# 方式 D: 单工具手动创建
adapter = LangChainToolAdapter.from_tool_info(tool_info, my_callable)

Skill 是另一种接入模式——知识包下载到本地后注入 deepagents(不走 ToolClient):

from wisruntime.adapters.langgraph.builtin import create_skill_sources

# 一行下载并返回 deepagents skills= 参数(skillCode 为平台唯一标识)
skill_paths = await create_skill_sources(
    remote_skills=["skill_k67go1cujb6ji"],
    skill_versions={"skill_k67go1cujb6ji": "1.0.4"},   # 严格 SemVer,可选
)
agent = create_deep_agent(model=model, tools=tools, skills=skill_paths)

Skill 客户端完整用法(发现/详情/批量下载)见 docs/wishub-skill-api.md。

核心能力: from_tool_info() 将 ToolInfo + 调用函数转为 StructuredTool(BaseTool 子类),内部自动完成 JSON Schema → Pydantic Model 的动态生成,无需手写参数校验。from_client() / create_mcp_tools() 一键发现所有工具并批量创建。MCPClient 无状态设计——每次 call_tool() 自包含 session,无需显式管理生命周期。

4.4 Adapter 层

定位: 用户可选工具层。Server 不调用 Adapter,Adapter 是用户在 invoke_agent 中使用的辅助工具。

与 AgentRun 的设计一致:AgentRun 的 AgentRunConverter(仅 LangGraph 需要)是用户在 invoke_agent 内部自行使用的,不是 Server 的依赖。

两种使用方式:

方式 A: 用 wrap_agent 把 Agent 对象包装成 invoke_agent 函数 (便捷)
════════════════════════════════════════════════════════════════════

  from wisruntime.adapters.langgraph 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.langgraph import LangGraphEventConverter

  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,一行代码桥接为 DeepAgents 工具        │
│                                                                   │
│  验收标准:                                                          │
│    from wisruntime.adapters.langgraph import create_mcp_tools        │
│    from deepagents import create_deep_agent                          │
│                                                                   │
│    tools = await create_mcp_tools()  # 一行,自动读 WIS_HUB_API_KEY │
│    agent = create_deep_agent(model=model, tools=tools)               │
│    # → Agent 调用工具时,MCPClient 自动创建 session 并携带 Bearer 头  │
└─────────────────────────────────────────────────────────────────────┘

故事 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.langgraph import wrap_agent             │
│                                                                   │
│    agent = create_deep_agent(tools=[...])                             │
│    create_server(wrap_agent(agent)).start(8000)                       │
│    # → wrap_agent 内部: astream_events → CanonicalEvent → yield      │
└─────────────────────────────────────────────────────────────────────┘

故事 5 ──────────────────────────────────────────────────────────────
│  用户故事: 作为 AI 工程师,我可以用一行代码从 WIS Hub 下载 Skill 知识包  │
│           并注入 deepagents,Agent 自动获得平台沉淀的领域能力            │
│                                                                   │
│  验收标准:                                                          │
│    # skillCode 为平台唯一标识;下载后注入 deepagents skills=          │
│    from wisruntime.adapters.langgraph.builtin import (               │
│        create_skill_sources)                                         │
│    skill_paths = await create_skill_sources(                        │
│        remote_skills=["skill_k67go1cujb6ji"],                        │
│        skill_versions={"skill_k67go1cujb6ji": "1.0.4"},             │
│    )                                                                │
│    agent = create_deep_agent(model=model, skills=skill_paths)       │
└─────────────────────────────────────────────────────────────────────┘

故事 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: 平台能力接入 (MCP ✅ 已完成 — v0.3)
═══════════════════════════════
  目标: 用户故事 2 + 5 — MCP/Skill/Sandbox 接入

  ✅ tool/ 模块: ToolInfo, ToolResult, ToolClient (ABC), MCPClient
  ✅ MCPClient 无状态设计 (v0.3.1) — 每次调用自包含 session,无需生命周期管理
  ✅ LangChainToolAdapter: ToolInfo → StructuredTool(BaseTool 子类, JSON Schema → Pydantic)
  ✅ builtin.py: create_model() / create_mcp_tools() 一行接入
  ✅ ToolException + handle_tool_error 错误处理对齐 LangChain 标准
  ✅ SkillClient + Skill 数据模型 (v0.3.x) — 对接中台 wishub-skill openapi/v1:
     分页发现 / 详情 / 302 预签名下载 / 严格 SemVer 版本, create_skill_sources() deepagents 集成
  □ SandboxClient + Sandbox 数据模型

Phase 4: 高级特性
════════════════
  目标: 用户故事 6 — Checkpoint、多协议

  □ CheckpointClient + 自动持久化
  □ AGUIHandler
  □ Stop endpoint
  □ ApiKeyMiddleware

Phase 4: 多框架 + 多协议
══════════════════════
  目标: 验证可扩展性

  □ AgentScope Adapter
  □ AGUI Handler
  □ 内部 PyPI 发布
  □ 性能基准测试

5.3 目录结构 (目标态)

wisruntime/
├── __init__.py                  # 顶层导出: create_server, Config, ToolInfo, ToolResult
├── config.py                    # Config, CredentialContext
├── model.py                     # CanonicalEvent (13 种), AgentRequest, Message
├── exceptions.py                # 自定义异常
│
├── tool/                        # 平台工具接入层 (✅ v0.3 — 一个模块)
│   ├── __init__.py              #   exports: ToolClient, MCPClient, ToolInfo, ToolResult
│   ├── model.py                 #   ToolInfo, ToolResult (工具共享数据契约)
│   ├── protocol.py              #   ToolClient (ABC) — 抽象协议
│   └── client.py                #   MCPClient(ToolClient) — 薄封装标准 mcp 包
│
├── adapters/                    # Adapter + Capability Bridge
│   └── langgraph/               #   LangGraph/LangChain/DeepAgents 框架 (✅ v0.3)
│       ├── __init__.py          #     导出 LangGraphEventConverter, wrap_agent, LangChainToolAdapter, create_model, create_mcp_tools
│       ├── converter.py         #     LangGraphEventConverter (astream_events → CanonicalEvent)
│       ├── wrap_agent.py        #     wrap_agent() 糖函数
│       ├── tool_adapter.py      #     LangChainToolAdapter (ToolInfo → StructuredTool)
│       └── builtin.py           #     create_model() / create_mcp_tools() 一行接入
│
├── server/                      # Server 层
│   ├── __init__.py
│   ├── server.py                #   WisRuntimeServer (FastAPI 组装)
│   ├── invoker.py               #   AgentInvoker (函数归一化)
│   ├── dify_handler.py          #   DifyHandler (SSE + Blocking)
│   └── protocol.py              #   ProtocolHandler 抽象基类
│
├── skill/                       # Skill 知识包接入层 (✅ v0.3.x — wishub-skill openapi/v1)
│   ├── __init__.py              #   exports: SkillClient, SkillInfo, SkillDetailInfo, SkillPage
│   ├── model.py                 #   SkillInfo / SkillDetailInfo / SkillPage(信封分页模型)
│   └── client.py                #   SkillClient — 分页发现 / 详情 / 302 预签名下载
│
└── (未来: sandbox/, checkpoint/)  # 远程工具接入走 ToolClient 协议,不同实现

六、技术规格

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


七、参考资源

Metadata

Release files for wisruntime 0.4.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for wisruntime 0.4.2
File Size Uploaded
wisruntime-0.4.2.tar.gz 87.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for wisruntime 0.4.2
File Interpreter ABI Platform
wisruntime-0.4.2-py3-none-any.whl Python 3 none any Details

Total release size: 168.1 kB

Release files / wisruntime-0.4.2.tar.gz

Download URL wisruntime-0.4.2.tar.gz
Size 87.7 kB
Tags Source
SHA-256 checksum
How to use checksums
fe5387af2df740761e2fac344ab279c2d1276dfd0625a3168f3db9e3e335dce2
BLAKE2b-256 checksum
How to use checksums
1481b1bc74cce55e0981b7b2d6b6a2a77ee3a8023ab3c610fac4828d82029e5e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.5

Release files / wisruntime-0.4.2-py3-none-any.whl

Download URL wisruntime-0.4.2-py3-none-any.whl
Size 80.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2d3c50315df6facc8f937e987937e18b0ae0b1630196420fda79988e2366f350
BLAKE2b-256 checksum
How to use checksums
e90db9922343e12a004eba069bf59bc58e5ad507005f7ab4c7f2799c1f3a49fa
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.5

Release history Release notifications | RSS feed

0.5.0

2 release files

This release

0.4.2 This release

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page