要不要把大模型搬回本地

<p>用过云端大模型的人,大概都撞过两堵墙:一是账单——哪怕用最便宜的 DeepSeek,持续调用也是一笔固定支出;二是隐私——有些资料、有些数据,你并不想让它走出自己的电脑。</p> <p>于是问题自然浮现:能不能把大模型搬回本地,自己跑?</p> <h2 id="为什么-llm-需要本地化">为什么 LLM 需要本地化?</h2> <p><strong>第一是成本。</strong> 上云再便宜,也是持续的支出;本地一次投入硬件,之后调用几乎零边际成本。</p> <p><strong>第二是安全。</strong> 本地的一些资料、内容,本来就不便公开分享。数据不出本机,就是最彻底的「不外传」。</p> <p><strong>第三是开放。</strong> 有些时候,我们想利用大语言模型没有围栏的那些特性,让它的输出尺度更宽、内容更开放。这一点,云端服务很难满足。</p> <p>除此之外,从工程角度看,使用本地的模型既可以作为断网情况下的一种保障方案,也可以在不改变工作流程的情况下,把本地模型作为一个测试开发环境。</p> <h2 id="量化">「量化」</h2> <p>本地跑大模型的第一个坎,是内存。</p> <p>大模型最初是用 16 位浮点数训练的。一个 70 亿参数(7B)的 16 位模型,光是加载进内存,就要大约 14GB。</p> <p>所以你在下载模型时,会看到 <code>Q4_K_M</code>、<code>Q8_0</code> 这样的标签——这就是量化。Q4 把权重压缩到 4 位,内存需求直接减半甚至更多。还是那个 7B 模型,它的 <code>Q4_K_M</code> 版本,只需要约 4.5GB 的 RAM。</p> <p>量化通过牺牲部分精度,换取了将大模型部署在普通电脑上的可能。虽然模型在准确性上,和云上的所谓“满血版”相比,会稍有折扣,但在本地场景下完全具备实用价值。</p> <h2 id="本地部署大模型的流程">本地部署大模型的流程</h2> <h4 id="第一步评估本地硬件与模型的适配程度">第一步:评估本地硬件与模型的适配程度</h4> <p>技术上的可行,不代表日常的可用。在本地开始使用某个大语言模型之前,建议先进行基准测试。在本地机器上运行以下脚本,若聊天模型的每秒词元数(Tokens/sec)低于 15,则建议更换更小的模型或更高的量化级别。运行该脚本后,将输出首个词元延迟(TTFT)、总词元数以及每秒词元数:</p> <pre tabindex="0"><code>import time from ollama import chat PROMPT = &#34;Write a Python class that implements a thread-safe LRU cache.&#34; start = time.perf_counter() token_count = 0 stream = chat( # model=&#34;qwen2.5-coder:7b&#34;, model=&#34;deepseek-r1:latest&#34;, messages=[{&#34;role&#34;: &#34;user&#34;, &#34;content&#34;: PROMPT}], stream=True, ) first_token_time = None for chunk in stream: content = chunk[&#34;message&#34;][&#34;content&#34;] if content: if first_token_time is None: first_token_time = time.perf_counter() token_count += 1 print(content, end=&#34;&#34;, flush=True) elapsed = time.perf_counter() - start ttft = (first_token_time - start) if first_token_time else 0 print(f&#34;\n\n--- Benchmark ---&#34;) print(f&#34;Time to first token : {ttft:.2f}s&#34;) print(f&#34;Total tokens : {token_count}&#34;) print(f&#34;Total time : {elapsed:.2f}s&#34;) print(f&#34;Tokens/sec : {token_count / elapsed:.1f}&#34;) </code></pre><p>除了使用脚本的方式,还可以通过 Cherry Studio 加载本地模型,来查看模型的基准测试效果。</p> <p><img src="https://imgproxy.citrusreader.com/xeABu2bTycUzu4laG3B-m1CgwxRqlD1xNIeMCkEdm2M/raw:1/aHR0cHM6Ly9hcnRpY2xlLTEyNTcwOTUwMTYuY29zLmFwLWJlaWppbmcubXlxY2xvdWQuY29tL2ltYWdlLTIwMjYwODIxMjA1NjM0NTE5LnBuZw" alt="cherry studio 测试统计"></p> <h4 id="第二步选择与使用推理引擎">第二步:选择与使用推理引擎</h4> <p><img src="https://imgproxy.citrusreader.com/RVXzXeJyAfP07KKyQuOTBErBuYN88Ad2-PbM6vWN9Pc/raw:1/aHR0cHM6Ly9hcnRpY2xlLTEyNTcwOTUwMTYuY29zLmFwLWJlaWppbmcubXlxY2xvdWQuY29tL0NsZWFuU2hvdCUyMDIwMjYtMDgtMjElMjBhdCUyMDIwLjU3LjA2QDJ4LnBuZw" alt="常见推理引擎对比"></p> <p>在众多推理工具中,LM Studio 和 Ollama 的底层引擎均基于 <code>llama.cpp</code>。</p> <p>目前,Ollama 是大多数开发者的首选。它将复杂的推理引擎封装成一个简单的命令行工具。</p> <p>Ollama 提供了与 OpenAI 兼容的 API 接口。这意味着,原本为云端模型编写的脚本或框架,只需更改 Base URL 即可直接无缝对接本地模型。</p> <pre tabindex="0"><code>from openai import OpenAI # Point the standard OpenAI client at your local Ollama instance client = OpenAI( base_url=&#34;http://localhost:11434/v1/&#34;, api_key=&#34;ollama&#34;, # required by the SDK but not validated ) response = client.chat.completions.create( model=&#34;qwen3-coder:30b&#34;, messages=[ {&#34;role&#34;: &#34;user&#34;, &#34;content&#34;: &#34;Explain the difference between a mutex and a semaphore.&#34;}, ], ) print(response.choices[0].message.content) </code></pre><p>此外,Ollama 也支持在命令行窗口中直接交互:</p> <ul> <li><strong>直接运行模型</strong></li> </ul> <div class="highlight"><pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>ollama run qwen2.5-coder:7b </span></span></code></pre></div><ul> <li><strong>结合开发工具使用(如配合 codex 提供模型接口能力)</strong>:</li> </ul> <div class="highlight"><pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>ollama launch codex --model qwen2.5-coder:7b </span></span></code></pre></div><p>最后,Ollama还可以和图形化界面(Cherry Studio)结合使用。</p> <p>通过Cherry Studio的模型服务,在 Ollama 配置页面添加对应的模型,即可实现直观的本地可视化交互与管理。</p> <p><img src="https://imgproxy.citrusreader.com/khMbbOXGrfCNzv96-YcvruSg7unlCiKDPnqMcp68e1w/raw:1/aHR0cHM6Ly9hcnRpY2xlLTEyNTcwOTUwMTYuY29zLmFwLWJlaWppbmcubXlxY2xvdWQuY29tL0NsZWFuU2hvdCUyMDIwMjYtMDgtMjElMjBhdCUyMDIxLjMzLjE4QDJ4LnBuZw" alt="cherry studio 配置页面"></p>

2026/8/21
阅读更多

AI 新手村:让大模型学会操作浏览器

