Download the PHP package likun-mci/php-ai without Composer
On this page you can find all versions of the php package likun-mci/php-ai. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download likun-mci/php-ai
More information about likun-mci/php-ai
Files in likun-mci/php-ai
Package php-ai
Short Description 框架无关的 PHP AI 标准库:一套接口访问 40 个国内外主流 AI 平台(通义千问 / 豆包 / 文心一言 / 智谱 GLM / Kimi / 混元 / 星火 / DeepSeek / OpenAI / Claude / Gemini / Grok / Mistral 等),内置流式输出、Agent 工具调用循环、AI 代码编辑协议与安全网页抓取。
License MIT
Homepage https://github.com/likun-mci/php-ai
Informations about the package php-ai
PHP AI 标准库
一个框架无关的 PHP AI 调用标准库,用统一接口屏蔽 40 个国内外主流 AI 平台在鉴权方式、请求协议、返回格式、流式协议上的差异——国内的通义千问、豆包、文心一言、智谱 GLM、Kimi、腾讯混元、讯飞星火、DeepSeek……,海外的 OpenAI、Claude、Gemini、Grok、Mistral……,以及 OpenRouter、硅基流动等聚合中转与 Ollama 等本地部署。切换平台只需换一个配置项。
除对话能力外,还内置了 Agent 工具调用循环、AI 代码编辑协议、带 SSRF 防护的网页抓取工具与 Agent 长期记忆,可直接用于构建后台 AI 助手、代码编辑助手、内容翻译等实际业务。
本文档中的示例均来自真实业务系统(一套 CodeIgniter 3 的建站系统)中的实际用法,非伪代码。
🌏 English documentation: README-en.md
特性
- 🔌 多平台:内置 40 个平台协议——国内 20 家(通义千问 / 豆包 / 文心一言 / 智谱 GLM / Kimi / 混元 / 星火 / MiniMax / 阶跃星辰 / 零一万物 / 百川 / 商汤 / 360 智脑 / 华为云 MaaS / DeepSeek 等)、海外 12 家(OpenAI / Claude / Gemini / Grok / Mistral / Cohere / Perplexity / Meta Llama / Azure OpenAI 等)、聚合中转 8 家(OpenRouter / 硅基流动 / 魔搭 / Groq / Together / Fireworks / DeepInfra / NVIDIA NIM 等)与本地部署(Ollama / LM Studio / vLLM)
- 🎯 统一接口:
AI::create()->chat(),切换平台只需换protocol或model,业务代码一行不用改 - 🧰 工具调用全平台统一:一套工具定义跑 40 个协议,库自动在 OpenAI 的
tool_calls与 Anthropic 的tool_use块之间翻译,Agent 换平台只改一行配置 - 🛡️ 生产级健壮性:429 限流与 5xx 自动退避重试(尊重
Retry-After)、请求体编码失败快速失败、SSRF 纵深防护 - ⚡ 并发批量:
chatBatch()用curl_multi并发跑批,实测 10 条提速 3.3 倍,单条失败不拖垮整批 - 🧩 任意模型 + 任意接口:模型名不受内置清单限制,可手选协议格式(
protocol)并指定自定义接口地址(base_url/endpoint),一套代码同时对接官方 API、第三方中转与自建网关 - 🌊 流式输出:一行
setStream(true),自动按 SSE 协议实时吐出数据块;常驻内存框架用setStreamCallback()接管分片 - 🧰 Agent 循环:挂载工具(函数)后自动完成「模型决策 → 执行工具 → 回填结果」多轮循环
- 🤖 Claude Code CLI:直接调用本机 claude 程序(
Ai\Cli\ClaudeCode),文件读写 / 工具执行 / 会话续接 / 结构化输出,路径自动检测并缓存 - 📊 CLI 信息查询:不发起对话即可读取版本、登录态、模型列表、额度用量与限流、生效设置、MCP 状态
- 🔌 常驻双工会话:
Ai\Cli\ClaudeCodeSession复刻官方 IDE 插件的进程模式,长驻进程多轮对话 + 工具权限实时回调 PHP 决策 + 优雅中断 - 📝 代码编辑协议:结构化编辑上下文 + 可校验的编辑动作,支持规划/审核/自动执行三种模式
- 🛡️ 安全抓取:
HttpFetch内置 SSRF、DNS rebinding、内网地址、协议逃逸防护 - 📄 多模态附件:图片等附件按各平台格式自动适配
- 🪶 零硬依赖:仅需 PHP 与 cURL,可 Composer 安装,也可单文件 autoload 引入
环境要求
| 项目 | 要求 |
|---|---|
| PHP | >= 7.1 |
| 扩展 | ext-curl、ext-json、ext-mbstring、ext-dom(仅 HTML 转换用到) |
| 网络 | 可访问对应平台 API;中国环境通常需配置代理,见 网络代理 |
安装
Composer
手动引入(不使用 Composer)
库自带 PSR-4 加载器,把整个目录放进项目后引入一次即可:
快速开始
换成国产平台,只需改配置——业务代码一行不用动:
完整的平台清单见 支持的平台与模型,可运行的示例见 examples_platforms.php。
chat() 接受字符串(自动包装为一条 user 消息)或完整 payload 数组:
多轮对话:默认不保存历史(
messages由业务层完整传入)。若想让库自动维护上下文,设rounds即可,见 多轮对话上下文。
支持的平台与模型
平台一览
protocol 传下表中的取值,api_key 传该平台控制台的密钥,其余写法所有平台完全一致:
中国大陆主流平台
| 平台 | protocol 取值 |
默认端点 | 模型名自动识别 |
|---|---|---|---|
| DeepSeek 深度求索 | deepseek 别名 deepseek-ai |
api.deepseek.com/v1/chat/completions |
deepseek-* |
| 阿里云百炼(通义千问) | qwen 别名 dashscope / bailian / tongyi / aliyun |
dashscope.aliyuncs.com/compatible-mode/v1/chat/completions |
qwen*、qwq* |
| 阿里云百炼(通义千问) | qwen-anthropic 别名 qwen-claude / dashscope-anthropic |
dashscope.aliyuncs.com/api/v2/apps/claude-code-proxy/v1/messages |
— |
| 火山方舟(豆包) | doubao 别名 ark / volcengine / volces / volc |
ark.cn-beijing.volces.com/api/v3/chat/completions |
doubao-* |
| 百度千帆(文心一言) | ernie 别名 qianfan / baidu / wenxin / yiyan |
qianfan.baidubce.com/v2/chat/completions |
ernie-* |
| 智谱 AI(GLM) | zhipu 别名 glm / bigmodel / chatglm / zhipuai |
open.bigmodel.cn/api/paas/v4/chat/completions |
glm-*、cogview-* |
| 智谱 AI(GLM) | zhipu-anthropic 别名 glm-anthropic / zhipu-claude |
open.bigmodel.cn/api/anthropic/v1/messages |
— |
| 月之暗面(Kimi) | moonshot 别名 kimi / yuezhianmian |
api.moonshot.cn/v1/chat/completions |
kimi-*、moonshot-* |
| 月之暗面(Kimi) | moonshot-anthropic 别名 kimi-anthropic / moonshot-claude |
api.moonshot.cn/anthropic/v1/messages |
— |
| 腾讯混元 | hunyuan 别名 tencent / tencent-hunyuan |
api.hunyuan.cloud.tencent.com/v1/chat/completions |
hunyuan-* |
| 讯飞星火 | spark 别名 xunfei / iflytek / xfyun / xinghuo |
spark-api-open.xf-yun.com/v1/chat/completions |
spark*、4.0Ultra |
| MiniMax(稀宇科技) | minimax 别名 xiyu / minimaxi |
api.minimaxi.com/v1/text/chatcompletion_v2 |
MiniMax-*、abab* |
| 阶跃星辰(Step) | stepfun 别名 step / jieyue |
api.stepfun.com/v1/chat/completions |
step-* |
| 零一万物(Yi) | yi 别名 lingyiwanwu / 01ai / 01-ai / zeroone |
api.lingyiwanwu.com/v1/chat/completions |
yi-* |
| 百川智能 | baichuan 别名 baichuan-inc |
api.baichuan-ai.com/v1/chat/completions |
Baichuan* |
| 商汤日日新(SenseNova) | sensenova 别名 sensetime / sensechat / shangtang |
api.sensenova.cn/compatible-mode/v1/chat/completions |
SenseChat-* |
| 360 智脑 | zhinao 别名 360 / qihoo / 360ai |
api.360.cn/v1/chat/completions |
360gpt* |
| 华为云 ModelArts(盘古 / MaaS) | modelarts 别名 huawei / maas / pangu |
api.modelarts-maas.com/v1/chat/completions |
— |
海外主流平台
| 平台 | protocol 取值 |
默认端点 | 模型名自动识别 |
|---|---|---|---|
| OpenAI | openai 别名 oai / openai-compatible / compatible / chat_completions |
api.openai.com/v1/chat/completions |
gpt-*、o3 |
| Anthropic Claude | claude 别名 anthropic / claude-messages / messages |
api.anthropic.com/v1/messages |
claude-* |
| Google Gemini | gemini 别名 google |
generativelanguage.googleapis.com/v1beta/openai/chat/completions |
gemini-* |
| Z.ai(智谱国际站) | zai 别名 z-ai / zhipu-global |
api.z.ai/api/paas/v4/chat/completions |
— |
| xAI(Grok) | grok 别名 xai / x-ai / x.ai |
api.x.ai/v1/chat/completions |
grok-* |
| Mistral AI | mistral 别名 mistralai |
api.mistral.ai/v1/chat/completions |
mistral-*、codestral-* |
| Meta(Llama API) | llama 别名 meta / meta-llama |
api.llama.com/compat/v1/chat/completions |
— |
| Cohere | cohere 别名 command |
api.cohere.ai/compatibility/v1/chat/completions |
command-* |
| Perplexity | perplexity 别名 pplx / sonar |
api.perplexity.ai/chat/completions |
sonar* |
| Azure OpenAI | azure 别名 azure-openai / azureopenai |
需自填base_url`` |
— |
聚合中转平台
| 平台 | protocol 取值 |
默认端点 | 模型名自动识别 |
|---|---|---|---|
| OpenRouter | openrouter 别名 or / open-router / open_router |
openrouter.ai/api/v1/chat/completions |
— |
| 硅基流动(SiliconCloud) | siliconflow 别名 silicon / siliconcloud / guiji |
api.siliconflow.cn/v1/chat/completions |
— |
| 魔搭社区(ModelScope) | modelscope 别名 moda / damo |
api-inference.modelscope.cn/v1/chat/completions |
— |
| Groq | groq 别名 groqcloud |
api.groq.com/openai/v1/chat/completions |
— |
| Together AI | together 别名 togetherai / together-ai |
api.together.xyz/v1/chat/completions |
— |
| Fireworks AI | fireworks 别名 fireworksai |
api.fireworks.ai/inference/v1/chat/completions |
— |
| DeepInfra | deepinfra |
api.deepinfra.com/v1/openai/chat/completions |
— |
| Cerebras | cerebras |
api.cerebras.ai/v1/chat/completions |
— |
| NVIDIA NIM | nvidia 别名 nim / nvidia-nim / build-nvidia |
integrate.api.nvidia.com/v1/chat/completions |
— |
本地 / 自建部署
| 平台 | protocol 取值 |
默认端点 | 模型名自动识别 |
|---|---|---|---|
| Ollama(本地) | ollama |
localhost:11434/v1/chat/completions |
— |
| LM Studio(本地) | lmstudio 别名 lm-studio |
localhost:1234/v1/chat/completions |
— |
| vLLM(自建) | vllm 别名 sglang / xinference |
localhost:8000/v1/chat/completions |
— |
表格由
$ai->listProtocols()/listProtocolGroups()提供程序化版本,可直接渲染后台下拉框;$ai->listKnownModels('qwen')返回该平台的常用模型清单(不发请求)。 别名只是同一协议的另一种写法,效果完全相同(protocol => 'kimi'等价于protocol => 'moonshot')。
内置模型标识(快捷方式)
下列模型标识可直接传给 model,无需 protocol,库会解析出平台、协议与端点:
| 平台 | 模型标识 | 实际协议 | 端点 |
|---|---|---|---|
| OpenAI | gpt-4.1 |
OpenAI | api.openai.com |
| OpenAI | gpt-4o |
OpenAI | api.openai.com |
| Claude | claude-3-opus |
Claude | api.anthropic.com |
| Gemini | gemini-2.5-pro |
Gemini(OpenAI 兼容端点) | generativelanguage.googleapis.com |
| DeepSeek | deepseek-chat |
OpenAI | api.deepseek.com |
| DeepSeek | deepseek-reasoner |
OpenAI | api.deepseek.com |
| DeepSeek | deepseek-v4-pro |
OpenAI | api.deepseek.com |
| DeepSeek | deepseek-v4-flash |
OpenAI | api.deepseek.com |
| DeepSeek | deepseek-anthropic |
Claude | api.deepseek.com/anthropic |
| OpenRouter | openai/gpt-4o 等完整标识 |
OpenRouter(OpenAI 兼容) | openrouter.ai/api |
deepseek-anthropic 是 DeepSeek 的 Anthropic 兼容端点,用 Claude 协议通信——需要工具调用(Agent)时用它,可以用 DeepSeek 的价格跑 Anthropic 的 tools 协议。
表外的模型也能直接用
上表只是「开箱即用的快捷标识」,model 并不限于表内的值:
- 官方新模型(如
claude-opus-5、gpt-5.1、qwen3-max、glm-4.6、kimi-k2-turbo-preview):库按模型名识别出协议家族与官方端点,直接可用,不必等库更新; - 被多家平台托管的开源模型(如
llama3、mixtral)或第三方中转/自建网关的模型:加上protocol或base_url即可。
模型名无法归属官方平台、又没给
base_url/endpoint时,请求前会抛ConfigException,而不是把 Key 发到不相干的官方域名。
protocol 除了上面平台表里的取值,还可以传实现了 Ai\Contracts\ProtocolInterface 的自定义协议类名(见「扩展开发」)。
不传 protocol 时按模型名推断(见平台表「模型名自动识别」列),也识别 厂商/模型 写法(如 qwen/qwen-max)。推断不出时按 openai 协议处理,此时必须给 base_url 或 endpoint。
只对厂商自有模型名做推断。
llama3、mixtral这类被多家平台托管的开源模型名不参与推断(无法判断你想连哪一家),需要显式给protocol或base_url。
协议差异(重要)
各平台协议对 payload 键的支持并不一致,写业务代码前需要知道:
| payload 键 | OpenAI 协议 | Claude 协议 | Gemini 协议 |
|---|---|---|---|
messages |
✅ | ✅(role: system 会被丢弃) |
✅ |
system(顶层) |
❌ 忽略 | ✅ | ❌ 忽略 |
tools / tool_choice |
❌ 忽略 | ✅ | ❌ 忽略 |
temperature / max_tokens |
✅ | ✅ | ✅ |
stream |
✅(自动附带 usage 统计) | ✅ | ✅ |
结论:
- OpenAI / Gemini 的系统提示词要写进
messages的role: system; - Claude 的系统提示词要写在顶层
system键; - Agent / 工具调用只能用 Claude 协议。除 Anthropic 官方外,以下国产平台提供了 Anthropic 兼容端点,可以用国产模型的价格跑 Agent:
protocol |
平台 | 鉴权用的密钥 |
|---|---|---|
deepseek-anthropic(模型标识) |
DeepSeek | deepseek__api_key |
zhipu-anthropic |
智谱 GLM | zhipu__api_key |
moonshot-anthropic |
月之暗面 Kimi | moonshot__api_key |
qwen-anthropic |
阿里云百炼 | qwen__api_key |
运行时查询平台与模型
真实用例——按平台分组拉取模型列表并本地缓存一周,供后台下拉框渲染:
配置项
setConfig() 是增量合并,可以分多次调用;每次传入 model 都会重建模型与协议实例。base_url、protocol 等既可以和 model 一起传,也可以在设置模型之后再补。
生成参数(max_tokens、max_completion_tokens、temperature、top_p、top_k、stop、presence_penalty、frequency_penalty、seed、response_format、system、tools、tool_choice、reasoning_effort、thinking)写在 setConfig() 里对所有请求生效,单次 chat() 的 payload 优先级更高;连接信息(api_key、base_url 等)不会进入请求体。
接口有私有参数时用 extra_body(直接并入请求体),需要特殊鉴权头时用 headers:
链式接口:
调试时可用 $ai->getLastInfo() 获取最近一次请求的 cURL 信息(http_code、总耗时等)。
网络代理
支持 http://、https://、socks5://、socks5h://(DNS 也走代理)、socks4://、socks4a://:
自定义接口地址(第三方转发 / 中转 / 自建网关)
默认端点是各平台的官方地址。要接入第三方转发、中转或自建网关,有两种配置方式,优先级从高到低:
1. base_url —— 接口根地址,与协议官方路径智能拼接(最常用)
只要给出根地址即可,库会补上协议对应的路径;根地址自带的路径前缀会保留,与官方路径重叠的片段自动去重:
base_url |
协议路径 | 实际端点 |
|---|---|---|
https://proxy.com |
/v1/chat/completions |
https://proxy.com/v1/chat/completions |
https://proxy.com/v1 |
/v1/chat/completions |
https://proxy.com/v1/chat/completions(重叠段去重) |
https://proxy.com/openai |
/v1/chat/completions |
https://proxy.com/openai/v1/chat/completions |
https://proxy.com/v1/chat/completions |
/v1/chat/completions |
原样(已是完整端点) |
127.0.0.1:8080 |
/v1/chat/completions |
https://127.0.0.1:8080/v1/chat/completions(缺 scheme 按 https) |
2. endpoint —— 完整端点覆盖,原样使用(最灵活)
当接口路径结构与官方完全不同时使用,直接给出完整 URL:
endpoint 优先级高于 base_url;两者都不设置时使用官方默认端点,完全向后兼容。
OpenRouter 聚合中转
OpenRouter 是一个 AI 模型聚合平台,通过统一的 OpenAI 兼容接口访问 OpenAI、Claude、Gemini、DeepSeek、Llama 等多种模型。库已内置 openrouter 协议,开箱即用:
OpenRouter 上的模型名使用 厂商/模型 格式,协议推断规则会自动提取厂商前缀:
openai/gpt-4o→ 协议推断为openai,自动使用 OpenAI 协议格式anthropic/claude-sonnet-4-20250514→ 协议推断为claude(但 OpenRouter 接口仍以 OpenAI 兼容格式应答,协议以protocol配置为准)deepseek/deepseek-chat→ 协议推断为deepseekgoogle/gemini-2.5-pro-exp-03-25→ 协议推断为gemini
OpenRouter 返回的 usage 字段是 OpenRouter 统计而非原始模型统计(可能包含缓存命中标记等扩展字段),
$response->getUsage()原样透传。
通过 OpenRouter 查看实时模型状态:
其他常见 AI 中转/聚合服务
OpenRouter、硅基流动、魔搭、Groq、Together、Fireworks、DeepInfra、NVIDIA NIM 已内置为协议(见「平台一览」),直接 protocol 指定即可。其余中转站与自建网关走 OpenAI 兼容协议(protocol=openai),配置 base_url 或 endpoint:
以上所有接入方式均支持流式输出、附件、回调等完整功能。
模型列表端点:
listModels()会跟随base_url走同一个网关;只配了endpoint时,库按对话端点同源推导(如.../v1/chat/completions→.../v1/models)。若网关的模型列表路径特殊,用endpoint_models单独完整覆盖(仅对listModels()生效)。OpenRouter 的模型列表接口返回完整定价数据,listModels(true)可获取。
当前实际请求端点可随时查询:
Claude Code CLI 程序调用
Ai\Cli\ClaudeCode 直接调用本机安装的 claude 可执行程序(Claude Code CLI),与 Ai\Protocol\Claude(Anthropic HTTP API)互补。适合让 AI 直接操作工作区文件:读写文件、执行工具,配合 acceptEdits 权限模式实现"AI 改代码"。
快速开始
默认参数(均经实测验证):
--print:非交互模式,一次查询后退出--setting-sources user,project,local:加载全部设置来源。print 模式下 CLI 默认不加载全部来源,不显式指定会导致项目CLAUDE.md、.claude/settings.json的权限规则、自定义 agent/skill 全部失效——官方 IDE 插件同样显式传这个参数--no-chrome:关闭 Chrome 集成(服务端无浏览器)--allowedTools Read Edit Write Grep Glob:免权限提示白名单(不是"可用工具集")--disallowedTools Bash:把 Bash 从工具集里摘掉,模型根本看不到(这才是硬性禁用)--permission-mode acceptEdits:文件编辑免确认- 提示词经临时文件 + stdin 重定向传入,避开命令行长度上限(SSH exec 通道受限时尤为重要)
--allowedTools与--tools语义不同:前者决定"哪些工具不用问",后者(setTools())决定"模型有哪些工具可用"。要真正限制能力范围用setTools()或setDisallowedTools()。
claude 路径:自动检测 + 缓存 + 手动指定
自动检测顺序:command -v claude(PATH)→ 常见安装路径(~/.local/bin、Homebrew、/usr/bin)→ nvm 最新版本 ~/.nvm/versions/node/*/bin/claude → 登录 shell command -v。结果缓存到进程内 + 文件(默认 sys_get_temp_dir()/ai_claude_binary_cache.json),避免 PHP-FPM 下每次请求重复探测。
自定义 CLI 参数
所有 claude 参数均可覆盖/新增/删除:
参数名不区分下划线:setFlag('permission_mode', 'plan') 等价于 --permission-mode plan。
多值参数会按 CLI 各自的解析规则渲染,无需自己拼串:setting-sources/tools/fallback-model 逗号连接,add-dir/mcp-config 一个 flag 跟多个值,plugin-dir/plugin-url 重复 flag。
注意:
claudeCLI 没有--working-dir、--max-budget、--proxy、--theme这些参数(实测会被拒绝)。工作目录用setWorkdir()(内部转成cd ... &&),代理走环境变量HTTP_PROXY/HTTPS_PROXY。
常用参数的专用方法
除了通用的 setFlag(),常用选项都有带取值校验的专用方法:
结构化输出
setJsonSchema() 约束模型最终必须输出符合 schema 的 JSON,getStructured() 直接拿数组:
流式调用(事件回调)
runStream() 逐事件回调,事件语义与官方 IDE 插件一致,可直接转发给 SSE:
result 事件是最终汇总:文本取自 result.result,费用取自 result.total_cost_usd(CLI 实测值,无需价格表),is_error:true 时 isSuccess() 返回 false。
响应对象除 getContent() / getSessionId() / getCostUsd() 外还提供:getStructured()、getThinking()、getToolUses()、getTools()、getPermissionDenials()、getSubtype()、getStopReason()、getInit()、getNumTurns()、getDurationMs()、getExitCode()、getCommand()。
多轮会话续接
每次执行后若输出带新 session_id,会自动回写到内部,getSessionId() 始终是最新的。
自定义执行器(远程 / 容器化场景)
默认用 proc_open 本地执行。需要经 SSH/SFTP 在宿主机跑 claude 时(如 Docker 容器内 PHP),注入自定义执行器:
配套设置:
setShellPrefix('export LANG=en_US.UTF-8; '):注入环境(如 locale、nvm PATH)setPromptDir('/data/ai_prompt_tmp'):提示词临时文件目录,容器与宿主机 1:1 挂载时指向双方可见路径(命令里的stdin重定向会在宿主机侧读取该文件)- 本地执行时默认自动把 nvm 下 claude 所在目录加入 PATH(
setAutoNvmPath(false)关闭)
信息查询(版本 / 登录态 / 模型列表 / 额度用量)
不需要发起对话就能读取 CLI 侧的各类信息,不消耗模型额度:
listModels() 与 Ai\AI::listModels() 保持同样的约定:默认返回可直接使用的模型标识数组,传 true 返回完整数据,适合后台下拉框渲染。
额度查询
每项还含 severity(normal / 告警级别)、resets_at(ISO8601)、is_active。按订阅计费时 limit_dollars 类字段为 null,只提供百分比;额度不可用时返回空数组。
getUsage() 的完整结构:
| 键 | 说明 |
|---|---|
session |
当前进程的 total_cost_usd、耗时、代码增删行数、分模型用量 |
subscription_type |
订阅类型,如 max / pro |
rate_limits |
各限流窗口原始数据 + 归一化后的 limits 数组 + extra_usage 额度包信息 |
behaviors |
近一天 / 一周的 request_count、session_count 等统计 |
在会话中查询:ClaudeCodeSession 上同名方法会复用已运行的进程,因此 getUsage() 拿到的 session.total_cost_usd 就是本会话的真实累计花费;另有 getSessionCost() 返回同交互式 /cost 的文本报告。
CLI 的
get_context_usage(上下文窗口占用)只在交互式 UI 下响应,headless 模式不回包,故本库未提供对应方法。
常驻双工会话(ClaudeCodeSession)
Ai\Cli\ClaudeCodeSession 复刻官方 IDE 插件(VSCode / JetBrains)的进程工作方式:claude 以长驻进程运行,stdin 持续接收 JSON 消息,stdout 持续吐事件,工具权限通过 stdio 上的 control_request 协议实时回调给 PHP 决策。
插件实测启动参数:
本类默认采用其中与服务端场景相符的部分(双工 + stdio 权限回调 + 全设置源 + 消息回显 + 禁用 Chrome);权限模式保持更保守的 acceptEdits,思考预算和调试日志默认不开,需要时用 setThinkingTokens(31999) / setDebug() 打开。
与一次性模式的区别
ClaudeCode |
ClaudeCodeSession |
|
|---|---|---|
| 进程 | 每轮起停一次 | 长驻,多轮共用 |
| 多轮 | --resume 重放历史 |
上下文常驻内存 |
| 工具权限 | 靠 flag 静态配置 | 逐次回调 PHP 决策 |
| 中断 | 只能 kill 进程 | interrupt() 优雅中断 |
| 运行时改配置 | 不支持 | 热切权限模式 / 模型 / 思考预算 |
| 受限 PHP 环境 | 支持自定义执行器(SSH) | 仅本地 proc_open |
中断与运行时控制
权限回调的边界(重要)
onPermission() 只会收到 CLI 认为"需要询问"的调用,它不是一道完整的拦截层。以下情况 CLI 自行放行、不问 PHP:
- 设置文件(
~/.claude/settings.json、项目.claude/settings.json)里已预授权的规则 —— 用setSettingSources([])不加载 - 当前
permission-mode已自动放行的类别(如acceptEdits下的文件编辑) - CLI 判定为只读、在沙箱中执行的安全命令(实测
Bash(echo hi)即使permission-mode manual也不会询问)
要硬性禁止某个工具,用 setDisallowedTools(['Bash'])(工具从工具集移除,模型根本看不到)或 setTools([...]) 限定可用集合,不要只依赖回调。
未注册 onPermission() 时,默认只自动放行 Read / Edit / Write / Grep / Glob,其余一律拒绝;setAutoApproveTools([...]) 改名单,allowAllTools() 全部交回 CLI 自己判断。
其余会话方法:start() / isRunning() / close() / kill() / sendMessage($contentBlocks) / getInit() / getAvailableTools() / getCommand()。ClaudeCode 的全部参数方法在会话类上同样可用(需在首次 send() 前设置)。
环境要求
- 已安装 Claude Code CLI(
npm install -g @anthropic-ai/claude-code或原生安装) - 本地执行需要
proc_open;shell_exec仅用于路径兜底探测,缺失时自动跳过 ClaudeCodeSession需要proc_open的双向管道,不支持自定义执行器
流式输出(SSE)
调用 setStream(true) 后,chat() 会在接收模型数据的同时直接向输出缓冲区写 SSE 数据(自动设置响应头、关闭 Nginx 缓冲),无需注册回调:
服务端实际输出的报文格式(固定协议,前端按此解析):
对应的前端消费代码:
真实用例——后台聊天接口(含代理、附件、流式):
PHP 环境注意:流式输出会清空并关闭所有输出缓冲。如果使用会话锁(
session_start()),建议在流式开始前session_write_close(),否则同一用户的其它请求会被阻塞。
常驻内存框架(Swoole / Workerman):setStreamCallback()
上面的默认行为是 echo 到输出缓冲区,只适用于 PHP-FPM / CLI。在 Swoole、Workerman、RoadRunner 这类常驻内存框架里,echo 会落到进程标准输出(日志文件),永远送不到客户端。这种场景注册回调,由调用方自己下发:
注册回调后库不再产生任何输出,SSE 帧格式完全由调用方决定。事件结构:
| 字段 | 说明 |
|---|---|
type |
stream_chunk 增量分片 / stream_end 结束 |
content |
stream_chunk 的增量文本,已由协议层归一化,跨平台通用;无正文的分片为 null |
raw |
stream_chunk 的平台原始分片数组,需要平台专有字段时才用 |
data |
stream_end 的汇总:content 完整正文、model、usage |
传 null(setStreamCallback(null))恢复默认的直接输出。
平台兼容性:40 个协议均已覆盖
「普通对话 + 流式输出 + token 统计」是每个协议的基础能力,tests/stream_test.php
用各平台真实的 SSE 报文逐个回放校验。三套测试都不联网、不需要 Key、不依赖 PHPUnit:
CI 会在 PHP 7.1 / 7.2 / 7.4 / 8.0 / 8.2 / 8.4 上跑同样的检查,并在 8.2 上额外跑一次静态分析 与 PHP 7.1 兼容性扫描。最低版本 7.1 是真的把整套测试跑一遍,不是只做语法检查。
库内所有公开方法都带完整的 phpdoc 类型(PHP 7.1 不支持类型化属性,类型只能写在注释里), IDE 能正确补全
getToolCalls()这类方法的返回结构。
覆盖的报文差异(都踩过坑,已在传输层统一处理):
| 差异 | 说明 |
|---|---|
data: 后不带空格 |
SSE 规范里冒号后的空格是可选的,讯飞星火等平台就不带;只认带空格的写法会导致整个流为空 |
| CRLF 行尾 | 部分网关/代理会改写换行符 |
| 末尾无换行符 | 最后一帧(往往正是带 usage 的收尾帧)没有换行时不能被丢弃 |
夹杂 event: / id: 等字段 |
Anthropic 协议每帧都带 event:,需正确跳过 |
| 用量分帧下发 | Anthropic 把 input_tokens 放在 message_start、output_tokens 放在 message_delta,需跨帧合并 |
| HTTP 200 但流里报错 | MiniMax(base_resp)、OpenAI 系(error)等出错时状态码仍是 200,需抛异常而不是返回空内容 |
usage 中的 prompt_tokens / completion_tokens / total_tokens 三个标准字段在所有平台一致可用
(Anthropic 系的 input_tokens / output_tokens 已自动映射),平台特有字段原样保留。
流式下 getStopReason() 与 getToolCalls() 同样可用:
自定义协议可选实现四个流式钩子(都不实现也能正常流式,只是拿不到对应信息):
钩子 作用 parseStreamUsage(array $chunk): ?array该帧的 token 用量,AI 层逐帧合并 parseStreamError(array $chunk): ?string该帧的错误信息,非空即抛异常 parseStreamStopReason(array $chunk): ?string该帧的结束原因(已归一) parseStreamToolCalls(array $chunk): ?array该帧的工具调用分片,返回 [索引 => ['id'=>, 'name'=>, 'arguments'=>片段]],AI 层按索引拼接
附件(多模态)
附件格式由模型层适配:视觉模型(如 gpt-4o)转成各平台的图片块;不支持多模态的模型(如 DeepSeek 系列)会把附件信息以文本形式追加到最后一条用户消息,避免请求直接报错。
附件在每次 chat() 后自动清空,不会影响下一轮对话。
多轮对话上下文
两种方式,按需选一:
方式一:设 rounds,库自动维护(推荐)
rounds 默认为 0(不启用),此时库完全不碰历史,行为与旧版本一致。
多用户 / 常驻进程下用 setSessionId() 隔离上下文,一个 AI 实例可服务多个会话:
历史存在内存里,进程退出即失。跨请求要持久化就自己落库:
| 方法 | 说明 |
|---|---|
setSessionId(string) / getSessionId() |
切换/读取当前会话,不同会话各自独立一份历史 |
getHistory() / setHistory(array) |
读写当前会话的历史消息 |
clearHistory(bool $all = false) |
清空当前会话;传 true 清空全部会话 |
exportHistory() / importHistory(array) |
导出/导入全部会话,用于持久化 |
裁剪按「轮」而非按条数:一轮从一次真正的用户提问开始,只含 tool_result 的消息
算作上一轮工具调用的一部分。这样不会把 tool_use 和对应的 tool_result 切散——
切散会让下一次请求直接被平台拒绝。
chatBatch()不读写历史:批量里每条都是独立请求,混进同一份上下文没有意义。
方式二:自己维护 messages
不设 rounds 时库完全不介入,业务层自行拼接:
需要完全掌控裁剪策略(按 token 数、按重要性摘要等)时用这种方式。
请求前后回调
回调内抛出的异常会被捕获并记录到 error_log,不会中断主流程。
响应对象
chat() 返回 Ai\Contracts\AIResponseInterface:
| 方法 | 说明 |
|---|---|
getContent(): string |
回复文本(流式模式下为累积后的完整文本) |
getRaw(): array |
平台原始响应体(Agent 解析 tool_use 块靠它) |
getUsage(): array |
完整用量对象,含 prompt_tokens / completion_tokens / total_tokens 及平台返回的扩展字段(prompt_tokens_details.cached_tokens、cache_creation_input_tokens 等,因平台而异) |
tokens(): int |
总 tokens |
getModel(): string |
实际返回的模型名 |
isSuccess(): bool |
是否成功 |
cost(array $pricing): float |
按价格表估算费用,默认每千 tokens(与旧版本一致,不改动已有代码的账) |
costPerMillion(array $pricing): float |
同上但按每百万 tokens,可直接抄官网数字,如 ['prompt'=>5.0,'completion'=>25.0,'cached'=>0.5];cached 为命中缓存的输入价,两大家族的字段名都认 |
getToolCalls(): array |
模型发起的工具调用,已归一:[['id'=>..,'name'=>..,'input'=>[..]]] |
hasToolCalls(): bool |
本轮是否要求调用工具 |
getStopReason(): string |
结束原因(已归一):end_turn / tool_use / max_tokens / content_filter / refusal |
toAssistantMessage(): array |
转成可回填进 messages 的 assistant 回合 |
getError(): string |
失败原因(仅 chatBatch() 这类不抛异常的场景会填充) |
toArray() / __toString() |
转数组 / 直接当字符串用 |
异常处理
RequestException 由传输层抛出,chat() 内部会将其转成携带平台信息的 AIException,业务层通常只需捕获 AIException。
健壮性:超时与自动重试
AI 接口最高频的失败不是「打不通」,而是 429 限流和 5xx 临时故障。库默认已处理:
| 情形 | 行为 |
|---|---|
| 408 / 409 / 429 / 500 / 502 / 503 / 504 / 529 | 自动重试 |
响应带 Retry-After(秒数或 HTTP 日期) |
优先按服务端给的时间等待,并有上限保护 |
无 Retry-After |
指数退避 + 抖动(避免多进程被同时限流后又齐刷刷重试) |
| 4xx(除上表)如 400 / 401 / 403 | 不重试,立即抛异常 |
| 流式请求 | 不重试——分片已经吐给调用方,重试会造成重复输出 |
| 请求体含非 UTF-8 字节 | 立即抛 RequestException 并说明原因(此前会静默发出空请求体) |
需要接入连接池、换 HTTP 客户端或在单元测试里注入假传输层时,替换整个传输层:
并发批量:chatBatch()
批量翻译、摘要、打标签这类场景串行跑,总耗时是「单条 × 条数」,且每条都要重做一次
TLS 握手。chatBatch() 用 curl_multi 并发,总耗时约等于「最慢的一条 × 批次数」:
- 返回结果与入参键名一一对应且保序,可直接与原数组对齐
- 单条失败不抛异常,返回
isSuccess()为 false 的响应,用getError()取原因—— 批量场景不该因为一条失败就丢掉其它已经跑完的结果 - 不支持流式(并发流式的分片会互相穿插)
- 并发度调大容易触发平台限流,配合
setRetry()使用
日志:接到你自己的日志系统
库内不再硬编码 error_log()。注入后拉取模型列表失败、流式回调异常等都会走你的日志:
Agent:工具调用循环
Ai\Agent\Agent 实现完整的 agentic 循环:模型决定调用哪个工具 → 库执行工具 → 结果回填给模型 → 继续,直到模型给出最终答复或达到迭代上限。
40 个协议全部可用。各家把同一件事写成了两套结构,库在协议层吃掉了差异:
| OpenAI 系(36 个协议) | Anthropic 系(4 个协议) | |
|---|---|---|
| 工具定义 | {type:'function', function:{name, parameters}} |
{name, description, input_schema} |
| 模型发起 | message.tool_calls[],arguments 是 JSON 字符串 |
content 里的 tool_use 块,input 是数组 |
| 结果回填 | 独立的 {role:'tool', tool_call_id} 消息 |
user 消息里的 tool_result 块 |
| 结束原因 | finish_reason: 'tool_calls' |
stop_reason: 'tool_use' |
| 系统提示 | messages 首条 role:'system' |
顶层 system 字段 |
业务层只写一套(库的统一格式,采用 Anthropic 风格),换平台只改 protocol:
完整可运行示例见 examples_agent.php;跨平台一致性由 tests/tools_test.php 保证。
流式跑 Agent:setStream(true) 后每轮正文实时吐给回调,工具调用照常工作——
库会把各平台分片下发的 tool_calls 重组回来(OpenAI 系按 delta.tool_calls[].index
拼 arguments 字符串,Anthropic 系按 content_block_start + input_json_delta 拼)。
适合聊天界面:用户能一边看模型说话,一边看它去调工具。
Agent 只是临时借用你的 AI 实例:跑完会把 setStream() 恢复原状(异常路径也会),
不会影响后续无关的 chat()。
也可以直接写 OpenAI 原生格式(
{type:'function'}的工具定义、role:'tool'的消息), 库会识别并转成目标平台的结构,不强制迁移已有代码。
不用 Agent,自己控制循环
AIResponse 提供了平台无关的取用接口:
工具定义
handler 返回的字符串会作为 tool_result 回填给模型;抛出的异常会被自动转成 ERROR: 异常信息 交给模型,不会中断循环。
运行
事件类型
onEvent() 回调会依次收到:
| 事件 | 字段 | 含义 |
|---|---|---|
thinking |
iter |
第几轮迭代开始 |
agent_text |
text |
模型输出的自然语言 |
tool_call |
name、input |
模型决定调用某工具 |
done |
— | 正常结束 |
error |
message |
出错或达到最大迭代步数 |
工具内部的细粒度事件(如 diff、进度)由各 handler 自行通过闭包发出,库不做假设。
Editor:AI 代码编辑
Ai\Editor\* 提供一套「让 AI 改代码」的完整协议:把编辑器现场(当前文件、光标、选区、已打开文件、工作区规范)结构化后交给模型,模型返回可校验、可执行的编辑动作。
json\n{$ctxJson}\n
配合 Agent 可实现三种工作模式:plan(只读规划)、approval(产出待人工审核的建议)、auto(自动写入并备份)。
联网搜索
不少平台的模型能自己上网查资料再回答。各家的开关五花八门——有的是请求体顶层的一个布尔值,
有的要往 tools 里塞一个内置工具,还有的走插件系统。本库把它们归一成一个 search 配置:
换个平台只改 model,search 这行不动:
细化配置
search 传数组可以调细节。省略 enable 即视为开启:
平台不支持的细项会被静默忽略,不影响搜索本身开启。这是刻意的:统一层承诺的是 「搜索会开」,不是「每个细节都能在每个平台生效」。下表是各项的实际落点:
| 统一配置 | Claude | 通义千问 | 智谱 GLM | Kimi | 文心一言 | OpenRouter | Perplexity |
|---|---|---|---|---|---|---|---|
max_uses |
max_uses |
— | — | — | — | — | — |
count |
— | — | count |
— | search_number |
max_results |
— |
query |
— | — | search_query |
— | — | — | — |
recency |
— | — | search_recency_filter |
— | — | — | search_recency_filter |
forced |
— | forced_search |
require_search |
— | — | — | — |
citation |
总是开 | enable_citation |
— | — | enable_citation |
— | — |
sources |
— | enable_source |
search_result |
— | enable_trace |
— | return_related_questions |
allowed_domains |
✅ | — | 仅首个 | — | — | include_domains |
✅ |
blocked_domains |
✅ | — | — | — | — | exclude_domains |
加 - 前缀 |
几处需要留意的差异:
- Claude 的引用是常开的,没有开关;
allowed_domains与blocked_domains同时传会被平台判为 400,本库在发请求前就会拦下并报错。 - 智谱 的
search_domain_filter官方类型是字符串而非数组,多个域名只会取第一个。 - 智谱 没有「一小时内」这一档,
recency => 'hour'会并到oneDay。 - Perplexity 的 Sonar 系模型本来就是联网的,不存在开不开;
search在这里 只用来传过滤条件。 - Kimi 的内置搜索走的是 tool_calls 流程——模型只生成搜索参数,需要客户端回填结果
对话才会继续。所以它必须配合 Agent 循环使用,单发一次
chat()只会拿到一个工具调用:
哪些平台支持
没列进来的平台,配了 search 会直接抛 ConfigException,而不是静默忽略。
静默忽略在这里是最糟的选择:用户拿到的是一个「答得挺像样、但其实没上网」的回复,
内容陈旧却毫无征兆,往往要等到发现模型说的是去年的事才察觉。
需要特别说明的两个:
- OpenAI 的 Chat Completions 端点没有联网开关,联网搜索只在 Responses API
或
gpt-5-search-api这类专用搜索模型上提供。本库走的是 Chat Completions, 所以openai协议不声明支持。 - 通义千问 / 智谱 / Kimi 的 Anthropic 兼容端点(
qwen-anthropic等)同样不支持。 那些网关只翻译 Anthropic 的请求格式,Anthropic 的 web_search 是 Anthropic 自己的 服务端能力,不会随协议格式一起过来。要在这些平台上联网,请改用它们的 OpenAI 兼容协议。
平台私有参数:用 extra_body
统一配置只收各家都有的语义,平台独有的参数(通义的 search_strategy、
智谱的 search_engine、OpenRouter 的 engine 等)不进 search,用 extra_body 直接写:
extra_body 在请求体顶层做合并,同名字段会整体覆盖 search 生成的结果——
上例中最终发出的 search_options 只有 search_strategy,forced_search 会被顶掉。
要同时用两者,把所有子字段都写进 extra_body。
库对某平台的判断有误或过时时,extra_body 也是逃生口:它绕过全部声明检查,
可以直接发平台原生的搜索参数。
与 Ai\Tools\HttpFetch 的区别
两者都能让模型用上网页内容,但不是一回事:
search 配置 |
Ai\Tools\HttpFetch(见下一节) |
|
|---|---|---|
| 谁在联网 | 平台的服务器 | 你的 PHP 进程 |
| 计费 | 平台按次收搜索费 | 只有 token 费用,流量走你的服务器 |
| 能力 | 搜索引擎检索 | 抓取你指定的 URL |
| 可控性 | 只能给过滤条件 | 完全可控,含 SSRF 防护 |
| 支持范围 | 仅上表 7 个平台 | 所有平台 |
需要「模型自己决定搜什么」用 search;需要「读这几个我指定的页面」用 HttpFetch。
两者可以同时开。
Tools:安全网页抓取
让模型联网读网页时,最大的风险是 SSRF。Ai\Tools\HttpFetch 内置纵深防御:
- 仅允许
http/https,拒绝带user:pass@的 URL - 端口白名单(默认 80/443)
- 解析主机的所有 A/AAAA 记录,任一 IP 落在私有/保留/回环/链路本地/云元数据段即整体拒绝
- 用
CURLOPT_RESOLVE把连接钉死到已校验 IP,防 DNS rebinding - 不自动跟随重定向,逐跳重新校验
- 超过
max_bytes立即中断;校验 TLS;不带 Cookie;不走站点代理
把它包成 Agent 工具即可让模型自主上网:
Memory:Agent 长期记忆
把一个 Markdown 文件当作 Agent 的持久记忆(类似 CLAUDE.md)。文件位置由业务层决定,库不认识任何具体路径。
完整业务案例:批量 JSON 翻译
一个真实场景——把多语言词条按批交给 AI 翻译,要求模型严格返回 {"记录ID":"译文"} 的 JSON,并做失败重试、格式校验与结果回写。
json (json)?\s(.+?)\s
实战经验:
- 务必带
JSON_UNESCAPED_SLASHES,并在收到结果后兜底str_replace('\\/', '/', $text); - 模型常把 JSON 包在 json php-ai/ ├── src/ # 源代码(PSR-4 命名空间 Ai\) │ ├── AI.php # 主入口:配置、模型解析、对话、流式、回调 │ ├── Agent/ # Agent 循环 + 长期记忆 │ ├── Cli/ # ClaudeCode 一次性调用 / ClaudeCodeSession 常驻双工会话 + 响应对象 │ ├── Contracts/ # 接口定义:Model / Protocol / Transport / AIResponse │ ├── Editor/ # AI 代码编辑:上下文 / 协议 / 动作 / 执行器 / 工作区 │ ├── Exceptions/ # AIException / ConfigException / RequestException / ProcessException │ ├── Helpers/ # AIFile(附件封装)、Endpoint(端点解析)、Protocols(协议注册表) │ │ # Headers(请求头合并)、Tools(工具调用格式归一)、Log(可注入日志) │ ├── Models/ # 模型层:各平台模型的名称、端点、能力、默认配置 │ │ ├── BaseModel.php │ │ ├── CustomModel.php # 通用模型:任意模型名 + 手选协议 + 自定义接口地址 │ │ ├── OpenAI/ Claude/ Gemini/ DeepSeek/ │ ├── Protocol/ # 协议层:40 个平台协议 │ │ # ModelCatalog.php 常用模型清单 + 拉取失败兜底(trait) │ │ # OpenAI / Claude / Gemini 三种基础协议格式 │ │ # 国内:Qwen / Doubao / Ernie / Zhipu / Moonshot / Hunyuan / Spark / │ │ # MiniMax / StepFun / Yi / Baichuan / SenseNova / Zhinao / ModelArts … │ │ # 海外:Grok / Mistral / Cohere / Perplexity / Llama / Azure … │ │ # 聚合:OpenRouter / SiliconFlow / ModelScope / Groq / Together / Fireworks … │ │ # 本地:Ollama / LMStudio / VLLM │ ├── Response/ # 统一响应对象 │ ├── Tools/ # HttpFetch(SSRF 防护)、WebContent(格式化) │ └── Transport/ # cURL 传输层(含 SSE 解析、代理、超时) ├── autoload.php # PSR-4 加载器(不用 Composer 时引入) ├── composer.json ├── tests/ # 回归测试(纯 PHP,无需 PHPUnit、无需网络、无需 API Key) │ ├── smoke_test.php # 全部类可加载/可实例化、继承链签名兼容 │ ├── stream_test.php # 40 个协议 × 普通对话 / 流式 / token 统计 │ ├── tools_test.php # 工具调用跨平台一致性(同一段代码跑两个协议家族) │ ├── lib_test.php # 并发批量 / Memory 并发安全 / 计价 / 日志注入 │ ├── cli_test.php # CLI 参数渲染与命令注入防护 │ └── ssrf_test.php # SSRF 防护的全部已知绕过向量 ├── examples*.php # 使用示例(examples_platforms.php 为多平台接入示例) ├── LICENSE ├── README.md └── .gitignore
当前模型使用的协议不支持「图像生成」能力。本协议目前只支持对话(chat)
未指定实时通道协议。当前平台的语音能力只能通过 WebSocket 访问, 请显式调用 ->useWebSocket() 启用。……
提交 POST /v1/video_generation → task_id 查询 GET /v1/query/video_generation → status=Success,但只给 file_id 再取 GET /v1/files/retrieve → 真正的下载地址(有效期 9 小时)
库内自动走完三步,调用方感知不到差别。另外 MiniMax 的失败**不体现在 HTTP 状态码上**——
`base_resp.status_code` 非 0 才是失败,此时 HTTP 仍是 200,库内会检查这个字段。
#### 支持的平台与模型
| 平台 | 模型(据官方文档) |
|------|------|
| 通义万相 | `wan2.7-t2v`、`wan2.7-t2v-2026-06-12` |
| 智谱 | `cogvideox-3`、`cogvideox-2`、`cogvideox-flash`、`viduq1-*`、`vidu2-*` |
| 火山方舟 | Seedance 系列 |
| MiniMax | `MiniMax-Hailuo-2.3`、`MiniMax-Hailuo-02`、`T2V-01-Director`、`T2V-01` |
| Gemini | `veo-3.1-generate-preview`、`veo-3.1-lite-generate-preview`、`gemini-omni-flash` |
| Z.ai | 视频生成(据 z.ai 文档) |
⚠️ **结果 URL 都有有效期**(万相约 24 小时、MiniMax 仅 9 小时),
存 URL 进库很快就会失效,必须及时 `saveTo()` 落地。
### 媒体结果要及时落地
多数平台返回的图片/视频 URL **有效期只有几小时到 24 小时**,
只把 URL 存进库,第二天就会全部失效。用 `saveTo()` 及时取回:
下载走库内带 SSRF 防护的抓取器(IP 钉死、逐跳重校验),不是裸 `file_get_contents()`。
目标目录**必须已存在**——库不会自动创建,避免路径写错时在磁盘上散落一堆空目录。
### WebSocket 通道默认关闭
讯飞等平台的语音能力只提供 WebSocket。本库已集成,
但**必须显式启用**,因为 WS 是长连接,超时与错误语义都和普通 HTTP 请求不同:
不启用直接调用会得到明确提示,而不是含糊的连接失败。
### 自定义网关
把 `base_url` 指向自建网关或中转服务时,图像/语音端点会**自动跟着走同一个网关**,
不会回落到官方地址(那意味着把数据发到你没指定的服务器上)。
需要单独指定某个能力的完整地址时,用 `<能力名>_endpoint` 配置项:
这个配置项同时是**逃生口**:库对某平台能力的判断有误或过时时,配上
`<能力名>_endpoint` 就能绕过声明检查。若该协议族本身没有对应形态的解析器
(比如 Claude 之于图像),仍会报错,但会点明是哪一层挡住的,并建议改用
`protocol` + `base_url`。
### 给自定义协议类的迁移说明
`ProtocolInterface` 在 v1.14.0 新增了 4 个能力方法。
- **继承内置协议的(`extends OpenAI` / `extends Claude` 等):无需任何改动。**
README「扩展开发」一节教的就是这种写法,库内 38 个厂商协议类也都是这么写的。
- **裸实现接口的(`implements ProtocolInterface`):加一行即可。**
---
## 已知限制
- 会话历史存在内存里,进程退出即失,跨请求需用 `exportHistory()` / `importHistory()` 自行落库;
- 流式下的工具调用已支持(分片会自动重组)。若模型声明本轮要调工具、却一个都重组不出来(说明该平台用了本库尚未覆盖的分片结构),会抛 `stream_tool_calls_unassembled` 异常而非静默返回空响应;
- 流式输出只提取正文增量,推理模型的思维链(`reasoning_content` / `thinking` 块)不计入 `getContent()`,需要时从 `stream_chunk` 事件的 `raw` 字段自取;
- 各平台的 `knownModels()` 常用模型清单是库内维护的静态快照,仅用于离线渲染下拉框与拉取失败兜底,最新可用模型请以 `listModels()` 的实时结果为准;
- Azure OpenAI 只覆盖了新版 `/openai/v1` 路由,旧版「部署名 + api-version」路由需自行用 `endpoint` 配置完整 URL;AWS Bedrock、Google Vertex AI 因需要 SigV4 / OAuth 签名,暂未内置;
- 自定义模型的 `supports()` 能力是乐观默认值(对方接口实际支持什么库无从得知),需要准确值时用 `features` 配置项自行声明;
- `Ai\Protocol\Gemini::convertMessages()` 未被调用——Gemini 走的是 OpenAI 兼容端点,消息直接透传;
- `chatBatch()` 并发批量不支持流式,也不走 `setAttachments()`(附件请写在各自 payload 里);
- `cost()` 需自行传入价格表,库不内置各平台价格(价格变动频繁,内置必然过期);
- `Ai\Cli\ClaudeCode` 依赖本机已安装 claude 程序;`proc_open` / `shell_exec` 被禁用的受限 PHP 环境需改用自定义执行器(如 SSH/SFTP)。
- 视频生成只覆盖**文生视频与首帧图生视频**;
- 图像编辑只覆盖走 `/images/edits` 的平台;硅基流动与通义的图生图形态不同,未接入;
- `wait()` 是阻塞的,只适合 CLI 与队列 worker;Web 请求里请用「提交存库 + 定时任务轮询」的写法;
- WebSocket 通道只做「一次会话、发完收完就关」这一种模式,够覆盖讯飞 TTS/ASR;不支持并发多连接、自动重连、服务端模式与 permessage-deflate 压缩扩展;
- MiniMax 的语音识别形态与 OpenAI 差异较大,暂未接入;
- 向量化默认不分批,超出平台单次上限时需自行传 `batch_size`——各平台上限差异大且文档未必写明,库不预设一个「保险的小值」,那会让本可一次发完的平台白白多发几十个请求;
- 视频生成一律异步,`AsyncTask::wait()` 会阻塞,不可在 Web 请求中使用;跨请求恢复需自行把 `toArray()` 的结果落库;
- 火山方舟的视频模型清单与阶跃星辰的 ASR/音色清单**刻意留空**:其文档站是 JS 渲染的,未能查证。留空会回退到平台自己的模型列表接口,填错则会让用户拿着不存在的模型名去调。
---
## 许可证
MIT LicenseAll versions of php-ai with dependencies
ext-curl Version *
ext-json Version *
ext-mbstring Version *