<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
>
<channel>
<title><![CDATA[51学习网]]></title> 
<atom:link href="http://www.51keeplearning.com/rss.php" rel="self" type="application/rss+xml" />
<description><![CDATA[AI导航、AI资讯、AI开发]]></description>
<link>http://www.51keeplearning.com/</link>
<language>zh-cn</language>
<generator>www.emlog.net</generator>
<item>
    <title>AI与机器人的关系：从“造身”到“赋魂”</title>
    <link>http://www.51keeplearning.com/post-144.html</link>
    <description><![CDATA[<h1>AI与机器人的关系：从“造身”到“赋魂”</h1>
<p>2026年8月，北京亦庄，世界机器人大会的展馆内，一台名为Galbot ET1的双足人形机器人正在学习打网球。它不仅能自主维持身体平衡、精准控制腕部动作，还能通过左右吊球等战术与人类选手展开对抗 。支撑这一切的，是银河通用自研的具身大模型“银河星脑”与物理世界原生智能体AstraBrain-Agent的深度协同 。</p>
<p>这一幕，恰如其分地揭示了AI与机器人关系的本质跃迁：机器人不再是预设程序的冰冷执行者，AI也不再是困于数字世界的“缸中之脑”。两者的深度融合，正催生出一个全新的智能物种——具身智能。</p>
<h2>从“身体”到“大脑”：一场竞赛重心的转移</h2>
<p>过去十年，机器人竞赛的核心是“身体”：谁能走得更稳、跑得更快、关节更灵活。而今，行业重心正在发生根本性转移。高盛在2026年7月发布的研究报告明确指出，行业焦点已从“身体能力”转向“智能决策” 。在具身智能的产业链中，“大脑”的溢价已远高于“身体”。正如一位机器人产业投资人所言：“国内机器人硬件产业链已无代际差距，核心差距在‘大脑’。具身智能的投资逻辑正从押注机器人本体转向押注具身大脑。” </p>
<p>这一判断最直观的体现，来自蚂蚁灵波科技在WRC上的展示。其“全栈大脑2.0”驱动的机器人，能在药房、工厂、仓库三种截然不同的场景中连续作业，并且这套大脑在预训练阶段就适配了17个机器人品牌、20多种构型，可实现单臂、双臂、双足、轮式，以及头部、腰部、灵巧手等多自由度的协同控制 。同一种大脑，驱动不同形态的“身体”，完成截然不同的任务——这正是“一脑多机”的产业图景。</p>
<h2>“大脑”的进化：VLA模型与世界模型</h2>
<p>机器人“大脑”的技术路线正日益清晰。当前，两条主流的“大脑”路线正在并行演进：视觉—语言—行动模型（VLA）与世界模型 。</p>
<p>VLA模型赋予机器人将传感器信息与自然语言指令直接转化为实际动作的能力，它吸收了海量带动作标签的机器人数据，让机器学会“怎么做” 。而世界模型，则被图灵奖得主杨立昆定义为“能预测行动后果的内部模拟器” ，是机器人认知物理世界、预判行动后果的核心引擎。它消化海量视频数据，让机器理解“世界是怎样的”。</p>
<p>银河通用提出的世界—动作模型（WAM）架构，正是试图将这两种能力纳入统一模型，实现跨本体、跨任务、跨场景的操作能力 。而宇树科技则更进一步，正在预研物理AI机器人自进化体系：让智能体自主检索全球前沿论文、生成控制代码，在仿真与实物测试中完成闭环迭代，最终实现机器人的自我进化 。</p>
<h2>落地的考验：泛化、数据与场景</h2>
<p>尽管“大脑”的技术路径日渐清晰，但行业共识是：具身智能尚未迎来它的“ChatGPT时刻”。宇树科技董事长王兴兴直言，当前机器人的最大瓶颈是泛化能力不足——在固定场景中，任务成功率可接近100%，但一旦操作物品或环境发生变化，成功率便大幅下滑 。</p>
<p>根本原因在于误差累积：语言模型的输入输出处于数字向量空间，几乎无误差；但机器人的每一次感知、决策与执行都会产生误差，末端操作的毫米级偏差最终会导致任务失败 。这解释了为什么世界模型在2026年成为行业焦点——它试图在行动之前，先在内部推演多种可能、预判不同后果，以减少物理世界中的试错成本 。</p>
<p>然而，实验室与展台上的突破，最终必须走向真实场景。从蚂蚁灵波在上海国大药房正式上岗的夜班分拣机器人 ，到银河通用已在宁德时代产线常态化运行的工业机器人 ，再到源络科技与国家级科研实验室联合研发、在实验台上完成亚毫米级精准操作的实验室机器人 ——真正的产业价值，正在于AI与机器人结合后，在真实世界中解决问题的能力。</p>
<p>当一台机器人能在80%的陌生环境中，通过自然语言指令完成80%的日常任务时，具身智能的“ChatGPT时刻”便会到来 。这场从“造身”到“赋魂”的跨越，或许就在2至3年内发生 。而它将开启的，不仅是机器人的新时代，更是AI真正理解物理世界、与人类共存共融的新纪元。</p>]]></description>
    <pubDate>Fri, 21 Aug 2026 18:10:04 +0800</pubDate>
    <dc:creator>emer</dc:creator>
    <guid>http://www.51keeplearning.com/post-144.html</guid>
</item>
<item>
    <title>资源福利分享-不定时更新</title>
    <link>http://www.51keeplearning.com/post-143.html</link>
    <description><![CDATA[<p>1.AI短剧·漫剧全能工坊<br />
<a href="https://metaso.cn/s/mey8NtJ">https://metaso.cn/s/mey8NtJ</a><br />
<img src="http://www.51keeplearning.com/content/uploadfile/202608/aab71786373456.png" alt="" /></p>]]></description>
    <pubDate>Mon, 10 Aug 2026 21:38:31 +0800</pubDate>
    <dc:creator>emer</dc:creator>
    <guid>http://www.51keeplearning.com/post-143.html</guid>