<p>传统的 LLM 只是一个文本语言模型,只能以 chat bot 的形式和我们进行对话。但是在日常工作生活中,浏览器是一个占据我们大多数时间的工具,如何让大语言模型也具备操作浏览器的能力呢?</p> <p>最近我做了一个小实验:用 Agent + Playwright,让大模型自己打开网页、看懂页面结构、决定点哪个按钮,最终完整处理了一个客服工单。这篇文章把原理、落地过程整理出来。</p> <p>从宏观上看,给 LLM 配置上工具、组成一个 Agent,就能实现与浏览器的交互。这个 Agent 不断执行「观察 + 执行」的循环:</p> <ol> <li> <p>Agent 接受任务和当前浏览器状态</p> </li> <li> <p>根据任务分析当前状态,决定下一步操作</p> </li> <li> <p>把操作交给工具执行</p> </li> <li> <p>执行后浏览器产生新的状态,作为下一次循环的输入</p> </li> </ol> <p>循环一直持续,直到 Agent 认为任务完成。</p> <p>为了使循环正常工作,我们需要在代理和浏览器之间建立两个连接:</p> <ol> <li><strong>观察通道</strong> ,让 Agent 能接收当前浏览器状态</li> <li><strong>操作通道</strong> ,让 Agent 能与浏览器交互</li> </ol> <h2 id="两种落地方案">两种落地方案</h2> <table> <thead> <tr> <th>方案</th> <th>观察方式</th> <th>操作方式</th> <th>对 LLM 的要求</th> <th>token 消耗</th> </tr> </thead> <tbody> <tr> <td>方案一</td> <td>屏幕截图</td> <td>基于坐标的鼠标键盘操作</td> <td>图像识别能力</td> <td>高</td> </tr> <tr> <td>方案二</td> <td>结构化页面状态</td> <td>元素定向操作</td> <td>基本文本能力</td> <td>低</td> </tr> </tbody> </table> <p>方案一需要 LLM 具备图像识别能力,方案二只需要基础文本能力。本文聚焦<strong>方案二</strong>,选用的工具是 <strong>Playwright CLI</strong>——微软推出的命令行浏览器自动化工具,它把页面输出为结构化状态,token 使用更高效。</p> <h2 id="playwright-cli基本使用">Playwright CLI基本使用</h2> <p>1.安装 playwright cli</p> <pre tabindex="0"><code>npm install -g @playwright/cli@latest playwright-cli --help </code></pre><p>2.playwright cli 的使用</p> <div class="highlight"><pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#09f;font-style:italic"># 有头模式:可视化调试,便于观察页面自动化访问过程 </span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>playwright-cli open google.com --headed </span></span></code></pre></div><p>有头浏览器,可视化的方式展示前端页面的自动化访问,便于调试。</p> <p>默认为无头模式,不加 <code>headed</code> 参数即可,节省内存但看不到页面。在命令行里跑的时候建议用无头模式,减少干扰,运行更清爽。</p> <p>3.持久化会话</p> <div class="highlight"><pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>playwright-cli open google.com --headed --persistent </span></span></code></pre></div><p>Cookie 等信息会存储在本地,下次访问自动复用登录状态,适合需要登录的站点。</p> <h2 id="如何与大模型配合">如何与大模型配合</h2> <p>playwright cli 是微软推出的一个命令行工具,大模型并不知道如何使用,但是搭配上 skill 这个说明文档之后,大模型就可以立即掌握工具的所有用法。</p> <p><strong>方式一:安装 skill</strong></p> <pre tabindex="0"><code>playwright-cli install --skills </code></pre><p>skill 会直接安装到当前项目的 <code>.claude/skills/playwright-cli</code>。如果要适配 Codex,只需把 <code>.claude</code> 改成 <code>.codex</code>,在 Codex 命令行下输入 <code>/skill</code> 即可验证。</p> <p><img src="https://imgproxy.citrusreader.com/U-Wp--qxYaEoKDxkCSaMRZ3QBXZ1vOurmuy34lHZQsY/raw:1/aHR0cHM6Ly9hcnRpY2xlLTEyNTcwOTUwMTYuY29zLmFwLWJlaWppbmcubXlxY2xvdWQuY29tL0NsZWFuU2hvdCUyMDIwMjYtMDgtMDclMjBhdCUyMDEyLjQ0LjE1QDJ4LnBuZw" alt="安装skill 之后"></p> <p><strong>方式二:挂载 MCP</strong></p> <p>另一种方式是把 Playwright 以 MCP 的形式挂进大模型的工具列表。注意这里用的是官方独立的 <code>@playwright/mcp</code> 包,和上面的 skill CLI 是两条路径:CLI 是给工具配说明书,MCP 是直接把工具接进 Agent 的工具箱。下面的实战场景 2 用的就是 MCP 方式。</p> <h2 id="使用场景">使用场景</h2> <h3 id="场景-1修改网页标题的颜色">场景 1:修改网页标题的颜色</h3> <p>这是我本地启动的一个网页前端,我让 LLM 帮我实现更改标题颜色。</p> <p><img src="https://imgproxy.citrusreader.com/mjHROf3fypvswWpY91cZKlxV41cogj2rfWiG8Gykdg0/raw:1/aHR0cHM6Ly9hcnRpY2xlLTEyNTcwOTUwMTYuY29zLmFwLWJlaWppbmcubXlxY2xvdWQuY29tL0NsZWFuU2hvdCUyMDIwMjYtMDgtMDclMjBhdCUyMDExLjQzLjE4QDJ4LnBuZw" alt="前端网页"></p> <p>先校验大模型能否看到网页内容——这是观察通道是否打通的第一关:</p> <p><img src="https://imgproxy.citrusreader.com/xGUB7fbpefZrVAC3mnE2vD4ZgbG8znSFvXsTUJTOSX8/raw:1/aHR0cHM6Ly9hcnRpY2xlLTEyNTcwOTUwMTYuY29zLmFwLWJlaWppbmcubXlxY2xvdWQuY29tL0NsZWFuU2hvdCUyMDIwMjYtMDgtMDclMjBhdCUyMDEzLjU2LjMzQDJ4LnBuZw" alt="观察"></p> <p>确认可见之后,下达修改指令:让大模型帮我更改标题的颜色。</p> <p><img src="https://imgproxy.citrusreader.com/cxrlOf6ip50lhO5R5UcqTitEVLYUTKBf_j03BLdyqRo/raw:1/aHR0cHM6Ly9hcnRpY2xlLTEyNTcwOTUwMTYuY29zLmFwLWJlaWppbmcubXlxY2xvdWQuY29tL0NsZWFuU2hvdCUyMDIwMjYtMDgtMDclMjBhdCUyMDEzLjU3LjU4QDJ4LnBuZw" alt="行动"></p> <p>改完后刷新页面确认标题颜色已生效,「观察 → 执行 → 验证」的闭环就完整了。</p> <p>![修改之后的前端](../md_img/给 Agent 配一个浏览器/CleanShot 2026-08-07 at <a href="mailto:14.27.22@2x.png">14.27.22@2x.png</a>)</p> <h3 id="场景-2让-agent-自主处理客服工单">场景 2:让 Agent 自主处理客服工单</h3> <p>通过 Playwright MCP 操控 Chrome 浏览器,打开本地客服控制台,自主完成以下任务:</p> <ol> <li>打开客服控制台页面</li> <li>找到工单 <code>ORD-1048</code>(客户投诉收到了错误的商品)</li> <li>搜索关联订单,查看订单详情和客户档案</li> <li>对照解决方案规则,判断应该换货还是退款</li> <li>提交解决方案并添加内部备注</li> <li>验证工单状态变为&quot;已解决&quot;</li> </ol> <p><strong>关键点</strong>:Agent 收到的只是业务目标(&ldquo;解决 ORD-1042 工单&rdquo;),不包含任何操作步骤。它需要自己看页面结构、理解内容、决定点哪个按钮——这是 LLM Agent 和传统自动化脚本的本质区别。</p> <p>前端的客服控制台是一个简单的 html 页面。</p> <p><img src="https://imgproxy.citrusreader.com/nXDwPhYtyT2YmEV7F7KowisbHBiwfQXhHbS5HDXLiwQ/raw:1/aHR0cHM6Ly9hcnRpY2xlLTEyNTcwOTUwMTYuY29zLmFwLWJlaWppbmcubXlxY2xvdWQuY29tL0NsZWFuU2hvdCUyMDIwMjYtMDgtMDclMjBhdCUyMDE0LjAzLjMwQDJ4LnBuZw" alt="前端页面"></p> <p>整体的技术架构如下:</p> <pre tabindex="0"><code>用户任务 → Agents SDK(工具循环引擎) ↓ Playwright MCP(浏览器操作能力) ↓ Chrome 浏览器(实际操作页面) </code></pre><p>关键代码:</p> <div class="highlight"><pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span> TASK <span style="color:#555">=</span> <span style="color:#c30">f</span><span style="color:#c30">&#34;&#34;&#34; </span></span></span><span style="display:flex;"><span><span style="color:#c30"> 打开 </span><span style="color:#a00">{</span>APP_URL<span style="color:#a00">}</span><span style="color:#c30">,处理订单 CASE-4108 的客服工单。 </span></span></span><span style="display:flex;"><span><span style="color:#c30"> 请利用系统内现有信息判定并执行合适的解决方案; </span></span></span><span style="display:flex;"><span><span style="color:#c30"> 添加简洁的内部备注,并确认处理结果已成功保存。 </span></span></span><span style="display:flex;"><span><span style="color:#c30"> 任务完成后汇报你的操作过程。 </span></span></span><span style="display:flex;"><span><span style="color:#c30"> &#34;&#34;&#34;</span><span style="color:#555">.</span>strip() </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#366">print</span>(<span style="color:#c30">&#34;</span><span style="color:#c30;font-weight:bold">\n</span><span style="color:#c30">[agent] 启动 Playwright MCP server...&#34;</span>) </span></span><span style="display:flex;"><span> <span style="color:#09f;font-style:italic"># async with 保证 agent 跑完后自动关闭 MCP 子进程,不留残余 Chrome</span> </span></span><span style="display:flex;"><span> <span style="color:#069;font-weight:bold">async</span> <span style="color:#069;font-weight:bold">with</span> ScriptMCPServerStdio( </span></span><span style="display:flex;"><span> name<span style="color:#555">=</span><span style="color:#c30">&#34;Playwright MCP&#34;</span>, </span></span><span style="display:flex;"><span> params<span style="color:#555">=</span>{ </span></span><span style="display:flex;"><span> <span style="color:#c30">&#34;command&#34;</span>: <span style="color:#c30">&#34;npx&#34;</span>, </span></span><span style="display:flex;"><span> <span style="color:#c30">&#34;args&#34;</span>: [<span style="color:#c30">&#34;-y&#34;</span>, <span style="color:#c30">&#34;@playwright/mcp@latest&#34;</span>, <span style="color:#c30">&#34;--browser&#34;</span>, <span style="color:#c30">&#34;chrome&#34;</span>], </span></span><span style="display:flex;"><span> }, </span></span><span style="display:flex;"><span> client_session_timeout_seconds<span style="color:#555">=</span><span style="color:#f60">60</span>, <span style="color:#09f;font-style:italic"># 首次 npx 下载安装依赖可能慢,给足超时</span> </span></span><span style="display:flex;"><span> ) <span style="color:#069;font-weight:bold">as</span> playwright_server: </span></span><span style="display:flex;"><span> <span style="color:#366">print</span>(<span style="color:#c30">&#34;[agent] MCP server 已连接,创建 agent...&#34;</span>) </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> agent <span style="color:#555">=</span> Agent( </span></span><span style="display:flex;"><span> name<span style="color:#555">=</span><span style="color:#c30">&#34;Support Console Browser Agent&#34;</span>, </span></span><span style="display:flex;"><span> instructions<span style="color:#555">=</span><span style="color:#c30">&#34;You are an agent that can interact with a web browser.&#34;</span>, </span></span><span style="display:flex;"><span> model_settings<span style="color:#555">=</span>ModelSettings(), <span style="color:#09f;font-style:italic"># 浏览器任务用默认设置即可</span> </span></span><span style="display:flex;"><span> mcp_servers<span style="color:#555">=</span>[playwright_server], <span style="color:#09f;font-style:italic"># ← 浏览器工具来源</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> ) </span></span><span style="display:flex;"><span> <span style="color:#366">print</span>(<span style="color:#c30">f</span><span style="color:#c30">&#34;[agent] 任务: </span><span style="color:#a00">{</span>TASK[:<span style="color:#f60">80</span>]<span style="color:#a00">}</span><span style="color:#c30">...&#34;</span>) </span></span><span style="display:flex;"><span> <span style="color:#366">print</span>(<span style="color:#c30">&#34;[agent] 开始运行(Chrome 窗口会弹出,请勿手动关闭)...</span><span style="color:#c30;font-weight:bold">\n</span><span style="color:#c30">&#34;</span>) </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#069;font-weight:bold">try</span>: </span></span><span style="display:flex;"><span> result <span style="color:#555">=</span> <span style="color:#069;font-weight:bold">await</span> Runner<span style="color:#555">.</span>run( </span></span><span style="display:flex;"><span> agent, </span></span><span style="display:flex;"><span> TASK, </span></span><span style="display:flex;"><span> max_turns<span style="color:#555">=</span><span style="color:#f60">30</span>, <span style="color:#09f;font-style:italic"># 浏览器任务步骤多,给足轮次</span> </span></span><span style="display:flex;"><span> run_config<span style="color:#555">=</span>RunConfig(model_provider<span style="color:#555">=</span>CUSTOM_MODEL_PROVIDER), </span></span><span style="display:flex;"><span> ) </span></span><span style="display:flex;"><span> <span style="color:#069;font-weight:bold">except</span> <span style="color:#c00;font-weight:bold">Exception</span> <span style="color:#069;font-weight:bold">as</span> e: </span></span><span style="display:flex;"><span> <span style="color:#366">print</span>(<span style="color:#c30">f</span><span style="color:#c30">&#34;</span><span style="color:#c30;font-weight:bold">\n</span><span style="color:#c30">[agent] 运行异常: </span><span style="color:#a00">{</span><span style="color:#366">type</span>(e)<span style="color:#555">.</span>__name__<span style="color:#a00">}</span><span style="color:#c30">: </span><span style="color:#a00">{</span>e<span style="color:#a00">}</span><span style="color:#c30">&#34;</span>) </span></span></code></pre></div><p><img src="https://imgproxy.citrusreader.com/9BfEliF19sSHUjQ8_rUUnTMm5stY2gSkCU88IVc2koM/raw:1/aHR0cHM6Ly9hcnRpY2xlLTEyNTcwOTUwMTYuY29zLmFwLWJlaWppbmcubXlxY2xvdWQuY29tL0NsZWFuU2hvdCUyMDIwMjYtMDgtMDclMjBhdCUyMDE0LjExLjE4LmdpZg" alt="整个流程"></p> <p>最后,Agent 打印出完整的执行过程。整个过程 Agent 没有收到任何一步操作指令,全靠自己读页面、做判断——这就是 Agent 与传统脚本的分水岭。</p> <pre tabindex="0"><code>[agent] 运行完成! ============================================================ Agent 汇报: ============================================================ 工单处理已经成功完成。让我确认一下处理结果: 1. **CASE-4108 状态已变为&#34;已解决&#34;**:在收件箱列表中显示&#34;CASE-4108, 物品损坏, order ORD-1088, 已解决&#34; 2. **待处理案例数从 4 减少到 3**:证明该案例已处理完毕 3. **&#34;已记录解决方案&#34;验证区显示**: - 案例:CASE-4108 - 订单:ORD-1088 - 状态:已解决 - 解决方案操作:Refund(退款) - 解决时间:2026-08-07 14:12 - 内部备注:已正确保存 4. **审计日志**也记录了该处理 处理结果已成功保存。让我向用户汇报整个操作过程。 --- ## 操作过程汇报 我已完成 **CASE-4108** 客服工单的处理,以下是完整的操作过程: ### 1. 打开系统 - 访问了北极星客服控制台(http://127.0.0.1:8001),进入退货队列。 ### 2. 定位并查看工单 - 从收件箱中选择了 **CASE-4108(物品损坏)** 工单,涉及订单 **ORD-1088**。 - 客户描述:**马库斯·陈** 的登山日用背包到达时侧口袋破损,客户倾向退款,并表示需要时可上传照片。 ### 3. 收集订单与客户信息 - **订单 ORD-1088**:订购商品与发货商品均为&#34;登山日用背包&#34;,已送达(2026-07-12),已支付。 - **换货库存**:库存有限(不足)。 - **客户档案(马库斯·陈)**:忠诚度等级为**标准**(非金卡),之前退款数 **2** 次,风险标记**清洁**。 ### 4. 依据政策判定解决方案 参考「物品损坏」政策规则:*已送达的损坏物品,除非客户是金卡且无风险标记,否则退款前要求照片证明;库存充足时首选换货。* - **换货**:换货库存有限(不足),且客户明确倾向退款 → **不采用换货**。 - **风险升级**:账户无风险标记、退款未超3次 → **不需要升级**。 - **退款**:客户明确倾向退款且愿意提供照片证明,账户无风险 → **采用退款**。 ### 5. 执行处理 - 选择 **退款(Refund)** 解决方案。 - 填写内部备注:*&#34;背包侧口袋破损,属物品损坏。换货库存有限,客户明确倾向退款并愿提供照片证明。账户无风险标记,退款数2次,未超限。决定退款。&#34;* - 点击「提交解决方案」。 ### 6. 确认结果已保存 - 系统显示 **CASE-4108 状态变为「已解决」**,待处理案例从 4 降至 3。 - 「已记录解决方案」验证区确认: - 案例:CASE-4108 / 订单:ORD-1088 / 状态:已解决 - 解决方案操作:**Refund(退款)** - 解决时间:2026-08-07 14:12 - 内部备注及操作已写入**审计日志**,处理结果**成功保存**。✅ </code></pre>

