LINUX SB 快照站

自用php请求ds驱动...设计理念比较奇怪。

原帖: linux.sb/topic/13033 · 共 2 楼 · 标题快照 2026-08-16 17:34:30

楼主
发帖 2026-08-16 17:31:01 · 快照 2026-08-16 17:34:30

主要是php端没什么我很喜欢的api请求器,所以我做了这个。
然后,在这里,所有数据结构都是开放的array,让你可以用php的array生态管理上下文。
请求了之后需要你主动把消息塞到上下文,非常自由开放。
演示请求代码:

$chat = DeepSeekChat::getInstance();

$systemMessage = $chat->createMessage('', 0, '你是一个有帮助的AI助手');
$chat->messages[] = $systemMessage;

$toolDefinition = $chat->registerFunction(
    function ($params) {
        return "查询结果:php无需学习";
    },
    'search',
    '搜索函数,用于查询信息',
    [
        'type' => 'object',
        'properties' => [
            'query' => ['type' => 'string', 'description' => '搜索关键词']
        ],
        'required' => ['query']
    ]
);

$chat->tools[] = $toolDefinition;


$userMessage = $chat->createMessage('user1', 1, "帮我搜索'PHP是否需要学习'");
$chat->messages[] = $userMessage;
for (;;) {
    $response = $chat->requestDeepSeek(
        'cs-sk-ecfeb2a3-6721-422a-a313-935173adce2c',
        'http://192.168.1.20:55566/v1/chat/completions',
        'zhipu:glm-5',
        0.7
    );

    $chat->messages[] = $response['message'];

    if ($response['called']) {
        echo "DS调用了函数,结果:" . json_encode($response, JSON_UNESCAPED_UNICODE) . "\n";
        $chat->messages[] = $chat->createMessage('tool_return', 1, $response["result"]);
    } else {
        echo "AI回复: " . $response['message']['content'] . "\n";
        break;
    }
}

驱动代码:

<?php

class DeepSeekChat
{
    public $messages = [];
    public $tools = [];
    public $functions = [];
    private static $instance = null;

    private function __construct() {}

    public static function getInstance()
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * 注册一个可以被DS调用的函数(如果函数名已存在,则覆盖旧的)
     * @param callable $function 被调用的函数
     * @param string $name 函数名称
     * @param string $description 函数描述
     * @param array $parameters 参数定义
     * @return array 返回完整的toolDefinition
     */
    public function registerFunction($function, $name, $description, $parameters)
    {
        $toolDefinition = [
            "type" => "function",
            "function" => [
                "name" => $name,
                "description" => $description,
                "parameters" => $parameters
            ]
        ];

        $this->functions[$name] = [
            'function' => $function,
            'definition' => $toolDefinition
        ];

        return $toolDefinition;
    }

    /**
     * 创建一条消息(不自动添加到messages)
     * @param string $name 消息名称
     * @param int $roleType 0=system, 1=user, 2=assistant
     * @param string|array $content 消息内容(字符串或带图片的多模态数组)
     * @param array $toolCalls 可选的tool_calls数组
     * @param array $images 图片数组,格式 [["bin"=>"二进制数据", "mime"=>"image/png"], ...]
     * @return array 返回message数组
     */
    public function createMessage($name, $roleType, $content, $toolCalls = [], $images = [])
    {
        $roles = ['system', 'user', 'assistant'];
        $finalContent = $content;
        if (!empty($images)) {
            $finalContent = [];
            if (!empty($content)) {
                $finalContent[] = [
                    'type' => 'text',
                    'text' => $content
                ];
            }
            foreach ($images as $image) {
                $base64Data = base64_encode($image['bin']);
                $finalContent[] = [
                    'type' => 'image_url',
                    'image_url' => [
                        'url' => 'data:' . $image['mime'] . ';base64,' . $base64Data
                    ]
                ];
            }
        }
        
        $message = [
            'role' => $roles[$roleType],
            'content' => $finalContent
        ];

        if (!empty($name)) {
            $message['name'] = $name;
        }

        if (!empty($toolCalls)) {
            $message['tool_calls'] = $toolCalls;
        }

        return $message;
    }

    /**
     * 请求DS API
     * @param string $apiKey API密钥
     * @param string $apiUrl API地址
     * @param string $model 模型名称
     * @param float $temperature 温度参数,默认0.9
     * @return array 返回 ["message" => 消息数组, "called" => bool, "result" => toolcall结果]
     * @throws Exception
     */
    public function requestDeepSeek($apiKey, $apiUrl, $model = 'qwen/qwen3-next-80b', $temperature = 0.9)
    {
        $response = $this->makeApiRequest($apiKey, $apiUrl, $model, $temperature);

        if ($response && isset($response['choices'][0]['message'])) {
            $assistantMessage = $response['choices'][0]['message'];
            $called = false;
            $result = null;

            if (isset($assistantMessage['tool_calls']) && !empty($assistantMessage['tool_calls'])) {
                $called = true;
                $results = [];

                foreach ($assistantMessage['tool_calls'] as $toolCall) {
                    $functionName = $toolCall['function']['name'];
                    $arguments = json_decode($toolCall['function']['arguments'], true);

                    if (isset($this->functions[$functionName])) {
                        $registered = $this->functions[$functionName];
                        $functionResult = call_user_func($registered['function'], $arguments);
                        $results[] = $functionResult;
                    }
                }
                $result = $results;
            }

            // 处理think标签(仅当content为字符串时)
            if (isset($assistantMessage['content']) && is_string($assistantMessage['content'])) {
                $content = $assistantMessage['content'];
                if (strpos($content, "</think>") !== false) {
                    $content = explode("</think>", $content);
                    array_shift($content);
                    $content = implode("", $content);
                    $assistantMessage['content'] = $content;
                }
            }

            return [
                'message' => $assistantMessage,
                'called' => $called,
                'result' => $result
            ];
        }

        throw new Exception('API请求失败: ' . ($response['error']['message'] ?? '未知错误'));
    }

