收藏文章 楼主

小绿叶 AI 前端和 php 连接 代码

版块:ubuntu   类型:普通   作者:小绿叶技术博客   查看:8   回复:0   获赞:0   时间:2026-08-30 21:49:10

index.html


<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>小绿叶AI</title>
<style>
body{font-family:system-ui;margin:20px;}
#inputBox{width:100%;height:80px;padding:8px;box-sizing:border-box;}
#sendBtn{margin:8px 0;padding:6px 16px;}
#result{white-space:pre-wrap;border:1px solid #ccc;padding:10px;min-height:300px;margin-top:10px;}
</style>
</head>
<body>
<h3> 小绿叶AI Ollama </h3>
<textarea id="inputBox" placeholder="请输入问题,例如:2026年最新AI新闻"></textarea>
<br>
<button id="sendBtn">发送(开启联网搜索)</button>
<div id="result"></div>
<script>
let es = null;
const resultDom = document.getElementById('result');
const inputDom = document.getElementById('inputBox');
const sendBtn = document.getElementById('sendBtn');

sendBtn.onclick = ()=>{
const prompt = inputDom.value.trim();
if(!prompt) return;
resultDom.textContent = "";
if(es) es.close();
es = new EventSource(`stream.php?prompt=${encodeURIComponent(prompt)}`);

es.onmessage = (e)=>{
const data = JSON.parse(e.data);
if(data.type === 'chunk'){
resultDom.textContent += data.text;
}else if(data.type === 'end'){
es.close();
}
}

es.onerror = ()=>{
es.close();
resultDom.textContent += "\n\n【连接中断】";
}
}
</script>
</body>
</html>


stream.php

<?php
ob_implicit_flush(true);
ob_end_flush();
header("Content-Type: text/event-stream");
header("Cache-Control: no-cache");
header("Connection: keep-alive");
header("Access-Control-Allow-Origin: *");

$prompt = trim($_GET['prompt'] ?? "");
if(empty($prompt)){
    echo "data: " . json_encode(["type"=>"end"]) . "\n\n";
    flush();
    exit;
}

// ========== 天气意图识别 ==========
$isWeatherQuery = false;
$cityName = "重庆";
if(preg_match('/(天气|气温|温度|下雨|晴天|预报)/u',$prompt)){
    $isWeatherQuery = true;
    //简单提取城市,这里示例优先匹配重庆,你可以扩展正则提取任意城市
    if(preg_match('/(北京|上海|广州|深圳|成都|重庆|天津|杭州)/u',$prompt,$m)){
        $cityName = $m[1];
    }
}

// ========== 调用中国天气网公开接口 ==========
$weatherData = null;
if($isWeatherQuery){
    $weatherUrl = "http://wthrcdn.etouch.cn/weather_mini?city=".urlencode($cityName);
    $ctxWeather = stream_context_create([
        'http'=>[
            'timeout'=>8,
            'user_agent'=>'Mozilla/5.0'
        ]
    ]);
    $raw = @file_get_contents($weatherUrl,false,$ctxWeather);
    if($raw){
        $json = json_decode($raw,true);
        if($json && $json['status'] == 1){
            $weatherData = $json['data'];
        }
    }
}

// 天气接口成功,直接输出结果
if($weatherData !== null){
    $today = $weatherData['forecast'][0];
    $text = sprintf(
        "%s实时天气:%s,温度%s~%s℃,风向%s,风力%s。\n%s",
        $cityName,
        $today['type'],
        $today['low'],
        $today['high'],
        $today['fengxiang'],
        $today['fengli'],
        $weatherData['ganmao']
    );
    echo "data: " . json_encode(["type"=>"chunk","text"=>$text]) . "\n\n";
    echo "data: " . json_encode(["type"=>"end"]) . "\n\n";
    flush();
    exit;
}

// ========== 接口失败,回退原有searxng搜索逻辑 ==========
$searxUrl = "http://192.168.122.1:90/search?" . http_build_query([
    "q" => $prompt,
    "format" => "json",
    "language" => "zh",
    "time_range" => "day"
]);

$searchCtx = "";
$ctx = stream_context_create(['http'=>['timeout'=>10]]);
$searchRaw = @file_get_contents($searxUrl,false,$ctx);

if ($searchRaw !== false) {
    $searchData = json_decode($searchRaw,true);
    if(is_array($searchData['results']) && count($searchData['results'])>0){
        $searchCtx .= "【参考网络搜索结果】\n";
        foreach(array_slice($searchData['results'],0,3) as $item){
            $title = $item['title'] ?? '';
            $content = $item['content'] ?? '';
            $searchCtx .= "标题:{$title}\n摘要:{$content}\n---\n";
        }
    } else {
        $searchCtx = "【提示:未检索到网络相关资料】\n";
    }
} else {
    $searchCtx = "【警告:联网搜索失败】\n";
}

$systemPrompt = <<<SYS
你是检索增强问答助手。
规则:
1. 优先使用【参考网络搜索结果】中的内容回答用户问题。
2. 禁止输出“我无法联网”、“没有联网能力”这类语句。
3. 如果搜索到的资料有限,可基于已有摘要信息整理回答;不要编造虚假的实时气象数值。
SYS;

$userContent = $searchCtx . "\n用户问题:{$prompt}\n请根据上面参考信息回答问题。";
file_put_contents("debug_prompt.log", $userContent);