2026/8/7
阅读更多

我的笔记系统

<p>最近梳理了一下自己在 onsidian 上记的笔记,发现过去很多记的笔记都比较零散,记到电脑或者手机上之后,就很少再会查看或者利用了。</p> <p>为了让自己过去的笔记内容不再荒废,为了让现在的我支援未来的自己,我研究了一下如何让笔记流动起来的方法。</p> <h2 id="调研">调研</h2> <p>市场上常用的笔记组织方法有 2 种,PARA 记录和Zettelkasten记录。</p> <p>1/ PARA</p> <p>笔记的目录结构分为 4 类:</p> <ul> <li> <p><strong>项目(project)</strong>:有明确截止日期的进行中项目(例如,&ldquo;第三季度上线公司网站&rdquo;)</p> </li> <li> <p><strong>领域(area)</strong>:持续负责的领域,没有截止日期(例如,&ldquo;健康&rdquo;、&ldquo;财务&rdquo;、&ldquo;XYZ 团队&rdquo;)</p> </li> <li> <p><strong>资源(resource)</strong> :按兴趣分类的参考资料(例如,&ldquo;机器学习&rdquo;、&ldquo;斯多葛哲学&rdquo;)</p> </li> <li> <p><strong>存档(archive)</strong> :以上三个类别中的非活跃项目</p> </li> </ul> <p>2/ Zettelkasten</p> <p>Zettelkasten 的核心是原子笔记(每条笔记只记录一个想法)。</p> <p><strong>Zettelkasten 的核心原则</strong>:每张永久笔记都必须至少与其他一张笔记关联;无法与任何笔记关联的笔记,或许就不该作为独立笔记存在。</p> <p>笔记的目录结构分为 3 类:</p> <ul> <li><strong>转瞬即逝的笔记(Fleeting notes)</strong>:快速记录、处理后丢弃。</li> <li><strong>阅读笔记(Literature notes)</strong>:关于你所读内容的笔记。</li> <li><strong>永久笔记(Permanent notes)</strong>:用你自己的语言记录的原子笔记,随时可查。</li> </ul> <h2 id="我的思考">我的思考</h2> <p>我的笔记组织结构核心想法是不想太复杂,目录结构尽量不要嵌套多层,这是我以前的苹果备忘录的组织结构:</p> <p><img src="https://imgproxy.citrusreader.com/6ILRDHcWacO-StJ_uQbaPWvQ9yo0Z8GuN3E2S6edrAg/raw:1/aHR0cHM6Ly9hcnRpY2xlLTEyNTcwOTUwMTYuY29zLmFwLWJlaWppbmcubXlxY2xvdWQuY29tL2ltYWdlLTIwMjYwNzIyMTAwMzU0ODY5LnBuZw" alt="image-20260722100354869"></p> <p>整体看,整个目录结构并不像 PARA 那样相互独立,比如有的笔记既可以放在商业目录下,也可以放在数据目录下,其实这种情况用「标签」的方式组织或许会更好。</p> <p>除了结构简单,我对笔记系统的核心速求是要用起来,可以有产出。于是,在纸上勾勾画画之后,我总结了整体的框架如下:</p> <p><img src="https://imgproxy.citrusreader.com/k7eOldSaNyb_4nKI0jN33TP7_34vIKXO_Wkd_IvXbyg/raw:1/aHR0cHM6Ly9hcnRpY2xlLTEyNTcwOTUwMTYuY29zLmFwLWJlaWppbmcubXlxY2xvdWQuY29tL2ltYWdlLTIwMjYwNzIyMTAzNDI2OTQ5LnBuZw" alt="我的笔记系统"></p> <h2 id="实践">实践</h2> <p>基于上面的思考,我重新设置我的obsidian 的目录,结构如下:</p> <p><img src="https://imgproxy.citrusreader.com/gHMMSF6IWZRZYEy0H9l5KcRqWDER2GTjR3KVeHbjbrg/raw:1/aHR0cHM6Ly9hcnRpY2xlLTEyNTcwOTUwMTYuY29zLmFwLWJlaWppbmcubXlxY2xvdWQuY29tL2ltYWdlLTIwMjYwNzIyMTA0NDU4NjY0LnBuZw" alt="我的 onsidian 目录"></p>

2026/7/22
阅读更多

模型 API 地址配置管理

<p>注册使用过的第三方中转站太多了,如何管理成了一个问题。</p> <p>今天调研使用了一下 cc switch,感觉还可以。</p> <p>下载地址:https://github.com/farion1231/cc-switch/tree/main 5万多个 star,应该还算靠谱</p> <h2 id="使用">使用</h2> <p>1/ 添加基本配置,主要是 <strong>API Key</strong> 和<strong>请求地址</strong></p> <p><img src="https://imgproxy.citrusreader.com/PQuvCuwur2-2zXdg1J5ctnSzZABR4TC4Bja0K-TiP9g/raw:1/aHR0cHM6Ly9hcnRpY2xlLTEyNTcwOTUwMTYuY29zLmFwLWJlaWppbmcubXlxY2xvdWQuY29tL2ltYWdlLTIwMjYwNTI4MTExMTM3ODA1LnBuZw" alt="模型厂商配置页面"></p> <p>2/ 配置一下模型映射 <img src="https://imgproxy.citrusreader.com/tdkkGUYH2dpLznyIE5QaUhR0nSGaiCRPkCbEqwz4CQw/raw:1/aHR0cHM6Ly9hcnRpY2xlLTEyNTcwOTUwMTYuY29zLmFwLWJlaWppbmcubXlxY2xvdWQuY29tL2ltYWdlLTIwMjYwNTI4MTEyMDU5MTM1LnBuZw" alt="高级选项"></p> <p>3/ 点击一下&quot;启用&quot;按钮就可以正常使用了 <img src="https://imgproxy.citrusreader.com/2zfsh7JueRs_Q9QvKF8JD7ly0Os3_xuQU7G8Js4lNhs/raw:1/aHR0cHM6Ly9hcnRpY2xlLTEyNTcwOTUwMTYuY29zLmFwLWJlaWppbmcubXlxY2xvdWQuY29tL2ltYWdlLTIwMjYwNTI4MTEyMTQ5ODkxLnBuZw" alt="image-20260528112149891"></p> <p><img src="https://imgproxy.citrusreader.com/cKzASeLHpLDLjlOjp46a_f9KLcjJNSHq0fvWCEtQYmY/raw:1/aHR0cHM6Ly9hcnRpY2xlLTEyNTcwOTUwMTYuY29zLmFwLWJlaWppbmcubXlxY2xvdWQuY29tL2ltYWdlLTIwMjYwNTI4MTEyMjA2Njk5LnBuZw" alt="image-20260528112206699"></p> <h2 id="效果">效果</h2> <p>cc-switch 是实时热加载配置,即改即用。目前主要使用 anyrouter 的中转站,效果还不错,可以把以前在 <code> ~/.zshrc</code>中手动添加的配置注释掉了。</p> <p><img src="https://imgproxy.citrusreader.com/uyzqbl3FZnUHB8I6T6I0sC2I1jHRNhkpApCBLJ0ithk/raw:1/aHR0cHM6Ly9hcnRpY2xlLTEyNTcwOTUwMTYuY29zLmFwLWJlaWppbmcubXlxY2xvdWQuY29tL0NsZWFuU2hvdCUyMDIwMjYtMDUtMjglMjBhdCUyMDExLjI1LjA3QDJ4LnBuZw" alt="使用效果"></p>