    public function getMessages()
    {
        return $this->messages;
    }

    public function clearMessages()
    {
        $systemMessage = [];
        if (!empty($this->messages) && $this->messages[0]['role'] === 'system') {
            $systemMessage = [$this->messages[0]];
        }

        $this->messages = $systemMessage;
        return $this;
    }

    private function makeApiRequest($apiKey, $apiUrl, $model, $temperature)
    {
        $data = [
            'model' => $model,
            'messages' => $this->messages,
            'temperature' => $temperature,
            'stream' => false
        ];

        if (!empty($this->tools)) {
            $data['tools'] = $this->tools;
        }

        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_URL => $apiUrl,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => json_encode($data),
            CURLOPT_HTTPHEADER => [
                'Content-Type: application/json',
                'Authorization: Bearer ' . $apiKey
            ],
            CURLOPT_TIMEOUT => 300
        ]);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        curl_close($ch);

        if ($error) {
            throw new Exception('cURL错误: ' . $error);
        }

        if ($httpCode !== 200) {
            throw new Exception('HTTP错误代码: ' . $httpCode . " $response");
        }

        return json_decode($response, true);
    }

    private function __clone() {}
    private function __wakeup() {}
}

更多演示:

$chat = DeepSeekChat::getInstance();

$systemMessage = $chat->createMessage('', 0, '你是一个有帮助的AI助手');
$chat->messages[] = $systemMessage;

$toolDefinition = $chat->registerFunction(
    function ($params) {
        try{
            $a=eval($params["phpcode"]);
        }catch (Throwable $e){
            $a = "Error: " . $e->getMessage();
        }
        return $a;
    },
    'phpeval',
    '运行php代码',
    [
        'type' => 'object',
        'properties' => [
            'phpcode' => ['type' => 'string', 'description' => 'php代码,使用return获取输出。']
        ],
        'required' => ['phpcode']
    ]
);

$chat->tools[] = $toolDefinition;


$userMessage = $chat->createMessage('user1', 1, "你是使用php连接的,帮我查查你的环境,例如使用什么东西连接你、电脑环境。查看用于连接你的代码。如果你觉得没调查清楚,请调用函数调查,否则不要调用函数,调查结束。'");
$chat->messages[] = $userMessage;
for (;;) {
    //echo json_encode($chat->messages, JSON_UNESCAPED_UNICODE)."\n".json_encode($chat->tools, JSON_UNESCAPED_UNICODE)."\n";
    $response = $chat->requestDeepSeek(
        'cs-sk-ecfeb2a3-6721-422a-a313-935173adce2c',
        'http://192.168.1.20:55566/v1/chat/completions',
        'zhipu:glm-5',
        0.7
    );
    $chat->messages[] = $response['message'];

    if ($response['called']) {
        echo "DS调用了函数:" . $response['message']['content'] . "\n";;
        $chat->messages[] = $chat->createMessage('tool_return', 1, json_encode($response["result"], JSON_UNESCAPED_UNICODE));
    } else {
        echo "AI回复: " . $response['message']['content'] . "\n";
        break;
    }
}
$chat = DeepSeekChat::getInstance();

$systemMessage = $chat->createMessage('', 0, '你是一个有帮助的AI助手,可以识别图片内容');
$chat->messages[] = $systemMessage;

$imageData = file_get_contents('C:\Users\nint\Desktop\新建文件夹\W (19).jpg');

$userMessage = $chat->createMessage(
    'user1', 
    1, 
    '请描述这张图片的内容', 
    [], 
    [['bin' => $imageData, 'mime' => 'image/jpeg']]
);
$chat->messages[] = $userMessage;

$response = $chat->requestDeepSeek(
    'cs-sk-ecfeb2a3-6721-422a-a313-935173adce2c',
    'http://192.168.1.20:55566/v1/chat/completions',
    'zhipu:glm-5v-turbo',
    0.7
);

echo "AI回复: " . $response['message']['content'] . "\n";
#1
发帖 2026-08-16 17:35:33 · 快照 2026-08-16 17:36:49

这个设计理念确实挺有意思的,messages 和 tools 全开放成 public array,直接用 PHP 数组生态管上下文,自由度很高,调试 tool call 循环时特别直观。

createMessage 支持多模态图片、registerFunction 覆盖注册这些细节也挺实用,单例 + curl 封装看着干净。不过第二个演示里的 phpeval 直接 eval,自用玩玩可以,真要挂出去风险不小哈哈。API key 记得别长期暴露在示例里~

整体很适合喜欢自己掌控流程的人,感谢分享这种“裸奔式”写法,学到了 👍