// ========== Ollama /api/chat 流式 ==========
$ollamaApi = "http://192.168.122.1:11434/api/chat";
$postBody = json_encode([
    "model" => "deepseek-r1:1.5b",
    "messages" => [
        ["role"=>"system","content"=>$systemPrompt],
        ["role"=>"user","content"=>$userContent]
    ],
    "stream" => true,
    "options" => [
        "num_ctx" => 8192,
        "temperature" => 0.3
    ]
]);

$context = stream_context_create([
    'http' => [
        'method' => 'POST',
        'header' => "Content-Type: application/json\r\n",
        'content' => $postBody,
        'timeout' => 300
    ]
]);

$handle = @fopen($ollamaApi, 'r', false, $context);
if (!$handle) {
    echo "data: ".json_encode(["type"=>"chunk","text"=>"❌无法连接Ollama"])."\n\n";
    echo "data: ".json_encode(["type"=>"end"])."\n\n";
    flush();
    exit;
}

while (!feof($handle)) {
    $line = fgets($handle);
    $line = trim($line);
    if (empty($line)) continue;
    $obj = json_decode($line, true);
    if (json_last_error() !== JSON_ERROR_NONE) continue;

    if (isset($obj['message']['content'])) {
        $msg = json_encode(["type"=>"chunk","text"=>$obj['message']['content']], JSON_UNESCAPED_UNICODE);
        echo "data: {$msg}\n\n";
        flush();
    }
    if (isset($obj['done']) && $obj['done'] === true) {
        echo "data: ".json_encode(["type"=>"end"])."\n\n";
        flush();
        break;
    }
}
fclose($handle);
?>


ai 部署 


# nano docker.conf

server {
    listen 80;
    server_name docker.eisc.cn;

    location / {
        proxy_pass http://127.0.0.1:5000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        proxy_connect_timeout 300s;
        proxy_send_timeout 300s;
        proxy_read_timeout 300s;
        proxy_buffering on;
        proxy_buffer_size 16k;
        proxy_buffers 4 64k;
    }
}








sudo mkdir -p /usr/local/lib/docker/cli-plugins
sudo curl -SL https://github.com/docker/compose/releases/download/v2.30.0/docker-compose-linux-x86_64 -o /usr/local/lib/docker/cli-plugins/docker-compose
sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose
docker compose version
mkdir -p /datadisk/eisc/registry-proxy
cd /datadisk/eisc/registry-proxy

# nano docker-compose.yml
version: "3"
services:
  registry:
    image: registry:2
    restart: always
    ports:
      - "127.0.0.1:5000:5000"
    environment:
      - REGISTRY_PROXY_REMOTEURL=https://registry-1.docker.io
    volumes:
      - ./data:/var/lib/registry



# docker compose up -d





内网配置 

sudo snap install docker
sudo nano /var/snap/docker/current/config/daemon.json

{
  "registry-mirrors": [
    "http://docker.ddoss.cn"
  ],
  "insecure-registries": [
    "docker.ddoss.cn"
  ],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m",
    "max-file": "3"
  }
}

# 如果是二进制docker文件启动,填写内容
# sudo nano  /datadisk/eisc/server/docker27/daemon.json
{
  "registry-mirrors": ["http://docker.ddoss.cn"],
  "bip": "10.2.1.1/24",
  "userland-proxy": true,
  "userland-proxy-path": "/datadisk/eisc/server/docker27/docker-proxy",
  "features": {
    "buildkit": true
  },
  "data-root": "/datadisk/eisc/docker-data"
}

# sudo nano /etc/systemd/system/docker.service
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network-online.target firewalld.service
Wants=network-online.target

[Service]
Type=notify
ExecStart=/datadisk/eisc/server/docker27/dockerd
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutSec=0
RestartSec=2
Restart=always

[Install]
WantedBy=multi-user.target




sudo snap stop docker
sudo snap start docker

sudo docker info        # 查看是否有 docker.eisc.cn

curl -i http://docker.eisc.cn/v2/         测试联通性



sudo snap remove --purge docker  # 卸载 docker 






# docker 拉 deepseek 
sudo docker pull ollama/ollama
mkdir -p /datadisk/eisc/docker-data/ollama

# 模型框架
docker run -d \
  --name ollama \
  -p 11434:11434 \
  -v /datadisk/eisc/docker-data/ollama:/root/.ollama \
  --restart unless-stopped \
  ollama/ollama

# 网页版本接口
docker run -d \
  --name searxng \
  --restart always \
  -p 8080:8080 \
  searxng/searxng:latest


# 模型训练
docker exec -it ollama ollama pull deepseek-r1:1.5b

docker exec -it ollama bash 
ollama pull qwen2.5:7b
# 使用千问

# 搜索引擎
docker run -d \
  --name searxng \
  -p 90:8080 \
  -v searxng_data:/etc/searxng \
  --restart always \
  searxng/searxng:latest


docker exec -it searxng vi /etc/searxng/settings.yml
# 追加内容
search:
  formats:
    - html
    - json

engines:
  - name: duckduckgo
    enabled: true


# 启动UI
docker run -d \
  --name open-webui \
  -p 92:8080 \
  -e OLLAMA_BASE_URL=http://192.168.122.1:11434 \
  -v open-webui:/app/backend/data \
  --restart always \
  ghcr.io/open-webui/open-webui:main



docker rmi ghcr.io/open-webui/open-webui:main   # 删除残缺镜像   










本文章最后由 admin2026-08-30 21:50 编辑

提供企业建站服务,免费网防系统,提交信息登录 http://yundun.ddoss.cn 邮箱: proposal@ddoss.cn 
回复列表
默认   热门   正序   倒序

回复:小绿叶 AI 前端和 php 连接 代码

头像

用户名:

粉丝数:

签名:

资料 关注 好友 消息