2026/5/28
阅读更多

Claude Code 完美接入 Ollama 指南

<p>作为 Anthropic 官方推出的命令行编码助手(coding assistant),Claude Code 本质上是一个通过大模型(LLM)执行复杂任务的工具。通常情况下,它需要连接云端 API。但随着 Ollama 宣布兼容 Anthropic Messages API,我们现在可以轻松地将 Claude Code 与本地模型集成。</p> <p>从工程角度看,使用本地的模型既可以作为断网情况下的一种保障方案,也可以在不改变工作流程的情况下,把本地模型作为一个测试开发环境,极大节省 Token 开销。</p> <h2 id="操作步骤">操作步骤</h2> <p>1.安装 Claude Code 和 Ollama</p> <pre tabindex="0"><code>npm install -g @anthropic-ai/claude-code@latest </code></pre><p>Ollama 可以通过官网https://ollama.com/下载安装包安装。</p> <blockquote> <p>注意:Ollama 版本v0.14.0+,Claude Code版本v2.1.12+,可以通过下面命令验证</p> </blockquote> <div class="highlight"><pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>claude --version </span></span><span style="display:flex;"><span>ollama --version </span></span></code></pre></div><p>Ollama 安装后会自动作为后台服务运行。应该可以在 http://localhost:11434 上看到它运行。</p> <p>2.下载大模型</p> <p>可以通过 Ollama 的 WebUI 页面直接下载,如下图。</p> <p><img src="https://imgproxy.citrusreader.com/gAxAKxWzfRSeRSqEp_uUlWYdEhZ-KWTaCCCK0X2LR4U/raw:1/aHR0cHM6Ly9hcnRpY2xlLTEyNTcwOTUwMTYuY29zLmFwLWJlaWppbmcubXlxY2xvdWQuY29tL0NsZWFuU2hvdCUyMDIwMjYtMDEtMjIlMjBhdCUyMDE2LjQyLjM1QDJ4LnBuZw" alt="下载模型到本地"></p> <p>也可以通过命令行快速拉取适合编码的模型。</p> <div class="highlight"><pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#09f;font-style:italic">#查看本地模型</span> </span></span><span style="display:flex;"><span>ollama list </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#09f;font-style:italic">#下载新模型</span> </span></span><span style="display:flex;"><span>ollama pull qwen2.5-coder:7b </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#09f;font-style:italic">#删除模型</span> </span></span><span style="display:flex;"><span>ollama rm qwen2.5-coder:7b </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#09f;font-style:italic">#查看模型基本参数</span> </span></span><span style="display:flex;"><span>ollama show qwen2.5-coder:7b </span></span></code></pre></div><p>3.配置 Claude Code 连接本地 Ollama</p> <div class="highlight"><pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#366">export</span> <span style="color:#033">ANTHROPIC_AUTH_TOKEN</span><span style="color:#555">=</span>ollama </span></span><span style="display:flex;"><span><span style="color:#366">export</span> <span style="color:#033">ANTHROPIC_BASE_URL</span><span style="color:#555">=</span>http://localhost:11434 </span></span><span style="display:flex;"><span><span style="color:#09f;font-style:italic"># 启动 Claude Code 并指定本地模型</span> </span></span><span style="display:flex;"><span>claude --model qwen2.5-coder:7b </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#09f;font-style:italic"># 如果想使用云端模型,命令类似</span> </span></span><span style="display:flex;"><span>claude --model glm-4.6:cloud </span></span></code></pre></div><p><img src="https://imgproxy.citrusreader.com/reJEcUjh5A4jd65VBF0a7BTfxayeSx8Y7KqWSCsDR5k/raw:1/aHR0cHM6Ly9hcnRpY2xlLTEyNTcwOTUwMTYuY29zLmFwLWJlaWppbmcubXlxY2xvdWQuY29tL0NsZWFuU2hvdCUyMDIwMjYtMDEtMjIlMjBhdCUyMDE2LjU5LjUyLnBuZw" alt="使用本地模型"></p> <blockquote> <p>注意:如果你开启了系统代理,可能会遇到 <code>API Error: Connection error</code>。这是因为流量被转发到了代理服务器而找不到 localhost。请先在终端执行:</p> </blockquote> <div class="highlight"><pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#366">unset</span> https_proxy </span></span><span style="display:flex;"><span><span style="color:#366">unset</span> http_proxy </span></span></code></pre></div><p>4.使用Anthropic SDK</p> <p>如果我们想更精准的把控程序的执行流程,可以使用官方的 SDK。</p> <div class="highlight"><pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#069;font-weight:bold">import</span> <span style="color:#0cf;font-weight:bold">anthropic</span> </span></span><span style="display:flex;"><span><span style="color:#069;font-weight:bold">import</span> <span style="color:#0cf;font-weight:bold">httpx</span> </span></span><span style="display:flex;"><span>http_client <span style="color:#555">=</span> httpx<span style="color:#555">.</span>Client( </span></span><span style="display:flex;"><span> proxy<span style="color:#555">=</span><span style="color:#069;font-weight:bold">None</span>, </span></span><span style="display:flex;"><span> trust_env<span style="color:#555">=</span><span style="color:#069;font-weight:bold">False</span>, </span></span><span style="display:flex;"><span>) </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>client <span style="color:#555">=</span> anthropic<span style="color:#555">.</span>Anthropic( </span></span><span style="display:flex;"><span> base_url<span style="color:#555">=</span><span style="color:#c30">&#39;http://localhost:11434&#39;</span>, </span></span><span style="display:flex;"><span> api_key<span style="color:#555">=</span><span style="color:#c30">&#39;ollama&#39;</span>, </span></span><span style="display:flex;"><span> http_client<span style="color:#555">=</span>http_client, </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>) </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#069;font-weight:bold">with</span> client<span style="color:#555">.</span>messages<span style="color:#555">.</span>stream( </span></span><span style="display:flex;"><span> model<span style="color:#555">=</span><span style="color:#c30">&#39;qwen2.5-coder:7b&#39;</span>, </span></span><span style="display:flex;"><span> max_tokens<span style="color:#555">=</span><span style="color:#f60">1024</span>, </span></span><span style="display:flex;"><span> messages<span style="color:#555">=</span>[{<span style="color:#c30">&#39;role&#39;</span>: <span style="color:#c30">&#39;user&#39;</span>, <span style="color:#c30">&#39;content&#39;</span>: <span style="color:#c30">&#39;Count from 1 to 10&#39;</span>}] </span></span><span style="display:flex;"><span>) <span style="color:#069;font-weight:bold">as</span> stream: </span></span><span style="display:flex;"><span> <span style="color:#069;font-weight:bold">for</span> text <span style="color:#000;font-weight:bold">in</span> stream<span style="color:#555">.</span>text_stream: </span></span><span style="display:flex;"><span> <span style="color:#366">print</span>(text, end<span style="color:#555">=</span><span style="color:#c30">&#39;&#39;</span>, flush<span style="color:#555">=</span><span style="color:#069;font-weight:bold">True</span>) </span></span></code></pre></div><h2 id="调试秘籍如何与-ai-高效沟通报错">调试秘籍:如何与 AI 高效沟通报错?</h2> <p>这里再说一个调试的技巧:在配置过程中,不要死磕枯燥的错误堆栈信息。一个高效的调试技巧是:<strong>向 AI 描述你的完整环境和所做的工作,而不仅仅是报错代码。</strong></p> <p>此前我按照文档编写代码时,一直遇到 <code>InternalServerError (503)</code> 错误。我直接把错误代码粘贴给 chatgpt ,它尝试了很多方法但始终没有解决。</p> <div class="highlight"><pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>Traceback <span style="color:#555">(</span>most recent call last<span style="color:#555">)</span>: </span></span><span style="display:flex;"><span> File <span style="color:#c30">&#34;/Users/shaoyang/Project/stock_demo/day09/anthropic-demo.py&#34;</span>, line 12, in &lt;module&gt; </span></span><span style="display:flex;"><span> <span style="color:#033">message</span> <span style="color:#555">=</span> client.messages.create<span style="color:#555">(</span> </span></span><span style="display:flex;"><span> File <span style="color:#c30">&#34;/Users/shaoyang/Project/stock_demo/.venv/lib/python3.10/site-packages/anthropic/_utils/_utils.py&#34;</span>, line 282, in wrapper </span></span><span style="display:flex;"><span> <span style="color:#069;font-weight:bold">return</span> func<span style="color:#555">(</span>*args, **kwargs<span style="color:#555">)</span> </span></span><span style="display:flex;"><span> File <span style="color:#c30">&#34;/Users/shaoyang/Project/stock_demo/.venv/lib/python3.10/site-packages/anthropic/resources/messages/messages.py&#34;</span>, line 932, in create </span></span><span style="display:flex;"><span> <span style="color:#069;font-weight:bold">return</span> self._post<span style="color:#555">(</span> </span></span><span style="display:flex;"><span> File <span style="color:#c30">&#34;/Users/shaoyang/Project/stock_demo/.venv/lib/python3.10/site-packages/anthropic/_base_client.py&#34;</span>, line 1361, in post </span></span><span style="display:flex;"><span> <span style="color:#069;font-weight:bold">return</span> cast<span style="color:#555">(</span>ResponseT, self.request<span style="color:#555">(</span>cast_to, opts, <span style="color:#033">stream</span><span style="color:#555">=</span>stream, <span style="color:#033">stream_cls</span><span style="color:#555">=</span>stream_cls<span style="color:#555">))</span> </span></span><span style="display:flex;"><span> File <span style="color:#c30">&#34;/Users/shaoyang/Project/stock_demo/.venv/lib/python3.10/site-packages/anthropic/_base_client.py&#34;</span>, line 1134, in request </span></span><span style="display:flex;"><span> raise self._make_status_error_from_response<span style="color:#555">(</span>err.response<span style="color:#555">)</span> from None </span></span><span style="display:flex;"><span>anthropic.InternalServerError: Error code: <span style="color:#f60">503</span> </span></span></code></pre></div><p>于是,我换了一种思路,不是直接粘贴错误代码,而是前置一步,客观地描述了一下当前的环境。</p> <div class="highlight"><pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>import anthropic </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#033">client</span> <span style="color:#555">=</span> anthropic.Anthropic<span style="color:#555">(</span> </span></span><span style="display:flex;"><span> <span style="color:#033">base_url</span><span style="color:#555">=</span><span style="color:#c30">&#39;http://localhost:11434&#39;</span>, </span></span><span style="display:flex;"><span> <span style="color:#033">api_key</span><span style="color:#555">=</span><span style="color:#c30">&#39;ollama&#39;</span>, <span style="color:#09f;font-style:italic"># required but ignored</span> </span></span><span style="display:flex;"><span><span style="color:#555">)</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#033">message</span> <span style="color:#555">=</span> client.messages.create<span style="color:#555">(</span> </span></span><span style="display:flex;"><span> <span style="color:#033">model</span><span style="color:#555">=</span><span style="color:#c30">&#39;qwen3-coder&#39;</span>, </span></span><span style="display:flex;"><span> <span style="color:#033">max_tokens</span><span style="color:#555">=</span>1024, </span></span><span style="display:flex;"><span> <span style="color:#033">messages</span><span style="color:#555">=[</span> </span></span><span style="display:flex;"><span> <span style="color:#555">{</span><span style="color:#c30">&#39;role&#39;</span>: <span style="color:#c30">&#39;user&#39;</span>, <span style="color:#c30">&#39;content&#39;</span>: <span style="color:#c30">&#39;Hello, how are you?&#39;</span><span style="color:#555">}</span> </span></span><span style="display:flex;"><span> <span style="color:#555">]</span> </span></span><span style="display:flex;"><span><span style="color:#555">)</span> </span></span><span style="display:flex;"><span>print<span style="color:#555">(</span>message.content<span style="color:#555">[</span>0<span style="color:#555">]</span>.text<span style="color:#555">)</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>我是用的是mac电脑,本地配置了代理,我应该如何修改上面的代码保证可以运行。 </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>js版本是可以执行的 </span></span><span style="display:flex;"><span>import Anthropic from <span style="color:#c30">&#34;@anthropic-ai/sdk&#34;</span>; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>const <span style="color:#033">anthropic</span> <span style="color:#555">=</span> new Anthropic<span style="color:#555">({</span> </span></span><span style="display:flex;"><span> baseURL: <span style="color:#c30">&#34;http://localhost:11434&#34;</span>, </span></span><span style="display:flex;"><span> apiKey: <span style="color:#c30">&#34;ollama&#34;</span>, // required but ignored </span></span><span style="display:flex;"><span><span style="color:#555">})</span>; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>const <span style="color:#033">message</span> <span style="color:#555">=</span> await anthropic.messages.create<span style="color:#555">({</span> </span></span><span style="display:flex;"><span> model: <span style="color:#c30">&#34;minimax-m2:cloud&#34;</span>, </span></span><span style="display:flex;"><span> max_tokens: 1024, </span></span><span style="display:flex;"><span> messages: <span style="color:#555">[</span> </span></span><span style="display:flex;"><span> <span style="color:#555">{</span> role: <span style="color:#c30">&#34;user&#34;</span>, content: <span style="color:#c30">&#34;写一个求和的 python 函数&#34;</span> <span style="color:#555">}</span> </span></span><span style="display:flex;"><span> <span style="color:#555">]</span>, </span></span><span style="display:flex;"><span><span style="color:#555">})</span>; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>console.log<span style="color:#555">(</span>message.content<span style="color:#555">[</span>1<span style="color:#555">]</span>.text<span style="color:#555">)</span>; </span></span></code></pre></div><p>chatgpt立刻就发现了问题,并给到我正确的执行代码。</p> <p><img src="https://imgproxy.citrusreader.com/4sHXF-rAZ8rgymYohsJBvJZeRICQzo9a4-PtxNHqtp4/raw:1/aHR0cHM6Ly9hcnRpY2xlLTEyNTcwOTUwMTYuY29zLmFwLWJlaWppbmcubXlxY2xvdWQuY29tL0NsZWFuU2hvdCUyMDIwMjYtMDEtMjIlMjBhdCUyMDE3LjE1LjM5QDJ4LnBuZw" alt="ChatGPT 的回复"></p> <p>根据这次调试过程,大模型也帮我总结一个更有效的提问模板:</p> <div class="highlight"><pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>1.贴出当前代码。 </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>2.描述环境(如:Mac 系统、本地有代理)。 </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>3.提供一个对比参照(如:JS 版本可以运行,Python 不行)。 </span></span></code></pre></div>

