跳转至内容
  • 版块
  • 最新
  • 标签
  • 热门
  • 用户
  • 群组
皮肤
  • 浅色
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • 深色
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • 默认(LCZ-Blue)
  • 不使用皮肤
  • LCZ-Green
  • LCZ-Blue
  • LCZ-Black
折叠
品牌标识

抡锤者

首页 版块 标签 硬件 AI 广场
  1. 主页
  2. 版块
  3. AI硬件
  4. 额外加一张7900xtx用llama跑qwen3.8 27B Q4和Q4非审查的经验总结

额外加一张7900xtx用llama跑qwen3.8 27B Q4和Q4非审查的经验总结

已定时 已固定 已锁定 已移动 AI硬件
7900xtxqwen-27bllama.cpp
4 帖子 2 发布者 218 浏览
  • 从旧到新
  • 从新到旧
  • 最多赞同
回复
  • 在新帖中回复
登录后回复
此主题已被删除。只有拥有主题管理权限的用户可以查看。
  • yi songY 离线
    yi songY 离线
    yi song
    编写于 最后由 yi song 编辑
    #1

    RX 7900 XTX + llama.cpp(HIP)+ Qwen3.8-27B:单卡单服务三端共享完整搭建指南(DSH / Obsidian Copilot / Codex 桌面版)

    本文目标:完整记录一台 7900 XTX 专卡跑 llama.cpp 的全部配置。发到论坛存档,重装系统后把本文交给 AI,即可按步骤 100% 恢复到当前状态。
    最终效果:一个 llama-server 实例(8080 端口)同时服务三个客户端;启动时可选模型、上下文长度、思考模式开/关;Obsidian 文档总结秒出正文。

    • 机器:i5-13600KF / 64GB RAM / 技嘉 Z790M AORUS ELITE AX / Windows 11 26H1
    • 显卡分工:RX 7900 XTX(24GB)只跑 llama;RTX 4060 Ti 负责显示输出和其余一切
    • 实测性能(128K 上下文):预填充 ~800-900 tok/s,生成 ~34 tok/s(降压超频后约 270-290W)

    1. GPU 独占逻辑(7900 XTX 只跑 llama 的实现原理)

    核心思路:靠软件构建保证独占,而不是靠参数过滤。

    1. 使用 HIP/ROCm 版 llama.cpp(C:\llama\llama-hip\llama-server.exe,commit be4a6a6,Clang 23 构建对应官方 b10617 时代)。
      • HIP build 只枚举 AMD GPU(ROCm 只认到 7900 XTX 一张卡),结构上不可能调用 4060 Ti,不需要任何 --device 过滤参数
      • NVIDIA 4060 Ti 照常跑桌面、CUDA 应用,互不干扰
    2. Vulkan 构建已弃用:C:\llama 根目录还有一份 Vulkan 版(b10617 zip 解压),已不用。关键坑:必须把两个位置的 ggml-vulkan.dll 改名为 .bak,否则运行时 Vulkan 后端会抢跑、与 ROCm 冲突:
      • C:\llama\ggml-vulkan.dll → .bak
      • C:\llama\llama-hip\ggml-vulkan.dll → .bak(start.bat 启动时会自动检查改名)
    3. 7900 XTX 降压超频(AMD Adrenalin 手动设置,非脚本,重装系统后需重新设置):
      • 功耗限制 -10%、电压 -65mV
      • 效果:推理功耗约 270-290W(原版可达 350W+),性能损失可忽略

    重装系统时 HIP 版获取途径:llama.cpp GitHub Releases 下载 llama-bXXXX-bin-win-hip-x64.zip(需与 ROCm 驱动版本匹配),解压到 C:\llama\llama-hip\。

    2. 目录结构

    C:\llama\
    ├─ start.bat                  ← 总启动脚本(选模型/上下文/思考开关)
    ├─ qwen38_tolerant.jinja      ← 容错聊天模板(三端兼容的关键)
    ├─ restart_llama.ps1          ← AI 无人值守重启(固定 UD 模型+128K+关思考)
    ├─ codex-launcher.ps1         ← Codex 对齐+启动逻辑
    ├─ server.log                 ← 服务日志(--log-file 输出)
    ├─ ggml-vulkan.dll.bak        ← 已封印的 Vulkan 后端
    └─ llama-hip\                 ← HIP 版 llama.cpp(llama-server.exe 在这里)
    C:\model\
    ├─ Qwen3.8-27B-Q4_K_M.gguf
    └─ Qwen3.8-27B-UD-Q4_K_XL.gguf
    桌面快捷方式:
    ├─ 启动llama.lnk   → C:\llama\start.bat(工作目录 C:\llama)
    └─ Codex启动.bat   → 调 codex-launcher.ps1
    

    模型与 ID 对应关系(铁律:llama-server 一次只加载一个 gguf,且完全忽略请求里的 model 字段,所以各客户端选的模型必须和当前加载的 gguf 一致,否则 token 统计错位):

    gguf 文件 dsh slug Codex slug Copilot slug
    Qwen3.8-27B-Q4_K_M.gguf qwen3.8-27b-q4km qwen3.8-27b-q4km qwen3.8-27b-q4km
    Qwen3.8-27B-UD-Q4_K_XL.gguf qwen3.8-27b-ud-q4kxl qwen3.8-27b-ud-q4kxl qwen3.8-27b-ud

    两个模型 tokenizer 完全相同 → </think> token ID(248068)对两个模型都有效,思考开关对两个都适用。注意:模板和 token ID 是 Qwen3.8 系列专属,换成其他家族模型需另配。

    3. start.bat(总启动脚本,完整内容)

    启动流程:自动封印 Vulkan dll → 选模型 → 选上下文 → 选思考模式 → 启动服务。

    @echo off
    setlocal enabledelayedexpansion
    
    :: ============================================================
    :: GPU: use HIP backend for AMD RX 7900 XTX ONLY
    :: HIP build only sees the 7900 XTX (ROCm0), never the 4060 Ti
    :: ggml-vulkan.dll renamed to .bak to prevent Vulkan interference
    :: ============================================================
    
    :: Ensure ggml-vulkan.dll is disabled (must be .bak or absent)
    set "VULKAN_DLL=C:\llama\ggml-vulkan.dll"
    if exist "%VULKAN_DLL%" (
        echo [WARNING] ggml-vulkan.dll found, renaming to prevent Vulkan conflict...
        rename "%VULKAN_DLL%" "ggml-vulkan.dll.bak"
    )
    
    :: Also check HIP directory
    set "VULKAN_DLL2=C:\llama\llama-hip\ggml-vulkan.dll"
    if exist "%VULKAN_DLL2%" (
        echo [WARNING] ggml-vulkan.dll found in HIP dir, renaming...
        rename "%VULKAN_DLL2%" "ggml-vulkan.dll.bak"
    )
    
    :: HIP build path (gfx1100 = RDNA3, supports RX 7900 XTX)
    set SERVER_DIR=C:\llama\llama-hip
    set SERVER_EXE=%SERVER_DIR%\llama-server.exe
    
    set MODEL_DIR=C:\model
    
    echo ============================================
    echo Select model to load:
    echo ============================================
    set count=0
    for %%f in ("%MODEL_DIR%\*.gguf") do (
        set /a count+=1
        echo !count!. %%~nxf
    )
    if %count%==0 (
        echo Error: no .gguf files in %MODEL_DIR%
        pause
        exit /b
    )
    
    set /p choice="Model number (1-%count%): "
    set selected=
    set idx=0
    for %%f in ("%MODEL_DIR%\*.gguf") do (
        set /a idx+=1
        if !idx!==%choice% set selected=%%f
    )
    if "%selected%"=="" (
        echo Invalid input
        pause
        exit /b
    )
    echo Selected: %selected%
    
    echo.
    echo ============================================
    echo Select context size:
    echo 1. 8K   (8192)
    echo 2. 16K  (16384)
    echo 3. 32K  (32768)
    echo 4. 64K  (65536)
    echo 5. 128K (131072)
    echo ============================================
    set /p ctx_choice="Choice (1-5): "
    
    set ctx=8192
    if "%ctx_choice%"=="1" set ctx=8192
    if "%ctx_choice%"=="2" set ctx=16384
    if "%ctx_choice%"=="3" set ctx=32768
    if "%ctx_choice%"=="4" set ctx=65536
    if "%ctx_choice%"=="5" set ctx=131072
    
    echo Context: %ctx% tokens
    
    echo.
    echo ============================================
    echo Thinking mode:
    echo 1. Off (default) - summaries output instantly
    echo 2. On            - deep reasoning, slower summaries
    echo ============================================
    set /p think_choice="Choice (1-2, Enter=1): "
    
    set think_args=--chat-template-kwargs "{\"enable_thinking\":false}" --logit-bias 248068-100
    set think_label=OFF - no reasoning, fast summary
    if "%think_choice%"=="2" (
        set think_args=--chat-template-kwargs "{\"enable_thinking\":true}"
        set think_label=ON - deep reasoning, slower
    )
    
    echo.
    echo ============================================
    echo Starting llama-server (HIP build)...
    echo   Model : %selected%
    echo   Ctx   : %ctx%
    echo   Think : %think_label%
    echo   GPU   : AMD RX 7900 XTX (HIP/ROCm, 24GB)
    echo   FA    : ON (Flash Attention)
    echo   NGL   : 99 (all layers to GPU)
    echo   KV    : q4_0 (half size, prevents VRAM thrashing)
    echo   Par   : 1 (single slot, max speed for single app)
    echo   API   : http://localhost:8080
    echo   Press Ctrl+C to stop
    echo ============================================
    
    "%SERVER_EXE%" -m "%selected%" -c %ctx% -ngl 99 -n -1 -fa on --no-mmap ^
      --cache-type-k q4_0 --cache-type-v q4_0 ^
      --parallel 1 -b 512 -ub 256 ^
      --chat-template-file C:\llama\qwen38_tolerant.jinja ^
      %think_args% ^
      --log-file C:\llama\server.log ^
      --host 0.0.0.0 --port 8080
    
    pause
    

    参数逐条理由(都是验证过的正确做法,勿改):

    参数 理由
    -ngl 99 全部层进 7900 XTX
    -n -1 服务端不限输出 token 数,由客户端决定
    -fa on Flash Attention,省显存带宽
    --no-mmap 权重全进内存,避免缺页
    --cache-type-k/v q4_0 KV 缓存 4bit 量化:32K 上下文只占 4.3GB(q8_0 要 8.6GB),避免 24GB 卡在长文档时显存溢出换页
    --parallel 1 单槽位,单客户端场景最快
    -b 512 -ub 256 针对 7900 XTX 调优的批大小
    --chat-template-file 容错模板(见第 4 节)
    %think_args% 思考模式参数(见第 5 节)
    --log-file 日志必须写文件。绝对不要用 --verbose:终端高频输出会干扰 HTTP 流式响应,造成客户端 TRANSPORT 错误(血泪坑)
    --host 0.0.0.0 --port 8080 三端统一入口

    上下文怎么选:日常任务实际用量 ~20K,64K 够用;128K 可选(加载 1-2 分钟)。

    4. qwen38_tolerant.jinja(容错聊天模板,完整内容)

    相对模型内置模板的 3 处修改(重做模板时照此改):

    1. 合并多条 system 消息:Codex 每次请求带 3 条 developer 消息,原版模板直接 400("System message must be at the beginning")。本模板把所有 system/developer 内容合并成一个 merged_sys 块放开头
    2. 思考默认值反转:开启条件从 undefined or true 改为 defined and true → 不传参默认关思考(配合 llama-server 会注入 enable_thinking=true 的行为,必须显式压制)
    3. 关思考时输出空思考块:<think>\n\n</think>\n\n,模型直接续写正文
    {%- set image_count = namespace(value=0) %}
    {%- set video_count = namespace(value=0) %}
    {%- macro render_content(content, do_vision_count, is_system_content=false) %}
        {%- if content is string %}
            {{- content }}
        {%- elif content is iterable and content is not mapping %}
            {%- for item in content %}
                {%- if 'image' in item or 'image_url' in item or item.type == 'image' %}
                    {%- if is_system_content %}
                        {{- raise_exception('System message cannot contain images.') }}
                    {%- endif %}
                    {%- if do_vision_count %}
                        {%- set image_count.value = image_count.value + 1 %}
                    {%- endif %}
                    {%- if add_vision_id %}
                        {{- 'Picture ' ~ image_count.value ~ ': ' }}
                    {%- endif %}
                    {{- '<|vision_start|><|image_pad|><|vision_end|>' }}
                {%- elif 'video' in item or item.type == 'video' %}
                    {%- if is_system_content %}
                        {{- raise_exception('System message cannot contain videos.') }}
                    {%- endif %}
                    {%- if do_vision_count %}
                        {%- set video_count.value = video_count.value + 1 %}
                    {%- endif %}
                    {%- if add_vision_id %}
                        {{- 'Video ' ~ video_count.value ~ ': ' }}
                    {%- endif %}
                    {{- '<|vision_start|><|video_pad|><|vision_end|>' }}
                {%- elif 'text' in item %}
                    {{- item.text }}
                {%- else %}
                    {{- raise_exception('Unexpected item type in content.') }}
                {%- endif %}
            {%- endfor %}
        {%- elif content is none or content is undefined %}
            {{- '' }}
        {%- else %}
            {{- raise_exception('Unexpected content type.') }}
        {%- endif %}
    {%- endmacro %}
    {%- if not messages %}
        {{- raise_exception('No messages provided.') }}
    {%- endif %}
    {%- set sysacc = namespace(text='') %}
    {%- for message in messages %}
        {%- if message.role == 'system' %}
            {%- set part = render_content(message.content, false, true)|trim %}
            {%- if part %}
                {%- if sysacc.text %}
                    {%- set sysacc.text = sysacc.text + '\n\n' + part %}
                {%- else %}
                    {%- set sysacc.text = part %}
                {%- endif %}
            {%- endif %}
        {%- endif %}
    {%- endfor %}
    {%- set merged_sys = sysacc.text %}
    {%- set reasoning_instructions = '' %}
    {%- if enable_thinking is defined and enable_thinking is true %}
        {%- set resolved_reasoning_effort = reasoning_effort|default('xhigh') %}
        {%- if resolved_reasoning_effort not in ('xhigh', 'medium', 'low') %}
            {{- raise_exception('Unexpected reasoning effort ' ~ reasoning_effort ~ '. Supported types are xhigh (default), medium, and low.') }}
        {%- endif %}
        {%- if resolved_reasoning_effort == 'xhigh' %}
            {%- set reasoning_instructions = 'Reasoning effort is set to xhigh. Please think carefully through the task, validate key assumptions, consider plausible alternatives, and prioritize correctness, consistency, and clarity in the final answer.' %}
        {%- elif resolved_reasoning_effort == 'low' %}
            {%- set reasoning_instructions = 'Reasoning effort is set to low. Keep your thinking brief and focused, moving directly to the conclusion without unnecessary elaboration.' %}
        {%- endif %}
    {%- endif %}
    {%- if tools and tools is iterable and tools is not mapping %}
        {{- '<|im_start|>system\n' }}
        {%- if reasoning_instructions %}
            {{- reasoning_instructions + '\n\n' }}
        {%- endif %}
        {{- "# Tools\n\nYou have access to the following functions:\n\n<tools>" }}
        {%- for tool in tools %}
            {{- "\n" }}
            {{- tool | tojson }}
        {%- endfor %}
        {{- "\n</tools>" }}
        {{- '\n\nIf you choose to call a function ONLY reply in the following format with NO suffix:\n\n<tool_call>\n<function=example_function_name>\n<parameter=example_parameter_1>\nvalue_1\n</parameter>\n<parameter=example_parameter_2>\nThis is the value for the second parameter\nthat can span\nmultiple lines\n</parameter>\n</function>\n</tool_call>\n\n<IMPORTANT>\nReminder:\n- Function calls MUST follow the specified format: an inner <function=...></function> block must be nested within <tool_call></tool_call> XML tags\n- Required parameters MUST be specified\n- You may provide optional reasoning for your function call in natural language BEFORE the function call, but NOT after\n- If there is no function call available, answer the question like normal with your current knowledge and do not tell the user about function calls\n</IMPORTANT>' }}
        {%- if merged_sys %}
            {{- '\n\n' + merged_sys }}
        {%- endif %}
        {{- '<|im_end|>\n' }}
    {%- else %}
        {%- if merged_sys %}
            {{- '<|im_start|>system\n' + (reasoning_instructions + '\n\n' if reasoning_instructions else '') + merged_sys + '<|im_end|>\n' }}
        {%- elif reasoning_instructions %}
            {{- '<|im_start|>system\n' + reasoning_instructions + '<|im_end|>\n' }}
        {%- endif %}
    {%- endif %}
    {%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}
    {%- for message in messages[::-1] %}
        {%- set index = (messages|length - 1) - loop.index0 %}
        {%- if ns.multi_step_tool and message.role == "user" %}
            {%- set content = render_content(message.content, false)|trim %}
            {%- if not(content.startswith('<tool_response>') and content.endswith('</tool_response>')) %}
                {%- set ns.multi_step_tool = false %}
                {%- set ns.last_query_index = index %}
            {%- endif %}
        {%- endif %}
    {%- endfor %}
    {%- if ns.multi_step_tool %}
        {{- raise_exception('No user query found in messages.') }}
    {%- endif %}
    {%- for message in messages %}
        {%- set content = render_content(message.content, true)|trim %}
        {%- if message.role == "system" %}
        {%- elif message.role == "user" %}
            {{- '<|im_start|>' + message.role + '\n' + content + '<|im_end|>' + '\n' }}
        {%- elif message.role == "assistant" %}
            {%- set reasoning_content = '' %}
            {%- if message.reasoning_content is string %}
                {%- set reasoning_content = message.reasoning_content %}
            {%- endif %}
            {%- set reasoning_content = reasoning_content|trim %}
            {%- if preserve_thinking is undefined or preserve_thinking is true or loop.index0 > ns.last_query_index %}
                {{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content + '\n</think>\n\n' + content }}
            {%- else %}
                {{- '<|im_start|>' + message.role + '\n' + content }}
            {%- endif %}
            {%- if message.tool_calls and message.tool_calls is iterable and message.tool_calls is not mapping %}
                {%- for tool_call in message.tool_calls %}
                    {%- if tool_call.function is defined %}
                        {%- set tool_call = tool_call.function %}
                    {%- endif %}
                    {%- if loop.first %}
                        {%- if content|trim %}
                            {{- '\n\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
                        {%- else %}
                            {{- '<tool_call>\n<function=' + tool_call.name + '>\n' }}
                        {%- endif %}
                    {%- else %}
                        {{- '\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
                    {%- endif %}
                    {%- if tool_call.arguments is defined and tool_call.arguments != '' %}
                        {%- for args_name, args_value in tool_call.arguments|items %}
                            {{- '<parameter=' + args_name + '>\n' }}
                            {%- set args_value = args_value | string if args_value is string else args_value | tojson | safe %}
                            {{- args_value }}
                            {{- '\n</parameter>\n' }}
                        {%- endfor %}
                    {%- endif %}
                    {{- '</function>\n</tool_call>' }}
                {%- endfor %}
            {%- endif %}
            {{- '<|im_end|>\n' }}
        {%- elif message.role == "tool" %}
            {%- if loop.previtem and loop.previtem.role != "tool" %}
                {{- '<|im_start|>user' }}
            {%- endif %}
            {{- '\n<tool_response>\n' }}
            {{- content }}
            {{- '\n</tool_response>' }}
            {%- if not loop.last and loop.nextitem.role != "tool" %}
                {{- '<|im_end|>\n' }}
            {%- elif loop.last %}
                {{- '<|im_end|>\n' }}
            {%- endif %}
        {%- else %}
            {{- raise_exception('Unexpected message role.') }}
        {%- endif %}
    {%- endfor %}
    {%- if add_generation_prompt %}
        {{- '<|im_start|>assistant\n' }}
        {%- if enable_thinking is not defined or enable_thinking is false %}
            {{- '<think>\n\n</think>\n\n' }}
        {%- else %}
            {{- '<think>\n' }}
        {%- endif %}
    {%- endif %}
    

    对单条 system 的普通请求(dsh/Obsidian),本模板渲染结果与原版完全一致,已实测无影响。

    5. 思考模式开关(原理 + 各端表现)

    为什么需要:Qwen3.8 默认开思考,会在隐藏通道(reasoning_content)先生成大量思考内容再输出正文。实测 Obsidian 总结时思考多达 1.5万~4.4万 token,表现为"点总结后长时间空白,最后才一次性吐正文"。

    关思考 = 双保险:

    1. --chat-template-kwargs "{\"enable_thinking\":false}" — 模板层显式关闭(压制 llama-server 自动注入的 true)
    2. --logit-bias 248068-100 — 物理封禁 </think> token(Qwen3.8 词表 ID 248068),模型想思考也吐不出标记

    修复前后实测(模拟 Obsidian 总结请求):

    状态 reasoning_chars 说明
    修复前 142+ 先思考再出正文,慢
    修复后 0 直接出正文,133-194 token 秒回

    各端表现:

    场景 dsh Obsidian Codex
    思考关(默认) 不思考,直接出结果 不思考,总结秒出 自带思考参数,行为待实测(见第 9 节)
    思考开 思考 思考(总结变慢) 思考

    排障要点:

    • --logit-bias 格式是连字符 248068-100,写成 248068:-100 报错
    • PowerShell 脚本传这两个参数必须用单字符串(见第 6 节),用数组会拆碎 JSON 引号,参数静默失效(服务照常启动,极难察觉)
    • 批处理坑:if 块内 set 的值里不要用括号(set x=ON (reasoning) 的尾部 ) 会被解析器吞掉)

    6. restart_llama.ps1(AI 无人值守重启,完整内容)

    AI 助手用来把服务恢复到"UD 模型 + 128K + 关思考"状态。通过状态文件轮询,避免长 HTTP 调用被终端工具杀掉。

    $ErrorActionPreference = 'Stop'
    Set-Content C:\llama\restart_status.txt 'step1-killing'
    Get-Process llama-server -ErrorAction SilentlyContinue | Stop-Process -Force
    Start-Sleep -Seconds 3
    Set-Content C:\llama\restart_status.txt 'step2-starting'
    $server = 'C:\llama\llama-hip\llama-server.exe'
    $argline = '-m C:\model\Qwen3.8-27B-UD-Q4_K_XL.gguf -c 131072 -ngl 99 -n -1 -fa on --no-mmap ' +
               '--cache-type-k q4_0 --cache-type-v q4_0 --parallel 1 -b 512 -ub 256 ' +
               '--chat-template-file C:\llama\qwen38_tolerant.jinja ' +
               '--chat-template-kwargs "{\"enable_thinking\":false}" ' +
               '--logit-bias 248068-100 ' +
               '--log-file C:\llama\server.log --host 0.0.0.0 --port 8080'
    Start-Process -FilePath $server -ArgumentList $argline -WindowStyle Minimized
    Set-Content C:\llama\restart_status.txt 'step3-waiting'
    $ok = $false
    foreach ($i in 1..60) {
        Start-Sleep -Seconds 5
        try {
            $r = Invoke-RestMethod 'http://127.0.0.1:8080/props' -TimeoutSec 3
            if ($r.model_path -like '*UD-Q4_K_XL*') { $ok = $true; break }
        } catch {}
    }
    if ($ok) { Set-Content C:\llama\restart_status.txt 'done-ready' } else { Set-Content C:\llama\restart_status.txt 'timeout' }
    

    核心教训:Start-Process -ArgumentList 用数组传参会把 "{\"enable_thinking\":false}" 的引号拆碎 → 参数静默丢失。必须用单字符串 $argline。

    7. Codex 桌面版对接(codex-launcher.ps1 + Codex启动.bat)

    Codex 桌面版为 MSIX 包 OpenAI.Codex(26.820.9563.0),CLI 版已卸载(释放 385MB C 盘),但 ~/.codex 配置目录必须保留(桌面版依赖)。

    Codex启动.bat(桌面):

    @echo off
    chcp 65001 >nul
    :: Codex launcher: reuse the single llama-server on :8080 (no second server).
    :: Model switching happens in the llama shortcut; this only aligns + opens Codex.
    powershell -NoProfile -ExecutionPolicy Bypass -File "C:\llama\codex-launcher.ps1"
    if errorlevel 1 pause
    

    codex-launcher.ps1(完整内容):

    # Codex launcher: align Codex with the single shared llama-server on :8080.
    # - Does NOT create a second server. It reuses the one started by start.bat.
    # - If the running server lacks the Codex-compat chat template, it restarts
    #   THE SAME server with the SAME model & context (adds --chat-template-file).
    # - Syncs config.toml "model" to whatever gguf is currently loaded.
    # - Model switching is done via the desktop llama shortcut (start.bat).
    
    $ErrorActionPreference = 'Stop'
    $server   = 'C:\llama\llama-hip\llama-server.exe'
    $template = 'C:\llama\qwen38_tolerant.jinja'
    $log      = 'C:\llama\server.log'
    $codexDir = 'C:\test'
    $cfg      = Join-Path $env:USERPROFILE '.codex\config.toml'
    
    function Wait-Ready([int]$maxSec) {
        $deadline = (Get-Date).AddSeconds($maxSec)
        while ((Get-Date) -lt $deadline) {
            try {
                $r = Invoke-WebRequest -UseBasicParsing 'http://127.0.0.1:8080/health' -TimeoutSec 5
                if ($r.StatusCode -eq 200) { return $true }
            } catch { }
            Start-Sleep -Seconds 5
        }
        return $false
    }
    
    # ── 1) probe the running server ──────────────────────────────────────────────
    $modelPath = $null
    $ctx = 65536
    $fixed = $false
    try {
        $mm = Invoke-RestMethod 'http://127.0.0.1:8080/v1/models' -TimeoutSec 3
        $modelPath = $mm.data[0].id
        if ($mm.data[0].meta -and $mm.data[0].meta.n_ctx) { $ctx = [int]$mm.data[0].meta.n_ctx }
        $p = Invoke-RestMethod 'http://127.0.0.1:8080/props' -TimeoutSec 3
        if ($p.chat_template -match 'merged_sys') { $fixed = $true }
        if ($p.default_generation_settings -and $p.default_generation_settings.n_ctx) { $ctx = [int]$p.default_generation_settings.n_ctx }
    } catch { }
    
    if (-not $modelPath) {
        Write-Host ''
        Write-Host '[X] llama-server 未运行 (8080)。'
        Write-Host '    请先双击桌面「启动llama」选择模型启动,然后再运行「Codex启动」。'
        exit 1
    }
    
    Write-Host "llama-server 在线: $modelPath (ctx=$ctx)"
    
    # ── 2) ensure Codex-compat template (restart SAME server if missing) ─────────
    if (-not $fixed) {
        Write-Host "当前服务缺少 Codex 兼容模板,用同一模型/上下文($ctx)重启(约1-2分钟)..."
        Get-Process llama-server -ErrorAction SilentlyContinue | Stop-Process -Force
        Start-Sleep -Seconds 3
        Start-Process -WindowStyle Minimized -FilePath $server -ArgumentList @(
            '-m', $modelPath, '-c', "$ctx", '-ngl', '99', '-n', '-1', '-fa', 'on', '--no-mmap',
            '--cache-type-k', 'q4_0', '--cache-type-v', 'q4_0', '--parallel', '1',
            '-b', '512', '-ub', '256',
            '--chat-template-file', $template,
            '--log-file', $log, '--host', '0.0.0.0', '--port', '8080'
        )
        if (-not (Wait-Ready 300)) { Write-Host '[X] llama-server 重启超时,请手动检查。'; exit 1 }
        Write-Host 'llama-server 已就绪(带 Codex 兼容模板)。'
    } else {
        Write-Host '服务已带 Codex 兼容模板,直接复用(不重启)。'
    }
    
    # ── 3) sync config.toml model = currently loaded gguf ────────────────────────
    $slug = ([IO.Path]::GetFileNameWithoutExtension($modelPath)).ToLower().Replace('_', '')
    $t = [IO.File]::ReadAllText($cfg)
    $t = [regex]::Replace($t, '(?m)^model\s*=\s*".*?"', ('model = "' + $slug + '"'))
    [IO.File]::WriteAllText($cfg, $t, (New-Object Text.UTF8Encoding($false)))
    Write-Host "Codex 模型已对齐当前加载的模型: $slug"
    
    # ── 4) open Codex desktop app (fallback: CLI in terminal) ────────────────────
    $app = Get-AppxPackage -Name 'OpenAI.Codex' -ErrorAction SilentlyContinue
    if ($app) {
        Write-Host '打开 Codex 桌面版...'
        Start-Process ('shell:AppsFolder\' + $app.PackageFamilyName + '!App')
    } else {
        Write-Host '未找到 Codex 桌面版 (OpenAI.Codex)。CLI 版已于 2026-08-28 卸载。'
        Pause
    }
    exit 0
    

    ~/.codex/config.toml 关键行(其余为默认/插件配置,首次启动应用内登录一次):

    model = "qwen3.8-27b-q4km"          # 「Codex启动」会自动同步为当前加载的 gguf
    model_provider = "llama-local"
    preferred_auth_method = "apikey"
    forced_login_method = "api"
    model_catalog_json = 'C:\Users\Administrator\.codex\model_catalog.json'
    model_reasoning_effort = "low"
    
    [model_providers.llama-local]
    name = "llama-local (本地 llama-server :8080)"
    base_url = "http://127.0.0.1:8080/v1"
    wire_api = "responses"                # 必须!Codex 0.150+ 已删除 chat 通道
    experimental_bearer_token = "llama-local"
    

    model_catalog.json 中注册上面两个本地 slug(照抄表格里的 id/displayName 即可)。

    日志里 unsupported Responses tool type 'custom'/'namespace'/'web_search' skipped 属正常(llama.cpp 跳过不支持的工具类型,不影响使用)。

    8. dsh 与 Obsidian Copilot 配置

    dsh(C:\Users\Administrator\.dsh\settings.yaml,热重载,改完不用重启)

    llm-pi-ai:
      providers:
        llama-local:
          displayName: llama-server (本地 Qwen3.8-27B)
          api: openai-completions
          baseURL: http://127.0.0.1:8080/v1
          apiKeyEnv: LLAMA_LOCAL_API_KEY
          timeoutMs: 600000              # 本地推理慢,放宽超时防首 token 前被判超时
          streamIdleTimeoutMs: 600000
          defaultContextWindow: 65536
          defaultMaxTokens: 16384
          compat:                        # 关掉 llama-server 读不懂的 OpenAI 新字段
            supportsStore: false
            supportsDeveloperRole: false
            supportsReasoningEffort: false
            supportsStrictMode: false
            supportsUsageInStreaming: true
            maxTokensField: max_tokens
          models:
            - id: qwen3.8-27b-q4km
              name: Qwen3.8-27B Q4_K_M (本地)
              contextWindow: 65536
              maxTokens: 16384
            - id: qwen3.8-27b-ud-q4kxl
              name: Qwen3.8-27B UD-Q4_K_XL (本地)
              contextWindow: 65536
              maxTokens: 16384
    
    agent-default-model:
      provider: llama-local
      model: qwen3.8-27b-q4km            # 启动 UD 时在 /model 选择器里切换
    

    .credentials.yaml(占位 key,llama-server 不校验鉴权):

    version: 1
    refs:
      LLAMA_LOCAL_API_KEY: sk-no-key-needed
    

    Obsidian Copilot(E:\ObsidianVault\.obsidian\plugins\copilot\data.json 要点)

    • provider:openai-compatible,baseUrl: http://localhost:8080/v1,displayName "Llama",requiresApiKey true(占位 key 存 keychain)
    • 模型只保留两个本地:qwen3.8-27b-ud、qwen3.8-27b-q4km(activeModels / configuredModels / backends.chat.enabledModels / backends.opencode.enabledModels 四处都要有)
    • "contextTurns": 0(关键!总结不携带历史对话)
    • "activeBackend": "chat"(绝不用 opencode 后端:会自动累积历史,上下文越大越慢,O(n²) 卡死)
    • "temperature": 0.1,"autoCompactThreshold": 128000
    • 总结长度限制(500 字 / 3-5 要点)写在其系统提示词里

    9. 踩坑清单(按血泪程度排序,每条都注明正确做法)

    1. --verbose 毁掉流式响应:终端高频输出干扰 HTTP 流,客户端报 TRANSPORT 错误、任务失败。✅ 正确做法:--log-file C:\llama\server.log
    2. Vulkan 后端抢跑:ggml-vulkan.dll 存在时与 ROCm 冲突。✅ 改名 .bak(start.bat 启动时自动检查)
    3. llama-server 注入 enable_thinking=true:即使模板默认关思考,服务端也会注入 true 覆盖。✅ 必须 --chat-template-kwargs "{\"enable_thinking\":false}" 显式压制
    4. PowerShell 数组传参拆碎 JSON 引号:--chat-template-kwargs 静默失效,服务照常启动。✅ 用单字符串 $argline
    5. logit-bias 用冒号报错:✅ 格式为 248068-100(连字符)
    6. 原版模板拒绝多条 system 消息:Codex 每请求 3 条 developer 消息 → 400 "System message must be at the beginning"。✅ 容错模板合并
    7. llama.cpp /v1/responses 转换器 bug(GitHub issue #26394 / PR #27751):特定 input 形状报 400 "Cannot determine type of 'item'"。✅ 真实 Codex 载荷能通过;别用手工合成的最小化请求下结论,要看真实客户端流量
    8. KV 缓存爆显存:q8_0 KV 在 32K 就要 8.6GB,27B 模型 + 长文档必翻车。✅ q4_0 减半(4.3GB@32K)
    9. 同时只能跑一个 llama-server:8080 端口 + 显存双重冲突,残留实例曾吃 6.2GB 内存。✅ 换模型流程:先关窗口 → 再「启动llama」重选;排障先查进程数
    10. model 字段被忽略:llama-server 只认加载的 gguf。✅ 各端 UI 选的模型必须与加载的 gguf 对齐(换模型后 dsh 用 /model 选,Codex 双击「Codex启动」自动同步,Copilot 设置里选)
    11. 批处理括号吞字:if 块内 set 值含括号会被解析器吃掉尾部 )。✅ 值里用短横线代替括号
    12. opencode 后端 O(n²) 卡顿:Obsidian Copilot 用 opencode 后端会自动累积历史。✅ 用 chat 后端 + contextTurns: 0
    13. 隐藏思考拖慢总结:总结慢先查 reasoning_content 长度,别误判为历史记录问题(有 contextTurns: 0 在,历史根本不参与)
    14. MTP 投机解码不适合长文档总结:--spec-type draft-mtp 草稿接受率低、频繁回溯,反而更慢。✅ 不用
    15. dsh 输出 token 限制报错:maxTokens 太小。✅ settings.yaml 热重载改 maxTokens: 16384、contextWindow: 65536,无需重启
    16. AI 助手操作教训(给恢复配置的 AI 看):
      • 超过 ~20 秒的 HTTP 调用会被终端工具杀掉 → 用分离进程 + 轮询状态文件(restart_llama.ps1 模式)
      • PowerShell 输出捕获偶发丢失 → 关键结论以文件落盘为准,勿轻信"无输出"
      • 长流式响应要限制读取窗口,否则流内 400 事件会被误判为成功

    10. 健康检查与验证

    # 服务是否在线 + 模板是否生效(chat_template 含 merged_sys = 容错模板已加载)
    Invoke-RestMethod http://127.0.0.1:8080/props
    # 当前加载的模型
    Invoke-RestMethod http://127.0.0.1:8080/v1/models
    # 日志
    Get-Content C:\llama\server.log -Tail 50
    

    思考开关验证:Obsidian 里发起文档总结 → 正文应立即流式输出(关思考),或先输出思考再出正文(开思考)。

    11. 重装系统恢复清单(按顺序执行,AI 可照此自动完成)

    1. 装 AMD Adrenalin(驱动),7900 XTX 调优:功耗 -10%、电压 -65mV
    2. 装 llama.cpp HIP 版到 C:\llama\llama-hip\(Releases 的 win-hip-x64 包);若有 ggml-vulkan.dll 改名 .bak
    3. 模型放 C:\model\(两个 Qwen3.8-27B gguf)
    4. 按本文第 3/4/6/7 节创建 start.bat、qwen38_tolerant.jinja、restart_llama.ps1、codex-launcher.ps1
    5. 桌面建快捷方式:启动llama.lnk → C:\llama\start.bat(工作目录 C:\llama);Codex启动.bat 按第 7 节
    6. 装 Codex 桌面版(MSIX,首次启动应用内登录),配置 ~/.codex/config.toml 关键行 + model_catalog.json 注册两个本地 slug
    7. dsh:settings.yaml(第 8 节)+ .credentials.yaml 占位 key
    8. Obsidian Copilot:data.json 按第 8 节要点配置
    9. 双击「启动llama」→ 选模型 → 选 64K → 回车(关思考)→ 用第 10 节验证三端

    12. 已知未验证项(诚实声明)

    • 思考关(默认)模式下的 Codex 桌面版:Codex 自带思考参数,而服务端此时封死了 </think> token,两者叠加的行为未实测。若 Codex 输出异常,临时方案是启动时选"开思考",或去掉 --logit-bias 只靠模板关思考
    • 模板与 token ID(248068)为 Qwen3.8 系列专属,其他模型家族需另配模板与思考标记 ID

    整理日期:2026-08-28。所有参数均经实测验证,标注"待实测"的除外。

    1 条回复 最后回复
    0
    • yi songY 离线
      yi songY 离线
      yi song
      编写于 最后由 编辑
      #2

      全文自包含(论坛读者/AI 不需要任何本地上下文就能恢复),12 个章节:

      章节 内容
      1 7900 XTX 独占逻辑:HIP 构建结构性独占(不是参数过滤)、Vulkan dll 封印、Adrenalin 降压设置
      2 目录结构 + 两个模型的四端 slug 对应表
      3 start.bat 完整内容 + 每个启动参数的理由表
      4 qwen38_tolerant.jinja 完整内容(170 行全量,重装不丢)+ 3 处修改说明
      5 思考开关原理(双保险)、修复前后实测数据、各端表现表、批处理/PowerShell 坑
      6 restart_llama.ps1 全量 + 单字符串传参教训
      7 Codex 对接:launcher 全量、config.toml 关键行、wire_api="responses" 必须性
      8 dsh settings.yaml + Obsidian data.json 完整要点
      9 踩坑清单 16 条,每条都注明 ✅ 正确做法
      10 健康检查命令
      11 重装系统恢复清单(9 步顺序,AI 可照做)
      12 诚实声明:Codex 关思考模式待你下次实测,模板/token ID 为 Qwen 专属
      核对时确认过的关键事实都已写入:HIP 构建 commit be4a6a6(Clang 23)、KV 缓存 4.3GB@32K 实测数据、Codex slug 同步规则(文件名小写去下划线)、占位 key sk-no-key-needed 等。

      1 条回复 最后回复
      0
      • yi songY 离线
        yi songY 离线
        yi song
        编写于 最后由 编辑
        #3

        有类似需求就甩给AI来配置。我发这个贴是为了以后重装系统把这个甩给AI。
        也跟大家分享一下这三天遇到的各种问题。

        1 条回复 最后回复
        0
        • terryT 在线
          terryT 在线
          terry
          超级版主
          编写于 最后由 编辑
          #4

          😢,直接AI自己去整理就好了,不过非常好的分享。我都是让Codex和Hermes来配置的。

          油管:https://www.youtube.com/@抡锤者

          1 条回复 最后回复
          0

          你好!看起来您对这段对话很感兴趣,但您还没有一个账号。

          厌倦了每次访问都刷到同样的帖子?您注册账号后,您每次返回时都能精准定位到您上次浏览的位置,并可选择接收新回复通知(通过邮件或推送通知)。您还能收藏书签、为帖子顶,向社区成员表达您的欣赏。

          有了你的建议,这篇帖子会更精彩哦 💗

          注册 登录
          回复
          • 在新帖中回复
          登录后回复
          • 从旧到新
          • 从新到旧
          • 最多赞同


          • 登录

          • 登录或注册以进行搜索。
          • 第一个帖子
            最后一个帖子
          0
          • 版块
          • 最新
          • 标签
          • 热门
          • 用户
          • 群组