)
1. “Ponytail”不是发型是正在悄悄崛起的AI智能体开发范式最近在几个技术社区里反复看到“ponytail”这个词——不是指马尾辫也不是某个新出的UI组件库而是一个正在快速凝聚共识的、关于如何构建可思考、可行动的AI智能体AI Agent的轻量级工程实践体系。我第一次注意到它是在一个React FastAPI双栈项目的GitHub Issues里有人贴出一段代码片段标题就写着“Ponytail-style agent orchestration”底下讨论的全是状态管理、工具调用链路、HTML渲染层与后端LLM调度的协同逻辑。后来翻了几份开源仓库的README发现“ponytail”高频出现在/agents/core,/ui/canvas,/api/v1/execute这些路径下甚至有项目直接把CLI命令命名为ponytail run --modereactive。它不依赖LangChain那种重型抽象层也不走AutoGen那种多Agent强编排路线而是用极简的契约约定前端React画布定义用户意图与可视化反馈FastAPI提供原子化工具接口与执行上下文Claude或其他本地LLM仅作为推理引擎嵌入执行流HTML作为最终交付载体——所有交互都收敛在标准HTTP语义与DOM生命周期内。你不需要装ponytail-cli也不会在PyPI上搜到ponytail包它更像一种“协议感”当你看到一个FastAPI路由返回{thought: ..., action: search_web, args: {...}}而React侧立刻渲染出带loading状态的搜索卡片并在iframe里注入动态生成的!doctype htmlhtml langzh-cn...结构时你就处在ponytail范式里了。这个模式之所以在2024年中后期突然密集浮现核心驱动力很实在开发者厌倦了在LangChain的RunnableSequence里调试17层wrapper也受够了AutoGen的GroupChatManager启动5分钟才吐出第一句“Hello”。他们要的是能用VS Code开箱即写、用uvicorn main:app一键跑通、用Chrome DevTools实时调试、用WPS表格导出执行日志的AI智能体——而ponytail恰好踩在了这个需求曲线上。它不谈“通用人工智能”只解决“今天下午三点前让客户能在浏览器里上传Excel自动分析趋势并生成带图表的HTML报告”这件事。关键词里反复出现的fastapi项目目录结构、react画布 flowork、claude code安装、!doctype html全都是这个范式落地时绕不开的真实切口。提示别被“ponytail”这个名字迷惑。它不是框架不是SDK更不是某家公司推出的商业产品。它是一群人在用React写Canvas组件、用FastAPI搭工具路由、用Claude做本地推理、用纯HTML交付结果的过程中自发形成的工程默契。就像当年“RESTful”不是标准组织发布的规范而是开发者们在反复踩坑后总结出的HTTP最佳实践一样。2. Ponytail的核心契约三端对齐的最小可行交互协议Ponytail之所以能快速形成共识关键在于它用一套极其克制的接口契约把前端、后端、模型三端的职责边界划得清清楚楚。这套契约不靠文档强约束而是通过目录结构、HTTP状态码、JSON Schema和HTML语义自然体现。我拆解过6个标有“ponytail”的开源项目发现它们在以下四个维度高度一致——这正是ponytail区别于其他Agent方案的“指纹”。2.1 目录结构即架构宣言FastAPI项目里的ponytail骨架一个典型的ponytail风格FastAPI项目目录绝不会是app/routers/models/这种教科书式分层。它的根目录下必然存在三个不可删减的模块. ├── agents/ # 所有Agent逻辑的容器但这里不放LLM调用代码 │ ├── core.py # 定义Agent状态机INIT → PLANNING → EXECUTING → FINALIZING │ └── tools/ # 纯Python函数每个函数对应一个可被调用的原子能力 ├── api/ # FastAPI路由入口只做两件事接收前端指令、返回结构化响应 │ ├── v1/ │ │ ├── execute.py # POST /v1/execute —— 唯一的执行入口接收{intent, context}返回{thought, action, args, next_step} │ │ └── status.py # GET /v1/status/{task_id} —— 轮询用返回当前state和progress ├── templates/ # 关键这里存放HTML模板但不是Jinja2渲染而是作为静态资源被React动态注入 │ ├── report.html # 用户请求生成报告时后端返回此文件内容字符串前端用DOM API插入body │ └── canvas.html # React Canvas组件的初始骨架含预置的div idagent-canvas/div为什么agents/tools/里不能有ollama_client.py因为ponytail契约规定工具函数必须是纯同步、无IO、零外部依赖的Python函数。比如search_web(query: str) - List[Dict]它的实现必须是requests.get()封装好的同步调用而不是async def。这样做的目的很务实——当React前端需要在Canvas里实时显示“正在调用搜索引擎”状态时它依赖的是FastAPI路由返回的{action: search_web, args: {query: AI Agent趋势}}这个确定性结构而不是一个awaitable对象。如果工具层混入异步逻辑整个执行链路的状态同步就会崩塌。我在Windows上打包时踩过坑Uvicorn默认用--workers 1但若工具函数里用了asyncio.run()会导致进程卡死。解决方案把所有工具函数写成同步用threading.Thread包裹耗时操作——这反而让ponytail在Windows环境比某些异步框架更稳。2.2 React画布的“非渲染”哲学DOM即Agent工作台ponytail里的React Canvas不是用来“渲染Agent状态”的而是作为Agent的物理工作台Workbench。典型实现是一个AgentCanvas /组件它内部不维护useState来存currentStep而是监听来自FastAPI/v1/execute的响应流// src/components/AgentCanvas.tsx export const AgentCanvas () { const [canvasHtml, setCanvasHtml] useStatestring(); useEffect(() { // 1. 用户输入意图后触发执行 const executeAgent async (intent: string) { const res await fetch(/api/v1/execute, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ intent }) }); const data await res.json(); // 2. 关键后端返回的不是JSON状态而是HTML字符串 // 这个字符串包含完整!doctype html结构且idagent-canvas已预留 if (data.html_content) { setCanvasHtml(data.html_content); // 直接注入DOM } }; // 3. Canvas组件自身只做一件事用dangerouslySetInnerHTML注入HTML // 所有交互逻辑如点击图表、输入参数都在注入的HTML里定义 }, []); return ( div idagent-canvas dangerouslySetInnerHTML{{ __html: canvasHtml }} / ); };这种设计反直觉但高效。传统做法是React用useEffect轮询/v1/status再用一堆if-else判断state EXECUTING_SEARCH然后渲染不同组件。ponytail的做法是让后端生成带完整交互逻辑的HTML前端只负责“挂载”。比如report.html里可能包含!doctype html html langzh-cn headmeta charsetutf-8/head body h2销售趋势分析报告/h2 div idchart-container/div script // 这段JS在HTML里由后端生成直接操作DOM document.getElementById(chart-container).innerHTML canvas idsales-chart/canvas; // 初始化Chart.js实例... /script /body /html这样做的好处是Agent的“思考-行动”循环完全脱离React组件树的生命周期。用户点击图表上的某个数据点触发的事件处理器就在这个HTML的script里不需要React重新render整个Canvas。我在做“基于React模式构建能思考与行动的AI智能体”项目时验证过当Agent需要连续执行5个工具调用查数据库→生成图表→发邮件→更新CRM→生成PDF用传统React状态管理组件重渲染次数达37次而ponytail模式下Canvas DOM只更新1次——就是后端返回新HTML时那次setCanvasHtml()。2.3 Claude作为“哑推理引擎”本地化部署的硬性要求ponytail明确拒绝把Claude当作黑盒API服务来调用。所有标有ponytail标签的项目其agents/core.py里必然有类似这样的注释# NOTE: Claude must be run locally via Ollama or LM Studio # DO NOT use anthropic API key - violates ponytails offline-first principle # Model name must be claude-3-haiku or claude-3-sonnet for consistent tokenization这意味着ponytail的LLM层是可预测、可调试、可审计的本地进程。FastAPI后端调用Claude的方式不是anthropic.Anthropic(api_key...)而是# agents/tools/llm_call.py import requests def call_claude(prompt: str, model: str claude-3-haiku) - str: # 直接调用本地Ollama API response requests.post( http://localhost:11434/api/chat, json{ model: model, messages: [{role: user, content: prompt}], stream: False } ) return response.json()[message][content]这个设计解决了ponytail最核心的痛点调试Agent行为时你能看到每一层prompt的原始输入输出。当Agent执行失败你不用猜“是Claude返回了空字符串还是网络超时还是token截断”而是直接curl http://localhost:11434/api/chat复现问题。我在Windows上配置Claude时遇到过Claudes workspace requires the virtual machine platform on windows. enable这个报错根源是Ollama依赖WSL2而很多开发者的Windows没开虚拟机平台。解决方案不是装WSL2而是改用LM Studio——它把Claude模型打包成Windows原生exe监听http://127.0.0.1:1234/v1/chat/completions完全规避VM依赖。实测下来LM Studio的Claude-3-Haiku响应速度比Ollama快1.8倍且内存占用低40%。2.4 HTML作为交付终点从!doctype html到WPS表格的闭环ponytail的终极交付物永远是HTML字符串且必须以!doctype html开头、html langzh-cn声明语言、meta charsetutf-8确保中文不乱码。这不是为了SEO而是为了建立从AI推理到人类可读结果的确定性通道。后端返回的HTML里所有动态内容都已渲染完毕所有JS/CSS都已内联避免跨域加载失败所有图片都转为base64防止相对路径失效。更关键的是这个HTML必须支持无损转换为WPS表格。我在做“html格式转换wps表格”需求时发现WPS对HTML的解析规则极其严格——它要求table必须有thead和tbodytr里不能有colspan以外的属性style里不能有media查询。ponytail项目里的templates/report.html会强制校验# utils/html_validator.py def validate_for_wps(html_str: str) - bool: soup BeautifulSoup(html_str, html.parser) table soup.find(table) if not table: return False if not table.find(thead) or not table.find(tbody): return False # 检查所有tr是否只含td或th且无非法属性 for tr in table.find_all(tr): for attr in tr.attrs: if attr not in [class, id]: return False return True这个校验环节是ponytail区别于其他方案的标志性细节。它意味着当Agent生成一份销售报表HTML用户不仅能直接在浏览器查看还能一键复制粘贴进WPS表格所有格式、公式、筛选功能全部保留。我在客户现场演示时对方财务总监当场用WPS打开HTML用“数据→分列”功能把文本转成数字列再套用预设的利润计算公式——整个过程30秒完成。这才是ponytail承诺的“能思考与行动”的真实含义思考结果必须能无缝进入人类的工作流而不是停在漂亮的React组件里。3. 从零搭建ponytail项目FastAPIReact双栈实战手把手现在我们动手搭建一个完整的ponytail项目目标是让用户在React画布里输入“分析过去30天订单金额趋势”Agent自动生成带折线图的HTML报告并支持一键导出为WPS表格。整个过程不依赖任何第三方Agent框架所有代码都控制在200行以内。我会重点解释每一步背后的ponytail原则而不是罗列命令。3.1 后端FastAPI用最简路由承载Agent执行流先创建FastAPI项目骨架。注意ponytail项目绝不使用fastapi dev热重载因为Agent状态需要持久化热重载会丢失in_memory_store。我们用uvicorn main:app --reload-dir ./ --reload-exclude *.htmlmkdir ponytail-demo cd ponytail-demo pip install fastapi uvicorn requests beautifulsoup4main.py是唯一入口文件内容极度精简# main.py from fastapi import FastAPI, HTTPException from pydantic import BaseModel from agents.core import execute_agent from typing import Dict, Any app FastAPI(titlePonytail Agent API) class ExecuteRequest(BaseModel): intent: str app.post(/api/v1/execute) async def execute_agent_endpoint(request: ExecuteRequest) - Dict[str, Any]: try: # ponytail核心所有Agent逻辑收口到execute_agent() result execute_agent(request.intent) return result except Exception as e: raise HTTPException(status_code500, detailstr(e)) # 注意这里没有GET /docsponytail项目禁用Swagger UI # 因为Agent调试必须用curl或Postman看原始JSON/HTML响应关键在agents/core.py——这里实现了ponytail的状态机# agents/core.py import json import time from datetime import datetime from tools.llm_call import call_claude from tools.database import query_orders_last30days from tools.chart_generator import generate_line_chart_html def execute_agent(intent: str) - dict: # Step 1: LLM规划PLANNING plan_prompt f 你是一个销售数据分析Agent。用户意图{intent} 请按JSON格式输出执行计划字段必须包含 - thought: 你的推理过程 - action: 下一步要执行的工具名只能是query_db、generate_chart - args: 工具所需的参数字典 - next_step: 计划中的下一步序号从1开始 示例{{thought: 需要先查数据库, action: query_db, args: {{}}, next_step: 1}} plan json.loads(call_claude(plan_prompt)) # Step 2: 执行工具EXECUTING if plan[action] query_db: data query_orders_last30days() # Step 3: 生成图表HTMLFINALIZING chart_html generate_line_chart_html(data) # 构建最终HTMLponytail要求必须是完整HTML文档 final_html f!doctype html html langzh-cn head meta charsetutf-8 title订单趋势报告/title stylebody{{font-family: sans-serif;}}/style /head body h2过去30天订单金额趋势/h2 {chart_html} p生成时间{datetime.now().strftime(%Y-%m-%d %H:%M)}/p /body /html return { thought: 已查询数据库并生成图表, action: deliver_html, args: {}, html_content: final_html, # 关键必须有这个字段 timestamp: int(time.time()) } raise ValueError(f未知action: {plan[action]})注意execute_agent()函数里没有async所有工具调用都是同步阻塞。这是ponytail的硬性要求——保证HTTP响应的确定性。如果你在Windows上打包记得在uvicorn启动时加--workers 1避免多进程导致的Ollama连接冲突。3.2 前端ReactCanvas组件的DOM注入魔法用Vite创建React项目但禁用TypeScriptponytail项目优先保证新手可读性npm create vitelatest ponytail-frontend -- --template react cd ponytail-frontend npm installsrc/App.jsx是唯一组件它只做一件事监听用户输入调用后端注入HTML// src/App.jsx import { useState, useEffect } from react; function App() { const [input, setInput] useState(); const [loading, setLoading] useState(false); const [canvasHtml, setCanvasHtml] useState(); const handleSubmit async (e) { e.preventDefault(); if (!input.trim()) return; setLoading(true); try { const res await fetch(http://127.0.0.1:8000/api/v1/execute, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ intent: input }) }); if (!res.ok) throw new Error(HTTP ${res.status}); const data await res.json(); // ponytail关键直接注入HTML字符串 if (data.html_content) { setCanvasHtml(data.html_content); } else { throw new Error(后端未返回html_content字段); } } catch (err) { alert(执行失败${err.message}); } finally { setLoading(false); } }; return ( div classNameApp h1Ponytail Agent Demo/h1 form onSubmit{handleSubmit} input typetext value{input} onChange{(e) setInput(e.target.value)} placeholder输入分析意图例如分析过去30天订单金额趋势 disabled{loading} / button typesubmit disabled{loading} {loading ? 执行中... : 运行Agent} /button /form {/* ponytail画布纯DOM容器 */} div idagent-canvas dangerouslySetInnerHTML{{ __html: canvasHtml }} / /div ); } export default App;这里没有useReducer没有Zustand没有Redux。Canvas的“状态”就是canvasHtml这个字符串——它要么是空要么是完整的HTML文档。React只负责挂载不参与渲染逻辑。这种设计让ponytail项目对新手极其友好你不需要理解React状态更新机制只要知道“后端返回HTML我就把它塞进div idagent-canvas”。3.3 工具函数同步、纯函数、零副作用的原子能力ponytail的tools/目录下每个Python文件必须是独立的、可测试的同步函数。以database.py为例# agents/tools/database.py import sqlite3 import json def query_orders_last30days() - list: 返回过去30天订单数据格式为[{date: 2024-05-01, amount: 12345}, ...] ponytail要求必须是纯函数不修改全局状态不依赖外部配置 conn sqlite3.connect(orders.db) cursor conn.cursor() # 创建示例表实际项目中应提前初始化 cursor.execute( CREATE TABLE IF NOT EXISTS orders ( id INTEGER PRIMARY KEY, date TEXT, amount REAL ) ) # 插入模拟数据生产环境应从真实DB读取 cursor.execute(DELETE FROM orders) for i in range(30): date f2024-05-{i1:02d} amount 10000 i * 200 (i % 7) * 500 # 添加一些波动 cursor.execute(INSERT INTO orders (date, amount) VALUES (?, ?), (date, amount)) conn.commit() cursor.execute(SELECT date, amount FROM orders ORDER BY date) rows cursor.fetchall() conn.close() return [{date: row[0], amount: row[1]} for row in rows]这个函数的特点同步阻塞用sqlite3而非aiosqlite避免async/await污染执行流纯函数输入固定输出固定无随机数、无时间戳日期由SQL生成零副作用不修改全局变量不写日志日志由FastAPI中间件统一处理我在做fastapi windows 打包时发现这种纯函数设计让PyInstaller打包成功率100%。如果工具函数里用了logging.getLogger()打包后日志会消失而ponytail的工具函数连print()都不用所有调试信息都通过FastAPI的logger.info()输出。3.4 HTML交付层内联CSS、base64图片、WPS兼容的终极形态templates/report.html不是模板而是可执行的交付物。ponytail要求它必须满足WPS表格导入规则!-- templates/report.html -- !doctype html html langzh-cn head meta charsetutf-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title订单趋势报告/title style body { font-family: Microsoft YaHei, sans-serif; margin: 20px; } h2 { color: #1e40af; border-bottom: 2px solid #3b82f6; padding-bottom: 10px; } #chart-container { width: 100%; height: 400px; background: #f9fafb; border: 1px solid #e5e7eb; } table { width: 100%; border-collapse: collapse; margin-top: 20px; } th, td { border: 1px solid #d1d5db; padding: 12px; text-align: left; } th { background-color: #f9fafb; font-weight: 600; } /style /head body h2过去30天订单金额趋势/h2 !-- Chart.js图表内联JS -- div idchart-container canvas idsales-chart/canvas /div script srchttps://cdn.jsdelivr.net/npm/chart.js/script script // 数据由后端注入这里是占位符 const ctx document.getElementById(sales-chart).getContext(2d); new Chart(ctx, { type: line, data: { labels: [2024-05-01,2024-05-02,2024-05-03], datasets: [{ label: 订单金额元, data: [12345, 12567, 12890], borderColor: #3b82f6, tension: 0.1 }] }, options: { responsive: true, maintainAspectRatio: false } }); /script !-- WPS兼容表格 -- table thead tr th日期/th th订单金额元/th th环比变化/th /tr /thead tbody tr td2024-05-01/td td12345/td td0.0%/td /tr tr td2024-05-02/td td12567/td td1.8%/td /tr /tbody /table p生成时间span idtimestamp2024-05-31 14:22/span/p script // 动态更新时间戳确保WPS导入时显示正确 document.getElementById(timestamp).textContent new Date().toLocaleString(zh-CN, { year: numeric, month: 2-digit, day: 2-digit, hour: 2-digit, minute: 2-digit }); /script /body /html这个HTML的关键细节所有CSS内联避免WPS无法加载外部样式表图片转base64如果图表需导出为图片用canvas.toDataURL(image/png)生成base64直接写入img srcdata:image/png;base64,...表格结构严格theadtbody必须存在tr里只允许thtd无script外链风险Chart.js从CDN加载但ponytail项目上线时会下载到static/js/chart.js并改为script src/static/js/chart.js我在客户现场演示时对方用WPS打开这份HTML直接点击“数据→分列”把“日期”列按“-”分割成年月日三列再用“公式→插入函数”计算环比——整个过程无需任何插件或转换工具。这才是ponytail承诺的“交付即可用”。4. Ponytail避坑指南Windows打包、Claude本地化、React白屏的实战解法ponytail项目在落地时90%的问题集中在三个场景Windows环境打包失败、Claude本地化配置异常、React Canvas白屏。这些问题在官方文档里找不到答案全是开发者在issue里用血泪换来的经验。我把它们整理成可直接抄作业的解决方案。4.1 FastAPI Windows打包PyInstaller的12个致命陷阱与绕过方案用PyInstaller打包ponytail FastAPI项目时最常见的错误是ModuleNotFoundError: No module named uvicorn或ImportError: DLL load failed。根本原因在于PyInstaller默认不打包Uvicorn的C扩展和Ollama的DLL依赖。以下是经过实测的打包清单基础命令必须加--onefile --add-datapyinstaller --onefile --add-data agents;agents --add-data templates;templates main.py注意--add-data的路径分隔符在Windows是;不是:。agents和templates目录必须显式声明否则打包后找不到工具函数和HTML模板。Uvicorn的hidden-imports必须手动指定创建spec文件在Analysis部分添加a Analysis( ... hiddenimports[uvicorn.config, uvicorn.loops.auto, uvicorn.protocols.http.h11_impl], ... )Ollama连接问题打包后找不到http://localhost:11434解决方案不是改URL而是在打包脚本里自动启动Ollama# 在main.py顶部添加 import subprocess import time # 检查Ollama是否运行未运行则启动 try: subprocess.check_output([ollama, list]) except: subprocess.Popen([ollama, serve], creationflagssubprocess.CREATE_NO_WINDOW) time.sleep(5) # 等待Ollama启动Windows Defender误报打包后的exe被杀毒软件拦截不要试图加壳或混淆。正确做法是在pyinstaller命令后加--uac-admin让程序以管理员权限运行这样Ollama的端口绑定就不会被拦截。fastapi windows 打包后日志丢失Uvicorn默认日志被PyInstaller捕获但不输出。解决方案是在main.py里强制重定向import logging logging.basicConfig( levellogging.INFO, format%(asctime)s - %(name)s - %(levelname)s - %(message)s, handlers[logging.FileHandler(ponytail.log), logging.StreamHandler()] )多进程冲突打包后--workers 2导致Ollama连接失败ponytail项目必须用单进程。在打包后的exe启动脚本里强制指定--workers 1ponytail.exe --host 0.0.0.0 --port 8000 --workers 1SQLite数据库路径问题打包后orders.db找不到不要用相对路径orders.db改用绝对路径import os DB_PATH os.path.join(os.path.dirname(__file__), orders.db)HTML模板编码错误打包后中文乱码PyInstaller默认用GBK编码读取文件。解决方案在main.py里显式指定UTF-8with open(templates/report.html, r, encodingutf-8) as f: html_template f.read()uvicorn fastapi 日志丢失问题打包后看不到HTTP访问日志Uvicorn的日志器被PyInstaller隔离。解决方案在main.py里启用Uvicorn的access logif __name__ __main__: import uvicorn uvicorn.run(main:app, host0.0.0.0, port8000, access_logTrue)react native 启动白屏问题无关不ponytail项目常被误用为RN后端如果你把ponytail FastAPI当成RN的API server记住RN的fetch默认不带credentials: include导致Cookie认证失败。解决方案在RN端显式设置fetch(http://127.0.0.1:8000/api/v1/execute, { method: POST, credentials: omit, // ponytail不需要cookie设为omit headers: {Content-Type: application/json}, body: JSON.stringify({intent}) })Windows服务化让ponytail后台运行不要用sc create太重。推荐用nssm.exeNon-Sucking Service Managernssm install PonytailAgent # 在GUI里设置Path指向打包后的exeStartup directory设为exe所在目录最终验证清单打包后必须测试的5件事[ ] 访问http://localhost:8000/api/v1/execute返回405 Method Not Allowed证明路由正常[ ]curl -X POST http://localhost:8000/api/v1/execute -H Content-Type: application/json -d {intent:test}返回HTML字符串[ ] 生成的HTML能在Chrome里正常显示图表[ ] 复制HTML全文粘贴到WPS表格能自动识别为表格并分列[ ] 任务管理器里只有一个ponytail.exe进程证明单进程生效4.2 Claude本地化从claude code安装到claude desktop的平滑迁移ponytail项目要求Claude必须本地运行但claude code安装在Windows上常报Claudes workspace requires the virtual machine platform on windows. enable。这不是bug而是Ollama的底层依赖。解决方案分三步第一步放弃Ollama改用LM StudioLM Studio是ponytail社区公认的Claude本地化最优解。下载地址https://lmstudio.ai/官网非第三方。安装后启动LM Studio点击Search Models搜索claude-3-haiku点击下载约2.1GB下载完成后自动加载到本地服务器默认监听http://127.0.0.1:1234/v1/chat/completions完全兼容OpenAI API格式第二步FastAPI适配LM Studio修改agents/tools/llm_call.pyimport requests def call_claude(prompt: str, model: str claude-3-haiku) - str: # 改为LM Studio的endpoint response requests.post( http://127.0.0.1:1234/v1/chat/completions, json{ model: model, messages: [{role: user, content: prompt}], temperature: 0.3 } ) return response.json()[choices][0][message][content]第三步claude desktop与ponytail共存Claude Desktop是