2026/1/22
阅读更多

开篇词

<p>从 2008 年开始接触股票投资,到现在已经接近二十年了。</p> <p>这段时间里,我经历过完整的市场周期:牛市的狂热、熊市的绝望,也经历过从&quot;以为自己懂了&quot;,到&quot;承认自己其实什么都不懂&quot;的反复过程。账户数字起起落落,但真正变化更大的,是我对投资这件事的理解方式。</p> <p>很多年里,我一直有一个模糊的念头:应该把这些经历写下来。不是为了证明自己赚过多少钱,而是想留下些什么——一些关于决策、关于错误、关于长期面对不确定性的记录。</p> <p>直到最近,这个念头才逐渐变得清晰起来。</p> <p>我决定开设这个专栏。它会以投资为核心,但并不只谈&quot;买什么、卖什么&quot;。我更关心的是:</p> <ul> <li> <p>投资背后的概念与逻辑</p> </li> <li> <p>普通人在真实世界中会遇到的情绪、误判与限制</p> </li> <li> <p>如何借助技术,让这些问题变得更可验证一些</p> </li> </ul> <p>在这个专栏里,我会一边讲自己的投资经历,一边引入相关的基础概念;同时,也会尝试使用 Python、数据分析、以及 AI 大模型,对一些直觉判断进行复盘、辅助和检验。</p> <p>这并不是一套&quot;成功方法论&quot;。更多时候,它可能是一份带着不确定性的思考记录:哪些想法在长期中站得住脚,哪些只是事后看起来合理。</p> <p>如果你也经历过市场的起伏,或者正在尝试把理性工具引入投资决策,希望这些文字能对你有所帮助;如果没有,那它至少是我给自己留的一份阶段性注脚。</p> <p>专栏会慢慢写,不追热点,也不保证结论。 唯一确定的,是我会用心。</p>

2026/1/13
阅读更多

使用大模型有几种方法呢?

