# 编写自定义 Adapter

每个目标 Agent 需要一个 Adapter，告诉 AgentSec 如何与它对话。

## 最简情况：HTTP API

如果目标 Agent 暴露的是标准 REST 接口，继承 `HttpAgentAdapter` 并覆盖请求/响应转换方法：

```python
# src/agentsec/adapters/my_agent.py
from .http import HttpAgentAdapter

class MyAgentAdapter(HttpAgentAdapter):
    def _build_request(self, message: str, session_id: str) -> dict:
        # 目标 Agent 期望的请求格式
        return {"query": message, "conv_id": session_id, "stream": False}

    def _parse_response(self, data: dict) -> str:
        # 从响应中提取纯文本回复
        return data["choices"][0]["message"]["content"]
```

使用：

```python
from agentsec.adapters.my_agent import MyAgentAdapter
adapter = MyAgentAdapter(
    name="my-agent",
    base_url="https://api.my-agent.internal",
    headers={"Authorization": "Bearer <token>"},
)
```

## 自定义协议（SDK、WebSocket 等）

继承 `AgentAdapter` 基类，实现 `send` 方法：

```python
from agentsec.adapters.base import AgentAdapter, AgentResponse
import time

class HermesAdapter(AgentAdapter):
    name = "hermes"

    def __init__(self, client):
        self._client = client  # 目标 Agent 的 SDK client

    async def send(self, message: str, session_id: str) -> AgentResponse:
        start = time.monotonic()
        reply = await self._client.chat(session_id=session_id, text=message)
        return AgentResponse(
            content=reply.text,
            raw=reply.raw_dict,
            latency_ms=(time.monotonic() - start) * 1000,
        )

    async def reset(self, session_id: str) -> None:
        await self._client.clear_session(session_id)
```

## 通过 CLI 选择 Adapter

```bash
agentsec run test_suites/openclaw/ \
  --adapter openclaw-gateway \
  --target https://openclaw.example.com:18789 \
  --token-env OPENCLAW_API_KEY
```

内置 adapter 名称：`http`（默认）、`openclaw-gateway`。`agentsec.adapters.registry` 提供三个函数：`register("name", AdapterClass)` 注册新 adapter，`get("name")` 查回类，`available()` 列出已注册名称。CLI 在解析 `--adapter` 时通过 `get()` 查找；用户的自定义 adapter 应在 import 时调用 `register(...)`，例如放在包的 `__init__.py` 里。

`OpenClawGatewayAdapter` 向 `/v1/chat/completions` 发送 OpenAI 兼容的请求体（spec §5.2）：

```json
{
  "model": "openclaw",
  "messages": [{"role": "user", "content": "..."}],
  "stream": false,
  "metadata": {"session_id": "..."}
}
```

`HttpAgentAdapter` 现在接受可选的 `path` 参数（默认 `/chat`），子类可以通过
`super().__init__(path="/v1/...")` 指定不同的端点而不必重写 `send`。

## 注意事项

- Adapter 持有目标 Agent 的认证凭据（API key、token 等），**绝不能**出现在测试输出或报告中。`--token-env` 在 v0.2.0 中将 token 注入 `Authorization: Bearer …` header，CLI 输出不会回显 token。
- `session_id` 由 runner 生成，Adapter 负责将其透传给目标 Agent，确保不同测试间的对话隔离
- 如果目标 Agent 不支持会话隔离，`reset()` 方法可以做清理，runner 会在每个测试前调用它
