Metadata-Version: 2.4
Name: wisruntime
Version: 0.1.0
Summary: 企业级 Agent Runtime SDK — 让 AI 工程师专注 Agent 能力开发，无需关心底层基础设施
Author: WIS Team
License: MIT
Project-URL: Homepage, https://github.com/infinitus-devops/aip-wisruntime
Project-URL: Repository, https://github.com/infinitus-devops/aip-wisruntime
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Framework :: FastAPI
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: fastapi>=0.110
Requires-Dist: uvicorn[standard]>=0.27
Requires-Dist: sse-starlette>=2.0
Requires-Dist: pydantic>=2.0
Requires-Dist: httpx>=0.27
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
Requires-Dist: httpx>=0.27; extra == "dev"

# wisruntime — 企业级 Agent Runtime SDK

> **版本**: v0.1.0 (MVP) | **日期**: 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](https://github.com/Serverless-Devs/agentrun-sdk-python) 的分层设计思想，在其基础上针对 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 11 种类型** | 综合 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 设计来源

综合分析了四套事件体系，取各自最佳实践：

| 事件体系 | 来源 | 特点 |
|---------|------|------|
| **LangChain v2** | `astream_events(version="v2")` | Runnable 层级事件，`on_*_start/stream/end` 模式，支持自定义事件 |
| **LangChain v3** | `astream_events(version="v3")` | Content-block 协议层级，`message-start → block-delta → message-finish`，推理/文本/工具调用独立块 |
| **Dify / Wisknow** | `event/` 目录中的实体定义 | SSE 协议事件，`agent_message`、`agent_thought`、`message_end`、`subagent_start/end` |
| **AgentRun** | `server/model.py` 中的 `EventType` | `TEXT`、`REASONING`、`TOOL_CALL`、`TOOL_CALL_CHUNK`、`TOOL_RESULT`、`ERROR`、`CUSTOM` |

### 3.2 11 种 CanonicalEvent 类型

```
┌──────────────────────────────────────────────────────────────────────┐
│                      CanonicalEvent 类型体系                           │
├──────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  ┌──────────────────────────────────────────────────────────────┐   │
│  │  内容流事件                                                     │   │
│  │                                                                  │   │
│  │  TextDelta         文本增量                                      │   │
│  │    ← v2 on_chat_model_stream(chunk.content)                     │   │
│  │    ← v3 content-block-delta(text-delta)                         │   │
│  │    ← Dify agent_message / text_chunk                            │   │
│  │    ← AgentRun TEXT                                              │   │
│  │                                                                  │   │
│  │  ReasoningDelta    推理增量 (CoT 思维链)                          │   │
│  │    ← v2 additional_kwargs.reasoning_content                     │   │
│  │    ← v3 content-block-delta(reasoning-delta)                    │   │
│  │    ← Dify reasoning_chunk                                       │   │
│  │    ← AgentRun REASONING                                         │   │
│  │                                                                  │   │
│  │  FileOutput        文件输出 (图片/音频/视频/文件)                  │   │
│  │    ← v3 image/audio/video/file content blocks                   │   │
│  │    ← Dify message_file                                          │   │
│  └──────────────────────────────────────────────────────────────┘   │
│                                                                      │
│  ┌──────────────────────────────────────────────────────────────┐   │
│  │  工具调用生命周期 (借鉴 LangChain v3 的 block 模型)              │   │
│  │                                                                  │   │
│  │  ToolCallStart     工具调用开始                                   │   │
│  │    ← v2 on_tool_start (data.input)                              │   │
│  │    ← v3 content-block-start(type=tool_call)                     │   │
│  │    ← Dify agent_thought (tool + tool_input 前半)                │   │
│  │    ← AgentRun TOOL_CALL / TOOL_CALL_CHUNK                       │   │
│  │    data: { id, name, args_delta }  (args 分块流式到达)           │   │
│  │                                                                  │   │
│  │  ToolCallEnd       工具调用结束 (含执行结果)                       │   │
│  │    ← v2 on_tool_end (data.output)                               │   │
│  │    ← v3 content-block-finish(type=tool_call)                    │   │
│  │    ← Dify agent_thought (observation 后半)                      │   │
│  │    ← AgentRun TOOL_RESULT                                       │   │
│  │    data: { id, result, error? }                                 │   │
│  └──────────────────────────────────────────────────────────────┘   │
│                                                                      │
│  ┌──────────────────────────────────────────────────────────────┐   │
│  │  子Agent生命周期 (灵知/Wisknow 自定义场景)                        │   │
│  │                                                                  │   │
│  │  SubAgentStart     子Agent开始运行                                 │   │
│  │    ← 灵知 subagent_start                                        │   │
│  │    data: { agent_name, run_id, task_description }                │   │
│  │                                                                  │   │
│  │  SubAgentEnd       子Agent运行结束                                 │   │
│  │    ← 灵知 subagent_end                                          │   │
│  │    data: { agent_name, run_id, status }                          │   │
│  └──────────────────────────────────────────────────────────────┘   │
│                                                                      │
│  ┌──────────────────────────────────────────────────────────────┐   │
│  │  元事件                                                         │   │
│  │                                                                  │   │
│  │  Usage             Token 用量统计                                 │   │
│  │    ← v2 usage_metadata (on_chat_model_end)                      │   │
│  │    ← v3 message-finish.usage                                    │   │
│  │    ← Dify metadata.usage                                        │   │
│  │    data: { prompt_tokens, completion_tokens, total_tokens }     │   │
│  │                                                                  │   │
│  │  Error             错误事件                                       │   │
│  │    ← 所有体系都有                                                │   │
│  │    data: { message, code?, recoverable? }                       │   │
│  │                                                                  │   │
│  │  Custom            自定义事件 (扩展通道)                           │   │
│  │    ← v2 on_custom_event                                         │   │
│  │    ← 灵知 todo_update / recommendation_card / next_step         │   │
│  │    data: { name, payload }                                      │   │
│  │                                                                  │   │
│  │  Done              流结束标记                                     │   │
│  │    ← v3 message-finish                                          │   │
│  │    ← Dify message_end                                           │   │
│  │    ← AgentRun 流自然结束 (隐式)                                   │   │
│  │    data: { metadata? }                                          │   │
│  └──────────────────────────────────────────────────────────────┘   │
│                                                                      │
└──────────────────────────────────────────────────────────────────────┘
```

### 3.3 数据流

```
                    ┌─── invoke_agent 函数 (用户编写) ───┐
                    │                                    │
                    │  用户可以在函数内部:                  │
                    │  · 直接 yield str / CanonicalEvent  │
                    │  · 使用框架 Agent + Adapter 转换     │
                    │  · 调用 MCPClient / SkillClient     │
                    │                                    │
                    └────────────┬───────────────────────┘
                                 │
                                 ▼
                    ┌─────────────────────────┐
                    │     AgentInvoker         │  ← 归一化: str → TextDelta
                    │                          │           异常 → Error
                    │  AsyncGenerator[          │           透传 CanonicalEvent
                    │    CanonicalEvent]        │
                    └────────────┬────────────┘
                                 │
                                 ▼
                    ┌─────────────────────────┐
                    │   ProtocolHandler        │  ← 序列化: CanonicalEvent → SSE/JSON
                    │                          │
                    │  DifyHandler → Dify SSE  │
                    │  AGUIHandler → AG-UI     │
                    └─────────────────────────┘

  Adapter 层 (用户可选工具): 框架 Agent + Adapter → CanonicalEvent 流 → 在 invoke_agent 中 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 只需要：

```python
# 统一模式 (以 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 对象。每个框架写一份。**

```python
# 以 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 — 归一化请求模型**

```python
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 (answer=delta)         │
│    ReasoningDelta → reasoning_chunk (reasoning=delta)    │
│    ToolCallStart  → agent_thought (tool, tool_input)    │
│    ToolCallEnd    → agent_thought (observation)          │
│    SubAgentStart  → subagent_start                      │
│    SubAgentEnd    → subagent_end                        │
│    FileOutput     → message_file                        │
│    Usage          → (累积到 message_end.metadata.usage)   │
│    Error          → error (message, code, status)        │
│    Custom         → {event: name, ...payload}           │
│    Done           → message_end                         │
│                                                          │
│  DifyHandler 内部维护的协议特有状态:                        │
│  ├── agent_thought 的 id (唯一标识) + position (递增)     │
│  │     CanonicalEvent 不携带 Dify 特定的 id/position，    │
│  │     由 Handler 自行生成，保持 CanonicalEvent 协议无关   │
│  └── 心跳: 每 10 秒自动发送 data: {"event": "ping"}\n\n  │
│        (asyncio background task)                         │
│                                                          │
│  AGUIHandler (规划中)                                      │
│    CanonicalEvent → AG-UI SSE 格式                        │
└─────────────────────────────────────────────────────────┘
```

**WisRuntimeServer — 组装**

```python
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 11 种类型定义
  □ AgentRequest + Message 模型
  □ Config + CredentialContext (contextvars 定义) — ApiKeyMiddleware 后置到 Phase 2
  □ AgentInvoker (async/sync/iterator 检测, str→TextDelta, Exception→Error, 自动 Done)
  □ DifyHandler (parse_request + _format_stream + 心跳)
      - agent_thought 的 id/position 由 Handler 内部维护
      - Blocking 模式: 收集全部事件 → JSONResponse
  □ WisRuntimeServer (start + as_fastapi_app) — as_router 不做
  □ ProtocolRegistry + create_server 顶层 API
  □ 单元测试 + SSE 端到端对接测试

  范围外 (后续故事):
  ✗ ApiKeyMiddleware (Phase 2, 配合 MCPClient)
  ✗ DeepAgentsAdapter / wrap_agent (Phase 3)
  ✗ AGUIHandler (Phase 4)
  ✗ Checkpoint 自动持久化 (Phase 3)
  ✗ Stop endpoint (Phase 3)
  ✗ Platform Client 层 (Phase 2)

Phase 2: 平台能力接入
══════════════════
  目标: 用户故事 2 + 5 — MCP/Skill/Sandbox 接入

  □ MCPClient + MCP 数据模型
  □ SkillClient + Skill 数据模型
  □ SandboxClient + Sandbox 数据模型
  □ DeepAgentsMCPTool (MCPClient → BaseTool)
  □ DeepAgentsSkillTool (SkillClient → BaseTool)
  □ DeepAgentsSandboxTool (SandboxClient → BaseTool)

Phase 3: Adapter + 高级特性
══════════════════════════
  目标: 用户故事 3 + 4 + 6 — wrap_agent、FastAPI 嵌入、Checkpoint

  □ DeepAgentsAdapter (astream_events → CanonicalEvent 转换)
  □ wrap_agent(agent) 工厂函数
  □ CheckpointClient + 自动持久化包装
  □ SubAgentStart/End 支持
  □ Custom 事件 (todo, recommendation_card, next_step)
  □ 错误恢复与 graceful shutdown
  □ Stop endpoint (对话中断)

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              #   导出 wrap_agent 等便捷函数
│   ├── base.py                  #   框架事件 → CanonicalEvent 转换基类
│   └── deepagents/              #   DeepAgents 框架
│       ├── __init__.py          #     导出 wrap_agent, DeepAgentsAdapter
│       ├── adapter.py           #     DeepAgentsAdapter (事件转换)
│       ├── mcp.py               #     DeepAgentsMCPTool (Capability Bridge)
│       ├── skill.py             #     DeepAgentsSkillTool
│       └── sandbox.py           #     DeepAgentsSandboxTool
│
├── 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 架构文档](event/agentrun-architecture.md) — 阿里云 AgentRun SDK 完整架构
- [LangChain 流式事件详解](event/LangChain流式事件详解.md) — v2/v3 事件体系
- [Dify vs 灵知 流式事件差异分析](event/diff-analysis.md) — 协议兼容性分析
- [PRD.md](PRD.md) — 原始产品需求文档