<h2 id="1web-端">1/Web 端</h2> <p>我们最常见的是使用网页端,有个对话框,输入我们想问的问题,点击&quot;发送&quot;按钮,然后就等待大模型给我们返回答案了。</p> <p>其实,这可以看成一个产品,以大模型为后端的聊天工具。</p> <p>这是最直观的可以给用户体验到大模型的方式,每个模型厂商都有类似产品。</p> <p><img src="https://imgproxy.citrusreader.com/C_jUEpR4Bfk0GVUdbMP9v5xDRhl_K5nRUdD5XclB4mE/raw:1/aHR0cHM6Ly9hcnRpY2xlLTEyNTcwOTUwMTYuY29zLmFwLWJlaWppbmcubXlxY2xvdWQuY29tL0NsZWFuU2hvdCUyMDIwMjUtMTItMjMlMjBhdCUyMDE2LjU0LjM0LnBuZw" alt="ChatGPT Web端页面"></p> <h2 id="2本地部署">2/本地部署</h2> <p>有的时候企业出于安全性的考虑,会在自己的内网部署开源的大模型或企业自己训练的大模型,这个时候可以选用类似 ollama 这样的模型部署工具来使用。</p> <div class="highlight"><pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#09f;font-style:italic"># 以命令行的方式和大模型交互</span> </span></span><span style="display:flex;"><span>ollama run deepseek-r1 </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#09f;font-style:italic"># 以api的方式和大模型交互</span> </span></span><span style="display:flex;"><span>curl -s <span style="color:#c30">&#34;http://127.0.0.1:11434/api/generate&#34;</span> <span style="color:#c30;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#c30;font-weight:bold"></span> -H <span style="color:#c30">&#34;Content-Type: application/json&#34;</span> <span style="color:#c30;font-weight:bold">\ </span></span></span><span style="display:flex;"><span><span style="color:#c30;font-weight:bold"></span> -d <span style="color:#c30">&#39;{ </span></span></span><span style="display:flex;"><span><span style="color:#c30"> &#34;model&#34;: &#34;deepseek-r1:latest&#34;, </span></span></span><span style="display:flex;"><span><span style="color:#c30"> &#34;prompt&#34;: &#34;请简要介绍一下牛顿第一定律。&#34;, </span></span></span><span style="display:flex;"><span><span style="color:#c30"> &#34;stream&#34;: false </span></span></span><span style="display:flex;"><span><span style="color:#c30"> }&#39;</span> </span></span></code></pre></div><h2 id="3模型中转平台">3/模型中转平台</h2> <p>模型中转平台是类似 openrouter(https://openrouter.ai/)和 模力方舟(https://ai.gitee.com/serverless-api)这样的把市面上主流的大模型放在一个平台上,通过统一的接口供大家使用。</p> <p>上面两个平台使用的 url 和 key 都是第三方的,除了这种聚合模式外,还有像 liteLLM 这样的平台,它是一个开源的库,也可以对外提供统一的接口供大家使用,但是使用的 url 和 key 都是模型厂商自己的。</p> <div class="highlight"><pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#069;font-weight:bold">from</span> <span style="color:#0cf;font-weight:bold">openai</span> <span style="color:#069;font-weight:bold">import</span> OpenAI </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>client <span style="color:#555">=</span> OpenAI( </span></span><span style="display:flex;"><span> base_url<span style="color:#555">=</span><span style="color:#c30">&#34;https://ai.gitee.com/v1&#34;</span>, </span></span><span style="display:flex;"><span> api_key<span style="color:#555">=</span><span style="color:#c30">&#34;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&#34;</span>, </span></span><span style="display:flex;"><span>) </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>response <span style="color:#555">=</span> client<span style="color:#555">.</span>chat<span style="color:#555">.</span>completions<span style="color:#555">.</span>create( </span></span><span style="display:flex;"><span> messages<span style="color:#555">=</span>[ </span></span><span style="display:flex;"><span> { </span></span><span style="display:flex;"><span> <span style="color:#c30">&#34;role&#34;</span>: <span style="color:#c30">&#34;system&#34;</span>, </span></span><span style="display:flex;"><span> <span style="color:#c30">&#34;content&#34;</span>: <span style="color:#c30">&#34;You are a helpful and harmless assistant. You should think step-by-step.&#34;</span> </span></span><span style="display:flex;"><span> }, </span></span><span style="display:flex;"><span> { </span></span><span style="display:flex;"><span> <span style="color:#c30">&#34;role&#34;</span>: <span style="color:#c30">&#34;user&#34;</span>, </span></span><span style="display:flex;"><span> <span style="color:#c30">&#34;content&#34;</span>: <span style="color:#c30">&#34;Can you please let us know more details about your &#34;</span> </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> ], </span></span><span style="display:flex;"><span> model<span style="color:#555">=</span><span style="color:#c30">&#34;DeepSeek-V3&#34;</span>, </span></span><span style="display:flex;"><span> stream<span style="color:#555">=</span><span style="color:#069;font-weight:bold">True</span>, </span></span><span style="display:flex;"><span> max_tokens<span style="color:#555">=</span><span style="color:#f60">1024</span>, </span></span><span style="display:flex;"><span> temperature<span style="color:#555">=</span><span style="color:#f60">0.6</span>, </span></span><span style="display:flex;"><span> top_p<span style="color:#555">=</span><span style="color:#f60">0.8</span>, </span></span><span style="display:flex;"><span> extra_body<span style="color:#555">=</span>{ </span></span><span style="display:flex;"><span> <span style="color:#c30">&#34;top_k&#34;</span>: <span style="color:#f60">20</span>, </span></span><span style="display:flex;"><span> }, </span></span><span style="display:flex;"><span> frequency_penalty<span style="color:#555">=</span><span style="color:#f60">1.1</span>, </span></span><span style="display:flex;"><span>) </span></span></code></pre></div><h2 id="4模型sdk">4/模型SDK</h2> <p>每家模型厂商一般除了 web 端的产品外,也提供 SDK 的方式对外提供使用,一般需要在模型厂商的官网新建 key,并且需要充值才能使用。</p> <div class="highlight"><pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#09f;font-style:italic"># deepseek的python 版本 sdk使用方法,也有 nodejs 版本和 bash 脚本的 </span> </span></span><span style="display:flex;"><span><span style="color:#069;font-weight:bold">import</span> <span style="color:#0cf;font-weight:bold">os</span> </span></span><span style="display:flex;"><span><span style="color:#069;font-weight:bold">from</span> <span style="color:#0cf;font-weight:bold">openai</span> <span style="color:#069;font-weight:bold">import</span> OpenAI </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>client <span style="color:#555">=</span> OpenAI( </span></span><span style="display:flex;"><span> api_key<span style="color:#555">=</span>os<span style="color:#555">.</span>environ<span style="color:#555">.</span>get(<span style="color:#c30">&#39;DEEPSEEK_API_KEY&#39;</span>), </span></span><span style="display:flex;"><span> base_url<span style="color:#555">=</span><span style="color:#c30">&#34;https://api.deepseek.com&#34;</span>) </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>response <span style="color:#555">=</span> client<span style="color:#555">.</span>chat<span style="color:#555">.</span>completions<span style="color:#555">.</span>create( </span></span><span style="display:flex;"><span> model<span style="color:#555">=</span><span style="color:#c30">&#34;deepseek-chat&#34;</span>, </span></span><span style="display:flex;"><span> messages<span style="color:#555">=</span>[ </span></span><span style="display:flex;"><span> {<span style="color:#c30">&#34;role&#34;</span>: <span style="color:#c30">&#34;system&#34;</span>, <span style="color:#c30">&#34;content&#34;</span>: <span style="color:#c30">&#34;You are a helpful assistant&#34;</span>}, </span></span><span style="display:flex;"><span> {<span style="color:#c30">&#34;role&#34;</span>: <span style="color:#c30">&#34;user&#34;</span>, <span style="color:#c30">&#34;content&#34;</span>: <span style="color:#c30">&#34;Hello&#34;</span>}, </span></span><span style="display:flex;"><span> ], </span></span><span style="display:flex;"><span> stream<span style="color:#555">=</span><span style="color:#069;font-weight:bold">False</span> </span></span><span style="display:flex;"><span>) </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#366">print</span>(response<span style="color:#555">.</span>choices[<span style="color:#f60">0</span>]<span style="color:#555">.</span>message<span style="color:#555">.</span>content) </span></span></code></pre></div><h2 id="5框架">5/框架</h2> <p>除了上面几种偏底层的模型的使用方法外,还有更偏向于应用侧的模型使用方式,那就是使用 Langchain 或者 openai agent sdk 这样的框架,这些框架是把大模型做了进一步的抽象,方便编写各种业务应用。</p> <div class="highlight"><pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#09f;font-style:italic"># 使用langchain调用 DeepSeek 模型</span> </span></span><span style="display:flex;"><span><span style="color:#069;font-weight:bold">from</span> <span style="color:#0cf;font-weight:bold">langchain_deepseek</span> <span style="color:#069;font-weight:bold">import</span> ChatDeepSeek </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>llm <span style="color:#555">=</span> ChatDeepSeek( </span></span><span style="display:flex;"><span> api_key<span style="color:#555">=</span>env_settings<span style="color:#555">.</span>DS_API_KEY, </span></span><span style="display:flex;"><span> model<span style="color:#555">=</span><span style="color:#c30">&#34;deepseek-ai/DeepSeek-R1&#34;</span>, </span></span><span style="display:flex;"><span> temperature<span style="color:#555">=</span><span style="color:#f60">0</span>, </span></span><span style="display:flex;"><span> max_tokens<span style="color:#555">=</span><span style="color:#069;font-weight:bold">None</span>, </span></span><span style="display:flex;"><span> timeout<span style="color:#555">=</span><span style="color:#069;font-weight:bold">None</span>, </span></span><span style="display:flex;"><span> max_retries<span style="color:#555">=</span><span style="color:#f60">2</span>, </span></span><span style="display:flex;"><span> api_base<span style="color:#555">=</span><span style="color:#c30">&#39;https://api.siliconflow.cn&#39;</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>) </span></span><span style="display:flex;"><span>messages <span style="color:#555">=</span> [ </span></span><span style="display:flex;"><span> (<span style="color:#c30">&#34;system&#34;</span>, <span style="color:#c30">&#34;You are a helpful translator. Translate the user sentence to French.&#34;</span>), </span></span><span style="display:flex;"><span> (<span style="color:#c30">&#34;human&#34;</span>, <span style="color:#c30">&#34;I love programming.&#34;</span>), </span></span><span style="display:flex;"><span>] </span></span><span style="display:flex;"><span><span style="color:#09f;font-style:italic"># print(llm.invoke(messages))</span> </span></span><span style="display:flex;"><span><span style="color:#09f;font-style:italic"># print(llm.invoke(&#34;hello&#34;).content)</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>ai_message <span style="color:#555">=</span> llm<span style="color:#555">.</span>invoke([ </span></span><span style="display:flex;"><span> SystemMessage(content<span style="color:#555">=</span><span style="color:#c30">&#34;please speak in chinese&#34;</span>), </span></span><span style="display:flex;"><span> HumanMessage(content<span style="color:#555">=</span><span style="color:#c30">&#34;hello&#34;</span>), </span></span><span style="display:flex;"><span>]) </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#366">print</span>(ai_message<span style="color:#555">.</span>content) </span></span></code></pre></div>

2025/12/23
阅读更多

当我使用Claude Code时我在想什么