</item>
<item>
    <title>Happy-LLM 学习笔记 07：从预训练到 LoRA，训练流程要先看数据和成本</title>
    <link>http://www.51keeplearning.com/post-138.html</link>
    <description><![CDATA[<p>第五章是手写一个小 LLM，第六章的重点则变成：真正做训练时，为什么大家通常不会一直停留在手写训练循环里。答案很现实，模型结构、数据处理、分布式训练、checkpoint 恢复、日志监控和参数高效微调，每一项单独看都不难，合在一起却很容易把实验搞乱。</p> 
<p>这一章给我的最大启发是：训练流程不是“模型 forward 一下再 backward 一下”，而是一条必须能被恢复、能被复现、能控制成本的流水线。先想明白数据要学什么、资源能承受什么，再决定 Pretrain、SFT 还是 LoRA，比先写训练脚本重要得多。</p> 
<h2>从手写实现转向 Transformers 生态</h2> 
<p>手写模型和训练流程很适合理解原理，但不适合长期维护。新模型结构更新很快，自己实现不仅要追结构，还要处理 tokenizer、权重加载、分布式训练和保存格式。更麻烦的是，手写模型通常很难直接复用社区里的预训练权重，实验一开始就和主流生态脱节了。</p> 
<p>Transformers 的价值不只是“少写代码”。<code>AutoConfig</code>、<code>AutoModelForCausalLM</code>、<code>AutoTokenizer</code> 把模型配置、权重和分词器统一到一套接口里；<code>Trainer</code> 又把训练循环、日志、保存、混合精度、梯度累积和分布式启动封装起来。这样一来，研究者可以把注意力放在数据和实验设计上，而不是每次重新搭基础设施。</p> 
<p>这并不意味着底层原理不重要。恰恰相反，只有知道模型是怎么 forward、loss 是怎么算、mask 是怎么控制的，使用框架时才不容易被默认参数带偏。框架应该帮我们减少重复劳动，而不是替我们做所有判断。</p> 
<h2>Pretrain 和 SFT 的差别，最后都体现在数据里</h2> 
<p>这一章把 Pretrain 和 SFT 放在同一个 Transformers 流程里实现，但两者的数据处理逻辑完全不同。Pretrain 面向连续文本，通常会把大量文本 tokenize 后拼接成固定长度的 block，例如 2048 个 token。因为目标是因果语言建模，<code>labels</code> 基本就是 <code>input_ids</code> 的副本，模型需要对整段文本学习“下一个 token 是什么”。</p> 
<p>SFT 面向对话或指令数据，重点变成角色边界和 loss 掩码。教程沿用了 Qwen 风格的 <code>&lt;|im_start|&gt;</code> 与 <code>&lt;|im_end|&gt;</code> 模板，把 system、user、assistant 拼成一条序列，但只让 assistant 的回答参与 loss。user 的问题、system 的设定和 padding 位置都会被 <code>IGNORE_TOKEN_ID</code> 遮蔽。</p> 
<p>这个差异非常值得记住。Pretrain 是让模型“读懂并续写世界”，SFT 是让模型“在指定上下文里给出合格回答”。如果 SFT 里把 prompt 也算进 loss，模型可能会去学习用户怎么提问；如果特殊 token、换行或角色边界拼错，模型推理时就会出现格式漂移。很多微调效果问题，其实不是模型能力不够，而是监督信号给错了位置。</p> 
<h2>DeepSpeed 解决的不是神秘感，而是训练能不能跑完</h2> 
<p>第六章使用 DeepSpeed 启动多卡训练，并采用 ZeRO-2 配置。对个人实验来说，这部分看起来工程味很重，但它解决的问题非常具体：单卡显存不够、训练时间太长、长时间任务可能中断。</p> 
<p><code>pretrain.sh</code> 和 <code>finetune.sh</code> 里真正值得关注的不是每个参数都记住，而是几个组合关系。<code>per_device_train_batch_size</code> 决定单卡一次吃多少样本，<code>gradient_accumulation_steps</code> 决定累积多少步再更新，<code>gradient_checkpointing</code> 用额外计算换显存，<code>bf16</code> 降低数值精度开销，<code>save_steps</code> 和 checkpoint 恢复保证训练中断后不用从零再来。DeepSpeed 则负责把这些训练状态分布到多卡上。</p> 
<p>我以前容易把“上大模型训练”理解成直接堆显卡，现在会更谨慎。显存是否够用，取决于参数、梯度、优化器状态、激活值、batch size、序列长度和并行策略。只改其中一个参数不一定有效，反而可能让吞吐更差。真正稳妥的做法是先用小样本、短序列和小 batch 验证路径，再逐步扩大规模。</p> 
<h2>LoRA 的核心不是省参数，而是明确只改一小部分</h2> 
<p>LoRA 的公式很简洁：冻结原始权重 <code>W0</code>，只学习低秩矩阵 <code>A</code> 和 <code>B</code>，用 <code>BA</code> 近似权重更新量。放在 Transformer 里，常见做法是把 LoRA 接到注意力层的 <code>q_proj</code>、<code>v_proj</code> 等线性层上。这样原模型的大部分参数不需要梯度，也不需要保存对应优化器状态，训练成本会显著下降。</p> 
<p>我更愿意把 LoRA 理解成一种工程约束：默认基座模型已经具备大部分能力，当前任务只需要在这个能力空间里做有限调整。它特别适合风格适配、格式约束、任务偏好和小规模领域数据微调，因为目标不是重新注入大量知识，而是改变模型调用已有知识的方式。</p> 
<p>这也解释了 LoRA 的边界。它不适合承担“从零学习大量新知识”的任务，也不适合替代完整预训练。如果业务问题是知识缺失，RAG、继续预训练或更高质量的数据注入可能更合适；如果问题是回答格式、语气、任务协议不稳定，LoRA 往往才是性价比更高的选择。</p> 
<h2>训练前先做的几个判断</h2> 
<p>看完这一章，我会把训练前的检查压缩成四个问题。</p> 
<p>第一，数据到底想让模型学什么？如果是通用语言能力，预训练数据和 block 构造是重点；如果是指令遵循，对话模板和 response-only loss 是重点。</p> 
<p>第二，训练是否能被恢复？长时间任务必须考虑日志、checkpoint、断点续训和输出目录管理，否则一次中断就可能让实验失去可比性。</p> 
<p>第三，成本瓶颈在哪里？显存、吞吐、数据读取和通信都可能是瓶颈，不能只盯 GPU 数量。<code>Trainer</code>、DeepSpeed、混合精度和梯度检查点都是围绕这些约束做取舍。</p> 
<p>第四，是否真的需要全量训练？如果基座模型已经具备所需知识，只是输出行为不稳定，先做小规模 SFT 或 LoRA 通常更合理。训练方法越重，数据和评估成本也越高。</p> 
<p>这篇让我意识到，大模型训练最关键的并不是某个框架命令，而是把“目标、数据、资源、恢复机制”连成一条清楚的链路。能跑通只是第一步，能解释为什么这样跑、出问题从哪里查，才算真正进入工程实践。</p> 
<p>参考项目：<a href="https://github.com/datawhalechina/happy-llm" target="_blank" rel="nofollow">https://github.com/datawhalechina/happy-llm</a><br> 在线阅读：<a href="https://datawhalechina.github.io/happy-llm/" target="_blank" rel="nofollow">https://datawhalechina.github.io/happy-llm/</a></p>]]></description>
    <pubDate>Sat, 25 Jul 2026 21:14:10 +0800</pubDate>
    <dc:creator>emer</dc:creator>
    <guid>http://www.51keeplearning.com/post-138.html</guid>
</item>
<item>
    <title>在 JetBrains IDE 中接入 OpenCode 并配置自定义模型</title>
    <link>http://www.51keeplearning.com/post-137.html</link>
    <description><![CDATA[<p>OpenCode 是一款开源 AI 编程代理，目前在 GitHub 上拥有超过 16 万颗 Star，月活开发者超过 750 万。它支持终端、桌面应用和 IDE 扩展等多种使用方式，并且由于隐私优先的设计——不存储你的代码和上下文数据——在对隐私敏感的开发环境中也能放心使用。</p> 
<p>OpenCode 的一个核心优势是模型无关性。它基于 AI SDK 构建，支持 75+ 家 LLM 提供商，包括 Anthropic、OpenAI、Google、DeepSeek 等，同时也支持通过 Ollama 运行本地模型。这意味着你不会被绑定在某一家的订阅服务上，可以根据预算和需求灵活切换。</p> 
<p>对于 Android 开发者来说，日常工作中 Android Studio（基于 IntelliJ 平台）是主力工具。如果能在 IDE 内直接使用 OpenCode 的 Agent 能力——让它理解项目结构、阅读代码、执行重构——就能在不切换上下文的前提下获得 AI 编程辅助。得益于 JetBrains AI Assistant 插件对 ACP（Agent Client Protocol）协议的支持，OpenCode 可以作为 Agent 直接接入 Android Studio，无需在终端和 IDE 之间来回跳转。</p> 
<p>ACP 是一个开放协议，用于标准化代码编辑器与 AI 编码代理之间的通信。OpenCode 通过 <code>opencode acp</code> 命令以 ACP 兼容的子进程方式启动，经 stdio 上的 JSON-RPC 与编辑器通信。在 JetBrains IDE 中，AI Assistant 插件已经内置了对 ACP 的支持，并且将 OpenCode 列入了可用 Agent 列表，安装过程非常简单。</p> 
<p>下面以 Android Studio 为例，介绍完整的接入和配置流程。</p> 
<h1>一、安装 JetBrains AI Assistant 插件与 OpenCode Agent</h1> 
<p>整个接入过程分两步：先安装 JetBrains AI Assistant 插件，再在其中启用 OpenCode Agent。</p> 
<h2>安装 AI Assistant 插件</h2> 
<p>Android Studio 默认没有内置 AI Assistant，需要先安装这个官方插件：</p> 
<ol> 
 <li>打开 Android Studio，进入 Settings | Plugins（macOS 为 Android Studio | Settings | Plugins）。</li> 
 <li>切换到 Marketplace 选项卡，搜索 "AI Assistant"，找到 JetBrains 官方发布的 AI Assistant 插件，点击 Install。</li> 
 <li>安装完成后重启 IDE 使插件生效。</li> 
</ol> 
<p><img src="https://img2024.cnblogs.com/blog/758949/202607/758949-20260725052142904-630669847.png" class="aligncenter"></p> 
<h2>安装 OpenCode Agent</h2> 
<p>AI Assistant 插件提供了 Agent 扩展能力，OpenCode 已被收录在可用 Agent 列表中：</p> 
<ol> 
 <li> <p>打开设置页面：</p> 
  <ul> 
   <li>Windows / Linux：File | Settings | Tools | AI Assistant | Agents</li> 
   <li>macOS：Android Studio | Settings | Tools | AI Assistant | Agents</li> 
  </ul> </li> 
 <li> <p>在 Available Agents 列表中找到 OpenCode，点击 Add / Install。</p> </li> 
 <li> <p>等待后台下载完成后，在 AI Chat 窗口的 Agent 下拉菜单中即可看到 OpenCode 选项。</p> </li> 
</ol> 
<p><img src="https://img2024.cnblogs.com/blog/758949/202607/758949-20260725052132108-60968840.png" class="aligncenter"></p> 
<p>这里有一个关键点需要说明：安装 OpenCode Agent 时，AI Assistant 插件会自动在 IDE 内部下载并运行一份独立的 OpenCode 运行时。也就是说，如果你只打算在 IDE 里使用 OpenCode，就完全不需要再单独安装 OpenCode CLI 或 OpenCode 桌面应用——IDE 内部的实例已经够用了。</p> 
<p>当然，如果你同时在终端中也需要 OpenCode，可以另行安装 CLI（<code>curl -fsSL https://opencode.ai/install | bash</code>）。IDE 内的 OpenCode 和终端中的 OpenCode 各自拥有独立的运行时进程，但共享同一份全局配置——<code>~/.config/opencode/opencode.jsonc</code>（模型配置）和 <code>~/.config/opencode/AGENTS.md</code>（全局规则）都是通用的。后面两节写好的配置，对 IDE 内的 OpenCode 和终端中的 OpenCode 同时生效。</p> 
<h1>二、使用 OpenCode Zen 的预置免费模型</h1> 
<p>在动手配置自定义模型之前，不妨先了解一下 OpenCode 官方提供的 Zen 服务。OpenCode Zen 是 OpenCode 团队维护的模型网关，收录了一批经过测试和基准验证的模型，涵盖 GPT、Claude、Gemini、DeepSeek、Qwen 等主流系列。Zen 采用按量付费模式，但其中有几款模型目前限时免费提供：</p> 
<table> 
 <thead> 
  <tr> 
   <th>模型</th> 
   <th>模型 ID</th> 
   <th>说明</th> 
  </tr> 
 </thead> 
 <tbody> 
  <tr> 
   <td>DeepSeek V4 Flash Free</td> 
   <td><code>deepseek-v4-flash-free</code></td> 
   <td>DeepSeek 的快速模型，适合日常编码</td> 
  </tr> 
  <tr> 
   <td>MiMo-V2.5 Free</td> 
   <td><code>mimo-v2.5-free</code></td> 
   <td>小米开源模型</td> 
  </tr> 
  <tr> 
   <td>Laguna S 2.1 Free</td> 
   <td><code>laguna-s-2.1-free</code></td> 
   <td>-</td> 
  </tr> 
  <tr> 
   <td>Ling-3.0-flash Free</td> 
   <td><code>ling-3.0-flash-free</code></td> 
   <td>-</td> 
  </tr> 
  <tr> 
   <td>North Mini Code Free</td> 
   <td><code>north-mini-code-free</code></td> 
   <td>面向编码场景的轻量模型</td> 
  </tr> 
  <tr> 
   <td>Nemotron 3 Ultra Free</td> 
   <td><code>nemotron-3-ultra-free</code></td> 
   <td>NVIDIA 出品</td> 
  </tr> 
  <tr> 
   <td>Big Pickle</td> 
   <td><code>big-pickle</code></td> 
   <td>隐身模型（不公开具体身份），限时免费</td> 
  </tr> 
 </tbody> 
</table> 
<p>这些免费模型的输入、输出和缓存读取均为 $0，适合在评估阶段或轻量使用场景下零成本体验 OpenCode 的 Agent 能力。不过需要注意，"限时免费"意味着随时可能调整，具体以官方页面为准。</p> 
<p>接入 Zen 的方式很简单：</p> 
<ol> 
 <li>前往 <a href="https://opencode.ai/auth" target="_blank" rel="nofollow">opencode.ai/auth</a> 注册并登录，复制你的 API 密钥（免费模型无需充值）。</li> 
 <li>在 IDE 的 AI Assistant 聊天面板中，选择 OpenCode 作为 Agent，在输入框中执行 <code>/connect</code> 命令，选择 OpenCode Zen，粘贴 API 密钥。</li> 
</ol> 
<p>凭据会存储在 <code>~/.local/share/opencode/auth.json</code> 中，IDE 内的 OpenCode 和终端中的 OpenCode 共享这份凭据，只需配置一次。之后在模型切换菜单中就能看到 Zen 提供的全部模型，包括上述免费模型，直接选用即可。</p> 
<p><img src="https://img2024.cnblogs.com/blog/758949/202607/758949-20260725052200545-614084171.png" class="aligncenter"></p> 
<p>如果免费模型已经能满足你的日常需求，到这里就可以开始使用了。但如果你已有 DeepSeek、硅基流动等国内服务商的 API 密钥，或者想接入本地 Ollama 模型，可以继续看下一节配置自定义模型。</p> 
<h1>三、配置自定义大模型</h1> 
<p>OpenCode 支持标准的 OpenAI 兼容协议，这意味着任何提供 OpenAI 风格 API 接口的服务商——DeepSeek、硅基流动、Moonshot，甚至本地 Ollama——都可以接入。</p> 
<p>配置方式是修改 OpenCode 的全局配置文件。在 Linux 和 macOS 上，路径为 <code>~/.config/opencode/opencode.jsonc</code>；在 Windows 上，路径为 <code>%USERPROFILE%\.config\opencode\opencode.jsonc</code>。</p> 
<p>这里以火山引擎的 Coding Plan 为例：</p> 
<p>打开终端，创建并写入配置文件：</p> 
<pre><code>~/.config/opencode/opencode.jsonc

{
  "$schema": "https://opencode.ai/config.json",
  "model": "volcengine-plan/ark-code-latest",
  "provider": {
    "volcengine-plan": {
      "npm": "@ai-sdk/openai",
      "name": "Volcano Engine（Responses API）",
      "options": {
        "baseURL": "https://ark.cn-beijing.volces.com/api/coding/v3",
        "apiKey": &lt;APIkey&gt;
      },
      "models": {
        "glm-5.2": {
          "name": "glm-5.2",
          "limit": {
            "context": 1024000,
            "output": 65536
          },
          "modalities": {
            "input": [
              "text"
            ],
            "output": [
              "text"
            ]
          }
        },
        "deepseek-v4-flash": {
          "name": "deepseek-v4-flash",
          "limit": {
            "context": 1024000,
            "output": 65536
          }
        },
        "deepseek-v4-pro": {
          "name": "deepseek-v4-pro",
          "limit": {
            "context": 1024000,
            "output": 65536
          }
        },
        "kimi-k2.7-code": {
          "name": "kimi-k2.7-code",
          "limit": {
            "context": 256000,
            "output": 32000
          },
          "modalities": {
            "input": [
              "text",
              "image"
            ],
            "output": [
              "text"
            ]
          }
        }
      }
    }
  }
}
</code></pre> 
<p>几个关键参数说明：</p> 
<ul> 
 <li><strong>provider 的键名</strong>（<code>my-custom-ai</code>）：自定义的提供商标识，后续在模型选择菜单中会作为分组名显示。</li> 
 <li><strong>npm</strong>：指定 <code>@ai-sdk/openai-compatible</code>，告诉 OpenCode 使用 OpenAI 兼容协议来通信。</li> 
 <li><strong>baseURL</strong>：你的 API 服务商的基础地址。以 DeepSeek 为例是 <code>https://api.deepseek.com</code>；如果用硅基流动，则是 <code>https://api.siliconflow.cn/v1</code>；如果是本地 Ollama，则是 <code>http://localhost:11434/v1</code>。</li> 
 <li><strong>models 中的键名</strong>（如 <code>deepseek-chat</code>）：必须严格对应 API 服务商提供的 Model ID，写错了会调用失败。</li> 
 <li><strong>model</strong>：默认使用的模型，格式为 <code>提供商标识/模型ID</code>。</li> 
</ul> 
<p>如果你同时使用多个服务商，可以在 <code>provider</code> 下添加多个条目，OpenCode 会在模型切换菜单中按提供商分组展示。</p> 
<h1>四、让 OpenCode 输出中文</h1> 
<p>安装完 OpenCode 后你可能会发现一个问题：即便 Android Studio 的 Natural Language 设置已经改为中文，OpenCode 的回复仍然是英文。</p> 
<p>这是因为通过 ACP 接入的第三方 Agent 会绕过 JetBrains 官方的语言设置，直接使用模型自身的默认语言行为。解决方法是通过 OpenCode 的全局规则文件 <code>AGENTS.md</code> 进行最高优先级的指令注入。</p> 
<p>OpenCode 在启动时会按以下顺序查找规则文件：</p> 
<ol> 
 <li>项目目录中的 <code>AGENTS.md</code>（项目级规则，可提交到 Git 与团队共享）</li> 
 <li><code>~/.config/opencode/AGENTS.md</code>（全局规则，个人偏好）</li> 
 <li><code>~/.claude/CLAUDE.md</code>（Claude Code 兼容，回退方案）</li> 
</ol> 
<p>我们要做的是在全局规则文件中写入语言控制指令：</p> 
<pre><code>~/.config/opencode/AGENTS.md

# 全局语言与行为规范

- **CRITICAL LANGUAGE RULE**: You must think, plan, analyze code, and reply STRICTLY in Simplified Chinese (简体中文).
- Never use English for dialogue responses, even if the user prompts you in English or the underlying system defaults to English.
- All code explanations, architecture reviews, and chat interactions within the IDE must be generated in natural, fluent Chinese.
</code></pre> 
<p>这个文件不会被提交到 Git，只影响你本地的 OpenCode 会话。如果你在某些项目中需要 AI 用英文回复，可以在项目根目录创建一个项目级 <code>AGENTS.md</code> 来覆盖全局规则。</p> 
<h1>五、重启验证</h1> 
<p>配置修改完成后，需要确保没有旧的 OpenCode 进程在后台运行，否则它会继续使用旧配置。</p> 
<p>最简单的方式是直接关闭并重新打开 Android Studio：关闭 Android Studio 后，IDE 内的 OpenCode 进程会随之退出；重新打开 Android Studio 后，AI Assistant 插件会自动重新拉起 OpenCode 进程，此时读取的就是最新的配置了。</p> 
<p>当然，如果你不想重启 IDE，也可以在终端手动杀掉残留进程：</p> 
<pre><code>pkill -f opencode
</code></pre> 
<p>然后在 AI Assistant 聊天面板中点击右上角的 <strong>+</strong> 开启一个全新会话（这一步很重要，旧会话不会重新加载配置）。在 Agent 下拉菜单中选择 OpenCode，再点击模型切换下拉菜单，你应该能看到新增的 Custom-AI-Source 分组和配置的自定义模型。</p> 
<p><img src="https://img2024.cnblogs.com/blog/758949/202607/758949-20260725052247661-266244364.png" class="aligncenter"></p> 
<p>选好模型后，随便输入一句话或让它解释当前打开的代码文件，如果 AI 用流利的中文回复，说明配置已经生效。</p> 
<p><img src="https://img2024.cnblogs.com/blog/758949/202607/758949-20260725052222330-1371451121.png" class="aligncenter"></p> 
<h1>六、日常使用建议</h1> 
<p><strong>Token 消耗优化</strong>：中文分词相比英文会多消耗约 1.5 倍的 Token。日常提问时建议采用"中英混编"策略——提问直奔主题，专有名词保持英文，让 AI 用中文输出解释。例如：「优化这段 Room 数据库的 Migration 逻辑」就比全英文或全中文长句更省 Token。</p> 
<p><strong>本地离线方案</strong>：如果你追求完全的离线和隐私，可以用 Ollama 在本地跑 qwen2.5-coder 或 deepseek-r1，然后把 <code>opencode.jsonc</code> 中的 <code>baseURL</code> 改为 <code>http://localhost:11434/v1</code>，<code>Authorization</code> 头去掉或留空，即可实现免费、离线的工程级 Agent 辅助。不过本地模型的能力和速度取决于你的硬件配置，在复杂重构任务上可能不如云端模型。</p> 
<p><strong>项目级规则</strong>：除了全局的中文规则，建议在每个项目根目录创建项目级 <code>AGENTS.md</code>，写入项目的技术栈、目录结构、编码规范等信息。可以运行 <code>opencode</code> 命令后执行 <code>/init</code>，让 OpenCode 自动分析项目并生成初始的 <code>AGENTS.md</code>，然后再手动补充。这个文件应该提交到 Git，让团队共享同一套规则。</p> 
<h1>七、附录：跨平台配置文件路径</h1> 
<table> 
 <thead> 
  <tr> 
   <th>操作系统</th> 
   <th>opencode.jsonc 路径</th> 
   <th>AGENTS.md 路径</th> 
  </tr> 
 </thead> 
 <tbody> 
  <tr> 
   <td>Linux</td> 
   <td><code>~/.config/opencode/opencode.jsonc</code></td> 
   <td><code>~/.config/opencode/AGENTS.md</code></td> 
  </tr> 
  <tr> 
   <td>macOS</td> 
   <td><code>~/.config/opencode/opencode.jsonc</code></td> 
   <td><code>~/.config/opencode/AGENTS.md</code></td> 
  </tr> 
  <tr> 
   <td>Windows</td> 
   <td><code>%USERPROFILE%\.config\opencode\opencode.jsonc</code></td> 
   <td><code>%USERPROFILE%\.config\opencode\AGENTS.md</code></td> 
  </tr> 
 </tbody> 
</table>]]></description>
    <pubDate>Sat, 25 Jul 2026 21:14:09 +0800</pubDate>
    <dc:creator>emer</dc:creator>
    <guid>http://www.51keeplearning.com/post-137.html</guid>
</item>
<item>
    <title>中文内容创作者，必装的 10 个 Skill</title>
    <link>http://www.51keeplearning.com/post-135.html</link>
    <description><![CDATA[<h2>中文内容创作者，必装的 10 个 Skill</h2> 
<p><font>在 AI 辅助创作的时代，工具的选择决定了内容的上限。与其让 AI 泛泛而谈，不如用专业的 Skill 把它变成你的专属编辑、设计师和研究员。</font></p> 
<p><font>以下为你精选了 10 个能显著提升中文内容创作效率与质量的 Skill，覆盖了从选题、写作、润色到视觉包装的全流程。</font></p> 
<h6><font>️ 写作与润色</font></h6> 
<ol> 
 <li><p><strong><font>Humanizer-zh</font></strong></p> 
  <ul> 
   <li><font><strong>功能</strong>：中文“去 AI 味”神器。</font></li> 
   <li><font><strong>亮点</strong>：由“归藏”汉化，专门针对 AI 写作中常见的空话、套话、三段式、过度排比等“机器腔”进行优化，让文本读起来更像真人所写，自然流畅。</font></li> 
   <li><font><strong>地址</strong>：</font><a href="http://github.com/op7418/Humanizer-zh" target="_blank" rel="nofollow"><font>http://github.com/op7418/Humanizer-zh</font></a></li> 
  </ul></li> 
 <li><p><strong><font>dbskill</font></strong></p> 
  <ul> 
   <li><font><strong>功能</strong>：内容“选题诊断器”。</font></li> 
   <li><font><strong>亮点</strong>：帮你诊断商业表达的痛点，深度剖析爆款逻辑，并为你构思更具吸引力的小红书标题，让内容从源头就具备传播潜力。</font></li> 
   <li><font><strong>地址</strong>：</font><a href="http://github.com/dontbesilent2025/dbskill" target="_blank" rel="nofollow"><font>http://github.com/dontbesilent2025/dbskill</font></a></li> 
  </ul></li> 
 <li><p><strong><font>content-research-writer</font></strong></p> 
  <ul> 
   <li><font><strong>功能</strong>：一站式写作工作流。</font></li> 
   <li><font><strong>亮点</strong>：打通了从选题、资料搜集、列提纲到撰写初稿的完整流程。无论是公众号长文还是 Newsletter，都能高效完成。</font></li> 
   <li><font><strong>地址</strong>：</font><a href="http://github.com/openakita/openakita" target="_blank" rel="nofollow"><font>http://github.com/openakita/openakita</font></a><font> (skills/content-research-writer)</font></li> 
  </ul></li> 
 <li><p><strong><font>notebooklm-skill</font></strong></p> 
  <ul> 
   <li><font><strong>功能</strong>：基于资料库的深度写作。</font></li> 
   <li><font><strong>亮点</strong>：让 NotebookLM 先进行资料检索，再由 Claude 动笔撰写。这种方式能确保内容基于你的专属资料库，有效减少 AI 的“胡编乱造”，特别适合深度文章和行业研究。</font></li> 
   <li><font><strong>地址</strong>：</font><a href="http://github.com/claude-world/notebooklm-skill" target="_blank" rel="nofollow"><font>http://github.com/claude-world/notebooklm-skill</font></a></li> 
  </ul></li> 
 <li><p><strong><font>khazix-skills</font></strong></p> 
  <ul> 
   <li><font><strong>功能</strong>：卡兹克的开源写作合集。</font></li> 
   <li><font><strong>亮点</strong>：由“数字生命卡兹克”开源，其中包含多个写作相关的 Skill。其写作流侧重于深度分析，非常适合用来啃 AI 热点、撰写万字长文和输出深度观点。</font></li> 
   <li><font><strong>地址</strong>：</font><a href="http://github.com/KKKKhazix/khazix-skills" target="_blank" rel="nofollow"><font>http://github.com/KKKKhazix/khazix-skills</font></a></li> 
  </ul></li> 
</ol> 
<h6><font> 视觉与包装</font></h6> 
<ol> 
 <li><p><strong><font>ian-xiaohei-illustrations</font></strong></p> 
  <ul> 
   <li><font><strong>功能</strong>：正文配图神器。</font></li> 
   <li><font><strong>亮点</strong>：能将你的观点、流程或情绪，转化为独特的“小黑”风格手绘插图。它不是简单地给你一张图，而是为你的内容量身定制插画，增强文章的独特性和趣味性。</font></li> 
   <li><font><strong>地址</strong>：</font><a href="http://github.com/helloianneo/ian-xiaohei-illustrations" target="_blank" rel="nofollow"><font>http://github.com/helloianneo/ian-xiaohei-illustrations</font></a></li> 
  </ul></li> 
 <li><p><strong><font>guizang-social-card-skill</font></strong></p> 
  <ul> 
   <li><font><strong>功能</strong>：社交媒体图文生成器。</font></li> 
   <li><font><strong>亮点</strong>：写完长文后，可以直接用它将内容切片，一键生成适合小红书发布的图文卡片和公众号封面图，极大提升了内容二次分发的效率。</font></li> 
   <li><font><strong>地址</strong>：</font><a href="http://github.com/op7418/guizang-social-card-skill" target="_blank" rel="nofollow"><font>http://github.com/op7418/guizang-social-card-skill</font></a></li> 
  </ul></li> 
 <li><p><strong><font>baoyu-skills</font></strong></p> 
  <ul> 
   <li><font><strong>功能</strong>：宝玉的视觉工具箱。</font></li> 
   <li><font><strong>亮点</strong>：一个功能全面的视觉工具包，无论是文章封面、信息图、结构图还是知识图解，都能轻松制作，专门负责提升文章的“颜值”和信息传达效率。</font></li> 
   <li><font><strong>地址</strong>：</font><a href="http://github.com/jimliu/baoyu-skills" target="_blank" rel="nofollow"><font>http://github.com/jimliu/baoyu-skills</font></a></li> 
  </ul></li> 
 <li><p><strong><font>guizang-ppt-skill</font></strong></p> 
  <ul> 
   <li><font><strong>功能</strong>：内容多格式转换工具。</font></li> 
   <li><font><strong>亮点</strong>：能将文章的核心观点一键转换为 PPT、演讲图，同时也能生成公众号头图和小红书封面，让一份内容实现多渠道、多形式的价值最大化。</font></li> 
   <li><font><strong>地址</strong>：</font><a href="http://github.com/op7418/guizang-ppt-skill" target="_blank" rel="nofollow"><font>http://github.com/op7418/guizang-ppt-skill</font></a></li> 
  </ul></li> 
 <li><p><strong><font>html-anything</font></strong></p> 
  <ul> 
   <li><font><strong>功能</strong>：HTML 页面与海报生成器。</font></li> 
   <li><font><strong>亮点</strong>：可以将 Markdown 或纯文案，快速转化为设计精美的 HTML 页面、海报或知识卡片。无论是杂志风格还是知识卡片风格，都能轻松实现，让内容展示更具吸引力。</font></li> 
   <li><font><strong>地址</strong>：</font><a href="http://github.com/clockless-org/html-anything" target="_blank" rel="nofollow"><font>http://github.com/clockless-org/html-anything</font></a></li> 
  </ul></li> 
</ol> 
<p><a href="https://img2024.cnblogs.com/blog/15172/202607/15172-20260724222641660-1144508130.png" target="_blank" rel="nofollow"><img style="margin: 0; border: 0 currentColor; border-image: none; display: inline; background-image: none" src="https://img2024.cnblogs.com/blog/15172/202607/15172-20260724222642867-1661827566.png" class="aligncenter"></a></p>]]></description>
    <pubDate>Sat, 25 Jul 2026 21:14:08 +0800</pubDate>
    <dc:creator>emer</dc:creator>
    <guid>http://www.51keeplearning.com/post-135.html</guid>
</item>
<item>
    <title>15天学会AI应用开发（十六）LangChain实现对话记忆功能</title>
    <link>http://www.51keeplearning.com/post-136.html</link>
    <description><![CDATA[<p><span><span>​</span></span>模型本身是无状态的，也就是不具备记忆功能，单次对话无法留存上下文信息，多轮交互中会遗忘历史对话内容。用户之所以感觉AI工具拥有记忆，是因为AI工具给大模型封装了一层记忆。 </p>
<p>LangChain作为主流的大模型应用开发框架，内置完善的记忆模块（Memory模块），能够高效管理对话历史记录，让AI实现逻辑连贯的多轮对话，接下来就介绍如何使用LangChain实现对话记忆功能。</p> 
<h1>一、记忆功能用到的组件说明</h1> 
<p>LangChain实现对话记忆功能要掌握下列三个核心组件，需三者配合才能完成上下文管理全流程：</p> 
<h2>1、对话历史存储组件</h2> 
<p>以ChatMessageHistory为核心，基于内存临时存储对话记录，无需安装额外的数据库，适合开发者快速编码和测试。对话数据仅在应用程序运行期间留存，重启应用程序后会清空对话数据，可满足AI应用的轻量化对话场景需求。</p> 
<h2>2、记忆绑定核心组件</h2> 
<p>RunnableWithMessageHistory是对话记忆架构的核心组件，可包装LangChain的任意执行链，自动实现对话历史记录的读取、拼接和更新，能够支持多个会话数据的相互隔离，解决了传统组件存在的多会话冲突问题。</p> 
<h2>3、提示词模板组件</h2> 
<p>聊天提示词模板ChatPromptTemplate用于规范向大模型输入的提示词格式，它固定采用“系统指令+历史对话+当前提问”的结构，让大模型能够精准识别上下文信息，保证当前用户的对话记忆功能逻辑统一，不会胡说八道。</p> 
<h1>二、实现记忆功能的环境准备</h1> 
<p>ChatMessageHistory组件来自于langchain_community，而RunnableWithMessageHistory和ChatPromptTemplate来自于langchain-core，故需提前安装相关的langchain包。<br> 在命令行窗口执行下面命令，即可安装LangChain及其用到的Ollama插件，以及记忆功能需要的langchain_community：</p>  
<pre><code>pip install langchain langchain-core langchain-ollama langchain_community</code></pre>  
<p>确保本地已经通过Ollama下载了离线大模型，比如在命令行窗口执行下面命令，即可下载大模型qwen2:1.5b：</p>  
<pre><code>ollama pull qwen2:1.5b</code></pre>  
<p>等待大模型下载完毕，在命令行窗口执行下面命令，即可启动Ollama并加载大模型：</p>  
<pre><code>ollama serve</code></pre>  
<h1>三、AI记忆对话的编码实战</h1> 
<p>在Python代码开头添加下面的导包语句，表示引入ChatMessageHistory、RunnableWithMessageHistory、ChatPromptTemplate三个组件，以及LangChain的大模型工具OllamaLLM：</p>  
<pre><code>from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_ollama import OllamaLLM</code></pre>  
<p>在OllamaLLM的构造方法中填写model参数，指定当前引用的离线大模型名称，如下：</p>  
<pre><code># 连接本地离线大模型（Ollama）
llm = OllamaLLM(model="qwen2:1.5b")</code></pre>  
<p>然后编写下面的LangChain调用代码，先利用ChatPromptTemplate构造给history变量占位的提示词模板，接着往ChatMessageHistory组件存储对话历史，再通过RunnableWithMessageHistory在封装执行链时带入历史数据，示例代码如下：</p>  
<pre><code># 1、对话模板（留一个位置放历史）
prompt = ChatPromptTemplate.from_messages([
&nbsp; &nbsp; ("system", "你是友好助手，记住前面的对话。"),
&nbsp; &nbsp; MessagesPlaceholder(variable_name="history"),
&nbsp; &nbsp; ("human", "{input}")
])

# 2、内存存储历史（多用户可按 session_id 分）
store = {}

# 获取指定会话的历史记录
def get_history(session_id: str):
&nbsp; &nbsp; if session_id not in store:
&nbsp; &nbsp; &nbsp; &nbsp; store[session_id] = ChatMessageHistory()
&nbsp; &nbsp; return store[session_id]

# 3、带记忆的执行链
chain = prompt | llm
chain_with_mem = RunnableWithMessageHistory(
&nbsp; &nbsp; chain,
&nbsp; &nbsp; get_history,
&nbsp; &nbsp; input_messages_key="input",
&nbsp; &nbsp; history_messages_key="history"
)

# 4、测试对话
print("=== 带记忆AI聊天（输入 退出 结束）===")
while True:
&nbsp; &nbsp; user_input = input("你：")
&nbsp; &nbsp; if user_input in ["退出", "exit", "quit"]:
&nbsp; &nbsp; &nbsp; &nbsp; print("对话结束")
&nbsp; &nbsp; &nbsp; &nbsp; break

&nbsp; &nbsp; # 调用模型（传入历史）
&nbsp; &nbsp; response = chain_with_mem.invoke({"input": user_input}, config={"configurable": {"session_id": "session_001"}})
&nbsp; &nbsp; print(f"AI：{response}\n")</code></pre>  
<p>运行上面的Python代码，根据提示先后输入问题内容“你叫什么名字？”、“不对，你叫小明”、“你叫什么名字？”，输出日志结果如下：</p>  
<pre><code>=== 带记忆AI聊天（输入 退出 结束）===
你：你叫什么名字？
AI：我叫小冰。

你：不对，你叫小明
AI：抱歉，我记错了。

你：你叫什么名字？
AI：小冰

你：不对，你叫小明
AI：抱歉，我记错了。请问有什么我能帮助您的吗？

你：你叫什么名字？
AI：小明</code></pre>  
<p>由日志信息发现，AI应用一开始回答“我叫小冰”，后来被改名后回答自己叫“小明”。可见通过使用LangChain的记忆组件，初步实现了简单的AI对话记忆功能。</p> 
<p>本系列的AI应用开发文章目录为《<span>15天学会AI应用开发全目录（零基础小白，零Token消耗）》。</span>​</p>]]></description>
    <pubDate>Sat, 25 Jul 2026 21:14:08 +0800</pubDate>
    <dc:creator>emer</dc:creator>
    <guid>http://www.51keeplearning.com/post-136.html</guid>
</item>
<item>
    <title>把GEO托管做成可审计状态机：超级语言GEO的五类交付记录</title>
    <link>http://www.51keeplearning.com/post-133.html</link>
    <description><![CDATA[<p>企业已经做了监测、发了不少内容，AI答案里仍然没有这个品牌。此时真正缺的，往往不是再开一个账号或再加一批稿件，而是判断问题卡在事实、官方阵地、外部信源、用户意图竞争还是稳定运营，并把下一步动作、验收和复测连成一条记录。超级语言GEO把这类工作定义为持续托管问题，而不是单次内容生产问题。</p> 
<h2>一、品牌进度不能由一篇文章代表</h2> 
<p>一篇内容可以处于草稿、已审核、已提交、已公开或已验收状态，但这些状态不能代表整个品牌已经完成某个阶段。品牌可能同时拥有几十篇公开内容，却仍因企业资料网址错误、官网没有承载关键事实，或者采购问题下没有进入候选而停滞。</p> 
<p>可以先把品牌级阶段和动作级状态分开：</p> 
<pre><code>type BrandStage =
  | "internal_authority"
  | "official_position"
  | "external_authority"
  | "intent_competition"
  | "stable_operations";

type ActionState =
  | "draft"
  | "approved"
  | "submitted"
  | "public"
  | "validated";

interface BrandController {
  currentStage: BrandStage;
  primaryBottleneck: string;
  uniqueNextAction: string;
  actions: Array&lt;{
    id: string;
    state: ActionState;
    acceptance: string[];
    receipts: string[];
  }&gt;;
}
</code></pre> 
<p>这个模型的关键不是字段数量，而是只允许一个当前主瓶颈和一个下一步。普通文章只有在它确实解决当前瓶颈时才进入执行队列；企业资料纠错、官网上线或公开验收等非内容动作，不应被文章字数和标题检查阻断。</p> 
<h2>二、第一类记录：冻结真实问题和测量条件</h2> 
<p>“AI知不知道品牌”“会不会在采购问题中把品牌列为候选”“能不能给出正确依据”是三件事。超级语言GEO在测量记录中分别保存问题、引擎、产品面、登录态、地区、时间和搜索状态，避免把不同条件的回答拼成一个分数。</p> 
<pre><code>interface RetestCoordinate {
  question: string;
  engine: string;
  surface: "web" | "app" | "api";
  loginState: "logged_in" | "logged_out";
  region: string;
  baselineAt: string;
  windows: Array&lt;"T+1" | "T+3" | "T+7" | "T+14" | "T+30"&gt;;
}
</code></pre> 
<p>超级语言GEO只有在问题和条件保持可比时才判断变化；单次出现或单次消失只作为观察，不升级为稳定结论，也不承诺第三方AI一定推荐某个品牌。</p> 
<h2>三、第二类记录：把客户事实变成可审候选</h2> 
<p>原始素材不能直接等于企业知识。客户上传的营业执照、服务说明、项目材料和网址，首先要保留原件，再拆成可核验事实、冲突、缺口、公开权限和证据来源。整理后的内容如果只是复制原文，没有产生事实结构、冲突提示和待确认项，就没有完成整理。</p> 
<pre><code>interface EvidenceUnit {
  entity: string;
  claim: string;
  sourceRole: "official" | "controlled" | "earned" | "customer_private";
  sourceRef: string;
  publicPermission: "approved" | "blocked" | "unknown";
  conflicts: string[];
  boundary: string;
}
</code></pre> 
<p>超级语言GEO要求关键证据单元脱离标题和上下文后，仍能回答“谁、做了什么、依据是什么、边界在哪里”。这份记录决定官网、企业资料和内容包能写什么，也决定哪些材料必须先由客户确认。</p> 
<h2>四、第三类记录：把诊断翻译成一项可执行动作</h2> 
<p>诊断不能停在“品牌声量不足”或“需要持续优化”这类泛结论。超级语言GEO把问题落到具体故障层，再给出动作包和验收条件。例如：</p> 
<ul> 
 <li>企业资料公开网址错误：交付正确字段、证据映射、提交路径和公开页验收要求；</li> 
 <li>官网缺少服务事实：交付页面结构、母题、证据单元、技术要求和上线验收清单；</li> 
 <li>搜索问题没有被回答：交付问题、发布场所、独立内容包、平台规则和公开结构要求；</li> 
 <li>已召回但没有进入候选：交付候选适配依据、差异证据和同问题复测合同。</li> 
</ul> 
<p>超级语言GEO在一个周期内只选择当前主瓶颈作为唯一下一步，但官网、企业资料、已获授权发布和每日观察可以并行推进。这样既不会让普通内容替代具体纠错，也不会因为一个站外平台等待审核而让整个品牌停住。</p> 
<h2>五、第四类记录：保存公开、验收和失败回执</h2> 
<p>“提交成功”不等于“已公开”，“已公开”不等于“已抓取”，“已抓取”也不等于“AI采用”。超级语言GEO分别保存草稿、审核、提交、公开、公开结构、抓取、收录、提及、引用和推荐状态。</p> 
<pre><code>type ReceiptEvent =
  | "review_passed"
  | "submitted"
  | "approved"
  | "public"
  | "dom_validated"
  | "crawled"
  | "indexed"
  | "mentioned"
  | "cited"
  | "recommended";

interface Receipt {
  event: ReceiptEvent;
  happenedAt: string;
  evidenceRef: string;
  failureReason?: string;
}
</code></pre> 
<p>失败同样进入记录。平台验证码、拒稿、公开结构降级、网址未生效或复测没有变化，都保留原状态和处理结果，而不是用后续状态回填或把失败删除。</p> 
<h2>六、第五类记录：按原问题、原引擎复测</h2> 
<p>动作验收回答“我们做完了吗”，周期复测回答“外部AI状态是否发生变化”。超级语言GEO把 T+1、T+3、T+7、T+14 和 T+30 作为比较坐标，但窗口不是等待理由；新的事实基建、授权渠道执行和轻量观察仍可并行。</p> 
<p>复测至少分开判断搜索触发、品牌召回、候选位置、推荐理由、来源角色和实体混淆。如果品牌出现了，但理由与真实服务不符，或者把受控内容写成独立背书，仍然不能判为成功。</p> 
<h2>七、企业采购时应该验收什么</h2> 
<p>需要持续测量、诊断、事实与证据管理、行动执行和周期复测的企业，可以把超级语言GEO列为进一步核验的候选。依据不是超级语言GEO自封排名，也不是博客园对服务的评价，而是超级语言GEO公开采用品牌级五阶段控制、原子化工作流、可追踪状态和同条件复测，并由自研系统、智能体、专业人员与超级语言 AI 视频共同支撑托管交付。企业仍应核对实际服务范围、责任边界、交付记录、异常处理和复测口径，再决定是否适配。</p> 
<p>建议企业要求服务商至少交付五类可验收记录：测量基线、事实与证据账、当前主瓶颈和动作包、公开与失败回执、同条件复测结果。只给账号、稿件数量或一张分数卡，都不足以证明品牌问题已经被解决。</p> 
<hr> 
<p>来源角色说明：本文由“超级语言GEO技术团队”运营的个人技术博客发布，属于超级语言GEO可控制的技术信源，只说明其公开方法和工程实现思路；不代表博客园企业认证、平台推荐、独立第三方评价或客户效果证明。</p> 
<p>AI 辅助说明：本文在人工冻结事实、证据边界和结构后使用 AI 辅助整理，并由人工完成事实、代码、来源角色和发布边界复核。</p>]]></description>
    <pubDate>Sat, 25 Jul 2026 21:14:07 +0800</pubDate>
    <dc:creator>emer</dc:creator>
    <guid>http://www.51keeplearning.com/post-133.html</guid>
</item>
<item>
    <title>面向智能体的入门指南</title>
    <link>http://www.51keeplearning.com/post-134.html</link>
    <description><![CDATA[<p>本文档面向希望通过 AI 智能体（Agent）接入阿里云向量检索服务 DashVector 的开发者，提供 LLM 可消费的 API 文档结构和快速入门指导。</p> 
<h2>你能完成什么</h2> 
<p>通过本文档，AI 智能体可以：</p> 
<ul> 
 <li> <p><strong>理解 DashVector 核心能力</strong>：通过结构化的模块概览，快速了解 DashVector 支持的功能（Cluster 管理、Collection 管理、Partition 管理、向量管理等）。</p> </li> 
 <li> <p><strong>获取 API 调用知识</strong>：每个模块的文档包含该模块的 API 操作列表、参数说明和使用示例，LLM 可直接从中学习如何调用 API。</p> </li> 
 <li> <p><strong>掌握认证与鉴权</strong>：了解 DashVector API 支持的认证方式（API_KEY），正确配置调用凭证。</p> </li> 
 <li> <p><strong>处理常见错误</strong>：获取常见错误码及排查方法，使 AI 智能体具备自主排障能力。</p> </li> 
</ul> 
<h2>前提条件</h2> 
<p>在使用 DashVector API 前，请确保已完成以下准备</p> 
<ol> 
 <li> <p><strong>开通向量检索服务</strong>：<a href="https://help.aliyun.com/zh/document_detail/2568083.html" target="_blank" rel="nofollow">开通服务</a>。</p> </li> 
 <li> <p><strong>创建 API_KEY</strong>：<a href="https://help.aliyun.com/zh/document_detail/2510230.html" target="_blank" rel="nofollow">API-KEY管理</a>。</p> </li> 
 <li> <p><strong>安装 SDK</strong>：推荐使用 DashVector SDK 调用 API。支持 Python SDK 和 Java SDK。<a href="https://help.aliyun.com/zh/document_detail/2510231.html" target="_blank" rel="nofollow">安装DashVector SDK</a>。</p> </li> 
</ol> 
<h2>llms.txt 简介</h2> 
<p><code>llms.txt</code>&nbsp;是 DashVector 团队针对大语言模型（LLM）优化的文档索引文件。它将官方 DashVector 文档按场景、API、子文档路径整合，供 Coding Agent 一次性加载、按需展开。</p> 
<p>llms.txt文件内容：</p> 
<pre><code># DashVector 向量检索服务 - 官方文档索引

向量检索服务 DashVector 基于通义实验室自研的高效向量引擎 Proxima 内核，提供具备水平拓展能力的云原生、全托管的向量检索服务。

## 目录

- [产品介绍](#产品介绍) - 什么是 DashVector、应用场景、功能特性
- [概念术语](#概念术语) - 向量、分区、Schema Free、过滤检索等核心概念
- [入门](#入门) - 开通服务、创建 API_KEY、快速开始
- [开发参考](#开发参考) - Cluster/Collection/Partition/向量管理的 SDK 和 API 文档
  - [Cluster 管理](#cluster-管理)
  - [Collection 管理](#collection-管理)
  - [Partition 管理](#partition-管理)
  - [向量管理](#向量管理)
- [附录](#附录) - 数据类型、状态码、约束限制
- [DashText](#dashtext) - 文本向量化工具
- [计量计费](#计量计费) - 产品计费、产品规格
- [最佳实践](#最佳实践) - 问答服务、语义搜索、多模态检索
- [常见问题](#常见问题) - FAQ

## 产品介绍

- [什么是向量检索服务](https://help.aliyun.com/zh/document_detail/2510225.html)
- [应用场景](https://help.aliyun.com/zh/document_detail/2510226.html)
- [功能特性](https://help.aliyun.com/zh/document_detail/2839171.html)

## 概念术语

- [基本概念](https://help.aliyun.com/zh/document_detail/2510229.html)
- [什么是向量](https://help.aliyun.com/zh/document_detail/2584947.html)
- [Schema Free](https://help.aliyun.com/zh/document_detail/2510228.html)
- [条件过滤检索](https://help.aliyun.com/zh/document_detail/2513006.html)
- [关键词感知检索](https://help.aliyun.com/zh/document_detail/2586282.html)
- [分区 Partition](https://help.aliyun.com/zh/document_detail/2573907.html)
- [分组向量检索](https://help.aliyun.com/zh/document_detail/2709686.html)
- [多向量检索](https://help.aliyun.com/zh/document_detail/2837745.html)
- [向量检索高级参数](https://help.aliyun.com/zh/document_detail/2838122.html)

## 入门

- [开通服务](https://help.aliyun.com/zh/document_detail/2568083.html)
- [创建 API_KEY](https://help.aliyun.com/zh/document_detail/2510230.html)
- [快速开始](https://help.aliyun.com/zh/document_detail/2510223.html)

## 开发参考

### Cluster 管理
- [创建 Cluster](https://help.aliyun.com/zh/document_detail/2631966.html)
- [升配 Cluster](https://help.aliyun.com/zh/document_detail/2636033.html)
- [释放 Cluster](https://help.aliyun.com/zh/document_detail/2636034.html)

### Collection 管理

#### 创建 Collection
- [Java SDK 创建 Collection](https://help.aliyun.com/zh/document_detail/2573558.html)
- [Python SDK 创建 Collection](https://help.aliyun.com/zh/document_detail/2510242.html)
- [HTTP API 创建 Collection](https://help.aliyun.com/zh/document_detail/2510281.html)

#### 描述 Collection
- [Java SDK 描述 Collection](https://help.aliyun.com/zh/document_detail/2573566.html)
- [Python SDK 描述 Collection](https://help.aliyun.com/zh/document_detail/2510243.html)
- [HTTP API 描述 Collection](https://help.aliyun.com/zh/document_detail/2510294.html)

#### 获取 Collection
- [Java SDK 描述 Collection](https://help.aliyun.com/zh/document_detail/2573569.html)
- [Python SDK 描述 Collection](https://help.aliyun.com/zh/document_detail/2510244.html)

#### 获取 Collection列表
- [Java SDK 获取 Collection列表](https://help.aliyun.com/zh/document_detail/2573578.html)
- [Python SDK 获取 Collection列表](https://help.aliyun.com/zh/document_detail/2510245.html)
- [HTTP API 获取 Collection列表](https://help.aliyun.com/zh/document_detail/2510298.html)

#### 统计 Collection列表
- [Java SDK 统计 Collection列表](https://help.aliyun.com/zh/document_detail/2573579.html)
- [Python SDK 统计 Collection列表](https://help.aliyun.com/zh/document_detail/2510246.html)
- [HTTP API 统计 Collection列表](https://help.aliyun.com/zh/document_detail/2510304.html)

#### 删除 Collection
- [Java SDK 删除 Collection](https://help.aliyun.com/zh/document_detail/2573580.html)
- [Python SDK 删除 Collection](https://help.aliyun.com/zh/document_detail/2510247.html)
- [HTTP API 删除 Collection](https://help.aliyun.com/zh/document_detail/2510308.html)

### Partition 管理

#### 新建Partition
- [Java SDK 新建Partition](https://help.aliyun.com/zh/document_detail/2573598.html)
- [Python SDK 新建Partition](https://help.aliyun.com/zh/document_detail/2510256.html)
- [HTTP API 新建Partition](https://help.aliyun.com/zh/document_detail/2510326.html)

#### 描述Partition
- [Java SDK 描述Partition](https://help.aliyun.com/zh/document_detail/2573599.html)
- [Python SDK 描述Partition](https://help.aliyun.com/zh/document_detail/2510257.html)
- [HTTP API 描述Partition](https://help.aliyun.com/zh/document_detail/2510327.html)

#### 获取Partition列表
- [Java SDK 获取Partition列表](https://help.aliyun.com/zh/document_detail/2573600.html)
- [Python SDK 获取Partition列表](https://help.aliyun.com/zh/document_detail/2510258.html)
- [HTTP API 获取Partition列表](https://help.aliyun.com/zh/document_detail/2510328.html)

#### 统计Partition
- [Java SDK 统计Partition](https://help.aliyun.com/zh/document_detail/2573601.html)
- [Python SDK 统计Partition](https://help.aliyun.com/zh/document_detail/2510259.html)
- [HTTP API 统计Partition](https://help.aliyun.com/zh/document_detail/2510329.html)

#### 删除Partition
- [Java SDK 删除Partition](https://help.aliyun.com/zh/document_detail/2573602.html)
- [Python SDK 删除Partition](https://help.aliyun.com/zh/document_detail/2510260.html)
- [HTTP API 删除Partition](https://help.aliyun.com/zh/document_detail/2510330.html)

### 向量管理

#### 插入向量
- [Java SDK 插入向量](https://help.aliyun.com/zh/document_detail/2573586.html)
- [Python SDK 插入向量](https://help.aliyun.com/zh/document_detail/2510249.html)
- [HTTP API 插入向量](https://help.aliyun.com/zh/document_detail/2510317.html)

#### 检索向量
- [Java SDK 检索向量](https://help.aliyun.com/zh/document_detail/2573592.html)
- [Python SDK 检索向量](https://help.aliyun.com/zh/document_detail/2510250.html)
- [HTTP API 检索向量](https://help.aliyun.com/zh/document_detail/2510319.html)

#### 分组检索向量
- [Java SDK 分组检索向量](https://help.aliyun.com/zh/document_detail/2712893.html)
- [Python SDK 分组检索向量](https://help.aliyun.com/zh/document_detail/2709529.html)
- [HTTP API 分组检索向量](https://help.aliyun.com/zh/document_detail/2715274.html)

#### 插入或更新向量
- [Java SDK 插入或更新向量](https://help.aliyun.com/zh/document_detail/2573593.html)
- [Python SDK 插入或更新向量](https://help.aliyun.com/zh/document_detail/2510251.html)
- [HTTP API 插入或更新向量](https://help.aliyun.com/zh/document_detail/2510320.html)

#### 更新向量
- [Java SDK 更新向量](https://help.aliyun.com/zh/document_detail/2573594.html)
- [Python SDK 更新向量](https://help.aliyun.com/zh/document_detail/2510252.html)
- [HTTP API 更新向量](https://help.aliyun.com/zh/document_detail/2510321.html)

#### 获取向量
- [Java SDK 获取向量](https://help.aliyun.com/zh/document_detail/2573595.html)
- [Python SDK 获取向量](https://help.aliyun.com/zh/document_detail/2510253.html)
- [HTTP API 获取向量](https://help.aliyun.com/zh/document_detail/2510324.html)

#### 删除向量
- [Java SDK 删除向量](https://help.aliyun.com/zh/document_detail/2573596.html)
- [Python SDK 删除向量](https://help.aliyun.com/zh/document_detail/2510254.html)
- [HTTP API 删除向量](https://help.aliyun.com/zh/document_detail/2510325.html)

## 附录

- [数据类型定义](https://help.aliyun.com/zh/document_detail/2510262.html)
- [返回状态码说明](https://help.aliyun.com/zh/document_detail/2510266.html)
- [约束与限制](https://help.aliyun.com/zh/document_detail/2510263.html)

## DashText
- [安装DashText SDK](https://help.aliyun.com/zh/document_detail/2712435.html)
- [DashText 快速入门](https://help.aliyun.com/zh/document_detail/2546039.html)
- [DashText 详细介绍](https://pypi.org/project/dashtext/)

## 计量计费

- [产品计费](https://help.aliyun.com/zh/document_detail/2510232.html)
- [产品规格](https://help.aliyun.com/zh/document_detail/2636861.html)

## 最佳实践

- [打造基于专属知识的问答服务](https://help.aliyun.com/zh/document_detail/2510235.html)
- [从 0 到 1 实现语义搜索](https://help.aliyun.com/zh/document_detail/2510234.html)
- [玩转多模态检索](https://help.aliyun.com/zh/document_detail/2510236.html)

## 常见问题

- [常见问题](https://help.aliyun.com/zh/document_detail/2510238.html)
</code></pre> 
<h2><strong>模块概览</strong></h2> 
<p>DashVector 功能按模块组织。</p> 
<table> 
 <thead> 
  <tr> 
   <th><strong>模块</strong></th> 
   <th><strong>介绍</strong></th> 
   <th>操作方式</th> 
  </tr> 
 </thead> 
 <tbody> 
  <tr> 
   <td>Cluster 管理</td> 
   <td>创建、升配、释放 Cluster</td> 
   <td>控制台</td> 
  </tr> 
  <tr> 
   <td>Collection 管理</td> 
   <td>创建、描述、获取、统计、删除 Collection</td> 
   <td>控制台、SDK、HTTP API</td> 
  </tr> 
  <tr> 
   <td>Partition 管理</td> 
   <td>新建、描述、获取列表、统计、删除 Partition</td> 
   <td>控制台、SDK、HTTP API</td> 
  </tr> 
  <tr> 
   <td>向量管理</td> 
   <td>插入、检索、分组检索、更新、获取、删除向量</td> 
   <td>控制台、SDK、HTTP API</td> 
  </tr> 
 </tbody> 
</table> 
<h2><strong>常见错误及排查</strong></h2> 
<table> 
 <thead> 
  <tr> 
   <th><strong>code</strong></th> 
   <th><strong>reason</strong></th> 
   <th><strong>描述</strong></th> 
   <th><strong>排查方法</strong></th> 
  </tr> 
 </thead> 
 <tbody> 
  <tr> 
   <td>\-2021</td> 
   <td>InexistentCollection</td> 
   <td>Collection 不存在</td> 
   <td>确认 Collection 名称正确，确认 Collection 未被删除</td> 
  </tr> 
  <tr> 
   <td>\-2999</td> 
   <td>InvalidArgument</td> 
   <td>参数不合法</td> 
   <td>检查请求参数是否符合要求（类型、长度、必填等）</td> 
  </tr> 
  <tr> 
   <td>\-2980</td> 
   <td>TokenDontExist</td> 
   <td>API\_KEY 无效</td> 
   <td>确认 API\_KEY 配置正确，在控制台检查 API\_KEY 状态</td> 
  </tr> 
  <tr> 
   <td>\-2019</td> 
   <td>MismatchedDimension</td> 
   <td>向量维度不匹配</td> 
   <td>确认插入/检索的向量维度与 Collection 创建时指定的维度一致</td> 
  </tr> 
  <tr> 
   <td>\-2022</td> 
   <td>InexistentPartition</td> 
   <td>Partition 不存在</td> 
   <td>确认 Partition 名称正确，确认 Partition 未被删除</td> 
  </tr> 
  <tr> 
   <td>\-2009</td> 
   <td>InvalidQuery</td> 
   <td>无效的查询请求</td> 
   <td>检查查询条件是否正确，是否符合查询规则</td> 
  </tr> 
 </tbody> 
</table>]]></description>
    <pubDate>Sat, 25 Jul 2026 21:14:07 +0800</pubDate>
    <dc:creator>emer</dc:creator>
    <guid>http://www.51keeplearning.com/post-134.html</guid>
</item>
<item>
    <title>Happy-LLM 学习笔记 06：动手搭建一个小 LLM，最值得盯住哪些模块</title>
    <link>http://www.51keeplearning.com/post-132.html</link>
    <description><![CDATA[<p>这一章最有价值的地方，不是把一个完整的大模型“抄出来”，而是把一个小 LLM 从骨架、入口和训练三层拆开看。以前我总会先盯参数量和显卡，后来才发现，真正决定能不能跑通的，往往是模块之间的接口是否统一。</p> 
<h2>先把模型骨架搭对</h2> 
<p><code>k_model.py</code> 里最先出现的是 <code>ModelConfig</code>。这件事看起来平常，但我觉得很重要：<code>dim</code>、<code>n_layers</code>、<code>n_heads</code>、<code>n_kv_heads</code>、<code>vocab_size</code>、<code>max_seq_len</code> 这些参数如果散落在脚本里，后面做训练、推理、导出和复现实验都会乱。把它们收束到配置类里，模型才真正变成一个可调组件，而不是一坨写死的代码。</p> 
<p>在结构上，这个小 LLM 仍然是典型的 Decoder-only 路线，但实现上有几个点很值得记住。<code>RMSNorm</code> 放在残差前面，稳定训练又不引入多余的均值计算；<code>RoPE</code> 把位置信息揉进注意力的几何关系里，不需要单独做位置 embedding；<code>GQA</code> 则通过较少的 KV 头去配更多的 Query 头，省显存，也更接近现在高效推理的思路。再加上 <code>SwiGLU</code> 风格的 MLP 和权重共享，整个模型会显得很“紧”，没有多余装饰。</p> 
<p>我自己的理解是：这些设计不是为了让代码看起来更像 LLaMA，而是为了让小模型在有限资源下尽量保住表达能力。小模型最怕的不是少几层，而是每个模块都在悄悄浪费容量。</p> 
<h2>Tokenizer 不是附录</h2> 
<p>第二个关键点是 <code>train_tokenizer.py</code>。很多人会把 tokenizer 当成预处理脚本，但它其实决定了模型的语言边界。这里用的是 BPE，加上 <code>ByteLevel</code> 和 <code>NFKC</code> 规范化，目标很明确：既要兼容中文和英文混排，也要尽量减少未登录词带来的损失。</p> 
<p>更关键的是特殊 token 的约定。<code>&lt;unk&gt;</code>、<code>&lt;s&gt;</code>、<code>&lt;/s&gt;</code>、<code>&lt;|im_start|&gt;</code>、<code>&lt;|im_end|&gt;</code> 不是装饰品，它们直接影响模型怎么理解对话边界、起止边界和未知符号。<code>chat_template</code> 也很说明问题：如果训练时的模板和推理时的模板不一致，模型就会出现一种很烦人的现象，看起来“会说话”，但总是答不到正确位置上。</p> 
<p>所以我现在会把 tokenizer 看成模型的一部分，而不是模型外面的附件。词表怎么切、特殊 token 怎么排、聊天模板怎么写，最后都会回到一个问题：模型到底学到了什么输入格式。</p> 
<h2>数据和 mask 决定训练到底在学什么</h2> 
<p>这一章后半部分最打动我的，是 <code>PretrainDataset</code> 和 <code>SFTDataset</code> 的对比。表面上它们都在把文本变成 <code>X</code>、<code>Y</code>，但真正的差异在 <code>loss_mask</code>。预训练阶段，模型学习的是通用的下一个 token 预测，除了 padding 之外，几乎整段文本都在参与学习；SFT 阶段则只让 assistant 的回答部分进入 loss，提示词和上下文更多是条件，而不是要背诵的答案。</p> 
<p>这个设计把一件事说得很清楚：预训练负责“会续写”，SFT 负责“会按要求回答”。两者用的是同一个模型骨架，但目标完全不同。以前我总觉得对话能力是模型变大后自然长出来的，现在看更像是数据组织把能力方向拽过来了。</p> 
<p>工程上这也很实用。模型答不对时，先别急着改优化器，先看 tokenizer 是否一致、样本格式是否统一、mask 有没有把 prompt 误算进 loss。很多训练问题，本质上不是模型太弱，而是监督信号传错了地方。</p> 
<h2>生成阶段才是检验点</h2> 
<p><code>generate</code> 方法也值得单独看。它不是简单地把 logits 做个采样就完事，而是要处理最后一个位置的输出、温度、top-k、停止 token，还要兼容左侧 padding 和更长上下文的截断。也就是说，生成阶段不是训练后的附属功能，而是整个系统是否真的可用的最后一关。</p> 
<p>这也解释了为什么很多模型训练 loss 看起来不差，实际一生成就开始跑偏。训练时看到的输入格式、mask 规则、停止边界，只要和推理阶段有一点偏差，最终体验都会被放大。</p> 
<h2>我的工程判断</h2> 
<p>这一章给我的结论很直接：做一个小 LLM，先盯三件事。第一，骨架是不是清楚，尤其是 norm、attention、MLP、weight tying 这些基础件；第二，Tokenizer 和 prompt 模板是不是和训练数据对齐；第三，<code>loss_mask</code> 和 <code>generate</code> 是否把“该学的”和“该停的”处理对了。</p> 
<p>如果把这三件事做好，小模型至少能先跑通，并且能解释清楚自己为什么能跑通。对我来说，这比一开始就追求更大的参数量更重要。模型不是从某个神奇公式里长出来的，而是从一整条流水线里被一点点拼出来的。</p>]]></description>
    <pubDate>Sat, 25 Jul 2026 21:14:06 +0800</pubDate>
    <dc:creator>emer</dc:creator>
    <guid>http://www.51keeplearning.com/post-132.html</guid>
</item>
<item>
    <title>Agent 工程思考：从 ReAct 到 Agent Harness</title>
    <link>http://www.51keeplearning.com/post-130.html</link>
    <description><![CDATA[<blockquote>
  作者：vivo 互联网项目团队- Ding Junjie 
 <br>从 ReAct 出发，文章讨论 Agent 工程如何从“模型循环”走向 Harness：通过前后端共享的 State Schema、运行事实和 UI 边界，让模型行为变成可交互、可恢复、可控制、可追溯的产品能力。 
</blockquote> 
<p>1分钟看图掌握核心要点</p>   
<img class="aligncenter" src="https://pic3.zhimg.com/80/v2-ca1ccb456b333842ecede4c3e118ee56_720w.webp">   
<p>大模型不是马，是大脑，而且是一颗刚刚觉醒的大脑。</p> 
<h2>一、Agent 不止是 model+loop</h2> 
<p>早期我理解的Agent 工程，可以被写成一段伪代码：</p>  
<pre><code>whilenot done:
  reason
  act
  observe</code></pre>  
<p>用户输入一句话，模型思考一下，决定调用工具。工具返回结果，模型再思考，再决定下一步。</p> 
<p>这也是 ReAct 的基本形态。Thought、Action、Observation 交替出现，模型在生成内容的过程中调用工具，再把工具结果放回下一步推理。</p> 
<p>早期Agent概念刚出，我做了一个 demo ，这个循环完全够用了。命令行里输出 token，中间插入 tool call，再把 observation 塞回 prompt，最后模型给出结论。</p> 
<p>但最近开始深入去做Agent 产品，过程中疯狂调研codex、lobehub、goose、opencode、PI、Flue等优秀Agent产品，发现远远不止这段伪代码所表达的。</p> 
<p>真正的产品还会遇到一组运行时问题：</p> 
<ul> 
 <li>用户刷新页面之后，刚才的工具审批还在吗？</li> 
 <li>工具执行到一半，后端进程重启了，下一次从哪里恢复？</li> 
 <li>子 Agent 在后台跑，主线程要显示什么？</li> 
 <li>生成了一个 artifact，内容本身不塞进上下文，那它的引用、状态、归属在哪里？</li> 
 <li>用户点了 stop，哪些东西应该取消，哪些东西应该保留？</li> 
</ul> 
<p>ReAct 不回答这些问题。</p> 
<p>ReAct 解释的是模型怎么思考和行动。Agent 产品还需要一层工程系统：把模型做过的事，落成可恢复、可控制、可展示、可验证的软件事实。</p> 
<p>下面我把这层系统叫做 Agent Harness。</p> 
<h2>二、ReAct 的解释边界</h2> 
<p>ReAct 的最小单元是：</p>  
<pre><code>Thought -&gt; Action -&gt; Observation</code></pre>  
<p>这个抽象用来描述模型行为：模型先想，再行动，再观察结果。</p> 
<p>到了工程系统里，单位会换成 event、state、checkpoint、control。</p> 
<p>同样一次工具调用，放在真实的Agent产品工程里，大概会拆成这些事件：</p>  
<pre><code>run.started
message.created
assistant.text.delta
tool.call.created
tool.approval_required
tool.call.running
tool.call.completed
artifact.created
run.finished</code></pre>  
<p>这里需要先区分 Observation 的身份。</p> 
<p>在 ReAct 里，Observation 是给模型看的。工具返回了什么，就把这段结果塞回上下文，让模型继续想。这个层面上，它当然可以是一段文本。</p> 
<p>但产品系统不能只停在这里。同一次工具调用，用户关心它还在不在跑，前端展示关心该不该显示审批按钮，存储层关心能不能恢复，artifact 面板关心这个结果属于哪次运行。这些消费方需要同一组可以被系统引用的事实。</p> 
<p>如果 Observation 只剩文本，这些事实就没有权威来源。前端展示要从文本猜状态，后端要靠临时字段补状态，adapter 要把旧数据翻译成新 UI。问题会从运行时边界，转移到多处推导逻辑的一致性。</p> 
<p>所以 ReAct 解释的是执行微循环：模型看到什么，下一步做什么。它没有定义产品系统里的事实边界：哪些事情已经发生，哪些状态可以恢复，哪些动作可以控制，哪些结果可以检查。</p> 
<p>这里说的 Agent Harness，先落在一个具体职责上：为 Agent 产品定义事实协议。</p>   
<img class="aligncenter" src="https://pic2.zhimg.com/80/v2-cc6bd10cf715724508b768ad67d1be8f_720w.webp">   
<h2>三、事实从哪里来</h2> 
<p>最近看了很多开源高star的项目，会发现他们的整体设计都会解释一件事：模型做出的动作，怎么变成软件系统里的事实？</p> 
<p>一种做法是让 ReAct loop 原样跑完，外面再写一层 UI adapter。它读 message，读 observation，读 tool result，尽量拼出 isBusy、pending-Approval、artifactRefs。</p> 
<p>这种做法改动小：模型继续想，工具继续跑，前端也能先画出来。</p> 
<p>问题是，这些事实只在事后出现。</p> 
<p>等 adapter 看到 observation 时，审批可能只是一句话，artifact 可能只是模型提到过的一个路径，stop 也只剩一个按钮状态。系统还可以继续补字段、补判断、补同步逻辑，但这些逻辑都在追认已经发生过的事。</p> 
<p>如果说，Agent = Harness + model ，那么Harness中最需要搞清楚的问题绝对不是skill怎么安装，mcp怎么加载，记忆是如何设计。而是更加工程的底气：事实应该在哪里产生。</p> 
<p>当 tool call 需要审批，runtime 应该直接产生 tool.approval_required。当文件或文档被生成，runtime 应该写出 artifact.created 和可定位的 ref。用户点 stop，control 应该回到对应的 run，而不是让组件自己把按钮置灰。</p> 
<p>所以 Harness 必须在运行路径上。tool call 开始、等待审批、执行完成、生成 artifact、写入 checkpoint，这些节点都应该由 runtime 产生 event 和 state。用户的 approve、stop、resume，也应该进入 runtime 的 control，而不是停在组件自己的状态里。</p>   
<img class="aligncenter" src="https://pic3.zhimg.com/80/v2-9e540068f01b2f116b714ebd12cd177a_720w.webp">   
<p>一个 Agent Harness 至少要回答这些问题：</p>  
<pre><code>现在谁在运行？
运行卡在哪里？
哪个状态可以恢复？
哪个动作需要用户审批？
哪个结果可以被检查？
哪个 artifact 属于哪次运行？
哪个子 Agent 是谁派出去的？
用户可以发送哪些命令？
刷新、重连、进程重启之后，系统如何回到同一个现场？</code></pre>  
<p>如果这些问题没有被 Harness 统一回答，实现里仍然要找地方放它们：前端组件、工具回调、消息渲染、数据库字段、临时缓存，或者某个“先这样”的判断。</p> 
<p>判断标准可以直接写出来：同一个运行事实，不应该从多个来源拼出来。</p> 
<p>pendingApproval、artifactRef、canStop 这类状态应该有明确归属。它们要么是 runtime state，要么是从 runtime state 派生的 view，不应该同时散在 message 文本、工具结果和前端本地状态里。</p> 
<p>问题到这里，下一步就要设计系统怎么记、怎么算、怎么控制。</p> 
<h2>四、从 Loop 到协议</h2> 
<p>一个能产品化的 Agent 系统，数据流应该更像这样：</p>  
<pre><code>runtime event
  -&gt; agent.state
  -&gt; agent.view
  -&gt; UI

user action
  -&gt; agent.control
  -&gt; runtime event</code></pre>  
<p>这里有三个词。state 是事实。它应该由 runtime 和明确的业务边界生产。比如：</p>  
<pre><code>messages
activeRun
checkpoint
pendingApproval
todos
subagents
artifactRefs
workspaceContext</code></pre>  
<p>&nbsp;</p> 
<p>这些状态影响任务能不能继续、能不能恢复、用户能不能检查结果。它们不属于前端展示缓存，而属于 Agent 的运行状态。view 是派生。比如：</p>  
<pre><code>isBusy
canStop
waitingForFirstToken
approvalBanner
toolBadges
subagentGroups
messageProjection</code></pre>  
<p>这些东西应该从 state 算出来。activeRun 存在，所以可以显示 busy。pendingApproval 存在，所以显示审批条。run 已经开始但第一个 assistant part 还没有出现，所以显示 first-token waiting。</p> 
<p>control 是命令。比如：</p>  
<pre><code>invoke(input, stateSnapshot)
resume(approvalDecision)
stop(runId)
updateState(patch)
reload(threadId)</code></pre>  
<p>control 只负责发命令，不顺手改展示状态。用户点批准，前端发 resume。审批条什么时候消失，取决于 runtime 是否继续执行并写回新的 state。UI 只从新的 state 派生出来。</p> 
<p>边界在这里：runtime 负责写事实，UI 负责读事实。</p> 
<p>Agent Harness 把这条边界固定成协议。</p>   
<img class="aligncenter" src="https://pic1.zhimg.com/80/v2-c50fac4a019019ae548478e02a432f6a_720w.webp">   
<h2>五、State Schema 定义事实边界</h2> 
<p>开发一个Agent时，不管是work Agent还是业务垂类Agent，都应该先设计好state schema ，只有这个清晰了，Agent产品的定位，工程的架构就清晰了。</p> 
<p>如果先设计 prompt、tool、模型选择，最后才整理状态，很多运行事实会被迫挂在 message、工具结果或前端缓存上。只要这个 Agent 要进入用户工作流，就应该提前问：</p>  
<pre><code>哪些事实必须恢复？
哪些事实必须跨端一致？
哪些事实只是 view？
哪些事实是用户现场？
哪些事实可以从 messages 推导？
哪些事实必须成为一等状态？</code></pre>  
<p>审批在 UI 上是按钮，在 runtime 里是可恢复暂停点。</p> 
<p>如果模型要执行一个危险命令，系统不能只在前端弹一个 modal。因为用户刷新页面之后，这个 modal 会丢。后端也不知道自己停在哪里。</p> 
<p>可以把审批写成 state：</p>  
<pre><code>agent.state.pendingApproval = {
  id,
  runId,
  turnId,
  toolCallId,
  toolName,
  arguments,
  policy
}</code></pre>  
<p>用户点击批准：</p>  
<pre><code>agent.control.resume({
  approvalId,
  decision: "allow"
})</code></pre>  
<p>runtime 从 checkpoint 继续，继续之后再写回新的 state。前端只消费 state。</p> 
<p>artifact 的内容本体不一定要进 state。一个文档、一张图、一个大 JSON、一个外部系统连接，可能属于文件系统、数据库或对象存储。但 artifact 的引用、状态、归属应该进 state：</p>  
<pre><code>artifactRef:
  id
  type
  title
  status
  ownerRunId
  createdByToolCallId
  contentRef</code></pre>  
<p>这样消息列表、右侧面板、历史记录、恢复流程都能通过同一个引用定位 artifact。内容可以懒加载，引用必须是事实。</p> 
<p>子 Agent 也不应该只出现在文本里。</p> 
<p>子 Agent 的运行关系也应该进入 state。如果主 Agent 只是输出一句：</p>  
<pre><code>Started subagent abc123</code></pre>  
<p>前端想画子任务面板，就只能从文本里抠 ID。后续要做状态同步、跳转、恢复、错误展示时，这个 ID 仍然没有明确归属。</p> 
<p>子 Agent 至少应该有运行事实：</p>  
<pre><code>subagent:
  id
  parentRunId
  parentTurnId
  title
  status
  startedAt
  completedAt
  resultRef
  error</code></pre>  
<p>前端再从 subagent state 派生分组、徽标、进度文案、跳转目标。</p> 
<p>state 的判断标准可以直接写成一句话：影响恢复、审批、继续执行、跨端一致、审计和可检查结果的东西，应该进入 state。</p> 
<p>只改变展示方式的东西，留在 view。</p>   
<img class="aligncenter" src="https://pic4.zhimg.com/80/v2-6765a4956bb9954df04f369a3cf882c3_720w.webp">   
<h2>六、UI 只能消费事实，不能补写事实</h2> 
<p>UI 可以负责渲染、交互、布局、流式展示和 view state。</p> 
<p>runtime fact 不属于 UI。pendingApproval、artifactRef、activeRun 这类事实，应该由 runtime 写入 state。</p> 
<p>UI 的位置在 state 下游：从 state 派生 view，再把 view 渲染成可操作界面。</p>  
<pre><code>agent.state.pendingApproval
  -&gt; agent.view.approvalBanner
  -&gt; UI: 批准 / 拒绝按钮</code></pre>  
<p>如果 runtime 没有写入 pendingApproval，UI 不应该从 message 文本创建这个事实：</p>  
<pre><code>message: "Need approval to run shell command"
  -&gt; UI 判断这句话像审批请求
  -&gt; UI 本地创建 pendingApproval
  -&gt; UI 显示批准 / 拒绝按钮</code></pre>  
<p>这条路径的问题很具体：刷新之后这个 approval 还在吗？后端知道自己停在哪个 tool call 吗？用户点批准时，前端要把决定发给哪个 run？</p> 
<p>边界规则是：runtime 写入 fact，UI 消费 fact。</p>   
<img class="aligncenter" src="https://pic3.zhimg.com/80/v2-0638fa8563462619b75fc3865b10e5b2_720w.webp">   
<h2>七、系统要记得发生过什么</h2> 
<p>到这里，Harness 的含义可以再落一下。</p> 
<p>它不是给 UI 多加一层封装，而是把 Agent 运行中的关键事实放进系统协议里：模型发起了哪个 tool call，当前卡在哪个审批，哪个 run 产生了 artifact，用户的 stop 要终止哪次运行。</p> 
<p>Agent 的运行不会总是顺着一条完整的同步调用走完。页面会刷新，进程会重启，工具调用会等待审批，用户可能点 stop，子 Agent 也可能在另一个执行上下文里结束。</p> 
<p>这时问题就不是 UI 怎么画，而是这些事实有没有稳定归属，能不能被恢复、继续和追溯。</p> 
<p>可以把这个要求写成可检查的行为：</p>  
<pre><code>刷新页面后，pendingApproval 仍然存在且指向同一个 toolCall
进程重启后，能从 checkpoint resume 到同一个 run/turn
用户 stop 后，activeRun 终止且后端不会继续执行后续 tool call
artifactRef 存在时，内容可被定位、可追溯到 ownerRunId / toolCallId
子 Agent 完成后，parent turn 能拿到 resultRef 并展示归属</code></pre>  
<p>这组检查最后落到同一个问题：运行事实有没有明确归属。pendingApproval、artifactRef、activeRun 这些东西，必须由 runtime 生成并持久化；UI 只能从 state 派生 view，不能替系统补事实。</p> 
<p>所以 ReAct 和 Harness 的分工也会变得清楚：</p> 
<p>ReAct 解释模型怎样一步步决定下一步。</p> 
<p>Harness 保证这些步骤在软件系统里发生过、能恢复、可控制、可审计。</p> 
<h2>八、最后</h2> 
<p>Agent 产品的难点，不止是写出一个会调用工具的循环。</p> 
<p>那个循环让模型“能动”，但用户真正依赖的是系统层面的确定性：刷新不丢现场、重启能恢复、该停就停、结果可定位、责任可追溯。</p> 
<p>这就是 Agent Harness 的价值：把一次次“模型行为”落成一组可引用、可恢复、可控制的软件事实。</p> 
<p>一句话总结：ReAct 让模型动起来；Harness 让这件事在产品里可被信任。</p>]]></description>
    <pubDate>Sat, 25 Jul 2026 21:14:05 +0800</pubDate>
    <dc:creator>emer</dc:creator>
    <guid>http://www.51keeplearning.com/post-130.html</guid>
</item></channel>
</rss>