<p>使用 Claude Code(又名 cc) 作为编程工具已经有一段时间了,claude code 也陆陆续续迭代了好几个版本,在编程上的体验可以说是越来越好,对我来说唯一还能吐槽的点就是由于网络的原因,速度上还是有些慢。</p> <p>使用 claude code 这一段时间,其实还改变了我一个坏毛病:接到需求直接编码。其实,不管使用哪款 AI 工具,都不能上来直接编码,其实你可以把 AI 当成一个普通员工,初次见面,你头脑里的很多想法不可能一次让对方全部理解。</p> <p>我总结出来的黄金工作流程是:<code>Research → Plan →Code</code></p> <p>不论让 Claude 开始编写一个新需求,还是修改任何代码之前,先探索你的代码库并收集上下文信息,都是最重要的事,也是整个流程的核心要义,具体来说:</p> <ol> <li>研究 (Research):研究一下 xx 系统是如何工作的。 找到所有相关的文件,并解释当前的流程。</li> <li>计划 (Plan):现在请制定一个关于添加 xx功能 的详细方案。 将其拆分为具体步骤,并识别可能出现的问题。</li> <li>编码 (Code):根据你的方案实现 xx 解决方案。 在执行过程中逐步验证每个步骤,并在完成时提交。</li> </ol> <h2 id="初始设置">初始设置</h2> <p>无论我们用哪个工具(更准确的应该叫 AI Agent)去协助我们编程,claude code、gemini 或者 cursor,他们都需要一份&quot;说明书&quot;来了解这个项目的背景和规则,否则每次它都要猜你的意图。在 Claude Code 中这份初始设置文件名是 CLAUDE.md。</p> <blockquote> <p>注意:各家 AI Agent 的初始设置文件的名字和位置不同,创建文件的时候一定要注意。</p> </blockquote> <p>初始设置文件的优先级顺序由高到低如下: 1.对话指令 2.项目中的设置文件 3.系统级的设置文件</p> <p>如果团队使用多种 AI 工具,可以试一下下面的结构:</p> <p><img src="https://imgproxy.citrusreader.com/iXv9oioT4z57tU1ucyMYX7WtpTepbgKtJJwDdGGDLLk/raw:1/aHR0cHM6Ly9hcnRpY2xlLTEyNTcwOTUwMTYuY29zLmFwLWJlaWppbmcubXlxY2xvdWQuY29tL0NsZWFuU2hvdCUyMDIwMjUtMTItMTElMjBhdCUyMDE0LjM0LjMwQDJ4LnBuZw" alt="项目结构"></p> <p>接下来,初始设置文件的内容该写些什么呢?</p> <p>1.项目基本背景、技术栈 2.代码风格 3.安全规范 4.测试要求 5.特殊规则</p> <p>举个例子,</p> <div class="highlight"><pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>claude.md </span></span><span style="display:flex;"><span>项目背景(Project Context) </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>本项目是一个基于 Node.js / TypeScript 的 API 服务,使用 Express 作为 Web 框架,并采用 Prisma ORM 进行数据库操作。 </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>代码风格(Code Style) </span></span><span style="display:flex;"><span>- 使用函数式组件与 hooks 风格 </span></span><span style="display:flex;"><span>- 优先使用 async/await,避免直接使用 promise 链 </span></span><span style="display:flex;"><span>- 单行最大长度限制为 <span style="color:#f60">100</span> 字符 </span></span><span style="display:flex;"><span>测试要求(Testing Requirements) </span></span><span style="display:flex;"><span>- 每个新功能都必须包含 单元测试 </span></span><span style="display:flex;"><span>- API 接口需撰写 集成测试 </span></span><span style="display:flex;"><span>- 使用 Jest 作为测试框架 </span></span><span style="display:flex;"><span>常见问题与注意事项(Common Pitfalls) </span></span><span style="display:flex;"><span>- 始终对用户输入进行验证 </span></span><span style="display:flex;"><span>- 使用参数化查询,绝不拼接字符串 </span></span><span style="display:flex;"><span>- 在访问数据前必须进行身份验证 </span></span><span style="display:flex;"><span>- 记录错误日志,但不要向客户端暴露堆栈信息 </span></span><span style="display:flex;"><span>- 列名使用 snake_case </span></span><span style="display:flex;"><span>- 表名使用复数形式 </span></span><span style="display:flex;"><span>- 每张表必须包含 created_at 与 updated_at 字段 </span></span><span style="display:flex;"><span>- 使用 deleted_at 字段进行软删除 </span></span></code></pre></div><h2 id="指令技巧">指令技巧</h2> <p>有了初始设置文件作为&quot;规范&quot;后,我们还需要了解和 AI 沟通协作的技巧:</p> <ol> <li>@指定特定的文件:让 AI 聚焦于相关代码。</li> <li>使用 plan mode 先做规划(shift+tab):强制 AI 思考(对应我们的 <strong>Plan</strong> 步骤)。</li> <li>/ 自定义命令:自动化重复性任务。</li> </ol> <p>举个例子, 每次<strong>提交代码</strong>都要思考如何撰写 <strong>commit message</strong>,能否自动生成提交信息?</p> <p>在项目根目录下建立文件:.claude/commands/commit.md</p> <p>输入以下内容:</p> <div class="highlight"><pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>--- </span></span><span style="display:flex;"><span>description: Generate commit message and commit </span></span><span style="display:flex;"><span>--- </span></span><span style="display:flex;"><span>请执行以下步骤: </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>1. 执行 <span style="color:#c30">`</span>git status<span style="color:#c30">`</span> 和 <span style="color:#c30">`</span>git diff<span style="color:#c30">`</span> 查看变更 </span></span><span style="display:flex;"><span>2. 分析变更内容,生成符合 Conventional Commits 规范的 commit message </span></span><span style="display:flex;"><span>3. 格式:<span style="color:#c30">`</span>type<span style="color:#555">(</span>scope<span style="color:#555">)</span>: description<span style="color:#c30">`</span> </span></span><span style="display:flex;"><span> - type: feat/fix/refactor/docs/test </span></span><span style="display:flex;"><span> - 描述需要清楚说明「做了什么」以及「为什么」 </span></span><span style="display:flex;"><span>4. 询问我是否要执行 commit </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>请不要直接 commit,先让我确认 message。 </span></span></code></pre></div><p>下次提交代码的时候在命令行中,直接输入/commit 就可以了。</p> <p><img src="https://imgproxy.citrusreader.com/1sgEPvkiE5qtN0BICzLVMSqD24dc16Xt5NPQMydjxBU/raw:1/aHR0cHM6Ly9hcnRpY2xlLTEyNTcwOTUwMTYuY29zLmFwLWJlaWppbmcubXlxY2xvdWQuY29tL0NsZWFuU2hvdCUyMDIwMjUtMTItMTElMjBhdCUyMDE0LjUxLjIyQDJ4LnBuZw" alt="使用/commit 命令后"></p> <h2 id="mcp">MCP</h2> <p>其实 mcp 也算是一种使用技巧,和上面介绍的通用技巧比需要提前安装一些插件。Claude Code 的 mcp 默认安装路径是在全局设置中,这一点要格外注意,默认路径在/Users/xxxx/.claude.json</p> <p>这里以常用的 pgsql 和 cotext7为例,介绍一下安装和使用的方法。</p> <div class="highlight"><pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#09f;font-style:italic">#安装</span> </span></span><span style="display:flex;"><span>claude mcp add postgres npx -- -y @modelcontextprotocol/server-postgres postgresql://localhost:5432 </span></span><span style="display:flex;"><span>claude mcp add --transport http context7 https://mcp.context7.com/mcp --header <span style="color:#c30">&#34;CONTEXT7_API_KEY:去官网申请&#34;</span> </span></span><span style="display:flex;"><span>记得在 claude.md 中增加下面内容,不然以后每次输入需求后,还要在最后加一句 use context7,特别麻烦。 </span></span><span style="display:flex;"><span>Always use context7 when I need code generation, setup or configuration steps, or </span></span><span style="display:flex;"><span>library/API documentation. This means you should automatically use the Context7 MCP </span></span><span style="display:flex;"><span>tools to resolve library id and get library docs without me having to explicitly ask. </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#09f;font-style:italic"># 查看 mcp,只针对本项目,不是全局的</span> </span></span><span style="display:flex;"><span>claude mcp list </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#09f;font-style:italic"># 使用 mcp</span> </span></span><span style="display:flex;"><span>claude <span style="color:#c30">&#34;Query the users table&#34;</span> </span></span></code></pre></div><p><img src="https://imgproxy.citrusreader.com/e0S4ZVixCUzS6OHh6lWsxaxO_rY5cXqV2MY97JtjMmM/raw:1/aHR0cHM6Ly9hcnRpY2xlLTEyNTcwOTUwMTYuY29zLmFwLWJlaWppbmcubXlxY2xvdWQuY29tL0NsZWFuU2hvdCUyMDIwMjUtMTItMTElMjBhdCUyMDE1LjA5LjIwQDJ4LnBuZw" alt="pgsql mcp 的使用效果"></p> <h2 id="关于需求">关于需求</h2> <p>最后我还想回归到最开始的需求,明晰清楚需求的内涵和外延是做好一个项目最终的事情,在使用 Claude Code 来构建项目之前,花 15-20 分钟进行研究:</p> <ul> <li>最佳实践:其他人是如何解决类似问题的?在网上搜索博客文章、Stack Overflow 讨论和文档。</li> <li>自己的模式:你是如何解决类似问题的?在现有的代码库中搜索类似的功能。</li> <li>库的功能:你的项目会使用外部库吗?如果你正在使用特定的代码库,让人工智能阅读其文档或源代码。</li> </ul> <p>让人工智能将这些研究综合成一个计划,内容包括:</p> <ul> <li>要解决的问题(一个清晰的句子)</li> <li>两到三种解决方案(每种方案都要诚实地列出其优缺点)</li> <li>任何边缘情况或安全考虑</li> </ul> <p>审查该计划并留意自己的反应。你的想法和反应很重要。当你认为 &ldquo;这太复杂了&rdquo; 或 &ldquo;我们已经有更好的方法来做这件事&rdquo; 时,不要只是修改这个计划,还要记录下你为什么会这么想。把它写下来。</p> <p>最后根据该计划开发这个功能。</p> <p>完成整个项目后,将最终的实现与原始计划进行比较。你在哪些地方偏离了计划?为什么?怎样才能让这个计划更好?将自己的反思记录下来,用于下一个项目的开发。</p> <p>在下一个项目中,把自己的反思添加到你的 CLAUDE.md 文件中。写下一条规则,比如 &ldquo;在做 X 类型的工作时,记得检查 Y&rdquo;,或者 &ldquo;我更喜欢方法 A 而不是方法 B,因为原因 C&rdquo;。</p> <p>另外,对于一些需求很模糊,甚至是只有一句话的需求,可以试试下面的提示词,让 AI 帮忙。</p> <div class="highlight"><pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>需求的确认: </span></span><span style="display:flex;"><span>你是一位资深产品顾问(C - Capacity) </span></span><span style="display:flex;"><span>背景:我在做一个电商平台,客户反应「结帐流程太慢」(I - Insight) </span></span><span style="display:flex;"><span>请用苏格拉底提问法,帮我厘清这个需求背后的真实问题(S - Statement) </span></span><span style="display:flex;"><span>请用反向提问的方式引导我思考,不要直接给答案(P - Personality) </span></span><span style="display:flex;"><span>最后提供 <span style="color:#f60">3</span> 种可能的理解方向让我选择(E - Experiment) </span></span></code></pre></div>

2025/12/11
阅读更多

如何使用Skill_Seeker助力 Skills 开发

<h1 id="claude-ai-skills-是什么">Claude AI skills 是什么</h1> <p>Claude AI skills 可以让 AI 获得特定领域的最新知识。实现某个专业领域的能力输出,对特定框架或者库的 API 理解,提供最佳代码实例。</p> <h2 id="如果手动创建-skills-有什么问题">如果手动创建 skills 有什么问题?</h2> <p>人工阅读文档非常耗时,整理和分类内容需要专业知识,实时保持版本最新十分困难。</p> <h2 id="如果使用ai帮忙呢">如果使用AI帮忙呢</h2> <p>快速开发一个 skill 能力,可以通过大模型来帮忙。比如我想写一个 xx 领域风格的文档,可以使用下面提示词。</p> <blockquote> <p>请为我开发一个简单的 Skill。如果你能构建正确的文件夹结构,并打包成一个 ZIP 文件供我下载,那是最理想的。</p> <p>这个 Skill 的目的,是为了让 AI 更符合xx领域的写作风格。通常情况下,当我给 AI 一个 codebase 的项目时,他会仔细阅读项目代码,然后根据相应的模板写出一根专业的文档 我希望句子、段落之间能够更加顺滑、自然地衔接。我希望它能在目标语言中正确使用标点符号。 我并不擅长xx领域技术文书的写作风格。我希望你能通过提问来帮助理解我真正想要的风格,然后再完成这个 Skill 的开发。</p> </blockquote> <h2 id="如果使用skill_seeker这个工具呢">如果使用Skill_Seeker这个工具呢</h2> <p>还有一种场景,我们只是针对一个网站或者一份 pdf 文档,想把它转化为专业的能力输出,这时就可以用到Skill_Seeker[https://github.com/yusufkaraaslan/Skill_Seekers]这个工具。</p> <pre tabindex="0"><code># 安装 uv tool install skill-seekers # 基本命令 skill-seekers scrape --url https://akshare.akfamily.xyz/ --name akshare skill-seekers pdf --pdf docs/manual.pdf --name myskill skill-seekers pdf --pdf docs/scanned.pdf --name myskill --ocr </code></pre><p>举个例子,我想学习 akshare 这个库的基本功能,它的在线文档的地址是https://akshare.akfamily.xyz/,如果一页一页的看太麻烦,于是使用skill-seekers帮我自动化的学习。</p> <p>学习完成后,会生成如下的目录结构。</p> <p><img src="https://imgproxy.citrusreader.com/RgI6i5FU68nMlwsF69uw7cXn1MMdZeweWKD9TFbhDB0/raw:1/aHR0cHM6Ly9hcnRpY2xlLTEyNTcwOTUwMTYuY29zLmFwLWJlaWppbmcubXlxY2xvdWQuY29tL0NsZWFuU2hvdCUyMDIwMjUtMTItMDglMjBhdCUyMDE2LjE1LjQ4QDJ4LnBuZw" alt="CleanShot 2025-12-08 at 16.15.48@2x"></p> <p>我们只需要拷贝 akshare 这个目录到我们代码根目录下的.claude/skills 下面,claude 就具备了 akshare 的基础知识。</p> <p><img src="https://imgproxy.citrusreader.com/1YCFXoeY7hN5fo0kM5A1CKlv5O7NwuIKAAOvj8SIfyw/raw:1/aHR0cHM6Ly9hcnRpY2xlLTEyNTcwOTUwMTYuY29zLmFwLWJlaWppbmcubXlxY2xvdWQuY29tL0NsZWFuU2hvdCUyMDIwMjUtMTItMDglMjBhdCUyMDE2LjE5LjE4LnBuZw" alt="CleanShot 2025-12-08 at 16.19.18"></p>

2025/12/8
阅读更多

使用 pyspark 处理数据的基本流程

<p><img src="https://imgproxy.citrusreader.com/likpSmpXmTscicP3DBO4tAjvnVaQI8QPr3p86-VRA3w/raw:1/aHR0cHM6Ly9waGhxeWJnOG1tLmZlaXNodS5jbi9zcGFjZS9hcGkvYm94L3N0cmVhbS9kb3dubG9hZC9hc3luY2NvZGUvP2NvZGU9WVdOaFlUUTBZall6WXpRME5EWmpPR014WlRGbU1qazBPVFF3T0RBMU1UTmZOWGN5U1ZWblRuWnFTbUkxVDJ4U1lraHJNM04zTVRWb2EzTXhVR05RYVhSZlZHOXJaVzQ2V2xVMGRXSlRlbk5XYjNaa1lWRjRTVVpGUW1NNGFWUnBibkJsWHpFM05qSTROVEV6TmpVNk1UYzJNamcxTkRrMk5WOVdOQQ" alt="总流程"></p> <h2 id="读数据">读数据</h2> <p>读数据前,要考虑两个方面:数据的外观(对外呈现给我们的样子)和内在(具体存放的位置)。具体来说,要考虑:</p> <ul> <li>表头</li> <li>schema</li> <li>存储格式(尽量用 parquet,列式存储,压缩性能,读取快)</li> <li>数据量</li> </ul> <div class="highlight"><pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Python" data-lang="Python"><span style="display:flex;"><span>df <span style="color:#555">=</span> spark<span style="color:#555">.</span>read<span style="color:#555">.</span>csv(<span style="color:#c30">&#34;s3://mybucket/large_dataset.csv&#34;</span>, header<span style="color:#555">=</span><span style="color:#069;font-weight:bold">True</span>, inferSchema<span style="color:#555">=</span><span style="color:#069;font-weight:bold">True</span>) </span></span><span style="display:flex;"><span>df <span style="color:#555">=</span> spark<span style="color:#555">.</span>read<span style="color:#555">.</span>parquet(<span style="color:#c30">&#34;s3://mybucket/large_dataset/&#34;</span>) </span></span></code></pre></div><p>读完数据后,要查看(探索)数据</p> <div class="highlight"><pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Python" data-lang="Python"><span style="display:flex;"><span><span style="color:#366">print</span>(<span style="color:#c30">f</span><span style="color:#c30">&#34;Total Rows: </span><span style="color:#a00">{</span>df<span style="color:#555">.</span>count()<span style="color:#a00">}</span><span style="color:#c30">&#34;</span>) </span></span><span style="display:flex;"><span>df<span style="color:#555">.</span>printSchema() </span></span><span style="display:flex;"><span>df<span style="color:#555">.</span>show(<span style="color:#f60">5</span>) </span></span></code></pre></div><h2 id="数据清洗">数据清洗</h2> <p>这个阶段就涉对数据内容的操作了,可以通过函数算子的方式(记不住的话可以通过官网查看),也可以通过 sql 的方式。</p> <div class="highlight"><pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Python" data-lang="Python"><span style="display:flex;"><span><span style="color:#069;font-weight:bold">from</span> <span style="color:#0cf;font-weight:bold">pyspark.sql.functions</span> <span style="color:#069;font-weight:bold">import</span> col, when, avg </span></span><span style="display:flex;"><span><span style="color:#09f;font-style:italic"># Drop null values</span> </span></span><span style="display:flex;"><span>df <span style="color:#555">=</span> df<span style="color:#555">.</span>na<span style="color:#555">.</span>drop() </span></span><span style="display:flex;"><span><span style="color:#09f;font-style:italic"># Filter out invalid entries</span> </span></span><span style="display:flex;"><span>df <span style="color:#555">=</span> df<span style="color:#555">.</span>filter(col(<span style="color:#c30">&#34;age&#34;</span>) <span style="color:#555">&gt;</span> <span style="color:#f60">0</span>) </span></span><span style="display:flex;"><span><span style="color:#09f;font-style:italic"># Add a calculated column</span> </span></span><span style="display:flex;"><span>df <span style="color:#555">=</span> df<span style="color:#555">.</span>withColumn(<span style="color:#c30">&#34;income_per_person&#34;</span>, col(<span style="color:#c30">&#34;total_income&#34;</span>) <span style="color:#555">/</span> col(<span style="color:#c30">&#34;family_size&#34;</span>)) </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#09f;font-style:italic"># Replace missing values with mean</span> </span></span><span style="display:flex;"><span>mean_val <span style="color:#555">=</span> df<span style="color:#555">.</span>select(avg(<span style="color:#c30">&#34;salary&#34;</span>))<span style="color:#555">.</span>collect()[<span style="color:#f60">0</span>][<span style="color:#f60">0</span>] </span></span><span style="display:flex;"><span>df <span style="color:#555">=</span> df<span style="color:#555">.</span>na<span style="color:#555">.</span>fill({<span style="color:#c30">&#34;salary&#34;</span>: mean_val}) </span></span></code></pre></div><h2 id="数据计算">数据计算</h2> <p>这个阶段也是对数据内容的操作,只是数据的内容和格式已经比较规范。这个阶段要注意对资源的利用,时刻通过 sparkUI 关注计算效率。尽量把 task 分配到不同 exector 上,最大化利用集群的力量。</p> <div class="highlight"><pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-SQL" data-lang="SQL"><span style="display:flex;"><span>region_stats<span style="color:#bbb"> </span><span style="color:#555">=</span><span style="color:#bbb"> </span>df.groupBy(<span style="color:#c30">&#34;region&#34;</span>)<span style="color:#bbb"> </span><span style="color:#a00;background-color:#faa">\</span><span style="color:#bbb"> </span></span></span><span style="display:flex;"><span><span style="color:#bbb"> </span>.agg(<span style="color:#069;font-weight:bold">avg</span>(<span style="color:#c30">&#34;income_per_person&#34;</span>).<span style="color:#069;font-weight:bold">alias</span>(<span style="color:#c30">&#34;avg_income&#34;</span>))<span style="color:#bbb"> </span><span style="color:#a00;background-color:#faa">\</span><span style="color:#bbb"> </span></span></span><span style="display:flex;"><span><span style="color:#bbb"> </span>.orderBy(col(<span style="color:#c30">&#34;avg_income&#34;</span>).<span style="color:#069;font-weight:bold">desc</span>())<span style="color:#bbb"> </span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>region_stats.<span style="color:#069;font-weight:bold">show</span>(<span style="color:#f60">10</span>)<span style="color:#bbb"> </span></span></span><span style="display:flex;"><span><span style="color:#bbb"> </span></span></span><span style="display:flex;"><span><span style="color:#bbb"> </span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#a00;background-color:#faa">这一阶段可能会遇到数据倾斜的问题。</span><span style="color:#bbb"> </span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#a00;background-color:#faa">对于小表</span><span style="color:#bbb"> </span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#069;font-weight:bold">from</span><span style="color:#bbb"> </span>pyspark.<span style="color:#069;font-weight:bold">sql</span>.functions<span style="color:#bbb"> </span>import<span style="color:#bbb"> </span>broadcast<span style="color:#bbb"> </span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#555">#</span><span style="color:#bbb"> </span>Broadcast<span style="color:#bbb"> </span><span style="color:#069;font-weight:bold">join</span><span style="color:#bbb"> </span><span style="color:#069;font-weight:bold">for</span><span style="color:#bbb"> </span>small<span style="color:#bbb"> </span>tables<span style="color:#bbb"> </span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>df<span style="color:#bbb"> </span><span style="color:#555">=</span><span style="color:#bbb"> </span>large_df.<span style="color:#069;font-weight:bold">join</span>(broadcast(small_df),<span style="color:#bbb"> </span><span style="color:#c30">&#34;customer_id&#34;</span>)<span style="color:#bbb"> </span></span></span><span style="display:flex;"><span><span style="color:#bbb"> </span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span><span style="color:#a00;background-color:#faa">对于大表</span><span style="color:#bbb"> </span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>df1<span style="color:#bbb"> </span><span style="color:#555">=</span><span style="color:#bbb"> </span>df1.repartition(<span style="color:#c30">&#34;customer_id&#34;</span>)<span style="color:#bbb"> </span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>df2<span style="color:#bbb"> </span><span style="color:#555">=</span><span style="color:#bbb"> </span>df2.repartition(<span style="color:#c30">&#34;customer_id&#34;</span>)<span style="color:#bbb"> </span></span></span><span style="display:flex;"><span><span style="color:#bbb"></span>joined_df<span style="color:#bbb"> </span><span style="color:#555">=</span><span style="color:#bbb"> </span>df1.<span style="color:#069;font-weight:bold">join</span>(df2,<span style="color:#bbb"> </span><span style="color:#c30">&#34;customer_id&#34;</span>)<span style="color:#bbb"> </span></span></span></code></pre></div><h2 id="数据保存">数据保存</h2> <p>一般保存成表或者文件</p> <div class="highlight"><pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Python" data-lang="Python"><span style="display:flex;"><span>df<span style="color:#555">.</span>write<span style="color:#555">.</span>mode(<span style="color:#c30">&#34;overwrite&#34;</span>)<span style="color:#555">.</span>parquet(<span style="color:#c30">&#34;s3://mybucket/processed_data/&#34;</span>) </span></span><span style="display:flex;"><span>df<span style="color:#555">.</span>write<span style="color:#555">.</span>format(<span style="color:#c30">&#34;jdbc&#34;</span>)<span style="color:#555">.</span>option(<span style="color:#c30">&#34;url&#34;</span>, <span style="color:#c30">&#34;jdbc:mysql://...&#34;</span>)<span style="color:#555">.</span>save() </span></span></code></pre></div><h2 id="数据可视化">数据可视化</h2> <div class="highlight"><pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-Python" data-lang="Python"><span style="display:flex;"><span><span style="color:#069;font-weight:bold">import</span> <span style="color:#0cf;font-weight:bold">matplotlib.pyplot</span> <span style="color:#069;font-weight:bold">as</span> <span style="color:#0cf;font-weight:bold">plt</span> </span></span><span style="display:flex;"><span>sample <span style="color:#555">=</span> df<span style="color:#555">.</span>limit(<span style="color:#f60">10000</span>)<span style="color:#555">.</span>toPandas() </span></span><span style="display:flex;"><span>sample[<span style="color:#c30">&#39;income_per_person&#39;</span>]<span style="color:#555">.</span>hist(bins<span style="color:#555">=</span><span style="color:#f60">50</span>) </span></span><span style="display:flex;"><span>plt<span style="color:#555">.</span>title(<span style="color:#c30">&#34;Income Distribution&#34;</span>) </span></span><span style="display:flex;"><span>plt<span style="color:#555">.</span>show() </span></span></code></pre></div>

2025/11/11
阅读更多

推荐订阅

Recent content on forecho's Blog

Provides a better reading experience (full articles) over the official ones.