


[{"content":"","date":"2026-04-06","externalUrl":null,"permalink":"/authors/aaron/","section":"Authors","summary":"","title":"Aaron","type":"authors"},{"content":"","date":"2026-04-06","externalUrl":null,"permalink":"/authors/","section":"Authors","summary":"","title":"Authors","type":"authors"},{"content":"","date":"2026-04-06","externalUrl":null,"permalink":"/","section":"Hideaway","summary":"","title":"Hideaway","type":"page"},{"content":"","date":"2026-04-06","externalUrl":null,"permalink":"/tags/karpathy/","section":"标签","summary":"","title":"Karpathy","type":"tags"},{"content":" 前言 # 前几天刷到 Karpathy 在 X 上发的一条帖子，核心意思是他最近把大量 Token 从写代码转移到了「操纵知识」上，用 LLM 来帮自己搭个人知识库。这条推文炸了之后，他干脆写了一份叫 llmwiki 的 idea file，把灵感升级成了可执行的设计文档。我看完之后觉得这套思路确实值得聊聊，它不只是换了一种 RAG 的姿势，而是在重新定义「第二大脑」该怎么搭。\nRAG 的硬伤：知识不会累积 # 过去两年我们对大模型的主流用法是什么？当搜索框、当问答机、当 RAG 前端。你丢一批 PDF、一堆 Markdown，建个向量库，提问的时候检索几个 chunk，拼个答案出来。\n这套做法单次看起来很聪明，但它有一个结构性问题：知识不会累积。每次提问题，模型都是从原始文档重新发现世界。同一个复杂问题，今天问一遍、下周问一遍，都是重新拼一遍碎片。你会觉得很熟悉的那种挫败感：明明这堆 PDF 已经问过十遍了，系统却像一个记忆力很差的助理，每次都从零开始翻箱倒柜1。\nKarpathy 想换掉的，就是这种永远在「现场拼答案」、从不把知识做成资产的默认体验。\n知识编译器：一个范式转换 # Karpathy 给出的反转设计，其实就一句话：别再把问答当终点，而要把问答当中间产物，写回一个长期维护的知识体系。\n在他的设计里，LLM 做的不是按需检索，而是增量编译。你往 raw 目录丢一篇新文章，它不会等你哪天来搜，而是立刻读完、抽取要点、更新相关概念页和实体页、补上交叉链接，甚至标记这条信息和旧说法冲突。\n如果你要把这套脑洞讲给团队听，用 Karpathy 自己的类比就够了：\nObsidian 是 IDE 前端，你在里面浏览 raw 资料、看 wiki 页、看 graph view，就像在 VS Code 里翻代码、看调用图 LLM Agent 是 programmer，它根据 schema（Claude.md、agents.md）来决定怎么 ingest、怎么 query、怎么 link Wiki 本身就是 codebase，它是可读的、可 diff 的、可以用 Git 管版本的。它不是向量数组，而是一堆你随时可以打开、重构、review 的知识代码 当你用这个视角看 llmwiki，就会发现 Karpathy 其实是把工程文化那套东西挪到了知识管理上：有源代码、有中间产物、有测试 lint、有版本历史。只是这次编译的不是二进制，而是你自己的第二大脑2。\n三层架构 # Karpathy 的设计很工程思维，整体分三层：\n最底下是 raw sources（原始资料）。论文、文章、代码仓库、截图、数据集，全都扔这里。模型只读不写，保证有一份不被 AI 润色过头的原版世界。\n中间是 wiki 层。全部是 LLM 写出来的 Markdown，所有 summary、概念页、实体页、对比表都在这里增量生成、维护、加 backlinks。你作为人类，主要也是读这一层。\n最上面压着一个 schema。它更像 Claude.md、agents.md 这种团队规范，约定目录结构、命名规则、ingest 和 lint 的流程，决定这套系统是混乱生长还是长期可维护。\n三层拆开看很简单，但真正的魔法发生在那几个核心动作里。\n四个核心动作 # Ingest：增量编译 # 很多人一听 ingest 会下意识以为就是「塞到向量库里」。在 llmwiki 里不是这样，ingest 更像一次增量编译。\n一个新来源进 raw，Agent 的流程大致是：先读完原文，对一下关键 takeaway，然后在 wiki 里新建 summary 页、更新 index，接着去相关的实体页和概念页把新信息融进去。一篇像样的文章，动辄会影响 10 到 15 个页面：人物关系会更新、时间线会更新、某个争议点的证据表也会多一行。\n所以在这套范式里，ingest 不是上传文件，更像是 commit 了一次完整的知识变更。\nQuery：结构优先的检索 # 传统 RAG 的检索是很 flat 的：给一个 query，在整个语料空间里做相似度排名，抓 top-k chunk 扔给模型。它并不知道哪些是纲要、哪些是某一页的一角。\nKarpathy 的做法是先花时间把结构铺好：有 index.md 这种内容导向的目录，每一页都有一行摘要，有实体页和概念页之间的 backlinks，有 log.md 记录最近 ingest 和 lint 的轨迹。在这种结构下，query 流程变成：先读 index，看有哪些页面命中这个问题，再顺着链接下钻到相关页面，最后才在小范围里综合答案。\n对 Agent 来说，这有点像先看目录和索引，再查具体章节，而不是在一堆碎片上做 KNN。\nFile Back：好回答本身就是资产 # 传统用法里，问完一个好问题、模型给了一个很强的分析，结果停在聊天窗口里，过几天这条对话就沉底了。\n在 llmwiki 的设计里，好回答本身就是资产。只要质量过关，就应该被写回 wiki：存成一个新的 concept 页面、一份决策记录、一张对比表，挂上 sources 和日期。这样你每次追问，其实都在让知识库升级一把。你的探索路径、你和模型一起踩过的坑，都会变成下一次 query 的可用上下文，而不是一堆失温的聊天记录。\nLint：知识版的测试套件 # 只长不体检，很快就会长成一团脂肪。Lint 在这里的含义和代码世界几乎一模一样：不是帮你写新东西，而是定期扫描 wiki 找问题。\n它找的是：不同页面里互相打架的结论、被新资料推翻但还挂着的老说法、没有任何入链的孤儿页面、反复被提到却没有独立页面的关键概念。更高级一点，你可以让 Agent 在 lint 过程中顺手提建议，比如「这里有个反复出现但没独立页面的 pattern，可以拉出来单独建一个页面」。\n有了 lint，这套系统才真的像一个长期维护的代码库，而不是自动生成的笔记堆3。\nFazapedia：把自己的人生编译成 Wiki # Farza 看到 Karpathy 讲「把 LLM 当成 personal knowledge base 的编译器」这句话，直接共鸣了。他没有停留在转发和点赞，而是找一个周末，把几乎整个人生的数字痕迹全部丢给 Claude 处理。\n不是几篇博客，而是 2500 多条本地素材：五年的 Apple Notes、私密原始日记、和朋友创始人的成千上万条 iMessage、一堆语音备忘录的转录。Agent 咀嚼了好几个小时，最后吐出来一个本地托管的 Markdown wiki，大概 400 篇互相链接的文章，起名叫 Fazapedia。\n这个系统不光知道他的创业时间线，还知道他的朋友网络、他爱看的动漫，几乎把他整个人重建了一遍。Karpathy 看到之后直接盖章说这就是他心目中的 gold standard，是这套想法的教科书级落地。\n对工作的实际收益 # Faza 后面的用法更加实际。他不再每次从零给 Claude 写一个长 prompt 解释「我是谁、我喜欢怎样的设计、我做过哪些项目」，而是直接让 Agent 以 Fazapedia 为知识底座，去翻阅他过去的设计批评、项目总结和审美吐槽。然后只需要说「帮我为这个 side project 做一个 landing page」，Agent 就会自然带上他过去骂过的 UI 反模式、讨厌的配色、偏好的语气风格，一起写出页面和代码。\n这就是从生成一次性答案，升级成基于自己的知识库做持续风格对齐和复用。\nKarpathy 给出的四个 endorsement # Karpathy 在后续讨论中给了四个非常工程化的 endorsement：\nExplicit（显式）：你可以清清楚楚读到模型知道什么，以纯文本形式存在，而不是一堆看不见的向量数组 Yours（归你）：数据物理上在你控制的本地文件夹里，不被任何云端 SaaS 困住 File over app（文件优先）：一切都是 Markdown，没有供应商锁定，任何编辑器、任何工具链都能接上 BYOAI（自带 AI）：是你把模型插到这个文件夹上，而不是把自己的数据塞进某个模型提供方的黑盒 质疑与护栏 # 这不就是换皮的 RAG 吗？ # 反对的人会说，不管叫它 wiki 也好、compiler 也好，本质上还是要在一堆 Markdown 里检索，把相关内容塞进 context 再生成答案。\n支持者强调三个差别：第一，知识是会写回去的；第二，模型是在一个已经被综合过、加过链接的中间层上工作；第三，多了一整套 lint 流程在主动维护结构。你可以这么理解：检索这一步确实还是 R，但 G 之前多了一层知识编译和健康检查。\nAI Slop 的担心 # 你老让模型改模型自己写的东西，最后不会变成一锅 AI 废料吗？模型从人类原文里提炼出 wiki 已经有损耗了，下一轮再从 wiki 里读、再生成新的总结，损耗再叠一层。\n这种担心是有道理的。这也是为什么 Karpathy 很强调 raw 目录的不可变性。raw 不能改，是随时可以回溯、校对、重新编译的那一份底稿。关键是在 schema 里明确规定什么场景必须回到 raw 对照，哪些结论不能只在 wiki 里闭环自转。\n规模问题 # 不少人会问：现在玩的是一两百篇 wiki，lint 跑一跑还行，涨到几万篇、上千万字还能撑得住吗？\n诚实地说，Karpathy 现在谈的确实偏个人研究和小型团队知识库。在这个量级上用 index 加下钻加适度 lint 是完全够用的。但如果你一上来就想拿它替代大型企业级 KM 系统，那确实需要更重的索引层。更合理的姿势是先承认它的 sweet spot 在个人和小团队，真把这块玩到上线了，再去谈规模化的分布式知识编译。\n认知习惯的 tradeoff # 很多做 PKM 的人会说，写 wiki、整理笔记本来就不只是为了留一个 artifact，而是在这个过程中逼自己重新思考、重组认知。如果你把所有写作与整理都外包给 LLM，自己只看成品，确实有可能慢慢失去那种在整理中思考的肌肉。\n我个人的建议是可以明确划分：命题型、需要严肃思考的内容，你自己写，AI 只做评论和补充；重复性高的结构化整理，比如把 50 篇访谈统一成一个对比表，让 Agent 去干。\n生态与展望 # 如果这套思路只是 Karpathy 的一条推文或者 Farza 的一次人生实验，其实很容易被当成网红概念。但现在你能看到的是：llmwiki 已经有开源实现把三层架构跑通，QMD 这类本地 Markdown 搜索引擎在帮你解决 wiki 做大之后怎么搜的问题，Remember 则从另一个角度强调跨工具共享的结构化大脑，用的同样是 Obsidian 兼容的文件层。\n这些都在印证一个方向：把知识堆在某个 SaaS 聊天窗里这条路线，正在被显式文件加本地工具加可插拔模型逐步蚕食。\n结语 # 从 Karpathy 的那条推文出发，一路讲到了三层架构、Fazapedia、质疑和护栏。如果要用一句话收尾：在 AI 时代，我们真正的护城河不是多会写 Prompt，也不是多会堆 RAG 组件，而是有没有把自己的知识整理成一套可复利的知识代码库。\n模型会变，框架会变，但这套 wiki 风格的文件层只要还在我们手里，就能持续接上下一代的 AI。如果你已经在用 Obsidian、Notion，可以试着问自己一个问题：从今天开始，你的 Token 要不要少花一点在写代码上，多花一点在编译知识上？\n传统 RAG 的核心流程是「文档 → embedding → 向量库 → 相似度检索 → 拼答案」，本质上是「临时翻书」，每次都是即时检索，没有沉淀。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n这种把工程方法论搬到知识管理上的思路，和软件工程中的「编译」概念非常类似：源代码经过编译器处理后生成结构化的中间产物，而不是每次运行时重新解释源码。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nLint 一词来自代码世界的 lint tool（如 ESLint、Pylint），用于静态扫描代码中的潜在问题。在这里被借用为知识库的「质量检查」环节。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2026-04-06","externalUrl":null,"permalink":"/posts/2026/04/llm-knowledge-compiler/","section":"所有文章","summary":"","title":"Karpathy 的「知识编译器」：从 RAG 到可复利的第二大脑","type":"posts"},{"content":"","date":"2026-04-06","externalUrl":null,"permalink":"/en/tags/knowledge-management/","section":"Tags","summary":"","title":"Knowledge Management","type":"tags"},{"content":"","date":"2026-04-06","externalUrl":null,"permalink":"/tags/llm/","section":"标签","summary":"","title":"LLM","type":"tags"},{"content":"","date":"2026-04-06","externalUrl":null,"permalink":"/tags/llmwiki/","section":"标签","summary":"","title":"Llmwiki","type":"tags"},{"content":"","date":"2026-04-06","externalUrl":null,"permalink":"/tags/rag/","section":"标签","summary":"","title":"RAG","type":"tags"},{"content":"","date":"2026-04-06","externalUrl":null,"permalink":"/en/categories/tech-reflections/","section":"Categories","summary":"","title":"Tech Reflections","type":"categories"},{"content":"","date":"2026-04-06","externalUrl":null,"permalink":"/tags/","section":"标签","summary":"","title":"标签","type":"tags"},{"content":"","date":"2026-04-06","externalUrl":null,"permalink":"/categories/","section":"分类","summary":"","title":"分类","type":"categories"},{"content":"","date":"2026-04-06","externalUrl":null,"permalink":"/categories/%E6%8A%80%E6%9C%AF%E6%80%9D%E8%80%83/","section":"分类","summary":"","title":"技术思考","type":"categories"},{"content":"您可以通过 RSS 订阅所有博客文章\n","date":"2026-04-06","externalUrl":null,"permalink":"/posts/","section":"所有文章","summary":"","title":"所有文章","type":"posts"},{"content":"","date":"2026-04-06","externalUrl":null,"permalink":"/tags/%E7%9F%A5%E8%AF%86%E7%AE%A1%E7%90%86/","section":"标签","summary":"","title":"知识管理","type":"tags"},{"content":"","date":"2026-04-05","externalUrl":null,"permalink":"/tags/agent/","section":"标签","summary":"","title":"Agent","type":"tags"},{"content":"","date":"2026-04-05","externalUrl":null,"permalink":"/tags/ai/","section":"标签","summary":"","title":"AI","type":"tags"},{"content":"","date":"2026-04-05","externalUrl":null,"permalink":"/tags/claude-code/","section":"标签","summary":"","title":"Claude Code","type":"tags"},{"content":" 前言 # 之前写了一篇 Claude Code 的上手指南，覆盖了安装配置和基础功能。后来深入使用的过程中，发现还有不少进阶玩法我之前没涉及到，比如 NoFlicker 模式、输入技巧、会话管理、缓存策略这些。这些功能单独看都是小细节，但组合起来对日常使用体验的提升非常大。这篇文章把这些内容整理出来，算是上一篇的补充1。\nNoFlicker 模式（无闪烁模式） # 传统模式下，Claude Code 每次对话都会把整个历史完整拉出刷新，超过终端刷新速度导致闪烁。NoFlicker 模式从根本上解决了这个问题。\n启用方式：在 settings.json 中添加 \u0026quot;noFlicker\u0026quot;: true，或使用命令 /tui fullscreen。恢复传统模式用 /tui default。\n三大改进：\n内置虚拟滚动：不再依赖终端滚动，类似 vim 的屏幕滚动方式，支持 Page Up/Page Down 和鼠标滚轮，滚动平滑 Focus 精简显示：每轮对话只显示问题加最终结果摘要（如「编辑了 5 个文件，增加 54 行，删除 25 行」），中间过程可展开查看，不关心就折叠 上下文快速定位：直接看到每轮对话的问题和回复的对应关系，不用滚很长距离 Ctrl+O 三级切换（传统模式只有一级）：\n第 0 级：Focus 模式，只显示摘要 第 1 次 Ctrl+O：显示每步操作概要（bash 仍精简） 第 2 次 Ctrl+O：完全展开，等同传统模式 其他交互：\n底部有「跳回底部」按钮 支持鼠标点击穿透 Ctrl+O 后按 V 可用 nvim 打开完整对话记录，方便复制和搜索（vim 用户狂喜） Ctrl+O 后按 [ 切换到终端原生滚动模式 输入技巧 # Ctrl+S：输入暂存 # 正在输入长提示词时，突然想先执行别的操作（切模型、运行命令、调用技能），按 Ctrl+S 把当前输入推到栈上，执行完其他操作后自动恢复。\nCtrl+G：外部编辑器编辑提示词 # 长提示词用 Ctrl+G 打开外部编辑器（需设置 EDITOR=nvim 或 EDITOR=code），多行编辑更省力，保存退出后自动填充到输入框。\nCtrl+V：粘贴图片 # 直接 Ctrl+V 粘贴截图到对话，可以对着截图提问、要求模仿 UI 设计等。Claude 会根据图片理解你的需求。\n!：就地运行 bash 命令 # 输入 ! ls 等命令可直接在 Claude Code 内执行，输出自动进入上下文。好处：\n运行后直接提问，Claude 能看到命令输出 报错后直接说 \u0026ldquo;fix\u0026rdquo;，Claude 看到错误就地修复 不用手动粘贴错误信息 btw：旁路提问（不进上下文） # 在 Claude 工作过程中，输入 btw \u0026lt;问题\u0026gt; 进行临时提问：\n不进入主上下文，不浪费 token 不打断当前正在执行的任务 一次性问答，用完自动消失 适合不确定是否相关的问题 会话管理 # clear：清空上下文 # /clear 清空当前上下文，开始全新对话。好处：\n省 token 模型更专注于当前任务 避免旧指令和新指令冲突导致降智 resume：恢复会话 # 误操作 clear 后可用 /resume 恢复之前的对话。\nrename：重命名会话 # /rename \u0026lt;名称\u0026gt;：手动命名 /rename（不带参数）：让 Claude 根据上下文自动生成名称 多开会话时方便识别每个会话在做什么 color：会话着色 # /color \u0026lt;颜色\u0026gt; 给会话设置主题色，视觉区分不同会话，快速联想。\nrecap：自动回顾 # 闲置一段时间后自动生成灰色 recap 摘要 切换回来一眼就知道之前在做什么 也可手动输入 /recap 触发 可在 config 中关闭此功能（觉得浪费 token 的话） copy：复制回复 # /copy：复制最近一条回复 /copy \u0026lt;数字\u0026gt;：复制指定编号的回复 NoFlicker 模式下也支持鼠标选中文本直接复制（使用 OSC52 终端功能） Rewind（回滚） # 回滚对话 + 代码 # 不仅回滚对话，同时撤销 Claude 的代码修改。\n仅回滚对话 # 只撤销对话，不修改代码（但已删除的文件无法恢复）。\n触发方式 # 命令行输入 /undo 或 rewind 按两下 Esc 也可触发 非交互模式（命令行直接调用） # claude \u0026quot;提示词\u0026quot; # 直接在终端执行，进入 TUI 后自动运行。\nclaude -p \u0026quot;提示词\u0026quot;（纯管道模式） # 不启动 TUI 界面，直接输出结果到终端。用途：\n搭配 claude -p --model haiku \u0026quot;make a commit\u0026quot; 用便宜模型快速生成 commit 结合脚本自动化工作流 适合只关心最终结果不关心过程的场景 自然语言转命令 # 直接用自然语言描述想做的事（如 \u0026ldquo;删除 wiki 相关的 mcp\u0026rdquo;），Claude 自动转换为正确命令执行。\nWorktree（工作树隔离） # 概念 # 在独立目录创建完整代码副本，修改不影响主工作区。多个 Claude 会话可同时在不同 worktree 中并行工作，互不干扰。\n使用方式 # claude -w \u0026lt;名称\u0026gt; 或对话中说 \u0026ldquo;switch to worktree\u0026rdquo; 在 worktree 中开发功能，完成后说 \u0026ldquo;merge back to main\u0026rdquo; 每个 worktree 自动创建独立分支 典型工作流 # 说 \u0026ldquo;switch to worktree, 开发 XXX 功能\u0026rdquo; Claude 自动创建 worktree 和命名分支 切换到 Plan 模式 → interview me（让 Claude 先提问再规划） 接受计划 → 执行 完成后说 \u0026ldquo;merge back to main\u0026rdquo; Plan 模式的 interview me # 在 Plan 模式中输入 interview me 让 Claude 先提问关键需求，避免：\n方案和预期不符导致返工 浪费时间重新对话纠正 提前确认好再开始实施 定时任务与循环 # Loop：循环任务 # /loop 1m report cpu usage 每隔指定时间重复执行提示词 使用 CronCreate 在 Claude 内部创建定时器 退出会话时 loop 停止 建议配合 tmux 使用，保持会话不关闭 Schedule：一次性定时任务 # 用自然语言描述即可：\n\u0026ldquo;16 点帮我运行 XXX\u0026rdquo; Claude 自动设置定时执行 到时间自动执行，终端需保持开启 可用 CronDelete 手动取消 Skills（技能系统） # 技能按需加载 # 技能只在需要时加载，不常驻上下文（一个 Skill 约 10 token） 对比 MCP：一个 MCP 动辄几千 token 占用上下文 技能通过 description 中的关键词/模式匹配触发 从对话创建技能 # 把当前对话中的常用任务转化为可复用的 Skill：\n检测对话中发生了什么有意义的操作 引导你设置技能名称、目标、触发条件、参数 保存后可随时调用 技能的 description 写法 # description 写在 Skill 工具的描述里，采用 Anthropic 官方推荐格式：\n描述技能功能。 Use this skill when: [触发条件/关键词模式] 触发强度取决于 description 中的模式匹配强度 越具体的模式越容易被 Claude 主动调用 技能内容按需加载 # Skill 主文件只包含基本信息 详细文档放在子目录的 reference 文件中 Claude 只在需要时才读取对应 reference 支持引用网页 URL 获取最新文档 Hooks（钩子） # 概念 # 在 Claude 执行特定操作（如调用 bash、编辑文件）前/后自动触发的脚本，写在 settings.json 中。\n典型用途 # a) 命令拦截替换\n检测到 pip → 提示改用 uv 检测到 npm → 提示改用 pnpm 可设置 fallback（特殊情况下允许跳过） b) 编辑后自动复查\n每次编辑/写入文件后自动重新读取检查 检查上下 30 行是否正确（如 JSON 少逗号等） 检查无误则静默，有问题则自动修复 比写规则更可靠，不增加编辑时的认知负担 c) 辅助技能触发\n检测到后台任务启动 → 注入提示让 Claude 联想 cache-keepalive 技能 弥补大模型注意力\u0026quot;中间弱\u0026quot;的问题 为什么用 Hook 而不是规则 # 低端模型（如 GLM）容易忘记文字规则 Hook 是程序化强制执行，100% 触发 规则太多会降低推理质量 Hook 不干扰编辑时的注意力，只在操作完成后触发 Review 技能（代码审查） # 代码审查要点 # 检查修改文件与上下文风格是否一致（如换行规范） 检查 AI 常见\u0026quot;拉屎模式\u0026quot;（smell detection） AI 常见 Smell 模式 # 防御性编程过度：except: pass 吞掉所有异常不报告 不必要的安全网：没出错也加默认值、类型转换 不修 root cause：上游有问题，中间加过滤器而不是修源头 死代码残留：改了功能但旧代码不删 硬编码步骤编号：1. 2. 3. 改成 1. 2a. 2b. 很恶心 复制粘贴改一点点：应该抽象成函数 适合用子代理（SubAgent） # 审查需要读大量文件但结论很少 → 用子代理隔离上下文，只返回关键结论给主代理。\n模型与配置建议 # 推荐使用 Opus # Opus 1M 上下文和 200K 同价（改价后） 记忆召回能力极强，很早的对话都能想起 Sonnet 200K 容易遗忘之前的设定 Effort 设置 # 必须设为 high 或 max auto 模式对简单问题会跳过思考，质量大幅下降 简单操作也可能涉及危险命令（如删除），跳过思考不可接受 关闭动态思考 # Claude 4.0+ 新增动态思考：简单问题可能不思考直接回答 建议关闭，让每次都用全量思考 虽然慢一点贵一点，但 Max 套餐根本用不完 Bypass Permission # 全程 bypass 可以减少手动确认 建议每个命令看一眼内容再执行 修改自身配置时仍会询问 上下文管理最佳实践 # 核心原则 # 不要把单个上下文当宠物养 —— Claude 的上下文是有限记忆窗口。\n关键建议 # 话题切换时 clear 新开上下文 无关上下文（如早上讨论吃什么）对当前任务（如讨论 Skill）毫无帮助 上下文太长 → 注意力分散 → 智商降低 大多数在 200K 时就已经开始降智，1M 只是延迟了压缩 重要结论沉淀为文件 # 与 Claude 讨论出的规则、规范写到 CLAUDE.md 或 docs 文件夹 上下文丢失后对话记录全无 文件可以按需加载，上下文里的不行 Compact（压缩上下文） # /compact 把之前上下文简化描述为第一条消息 接着在上面继续工作 规则太多记不住 → compact 一次重置 注意力分布规律 # 最新上下文：最熟悉（注意力最高） 最前上下文：次熟悉 中间上下文：最容易遗忘（注意力最弱） 缓存省钱策略（Cache Keepalive） # Anthropic 缓存机制 # 状态 价格倍率 首次请求（写缓存） 1.25x 缓存命中（读缓存） 0.1x 缓存失效（超过 TTL） 重新 1.25x 1 小时缓存选项 2.0x 五分钟策略 # 默认缓存 TTL = 5 分钟 每 5 分钟刷一次，保持缓存活跃 刷 10 次（50 分钟）= 1.0x，远比 50 分钟后缓存失效重新 1.25x 划算 通过 Hook + Skill 组合自动触发 cache keepalive 其他实用功能 # claude install \u0026lt;包名\u0026gt; # 自然语言安装软件，Claude 自动处理依赖和配置。\n自然语言配置系统 # 不用记复杂命令，直接用自然语言描述需求 Claude 自动转换为正确命令（如 systemd 配置、clash 守护进程等） 快捷键速查表 # 快捷键 功能 Ctrl+O 切换显示详细程度（NoFlicker 三级） Ctrl+S 暂存当前输入，执行其他操作后恢复 Ctrl+G 用外部编辑器编辑提示词 Ctrl+V 粘贴图片到对话 Esc Esc Rewind（回滚） Ctrl+O → V 用 vim 打开完整对话记录 Ctrl+O → [ 切换终端原生滚动 ! \u0026lt;命令\u0026gt; 就地运行 bash 命令 btw \u0026lt;问题\u0026gt; 旁路提问（不进上下文） 命令速查表 # 命令 功能 /clear 清空上下文 /resume 恢复之前的会话 /compact 压缩上下文 /undo 或 rewind 回滚 /rename \u0026lt;名\u0026gt; 重命名会话 /rename 自动生成会话名 /color \u0026lt;色\u0026gt; 设置会话主题色 /copy [N] 复制最近/指定回复 /recap 手动触发回顾 /loop \u0026lt;时间\u0026gt; \u0026lt;提示\u0026gt; 循环执行任务 /focus / /focus off 切换 Focus 模式 /tui fullscreen 启用无闪烁滚动模式 /tui default 恢复传统模式 claude -p \u0026quot;...\u0026quot; 非交互模式运行 之前那篇上手指南覆盖了安装配置、三种交互模式、Plan Mode、Figma MCP、CLAUDE.md、Hook、Agent Skill、SubAgent、Plugin 等基础功能，建议先看那篇再来看这篇。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2026-04-05","externalUrl":null,"permalink":"/posts/2026/04/claude-code-advanced-notes/","section":"所有文章","summary":"","title":"Claude Code 进阶笔记","type":"posts"},{"content":"","date":"2026-04-05","externalUrl":null,"permalink":"/en/categories/tutorials/","section":"Categories","summary":"","title":"Tutorials","type":"categories"},{"content":"","date":"2026-04-05","externalUrl":null,"permalink":"/categories/%E6%8A%80%E6%9C%AF%E6%95%99%E7%A8%8B/","section":"分类","summary":"","title":"技术教程","type":"categories"},{"content":"","date":"2026-03-23","externalUrl":null,"permalink":"/tags/harness-engineering/","section":"标签","summary":"","title":"Harness Engineering","type":"tags"},{"content":" 前言 # 最近 Harness Engineering 这个概念在 AI 圈火得不行，各路技术博客和播客都在讨论。我花了不少时间研究相关资料，包括 Anthropic 和 OpenAI 的公开实践，越看越觉得这东西确实是 Agent 落地过程中绕不过去的一环。很多人在做 Agent 的时候都有过类似的困惑：模型不差、提示词也改了好几版，但跑起来就是不稳定。问题的根源往往不在模型本身，而在模型外面那套运行系统。这套东西，现在有了一个统一的名字：Harness。这篇文章把我研究的心得整理出来，聊聊 Harness Engineering 到底是什么，包含哪些核心部分。\n三次重心转移 # 过去两年，AI 工程经历了三次明显的重心转移：从 Prompt Engineering 到 Context Engineering，再到最近的 Harness Engineering。表面上看是换了几个新名词，但它们分别对应了 AI 系统发展中的三个核心问题：\n模型有没有听懂你在说什么？ 模型有没有拿到足够且正确的信息？ 模型在真实执行中能不能持续做对？ 这些问题是一层一层往外扩张的。\nPrompt Engineering：把话说清楚 # 大模型刚火起来的时候，大家最直观的感受是：同一个模型，换一种说法，结果可能差很多。说「帮我总结一下这篇文章」得到一个平庸的摘要，换一种更结构化的表述，效果马上就不一样。\n于是大家开始疯狂研究提示词：角色设定、风格约束、Few-shot 示例、分步引导、输出格式等等。为什么这些东西有效？因为大模型本质上是一个对上下文非常敏感的概率生成系统。你给它什么身份，它就沿着那个身份回答；你给它什么样的示例，它就沿着那个范式补全；你强调什么约束，它就把那部分当成重点1。\n所以 Prompt Engineering 的本质不是命令模型，而是塑造一个局部的概率空间。这个阶段最重要的能力是语言的设计。\n但 Prompt Engineering 很快就遇到了天花板。很多任务不是你说清楚就行，而是真的需要信息支撑。比如让模型分析一份公司的内部文档、回答产品的最新配置、按规范写代码、在多个工具之间完成复杂任务。提示词写得再漂亮，也替代不了事实本身。\nPrompt 擅长的：约束输出、激发模型已有能力、短链路任务。不擅长的：弥补缺失的知识、管理大量动态信息、处理长链路任务里的状态。\nContext Engineering：把信息给对 # 当 Agent 开始兴起，模型不再只是回答问题，而是要进入真实环境做事情。它需要多轮对话、调浏览器、读写代码、操作数据库，还要在多个步骤之间传递中间结果，根据反馈不断修正计划。\n这时候系统面对的已经不是「一次回答对不对」，而是「整条链路的任务能不能跑通」。以一个真实任务为例：「分析这份需求文档，找出潜在风险，结合历史评审意见给出改进建议，再生成一版发给产品经理的反馈稿」。这已经完全不是一句提示词能解决的了。它至少需要拿到当前需求文档、历史评审记录、相关规范、当前目标、已分析的中间结论、输出的对象、语气要求等等。\nContext Engineering 的核心就变成了一句话：模型未必知道，系统必须在合适的时机把正确的信息送进去。\n这里的 Context 不只是几段背景资料。在工程意义上，它代表了所有影响模型当前决策的信息总和：用户输入、历史对话、检索结果、工具返回、任务状态、中间产物、系统规则、安全约束，以及其他 Agent 传过来的结构化结果。Prompt 其实只是 Context 的一部分2。\nRAG 算是一个典型的 Context Engineering 实践。但真正成熟的上下文工程关注的远不只是检索：文档怎么切块、结果怎么排序、长文怎么压缩、历史对话什么时候保留什么时候摘要、工具返回要不要全部塞给模型、多个 Agent 之间传原文还是摘要还是结构化字段。这些都需要精心设计。\nAgent Skill 的渐进式披露也是上下文工程的高级实践。它解决了一个现实问题：如果把十几个工具的说明、参数定义全部塞给模型，理论上它知道得更多，但实践效果往往更差。因为上下文窗口是稀缺资源，信息一多注意力就涣散。Skill 采用的策略是只给模型看最少量的元信息，等它真正需要触发某些能力时，再动态加载详细的参考信息和脚本。\n这个思路很关键：上下文的优化不是给得更多，而是按需给、分层给、在正确的时机给。\nHarness Engineering：让模型持续做对 # 但 Context Engineering 也不是终点。就算信息给对了，模型也不一定能稳定执行。它可能计划做得很好但执行跑偏了，调了工具但理解错了返回结果，在一个很长的链路里慢慢偏航而系统却没发现。\nPrompt 优化意图表达，Context 优化信息供给，但复杂任务里还有一个更难的问题：当模型开始连续行动时，谁来监督它、约束它、纠偏它？\n这就是 Harness Engineering 要解决的。\nHarness 是什么 # Harness 这个词原本是挽具、绳索、约束装置的意思。放在 AI 系统里，它提醒我们一件朴素的事情：当模型从回答问题走向执行任务，系统不仅要喂信息，还要驾驭整个过程。\n有人给过一个很简洁的公式：Agent = Model + Harness。也就是说，在一个 Agent 系统里，除了模型本身以外，几乎所有能决定它能不能稳定交付的东西，都属于 Harness 的范畴。\n打个比方。假设你要派一个新人去完成一次重要的客户拜访。\nPrompt 做的事，相当于把任务讲清楚：见面先寒暄、再介绍方案、再问需求、最后确认下一步。重点是把话说对 Context 做的事，相当于把资料准备齐全：客户背景、过往沟通记录、产品报价、竞品情况、这次会议的目标。重点是把信息给对 Harness 做的事，相当于建立完整的运行保障：让新人带着 Checklist 去，关键节点实时汇报，会后核实纪要和录音，发现偏差马上纠正，按明确标准验收结果。重点是有没有一套持续观测、纠偏和验收的机制 三者不是替代关系，而是包含关系：Prompt 是对指令的工程化，Context 是对输入环境的工程化，Harness 是对整个运行系统的工程化。边界一层比一层大。前两代工程关注的是「怎么让模型更会想」，Harness 关注的则是「怎么让模型别跑偏、跑得稳、出了错还能拉回来」。\nHarness 的六层结构 # 把一个成熟的 Harness 拆开来看，我认为可以分成六层。\n第一层：上下文管理 # 模型能不能稳定发挥，很多时候不取决于它聪不聪明，而取决于它看到了什么。Harness 的第一层职责是让模型在正确的信息边界内思考，通常包括三件事：\n角色和目标定义：模型要知道自己是谁、任务是什么、成功的标准是什么 信息裁剪和选择：上下文不是越多越好，而是越相关越好 结构化组织：固定规则、当前任务、运行状态、外部证据分层放好。信息一乱，模型就容易漏重点、忘约束，甚至自我污染 说到信息裁剪，OpenAI 踩过一个很典型的坑：他们早期写了一个巨大的 Agent 指令文档，把所有规范、框架、约定全部塞进去，结果 Agent 反而更糊涂了。上下文窗口是稀缺资源，塞得太满等于什么都没说。后来他们把文档改成了目录页，只保留核心索引，详细内容拆到架构文档、设计文档、执行计划、质量评分、安全规则等子文档里。Agent 先看目录，需要时再钻进去。这和 Agent Skill 的渐进式披露是同一个思路：不是一次性全给，而是按需暴露。\nAnthropic 在长时间自主任务中也遇到了类似问题。上下文越来越满，模型开始丢细节、丢重点，甚至出现一种有意思的现象：模型好像知道自己快装不下了，于是着急收尾。常见的做法是上下文压缩，但 Anthropic 发现对某些场景来说光压缩还不够，压缩只是变短了，不代表负担感真的消失。所以他们做了一件更激进的事：Context Reset。不是在原上下文里继续压，而是换一个干净的新 Agent 接手工作。这个思路很像工程里遇到内存泄漏，不是清缓存，而是直接重启进程再恢复状态3。\n第二层：工具系统 # 没有工具，大模型本质上还是一个文本预测器。连上工具后，模型才可以真正做事：搜网页、读文档、写代码、调 API。\n但 Harness 在这里做的不是简单地把工具挂上去，而是要解决三个问题：\n给什么工具：太少能力不够，太多模型会乱用 什么时候调用：不需要查的时候别乱查，该查的时候别硬答 工具结果怎么回喂：搜索返回的几十条结果不应该原封不动塞回去，要提炼筛选，保持和任务的相关性 OpenAI 在这方面的实践比较极端：他们不只给 Agent 接代码编辑器，还接上浏览器让 Agent 能截图、模拟用户操作，接上日志和指标系统让 Agent 能查 Log、查监控，每个任务都在独立隔离的环境里跑。Agent 不再是写完代码就说「写完了」，而是能真正跑起来、看结果、发现 Bug、修 Bug、再验证。工具系统的设计直接决定了 Agent 能做到多「真实」。\n第三层：执行编排 # 这一层解决的核心问题是：模型下一步该做什么。\n很多 Agent 的问题不是某一步不会，而是不会把所有步骤串起来。它会搜索、会总结、会写代码，但整个过程想到哪做到哪，最后交付一堆半成品。一个完整的任务通常需要这样的轨道：理解目标 → 判断信息够不够（不够就补）→ 基于结果分析 → 生成输出 → 检查输出 → 不满足要求就修正或重试。\nOpenAI 在这方面提出过一个很有启发性的观点：工程师在 Agent 时代的工作不再是写代码，而是设计环境。具体来说就是三件事：把产品目标拆解成 Agent 能力范围内的小任务；Agent 失败时不是让它更努力，而是问环境里缺了什么能力；建立反馈链路让 Agent 能看到自己的工作结果。「Agent 出了问题，修复方案几乎从来不是让它更努力，而是确定它缺了什么能力」。这本身就是典型的 Harness 思维。\n第四层：记忆和状态 # 没有状态的 Agent 每一轮都像失忆一样。它不知道自己刚做了什么，哪些结论已经确认，哪些问题还没解决。Harness 必须管理状态，至少要分清三类东西：\n当前任务的状态 会话中的中间结果 长期记忆和用户偏好 三类信息如果混在一起，系统会越来越乱。分清楚之后，Agent 才像一个稳定的协作者。\n第五层：评估和观测 # 这是很多团队最容易忽视的一层。很多系统不是生成不出来，而是生成完了之后根本不知道自己做得好不好。没有独立的评估能力，Agent 就会长期停留在「自我感觉良好」的状态。\n这一层通常包括：输出验收、环境验证、自动测试、日志和指标、错误归因。系统不仅要会做，还要知道自己有没有真的做对。\n这里有一个关键的工程原则：生产和验收必须分离。模型自己干活再自己打分，往往偏乐观，尤其在设计体验、产品完整度这类没有标准答案的问题上偏差更明显。Anthropic 的做法是把角色拆开：Planner 负责把模糊需求展开成完整规格，Generator 负责逐步实现，Evaluator 像 QA 一样真实测试。关键是 Evaluator 不只是看代码，而是真实操作页面、检查具体交互和实际结果。只要评估者足够独立，系统就能形成「生成 → 检查 → 修复 → 再检查」的有效循环。\n第六层：约束、校验与失败恢复 # 最后一层才是真正决定系统能不能上线的关键。因为在真实环境里，失败不是例外而是常态。搜索不准、API 超时、文档格式混乱、模型误解任务，这些都是家常便饭。\n如果没有恢复机制，Agent 每次出错就只能从头再来。一个成熟的 Harness 必须包括：\n约束：哪些能做，哪些不能做 校验：输出前、输出后怎么检查 恢复：失败后怎么重试、切路径、回滚到稳定状态 OpenAI 在这一层做了一个很值得借鉴的事：把资深工程师的经验直接编码为系统规则。模块怎么分层、哪一层不能依赖哪一层、什么情况下必须拦截、发现问题后怎么修。这些规则不只是报错，而是把修复方案一起反馈给 Agent，进入下一轮上下文。这已经不是传统意义上的代码规范，而是一套可持续运行的自动治理系统4。Agent 提交代码的速度远超人类 Code Review 的处理能力，所以必须靠系统规则来兜底，而不是依赖人工审查。\n总结 # 把整条线索串起来：\nPrompt Engineering 解决怎么把任务讲清楚 Context Engineering 解决怎么把信息给对 Harness Engineering 解决怎么让模型在真实执行中持续做对 Harness 不是在取代前两者，而是在更大的系统边界上把它们包含进来。当任务是简单的单轮生成时，Prompt 很重要；当任务依赖外部知识和动态信息时，Context 就很关键；当模型进入长链路、可执行、低容错的真实场景时，Harness 几乎不可避免。\n这也是为什么同样的模型在不同产品里表现差距会这么大。真正决定能不能上线的是模型，但真正决定能不能落地、能不能稳定交付的，是 Harness。AI 落地的核心挑战，正在从「让模型看起来更聪明」转向「让模型在真实世界里稳定工作」。\n这种对上下文的敏感性既是大模型的优势也是弱点。优势在于我们可以通过精心设计输入来引导输出，弱点在于细微的措辞变化可能导致截然不同的结果。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n广义上，Prompt 也是一种 Context。但在工程实践中，我们通常把用户直接编写的指令称为 Prompt，把系统自动组织和注入的信息称为 Context，以区分两者的管理方式。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nContext Reset 的本质是一种更激进的上下文管理策略。与压缩不同，它放弃了在原有上下文上继续工作的思路，转而通过状态序列化 + 新 Agent 加载的方式重启整个推理过程。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n这种「把工程师经验编码为系统规则」的思路，和传统软件工程中的 Lint 规则、CI 检查有异曲同工之妙，只是执行者从人变成了 Agent。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2026-03-23","externalUrl":null,"permalink":"/posts/2026/03/harness-engineering-secret/","section":"所有文章","summary":"","title":"Harness Engineering：AI Agent 稳定运行的秘密","type":"posts"},{"content":"","date":"2026-03-23","externalUrl":null,"permalink":"/tags/prompt-engineering/","section":"标签","summary":"","title":"Prompt Engineering","type":"tags"},{"content":"","date":"2026-02-22","externalUrl":null,"permalink":"/tags/mcp/","section":"标签","summary":"","title":"MCP","type":"tags"},{"content":"","date":"2026-02-22","externalUrl":null,"permalink":"/en/categories/tech-notes/","section":"Categories","summary":"","title":"Tech Notes","type":"categories"},{"content":"","date":"2026-02-22","externalUrl":null,"permalink":"/tags/token/","section":"标签","summary":"","title":"Token","type":"tags"},{"content":"","date":"2026-02-22","externalUrl":null,"permalink":"/categories/%E6%8A%80%E6%9C%AF%E7%AC%94%E8%AE%B0/","section":"分类","summary":"","title":"技术笔记","type":"categories"},{"content":" 前言 # 前段时间系统性地梳理了一遍 AI 相关的技术概念，发现很多东西之前只是零散地接触过，真要把它们之间的依赖关系说清楚，还真得花点功夫。整理完之后最大的感受是：这些概念不是孤立存在的，而是一层一层堆叠起来的，每一层都在补上一层的短板。这篇文章是我梳理过程的记录，试着从最底层一路往上捋，把整条链路串起来。\nLLM：一切的起点 # LLM 全称 Large Language Model，大语言模型，通常简称大模型。目前主流的大模型都基于 Transformer 架构1，这套架构最早由 Google 团队在 2017 年的论文 Attention Is All You Need 中提出。有意思的是，Google 虽然点燃了火种，但真正把它引爆的却是 OpenAI：2022 年底 GPT-3.5 横空出世，几个月后 GPT-4 发布，直接把 AI 能力的天花板拉到了新高度。到今天，GPT 家族仍然是行业标杆之一，不过 Claude、Gemini 等后来者也在各自擅长的领域强势竞争。\n大模型的工作原理其实很朴素：本质上就是一个文字接龙游戏。你给它一段输入，它预测下一个概率最高的词，然后把这个词追加到输入里，再预测下一个，如此循环，直到输出一个特殊的结束标识符。\n这也是为什么你在 ChatGPT 里看到回答总是一个字一个字地蹦出来，不是因为网速慢，而是它的工作机制就是逐词生成。\nToken：大模型的最小处理单元 # 前面说大模型做「文字接龙」，但这个说法其实是个简化。大模型本质上是庞大的数学函数，内部全是矩阵运算，它只认识数字，不认识文字。人和模型之间需要一个翻译层，这个翻译层就是 Tokenizer。\nTokenizer 的工作分两步：\n切分：把输入文本拆成最小的片段，每个片段就是一个 Token 映射：把每个 Token 映射到一个数字（Token ID），因为模型只认数字 比如一句话「今天天气真好」，Tokenizer 可能会把它切成「今天」「天气」「真」「好」四个 Token，再各自映射成不同的数字。模型输出的数字，也会经过反向映射还原成文字。\n但 Token 不等于我们日常理解的「词」。比如「人工智能」可能被切成「人工」和「智能」两个 Token，英文的「unbelievable」可能被切成「un」「believ」「able」三段。某些特殊字符甚至需要更多 Token 才能表示。Token 是模型在训练过程中自己学会的一套切分规则，跟自然语言的词并没有严格的对应关系。\n粗略估算，一个 Token 大约等于 0.75 个英文单词，或者 1.5 到 2 个汉字。40 万 Token 大概就是 60 到 80 万汉字2。\nContext 与 Context Window # 你可能好奇过：大模型为什么能「记住」你之前说的话？它又没有真正的记忆。\n秘密在于：每次你发消息时，背后的程序会把之前的完整对话历史连同你的新问题一起发给模型。模型每次看到的都是全部内容，所以它才知道之前发生了什么。\n这就引出了 Context（上下文）的概念。它代表大模型每次处理任务时接收到的信息总和，包括用户问题、对话历史、系统指令、工具列表等。你可以把它理解为大模型的临时记忆体。\n那这个记忆体能有多大？这就是 Context Window（上下文窗口）的范畴了。它规定了 Context 能容纳的最大 Token 数量。目前主流大模型的 Context Window 已经相当可观：GPT-5.4 是 105 万 Token，Gemini 3.1 Pro 和 Claude Opus 4.6 都是 100 万 Token。100 万 Token 约等于 150 万汉字，整个《哈利波特》全集都能装下。\n不过 Context Window 不是万能的。假如你有一份上千页的产品手册想让大模型据此回答用户问题，把全文塞进去虽然技术上可行，但成本会失控。这种场景更适合用 RAG（Retrieval-Augmented Generation）技术3，先从手册中检索出与用户问题最匹配的片段，只把这些片段发给模型，既不受 Context Window 限制，成本也低得多。\nPrompt：给模型的指令 # Prompt（提示词）就是你给大模型的具体问题或指令。「帮我写一首诗」就是一个 Prompt。听起来简单，但 Prompt 的写法直接决定了输出质量。说「帮我写一首诗」，模型可能给你古诗、现代诗甚至打油诗，因为它不知道你具体想要什么。改成「写一首五言绝句，主题是秋天的落叶，风格悲凉一些」，效果就好得多。\n这就是所谓 Prompt Engineering（提示词工程）。这个领域曾经很火，现在提的人不多了，一是因为门槛低（本质就是把话说清楚），二是因为模型越来越聪明，即使提示词含糊也能大致猜到你的意图。\nPrompt 分两种：\nUser Prompt（用户提示词）：你在对话框里直接输入的内容 System Prompt（系统提示词）：开发者在后台配置的人设和行为规则，用户看不到，但会持续影响模型的行为 举个例子。你在做一个客服机器人，不希望它随便承诺补偿或退款。于是在 System Prompt 里写「你是某电商平台的售后客服，遇到用户投诉时，先安抚情绪，再了解具体问题，不得自行承诺退款或赔偿，需要升级处理的引导用户填写工单」。用户输入「我收到的商品碎了，我要退款」，模型就会回答「很抱歉给您带来不好的体验，能拍一张破损照片发给我吗？确认情况后我会帮您尽快处理」而不是直接说「好的，马上退」。\nTool：让模型感知外部世界 # 大模型有一个明显的短板：它无法感知外界环境。问它「今天北京天气怎么样」，它会老老实实告诉你「我无法获取实时信息」，因为它的知识停留在训练数据截止的那一刻。\nTool（工具）就是用来弥补这个短板的。Tool 本质上就是一个函数，给它输入，它就给你输出。比如一个天气查询工具，接收城市和日期参数，内部调用气象局接口，返回天气信息。\n关键在于：大模型无法自己调用工具。它唯一的能力就是输出文本。所以整个流程需要一个中间角色（通常称为平台）来串联：\n用户的问题和可用工具列表一起发给模型 模型分析后输出一条工具调用指令（包含工具名称和参数） 平台收到指令，实际调用对应的工具函数 工具返回结果，平台把结果转发给模型 模型把结果整理成自然语言回复给用户 每个角色各司其职：模型负责选择工具和归纳总结，工具负责执行具体操作，平台负责串联整个流程。\nMCP：统一工具接入标准 # Tool 解决了能力问题，但带来了一个新的工程问题：接入标准不统一。\n给 ChatGPT 做工具接入，你得按 OpenAI 的规范写一套代码；给 Claude 做接入，按 Anthropic 的规范再写一套；给 Gemini 做接入，又得按 Google 的规范写第三套。同一个工具要写三遍，因为每家的接入格式都不一样。\nMCP（Model Context Protocol，模型上下文协议）4就是为了解决这个问题而生的。它定义了一套统一的工具接入标准，开发者只需按 MCP 规范开发一次，工具就能被所有支持 MCP 的平台使用。类比一下，就像所有手机都统一用 Type-C 充电口，配件厂商不用再为每种手机单独做适配了。\nAgent：自主规划的智能体 # 有了 Tool 和 MCP，大模型已经能做不少事了。但面对更复杂的需求，单次工具调用就不够用了。\n比如用户说：「我下午要出门跑步，帮我看看适不适合。」这需要调用多次工具，而且后续调用依赖前一步的结果：先定位获取经纬度，再用经纬度查天气和空气质量，拿到结果后根据多项指标综合判断是否适合户外运动。整个过程需要模型一步步思考当前状态、决定下一步做什么。\n这种能够自主规划、自主调用工具、持续工作直到完成任务的系统，就是 Agent。目前市面上的 Claude Code、Codex、Gemini CLI 等产品，本质上都是 Agent。它们的构建模式各有不同，比较经典的有 ReAct、Plan and Execute 等5。\nAgent Skill：给 Agent 的操作手册 # Agent 听起来已经很强大了，但在高频使用中你会发现一个痛点：个性化规则每次都得重复输入。\n举个例子。你希望 Agent 做你的健身顾问，每次运动前帮你评估身体状况和当天的运动条件。你有自己的一套偏好：膝盖不好的时候不做深蹲、空气质量指数超过 150 改室内运动、温度超过 35 度减量、运动后必须提醒拉伸。而且你希望输出格式固定：先一个综合评分，再列具体建议。\n如果没有额外设定，Agent 虽然会查数据，但不知道你的身体状况和运动偏好，给出的建议大概率很泛泛。于是你每次提问时都得附上一大段要求，复制粘贴，非常反人类。\nAgent Skill 就是解决这个问题的。它本质上是你提前写好的一份说明文档（Markdown 格式），告诉 Agent 在特定场景下应该怎么做事。分为两层：\n元数据层：相当于封面，标明这个 Skill 的名称和描述（比如名称 Fitness Advisor，描述「运动前综合评估并给出建议」） 指令层：具体的操作规则，包括目标、执行步骤、判断规则、输出格式和示例 定义好之后存到指定位置。以 Claude Code 为例，放在 ~/.claude/skills/ 目录下，文件夹名必须和 Skill 名称一致，里面的文件必须命名为 SKILL.md。\n运行时，Agent 启动时会加载所有 Skill 的元数据。当用户问题与某个 Skill 的描述相关时，Agent 才会读取该 Skill 的完整指令层，然后按照里面的规则执行。这种渐进式披露机制（只在需要时才加载完整内容）能有效节省 Token6。\n全景回顾 # 把这些概念串起来，整个 AI 技术栈的层次关系就清晰了：\n概念 定位 解决的问题 LLM 基座 文本生成的基础能力 Token 数据单元 人与模型之间的文字/数字转换 Context 记忆体 让模型拥有临时记忆 Prompt 指令 告诉模型做什么、怎么做 Tool 能力扩展 让模型感知和影响外部环境 MCP 协议标准 统一工具接入格式，避免重复开发 Agent 自主系统 多步规划和工具调用，完成复杂任务 Agent Skill 定制化 让 Agent 按你的规则和格式做事 每一层都建立在前一层的基础上，解决前一层遗留的问题。理解了这个层次关系，再看 Claude Code、Codex、OpenClaw 这些产品，你会发现它们的本质都是在这个框架下运作的。技术名词再多，底层逻辑是相通的。\nTransformer 架构的细节不是本文的重点，想深入了解可以阅读原论文 Attention Is All You Need。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n不同模型的 Tokenizer 实现不同，Token 与文字的对应比例会有差异，这里的数字是大致估算。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nRAG 的核心思路是「先检索，再生成」，通过向量相似度匹配找到最相关的文档片段，只把这些片段作为 Context 发给模型。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nMCP 由 Anthropic 在 2024 年底发布，目前已经被多个平台和工具采纳。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n关于 Agent 构建模式的详细拆解，包括 ReAct 和 Plan and Execute 的运行流程，可以参考相关专题文章。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nAgent Skill 还有运行代码、引用外部资源等高级功能，这里只介绍了最核心的用法。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2026-02-22","externalUrl":null,"permalink":"/posts/2026/02/ai-eight-layers/","section":"所有文章","summary":"","title":"理解 AI 的八个层次：LLM、Token、Agent 和它们之间的联系","type":"posts"},{"content":" 前言 # Claude Code 出来有一阵子了，身边不少朋友都试过，但大部分人还停留在「输入需求、等它写代码」的阶段。说真的，Claude Code 的能力远不止于此。想要真正把它用顺手、嵌入日常开发流程，有不少细节值得了解。这篇文章把我在实战中摸索出来的用法整理了一遍，从安装配置到高级定制，力求覆盖一条完整的链路。\n另外，像 Codex、OpenCode 这些同类产品，底层架构和使用方式跟 Claude Code 大同小异。把 Claude Code 摸透了，迁移到其他工具基本没什么门槛。\n安装与登录 # 安装就一行命令，去 Claude Code 官网复制安装脚本，在终端粘贴执行即可。\n安装完成后，在项目目录下运行 claude 命令启动。首次使用会提示登录，也可以手动执行 /login 触发。Claude Code 提供两种接入方式：\n订阅制：购买了 Claude Pro 或 Max 会员，直接授权使用，省心 API Key 按量计费：用多少花多少，适合轻量使用 如果你无法访问 Claude 官方服务，Claude Code 本身并不绑定特定模型。通过设置环境变量，可以接入 GLM、Minimax 等国产模型来驱动1。\n三种交互模式 # Claude Code 有三种工作模式，通过 Shift+Tab 循环切换。理解它们的区别，是用好 Claude Code 的基础。\n默认模式 # 启动后的初始模式。底栏只显示一行灰色提示 ? for shortcuts，没有额外的模式标签。这种「什么都不标注」的状态就是默认模式。\n在这个模式下，Claude Code 每次创建或修改文件前都会征求你的同意。三个选项分别是：\nYes：单次授权，仅同意当前这次操作 Yes, allow all edits during this session：本次会话期间，所有文件操作自动通过 No：拒绝，你可以继续输入想法 默认模式最为稳妥，适合对代码变更比较敏感的场景。\n自动模式（Accept Edits On） # 底栏显示 Accept Edits On 时表示处于自动模式。此时 Claude Code 会直接创建和修改文件，不再逐一询问。做需求迭代的时候效率很高，尤其是那种「让它一口气改十几个文件」的场景。\n规划模式（Plan Mode） # 底栏显示 Plan Mode On。这个模式只讨论方案，不写入任何文件。适合在动手之前理清思路、确定细节，后面会单独展开讲。\nPlan Mode：先想清楚再动手 # 假设你有一个用 HTML 写的小项目，想迁移到 React + TypeScript + Vite 的架构。这种涉及目录结构调整的大改动，直接让 Claude Code 开干容易翻车。更好的做法是先进入 Plan Mode，把方案聊清楚。\n操作流程是这样的：\n按 Shift+Tab 切到 Plan Mode 输入你的需求，比如「将当前项目重构为 React + TypeScript + Vite，保留所有现有功能，UI 风格保持一致」 这里有个小技巧：输入多行内容时，按 Shift+Enter 换行，直接按 Enter 会提交请求。如果你觉得终端的输入框不够好用，可以按 Ctrl+G 打开 VS Code 编辑器来写，保存关闭后内容会自动回到 Claude Code 的输入框里。\nClaude Code 会输出一份完整的计划，包含目标、文件清单、目录结构等。它还会给你三个选项：\n执行计划并进入自动模式：后续改文件不再询问 执行计划并保持默认模式：后续改文件仍需逐一确认 继续修改计划：对方案不满意，可以继续补充需求 比如你觉得计划里少了「给每个待办事项增加优先级标记（高/中/低），用不同颜色区分」这个需求，就选第三个选项补充进去。Claude Code 会重新生成一份修改后的计划。\n确认计划后选择执行，Claude Code 就开始按步骤实施了。\n一个容易踩的坑：自动模式只针对文件读写操作。终端命令（比如 mkdir、npm install）属于更敏感的操作，Claude Code 默认每次都会询问。如果你希望彻底跳过所有权限检查，可以在启动时加上 --dangerously-skip-permissions 参数。顾名思义，官方在参数名里就写了「dangerously」，意味着 Claude Code 将拥有完整的终端权限，不会征求任何确认。效率拉满，但风险自担。\n终端命令与后台任务 # 执行终端命令 # 在 Claude Code 的输入框里输入 ! 开头，就进入了 Bash 模式，可以运行任意终端命令。比如 ! ls 查看文件列表，! open index.html 在浏览器中打开页面。\n后台任务 # 有一个容易忽略的点：某些命令（比如 npm run dev 启动开发服务器）会阻塞 Claude Code。服务在跑的时候，Claude Code 无法响应新的请求。\n解决办法是按 Ctrl+B 把任务放到后台。之后你可以继续和 Claude Code 交互。用 /tasks 命令查看当前运行的后台任务，在任务列表里按 K 可以关掉指定的后台服务。\n回滚操作 # Claude Code 在你每次提交请求时都会自动创建一个回滚点。连按两下 Esc 就能进入回滚页面，选择一个回滚点后，有四种操作：\n回滚代码和会话：文件内容和对话记录都恢复 仅回滚会话：只恢复对话，文件不动 仅回滚代码：只恢复文件，对话保留 放弃回滚 回滚功能用起来很方便，但有一个限制：它只能回滚 Claude Code 自己写入的文件。通过终端命令创建的目录、安装的依赖包等，Claude Code 无法自动清理。如果你需要精准回滚，还是得靠 Git。\n图片输入与 Figma MCP # 直接传图片 # 想让 Claude Code 照着设计稿写页面？直接把图片拖进终端，或者复制图片后按 Ctrl+V 粘贴。注意，即使在 macOS 上也要用 Ctrl+V，Cmd+V 不起作用。\n传完图片后继续输入需求，Claude Code 就会参考图片来生成代码。不过说实话，纯靠图片做 UI 还原精度有限，字体大小、间距这些细节很难做到像素级准确。\n接入 Figma MCP # 如果设计稿在 Figma 上，有一个更精确的方案：接入 Figma 的 MCP Server2。\nMCP（Model Context Protocol）是大模型与外部工具通信的协议。通过 Figma MCP，Claude Code 不仅能拿到设计稿的截图，还能获取完整的组件间距、字体样式、颜色值等结构化信息。\n配置步骤：\n安装 Figma MCP Server（一行命令，官方文档有写） 重启 Claude Code，用 -c 参数（claude -c）恢复上次对话 执行 /mcp 命令，选择 Figma 工具进行授权 授权完成后，直接输入需求，比如「修改当前页面，使它与 Figma 稿件保持一致」，附上 Figma 的设计稿链接 Claude Code 会自动识别到 Figma MCP，调用 get_design_context 和 get_screenshot 等工具获取设计信息，然后根据这些结构化数据来修改代码。还原精度比单纯看图片高很多。\n上下文管理 # 对话进行到一定阶段，上下文里会堆积大量代码片段、工具调用结果等信息。这些信息中有用的和无用的混杂在一起，既影响模型性能，又浪费 Token。\n/compact：压缩上下文 # 执行 /compact 命令可以对上下文做智能压缩，Claude Code 会把冗余信息精简掉，保留核心内容。压缩完成后按 Ctrl+O 可以查看压缩结果。你会发现之前一大段对话内容被浓缩成了几行关键信息。\n你还可以在 /compact 后面附加具体的压缩策略，比如 /compact 重点保留用户提出的需求，让压缩结果更符合你的预期。\n不过压缩的可控性有限，你没法直接编辑压缩后的内容。\n/clear：清空上下文 # 比压缩更彻底。/clear 直接把所有上下文内容清空，适合前后任务完全无关的场景。\nCLAUDE.md：让 Claude Code 更懂你 # 无论是压缩还是清空，上下文始终和某次会话绑定。换个会话 Claude Code 就什么都不知道了。有没有办法让 Claude Code 每次启动都自动读取一些预设信息？\n这就是 CLAUDE.md 的作用。你可以在里面写明项目的技术栈、代码风格偏好、注意事项等等。Claude Code 每次启动都会自动加载这个文件。\n用 /init 命令可以自动生成一份初始的 CLAUDE.md，然后根据需要修改。比如我在末尾加了一条：「每次回答的最后必须追加 Happy Coding」。重启后随便问个问题，Claude Code 果然在回复末尾加上了这句话。\nCLAUDE.md 分两个级别：\n项目级：放在项目根目录，对这个项目生效，可以提交到 Git 供团队共享 用户级：放在用户目录下，对所有项目生效 用 /memory 命令可以快速打开对应级别的 CLAUDE.md 文件，不用在文件管理器里翻。\nHook：自动化你的工作流 # Hook 允许你在特定时机（工具执行前/后、执行失败时等）自动运行一段自定义逻辑。一个典型的应用场景是代码格式化：让 Claude Code 每次写完文件后自动跑一遍 Prettier。\n配置方式：\n执行 /hooks 进入 Hook 配置页面 选择触发时机，比如 Post Tool Use（工具执行后） 选择匹配的工具，比如 Write 或 Edit 输入要执行的命令 命令大概长这样：\necho \u0026#39;$TOOL_INPUT\u0026#39; | jq -r \u0026#39;.file_path\u0026#39; | xargs prettier --write Claude Code 在触发 Hook 时会传入一个 JSON，其中 file_path 就是刚编辑完的文件路径。用 jq 提取路径，再传给 Prettier 格式化。\nHook 的保存位置有三个选项：\n本地项目级（settings.local.json）：只在本机本项目生效，不进 Git 项目级（settings.json）：所有使用这个项目的人都生效，会随 Git 分发 用户级：对当前用户的所有项目生效 Agent Skill：可复用的提示词模板 # 如果你经常需要 Claude Code 按特定格式输出内容（比如每天写一份包含日期、开发摘要、开发详情的开发日志），每次手动粘贴格式要求太麻烦。这种场景适合用 Agent Skill 来解决3。\nAgent Skill 本质上是一个动态加载的 Prompt。你把格式要求写在 skill.md 里，Claude Code 会根据用户请求自动匹配并加载对应 Skill。它也可以通过 /skill-name 的方式手动调用，省去模型意图识别的环节。\nSubAgent：独立上下文的利器 # Agent Skill 运行时完全共享当前对话的上下文。这意味着 Skill 执行过程中的所有日志、思考过程都会塞进你的上下文窗口。对于轻量任务没什么问题，但如果让 Skill 去审核一个几万行代码的项目，中间过程会把上下文撑爆，Token 消耗飙升，模型也会因为上下文过载而变慢。\nSubAgent 解决了这个问题。它拥有完全独立的上下文，启动时开辟一个全新的对话窗口，所有中间过程都在那个窗口里完成。只有最终的执行结果会汇报回主对话。\n两者在上下文处理上的区别决定了各自的适用场景：\n维度 Agent Skill SubAgent 上下文 共享主对话 完全独立 适合场景 与上下文强关联、对上下文影响小 与上下文弱关联、对上下文影响大 典型任务 写开发总结、格式转换 代码审核、大规模重构分析 创建 SubAgent 的流程：执行 /agent 命令，选择「Create New Agent」，描述它的职责，配置可用工具（比如只读权限）和使用的模型。Claude Code 会生成一份初始描述文件，你可以根据需要修改。之后在对话中提出相关需求，Claude Code 会自动调用对应的 SubAgent。\nPlugin：一键安装全家桶 # Plugin 把 Skill、SubAgent、Hook、MCP 等能力打包成一个安装包，一键就能给 Claude Code 装备一整套高级能力。\n在 Claude Code 里执行 /plugin 进入插件管理器，可以浏览、安装和查看已安装的插件。安装时选择生效范围（用户级、项目级等），然后重启 Claude Code 即可生效。\n举个例子，Anthropic 官方提供了一个 frontend-design 插件，内置了一套 UI 设计规范。安装后让 Claude Code 做前端页面，它会自动加载这套规范，输出的界面在配色、排版、交互上都比默认效果好看不少。\nPlugin 市场正在快速增长。除了 UI 设计类的插件，还有针对特定编程语言的 LSP 插件等。如果你觉得自己积累的配置足够成熟，也可以打包成 Plugin 分享给团队或社区。\n总结 # 这篇文章覆盖了 Claude Code 从入门到进阶的完整链路：\n三种模式（默认/自动/规划）灵活切换，适应不同场景 Plan Mode 先规划后执行，降低改动风险 后台任务 解决长时间运行命令的阻塞问题 回滚功能 提供安全网，但复杂场景还是得靠 Git 图片和 Figma MCP 实现设计稿到代码的转换 上下文管理（/compact、/clear）控制 Token 消耗 CLAUDE.md 让 Claude Code 自动读取项目配置 Hook 在关键节点自动执行自定义逻辑 Agent Skill 和 SubAgent 分别处理轻量和重量级任务 Plugin 一键安装整套能力扩展 每个功能单独看都不复杂，组合起来就是一个相当完备的开发工作流。希望这篇梳理对你有帮助。\n具体配置方法网上有很多教程，核心就是设置几个环境变量来指定模型接口地址和认证信息。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n关于 MCP 的详细使用方法和设计原理，我在之前的文章里有专门讨论。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n关于 Agent Skill 的完整用法和底层设计思路，可以参考我之前写的 Agent Skill 专题文章。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2026-01-07","externalUrl":null,"permalink":"/posts/2026/01/claude-code-guide/","section":"所有文章","summary":"","title":"Claude Code 全面上手指南：不只是写代码","type":"posts"},{"content":"","date":"2026-01-07","externalUrl":null,"permalink":"/tags/skill/","section":"标签","summary":"","title":"Skill","type":"tags"},{"content":" 前言 # 最近在用 Claude Code 的时候，我发现一个功能越用越顺手，就是 Agent Skill。一开始我只把它当成一个「提示词存档」，后来才发现它的设计比我想象的要精巧得多。这篇文章聊聊 Agent Skill 到底是什么、怎么用，以及底层的设计思路，顺便和 MCP 做个对比，帮你在实际场景中选对工具。\nAgent Skill 是什么 # 最简单的理解：Agent Skill 就是一份大模型可以随时翻阅的说明文档。\n举个例子，你想做一个智能客服，可以在 Skill 里面写清楚「遇到投诉先安抚情绪，不得随意承诺」；想做会议总结，可以规定「必须按参会人员、议题、决定这个格式输出」。这样一来不用每次对话都重复粘贴一长串要求，模型自己翻翻这份文档就知道该怎么干活了1。\n当然，「说明文档」只是一个方便入门的理解。Skill 能做的事情远不止于此，后面会慢慢展开。\n基础用法：创建一个会议总结助手 # 以 Claude Code 为例，使用 Skill 的第一步是创建它。\nClaude Code 要求把 Skill 放在用户目录下的 .claude/skills/ 文件夹中。我们创建一个文件夹叫「会议总结助手」，文件夹名称就是 Skill 的名称。然后在里面新建一个 skill.md 文件。\n这个文件分两部分：\n头部是元数据（metadata），用三条横线包裹，包含 name 和 description 两个字段。name 必须和文件夹名字一致，description 则是向模型说明这个 Skill 是干什么的。\n剩余部分是指令（instruction），用来详细描述模型需要遵循的规则。比如我规定了必须总结参会人员、议题和决定这三个方面，并且给了一个输入输出的示例，确保模型真的理解了要求。\n创建完成之后，打开 Claude Code，问它「你有哪些 Skill」，它就会列出刚才创建的那个。然后输入一段会议转写文本，让它总结。Claude Code 会先识别到这个请求跟「会议总结助手」相关，请求使用这个 Skill，得到你的同意后读取 skill.md 的内容，最后按照规定格式输出总结。\n整个过程非常直观。\n流程拆解：Skill 的运行机制 # 基础用法看完了，不妨想想刚才到底发生了什么。\n整个流程中有三个角色：用户、Claude Code（宿主程序）、以及背后的大模型。流程是这样的：\n用户输入请求 Claude Code 把用户请求连同所有 Skill 的名称和描述一起发给大模型 大模型发现用户请求跟「会议总结助手」匹配，把这个信息返回给 Claude Code Claude Code 去读取匹配到的 Skill 的 skill.md 全文 Claude Code 把用户请求和完整的 skill.md 内容发给大模型 大模型按照 Skill 要求生成响应 这里有一个关键细节：第 2 步只发了名称和描述，第 4 步才读取全文。也就是说，哪怕你装了十几个 Skill，模型一开始看的也只是一份轻量级的目录。这引出了 Skill 的第一个核心机制：按需加载。\n高级用法一：Reference（条件加载文件） # 按需加载已经挺省 Token 了，但还不够极致。\n假设你的会议总结助手越来越高级：当会议涉及花钱时，能在总结里标注是否符合财务合规；涉及合同时，能提示法务风险。要实现这些，Skill 就需要知道相关的财务规定和法律条文。如果把所有这些内容都写进 skill.md，文件会变得非常臃肿，哪怕只是开个简单的技术复盘会，也要被迫加载一堆用不上的财务条款。\n能不能做到更细粒度的按需加载？比如只有当会议内容真的聊到了钱，才把财务规定加载进来？\n这就是 Reference 解决的问题。\n我们创建一个 集团财务手册.md 文件，写明各种费用的报销标准，然后在 skill.md 里加一条规则：仅在提到钱、预算、采购、费用的时候触发，触发时读取这个文件并根据内容判断金额是否超标。\n实际测试一下：如果会议内容涉及了预算，Claude Code 会先读取 skill.md，发现跟钱相关，再去加载财务手册，最终在总结中给出财务提醒。如果是个跟钱无关的技术复盘会，那个财务文件就只会安静地躺在硬盘上，不会占用哪怕一个 Token。\nReference 的核心特性：条件触发，只在需要的时候才加载，不需要就完全不碰。\n高级用法二：Script（执行代码） # 查资料只是第一步，能直接跑代码把活干了，才是真正的自动化。这就用到了 Skill 的另一大能力：Script。\n我们在文件夹里创建一个 upload.py 脚本用于上传文件，然后在 skill.md 里加一条规则：如果用户提到了「上传」「同步」「发送到服务器」，就必须运行这个脚本。\n实际测试时，Claude Code 会正常生成会议总结，然后直接执行 upload.py 完成上传。这里有个值得注意的细节：Claude Code 申请执行脚本时，并没有去读取脚本内容。它只关心怎么跑和跑出来的结果，至于代码写了什么，它毫不在意。\n这意味着哪怕你的脚本写了 1 万行复杂的业务逻辑，它消耗的模型上下文也几乎是零。\n所以 Reference 和 Script 虽然都属于高级功能，但对模型上下文的影响截然不同：\nReference 是读：把文件内容加载到上下文中，会消耗 Token Script 是跑：只执行不读取，几乎不占用上下文 渐进式披露：Skill 的三层架构 # 把上面的内容串起来，Skill 的设计其实是一个精密的渐进式披露结构，一共三层：\n第一层：元数据层。 所有 Skill 的名称和描述，始终加载，相当于目录。模型每次回答前都会扫一遍，判断用户的问题是否跟某个 Skill 匹配。\n第二层：指令层。 对应 skill.md 中除了元数据以外的部分。只有当模型发现用户问题跟某个 Skill 匹配时才加载，所以叫按需加载。\n第三层：资源层。 包含 Reference 和 Script（官方规范中还有 Assets，但与 Reference 有重叠，暂不展开）。这一层只在模型根据指令层判断出需要具体资源时才会触发，是在按需加载的基础上又做了一次按需加载，可以叫「按需中的按需」。\n三层之间层层递进，每一层的加载都建立在上层的判断之上，把 Token 消耗压到了最低。\nSkill 和 Prompt Engineering 是什么关系 # 聊到这里，另一个经常被问到的问题自然就浮现出来了：Skill 和 Prompt Engineering 到底什么关系？感觉都在「教模型做事」，有啥区别？\n我的理解是，它们解决的是不同层级的问题。\nPrompt Engineering 解决的是「如何思考」。 它的核心工作是引导模型进行正确的理解和推理：明确角色、提供上下文、规范输出格式、减少幻觉。本质上它属于认知层，决定模型「要做什么」「怎么拆解问题」「是否需要外部能力」。但 Prompt 本身不负责执行任何实际操作。\nSkill 解决的是「如何行动」。 它把模型的决策转化为可执行的行为：调用函数、跑脚本、读写文件。Skill 不参与思考，只负责拿到指令后干活并返回结果2。\n打个不太严谨但好记的比方：Prompt Engineering 像是给新人写了一份入职手册，告诉他遇到什么情况该怎么判断；Skill 则是给他配了一套工具箱，判断完了直接上手操作。一个是脑，一个是手。\n理解了这层关系，再看前面的三层架构就很清晰了：Skill 的指令层承载的是 Prompt Engineering 的成果，资源层承载的才是真正的执行能力。\nAgent Skill 和 MCP 怎么选 # 聊完用法，很多人会有个感觉：Skill 和 MCP 是不是有点像？本质上都是让模型连接和操作外部世界。\nAnthropic 官方有一句话把两者的关系说得很到位：\nMCP connects Claude to data. Skills teach Claude what to do with that data.\nMCP 给大模型供给数据，比如查询销售记录、读取物流状态；Skill 教会大模型如何处理数据，比如会议总结必须包含议题、汇报文档必须附上具体数据。\n有人可能会问：Skill 里面也能写代码连接数据，干嘛不直接用 Skill 把两件事都干了？\n确实能干，但不代表适合干。就像瑞士军刀也能切菜，但没人真拿它做饭。MCP 本质上是一个独立运行的服务程序，Skill 本质上是一段说明文档，两者在安全性、稳定性和适用场景上差别很大。Skill 更适合跑轻量脚本和处理简单逻辑，在复杂的数据连接方面不如 MCP 可靠3。\n实际场景中，很多时候需要把两者结合起来用：MCP 负责连接数据源，Skill 负责定义处理规则，各司其职。\n严格来说，Skill 不只是静态的说明文档。它还支持条件加载外部文件和执行代码脚本，具备一定的动态能力。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n这里的「不参与思考」是相对于 Prompt Engineering 而言的。Skill 的指令层本身包含 Prompt Engineering 的成分，但资源层的 Reference 和 Script 纯粹是执行，不涉及推理。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nSkill 中的脚本由 Claude Code 直接执行，缺乏 MCP 那样的沙箱隔离和权限管控机制，不适合处理敏感或高风险的数据操作。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2025-12-21","externalUrl":null,"permalink":"/posts/2025/12/agent-skill-design/","section":"所有文章","summary":"","title":"Agent Skill 的三层架构与设计哲学","type":"posts"},{"content":"","date":"2025-11-15","externalUrl":null,"permalink":"/tags/chatbi/","section":"标签","summary":"","title":"ChatBI","type":"tags"},{"content":" 前言 # 最近在研究怎么让非技术人员也能方便地查询数据，了解到 ChatBI 这个方向。传统的数据分析流程太长了：业务提需求，分析师写 SQL，跑查询，做图表，发邮件。一个问题从提出到拿到答案，快则几小时，慢则几天。ChatBI 的思路很简单——用户说一句话，系统直接出图出报告。听起来美好，但落地有不少坑，今天就来聊聊 ChatBI 的产品设计思路和关键难点。\nChatBI 到底解决什么问题 # 传统数据分析有三个老毛病。\n第一，门槛高。想查数据得会写 SQL，得懂表结构，得理解业务指标体系。这些技能不是一两天能学会的，所以企业必须养一批数据分析师。\n第二，响应慢。从提需求到拿到结果，快则几小时，慢则几天。业务节奏越来越快，等数据出来黄花菜都凉了。\n第三，浅尝辄止。传统模式下做一次深度分析的成本很高。大部分人只看固定报表，很少做探索性的深度分析和归因分析。\nChatBI 的核心价值很直接：用户说一句话，系统理解后生成 SQL，直接出图。 配合精心设计的 Prompt，还可以输出对应的数据分析报告。传统数据分析师出一份报告可能需要几天，ChatBI 几分钟就能完成。不是说要替代分析师，而是让分析师去做更有价值的深度研究，常规查询交给 ChatBI1。\n一句话出图的背后：NL2SQL # ChatBI 的产品逻辑可以用一条链路概括：\n用户说一句话 → 系统理解指标、维度、过滤条件、时间要求 → 生成 SQL → 结合可视化能力渲染图形\n举个例子。用户问：「华东区今年 Q1 的销售额同比增长了多少？」\n系统要做的事情拆解如下：\n识别指标：销售额（对应数据库的某个字段） 识别维度：华东区（区域维度）、Q1（时间维度） 识别对比关系：同比增长（需要查去年同期数据做对比） 生成 SQL：把以上理解翻译成数据库查询语句 选择合适的图表：对比类数据适合用柱状图或折线图 渲染结果：把图表和关键数据展示给用户 这就是所谓的 NL2SQL（Natural Language to SQL）2——让大模型充当「翻译官」，把人的自然语言翻译成数据库能懂的 SQL。\n三大落地难点 # 听起来很美好，但真要落地有三个绕不开的难点。\n大模型怎么理解你的数据模型 # 数据库里的字段通常是英文缩写——sal_amt、region_id、yr_qtr。大模型怎么知道 sal_amt 就是销售额，region_id 就是区域？\n解法是通过 Prompt 把数据库表字段的含义告诉大模型。在系统提示词中写清楚每个表的用途、每个字段的中文名和业务含义，让大模型在理解用户问题时有「词典」可查。这一步看似简单，实际在表结构复杂的场景下，Prompt 工程的工作量不小。\n怎么引导用户问出好问题 # 大多数用户面对一个开放的分析系统，要么不知道能问什么，要么问一些系统回答不了的问题。\n解法是做问题推荐。系统根据用户的角色和历史查询，推荐一些高频问题或可能有价值的分析方向。比如用户是华东区销售经理，系统可以推荐「华东区本月销售趋势」「华东区各城市业绩排名」这类问题。这本质上是一个推荐系统的问题3，需要结合用户画像和数据权限来设计。\n怎么让结果图文并茂 # 光给一串数字没有意义，需要用图表来直观呈现。\n解法是调用可视化图形插件。系统根据数据的特征自动选择合适的图表类型——趋势数据用折线图，对比数据用柱状图，占比数据用饼图。同时通过 Prompt 控制大模型的输出格式，确保数据能被图表组件正确渲染。\n还有一个扩展能力值得提一下：归因分析。通过 Prompt 控制大模型不仅给出数据结果，还能进一步分析原因。比如用户问「为什么华东区销售额下降了」，系统不只展示下降的数据，还能自动关联到可能的影响因素。\nChatBI 的三种产品形态 # 根据使用场景和实现难度的不同，ChatBI 可以做成三种形态。\n形态一：查全库 # 用户随便问，系统在多张表中查找相关字段。比如问「各个区的销售金额」，系统要在销售表、区域表、产品表中找到对应的字段，自动做表关联。难度最高，但用户体验最好。适合数据模型相对规范、表结构不太复杂的场景。\n形态二：指定数据模型查询 # 已知具体的数据模型，从该模型中查询。相当于提前告诉系统「这个问题应该查哪张表」，降低了大模型的理解难度。这是折中方案，实现难度适中，适合业务场景比较固定、数据模型已知的情况。\n形态三：KPI 深度分析 # 企业每天常看的指标（如日活、GMV、转化率），配合复杂的 Prompt 做深度分析。这类指标的定义和分析框架是预设好的，大模型主要负责生成分析报告和归因。难度最低，价值也最直接，适合先做 MVP 验证效果，再逐步扩展到更复杂的场景4。\n技术实现：搭建 Data Agent # 想快速搭一个 ChatBI 的 Demo，完全可以用 Coze 或 Dify 这样的低代码平台来实现5。核心思路是搭建一个 Data Agent，关键步骤如下：\n定义数据模型：把数据库表结构和字段含义配置好 设计 Prompt：写清楚 Agent 的角色（数据分析师）、能力边界（能查什么数据）、输出格式（图表 + 文字） 配置工具：连接数据库的查询工具、可视化图表插件 设置工作流：用户输入 → 意图识别 → SQL 生成 → 数据查询 → 图表渲染 → 结果输出 一个典型的 Prompt 结构可能长这样：\n角色: 你是一个专业的数据分析师 能力: 根据用户问题查询数据库并生成可视化图表 数据模型: - 表名: loan_application 字段: - loan_amt: 贷款金额（元） - region_id: 区域编码 - apply_date: 申请日期 输出格式: 表格数据 + 推荐图表类型 + 简要分析文字 不过要提醒一点：落地标准化的 ChatBI 产品，需要团队至少有一定的数据基础（至少会写 SQL）。不是搭个 Demo 就完事了，真实场景中的数据模型复杂度远超想象。权限控制、数据安全、查询性能优化、容错处理，这些都是 Demo 里不会碰到但生产环境必须面对的问题。\nGartner 的预测显示，到 2026 年超过 50% 的企业将采用 AI 增强的数据分析工具，ChatBI 是其中重要的形态之一。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nNL2SQL（Natural Language to SQL）是将自然语言问题自动转换为 SQL 查询的技术，是 NLP 和数据库领域的交叉研究方向。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n问题推荐本质上是一个上下文感知的推荐问题，需要结合用户角色、历史行为、数据权限等多维信息来生成候选问题。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n从 MVP（最小可行产品）角度，建议从「KPI 深度分析」入手，验证核心链路后再扩展到更复杂的产品形态。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nCoze 是字节跳动推出的 AI Bot 开发平台，Dify 是开源的 LLM 应用开发平台，两者都支持工具调用和工作流编排。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2025-11-15","externalUrl":null,"permalink":"/posts/2025/11/chatbi-product-design/","section":"所有文章","summary":"","title":"ChatBI 产品设计：让数据查询像对话一样自然","type":"posts"},{"content":"","date":"2025-11-15","externalUrl":null,"permalink":"/en/tags/data-analysis/","section":"Tags","summary":"","title":"Data Analysis","type":"tags"},{"content":"","date":"2025-11-15","externalUrl":null,"permalink":"/tags/nl2sql/","section":"标签","summary":"","title":"NL2SQL","type":"tags"},{"content":"","date":"2025-11-15","externalUrl":null,"permalink":"/en/categories/technical-practice/","section":"Categories","summary":"","title":"Technical Practice","type":"categories"},{"content":"","date":"2025-11-15","externalUrl":null,"permalink":"/categories/%E6%8A%80%E6%9C%AF%E5%AE%9E%E8%B7%B5/","section":"分类","summary":"","title":"技术实践","type":"categories"},{"content":"","date":"2025-11-15","externalUrl":null,"permalink":"/tags/%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90/","section":"标签","summary":"","title":"数据分析","type":"tags"},{"content":" 前言 # 在实际项目中经常遇到这种情况：算法工程师告诉你，模型准确率已经从 85% 提到 87%，用了两周时间。离目标的 90% 还差 3 个百分点，但按这个速度可能还得再花一个月。而产品下个月就要上线了。\n这时候怎么办？继续死磕算法？还是换个思路？\n这就是这篇文章想聊的核心主题：当算法效果有限时，怎么用产品设计手段来补齐短板。 这个思路在 AI 产品圈子里有一个通俗的说法，叫做\u0026quot;技术不够，产品来补\u0026quot;。\nAI 产品设计的三个容易踩的坑 # 在讲具体方法之前，先说三个在实际项目中反复出现的教训。\n别忽视前端交互 # AI 产品有一个很特别的地方：它像一座冰山。水面之下是算法工程师的大量工作和迭代逻辑策略，水面之上才是用户能看到的交互界面。\n大部分时间确实在研究算法、业务、数据和逻辑，但这不意味着可以忽视前端交互。因为用户体验才是第一位的，所有后端逻辑最终都是为用户端服务的1。\n我见过不少项目犯这个错误：花 80% 的时间研究算法效果，结果前端交互做得很粗糙，用户根本不买账。算法再好，用户不愿意用也是白搭。\n保证算法功能模块的闭环 # AI 产品有一个其他产品不太关注的要求：算法功能模块必须形成闭环。\n线下可以做的事情是：收集 Bad Case，训练模型，优化效果，上线。但线上产品也要保证这个闭环通畅：数据收集、模型训练、优化迭代、上线，整条路径必须顺畅且持续运转。\n如果设计的产品只有\u0026quot;用\u0026quot;的功能，没有\u0026quot;反馈\u0026quot;和\u0026quot;优化\u0026quot;的通道，那这个 AI 产品就是死的。上线那一刻就是它的巅峰，之后只会越来越差。\n算法效果有限是常态 # 算法优化有一个残酷的现实：付出 80% 的努力可能只提升 1-2 个百分点的准确率。 从 85% 到 87% 可能要两周，从 90% 到 92% 可能要两个月2。\n所以需要学会利用其他力量来提升效果：规则辅助、算法辅助、语料清洗等等。核心思路就是\u0026quot;技术不够，产品来补\u0026quot;或者\u0026quot;数据来补\u0026quot;。\n用智能客服场景落地：三个关键触点 # 说完注意事项，用智能客服机器人这个具体场景来落地。一个完整的智能客服有三个关键触点。\n触点一：访客端聊天页面 # 用户进入咨询页面的第一眼看到什么，决定了后续的体验。这个页面需要设计好几个元素：\n快捷入口：转人工按钮、评价按钮、常用操作入口，让用户随时有退路 问题推荐：用户刚提交贷款申请，就推荐审批进度查询、放款时间询问等可能关心的问题 理财产品/贷款产品信息：用户从理财产品详情页进来的就展示该产品，从官网首页进来的就展示近期热销产品 这些设计的目的不是炫技，而是在用户还没开口之前就预判他的需求，减少用户的操作成本。\n触点二：智能问答交互 # 这是用户和机器人直接对话的部分，也是最考验产品设计的地方。不同的场景需要设计不同的对话管理步骤：贷前咨询场景怎么引导，外呼营销场景怎么推进，提前还款场景怎么处理，都需要单独设计。\n这里有一个非常重要的设计原则：给用户可选择性内容，而非可指示性内容。\n举个具体的例子。普通做法是告诉用户\u0026quot;你在手机银行 APP 里面的贷款管理里可以查\u0026quot;，用户还得自己去找入口。更好的做法是直接展示用户待查询的贷款供其选择，用户点一下就搞定。\n让用户做选择题，不要让用户做填空题。 这个原则在 AI 产品设计中极其重要3。\n另外，如果产品涉及语音交互，还有一个专门的角色叫 VUI 设计师（Voice User Interface）。语音场景下用户连页面都没有，所有体验都来自每一轮对话，所以对话话术的设计会直接影响产品成败。\n触点三：管理后台 # 这是给运营人员用的，通常包含：\n配置访客端展示内容 维护知识库 训练和学习机器人 查看数据报表 如果是做 MVP 版本，这三个触点就是核心：用户与机器人聊天的客户端页面 + 智能问答逻辑 + 管理后台。其他功能可以后续迭代。\n\u0026ldquo;产品补技术\u0026quot;的两大方向 # 当算法效果不理想时，有两大方向可以发力。\n方向一：算法辅助识别 # 冷启动阶段扩充相似问题。 刚上线的时候，知识库里每个标准问题可能只有很少的相似问法。直接上算法做匹配，准确率会很低。\n解决办法是对所有标准问题进行扩写，或者用技术手段生成相似问题。比如标准问题是\u0026quot;能不能提前还清\u0026rdquo;，你可以扩充出：\n\u0026ldquo;怎么样提前还款\u0026rdquo; \u0026ldquo;我已经还了一年了还能提前结清吗\u0026rdquo; \u0026ldquo;提前还款入口在哪里\u0026rdquo; 问法越多，算法能匹配到的范围就越广。冷启动阶段，手动扩充相似问题是最简单有效的提升手段。\n添加正则或关键词强制召回。 这是一个非常实用的\u0026quot;兜底\u0026quot;策略。比如用户问题中包含\u0026quot;提前还款\u0026quot;两个字，不管算法怎么匹配，都直接强制召回\u0026quot;还款方式\u0026quot;这个标准问题。\n为什么要这么做？因为在冷启动阶段语料不够多，算法可能会漏掉一些很明显的匹配。关键词强制召回相当于给算法加了一道保险：即使算法不靠谱，关键词匹配不会漏。\n# 关键词强制召回的简单实现示例 FORCE_RECALL_RULES = { \u0026#34;提前还款\u0026#34;: \u0026#34;还款方式\u0026#34;, \u0026#34;利率调整\u0026#34;: \u0026#34;利率变更流程\u0026#34;, \u0026#34;提前结清\u0026#34;: \u0026#34;提前结清政策\u0026#34;, } def force_recall(user_input: str) -\u0026gt; str | None: for keyword, intent in FORCE_RECALL_RULES.items(): if keyword in user_input: return intent return None 引导用户点击选择。 还有一招是从用户侧入手，降低算法的难度：\n输入推荐：用户刚输入几个字，就把包含这些字的问题推荐出来。用户还没打完字就已经看到想问的问题了，直接点击就行 快捷入口：会话开始前展示用户最可能问的问题；用户提问后，预测下一个最可能的问题并展示 这些设计的本质是：不指望算法 100% 猜对用户意图，而是把\u0026quot;猜\u0026quot;的过程可视化，让用户帮算法做选择4。\n方向二：清洗数据降低算法噪声 # 有时候算法效果不好，不是因为算法不行，而是因为输入的数据太脏了。可以从数据清洗的角度来提升效果。\n维护转译词库。 在语音转文字（ASR）场景中，方言是个大问题。比如四川话说\u0026quot;咬嗳\u0026quot;意思是\u0026quot;好的\u0026quot;，算法不认识这个词。如果维护一个方言转译词库，把\u0026quot;咬嗳\u0026quot;自动转成\u0026quot;好的\u0026quot;，算法就能正确识别了。\n维护专有词和同义词库。 品牌名和专有名词经常被算法错误切分。比如\u0026quot;神州租车\u0026quot;可能被切成\u0026quot;神州\u0026quot;和\u0026quot;租车\u0026quot;，\u0026ldquo;网易七鱼\u0026quot;可能被切成\u0026quot;网易\u0026quot;和\u0026quot;七鱼\u0026rdquo;。维护一个专有词库，告诉算法这些词是一个整体，不要切分。\n同理，用户可能说\u0026quot;提前还款\u0026quot;、\u0026ldquo;提前结清\u0026rdquo;、\u0026ldquo;不想贷了\u0026rdquo;，其实都是同一个意思。维护同义词库可以帮助算法理解这些不同表达。\n# 专有词库示例 proper_nouns: - 神州租车 - 网易七鱼 - 飞猪旅行 # 同义词库示例 synonyms: 提前还款: - 提前结清 - 不想贷了 - 提前还清贷款 利率调整: - 利率变高了 - 利息太贵 - 能不能降利率 过滤无意义词句。 \u0026ldquo;嗯嗯嗯\u0026rdquo;、\u0026ldquo;这个那个\u0026rdquo;、\u0026ldquo;就是那个什么\u0026rdquo;，这些语气词对理解用户意图没有任何帮助，反而会干扰算法判断。维护一个屏蔽词库，把这些无意义的内容过滤掉，算法的效果会有明显提升。\n数据清洗的价值往往被低估，但它可能是投入产出比最高的优化手段。不需要改算法，不需要训练新模型，只需要在数据进入算法之前多做一步处理5。\nSaaS 产品 vs 定制产品：思路不同 # \u0026ldquo;技术不够产品来补\u0026quot;的具体策略，还要看产品类型。\nSaaS 产品面向大量企业使用，不可能为每个客户做线下数据采集、标注和训练。所以管理后台必须支持在线的完整闭环：在线收集数据、在线标注、在线训练、在线查看模型指标。客户的数据和模型训练过程都要产品化、自动化。这对产品设计的要求更高，但一旦做好，边际成本极低。\n深度定制产品就有更大的操作空间。比如可以为某个场景训练 10 个深度学习模型，每个分配 10% 的流量做 A/B 测试，一段时间后选效果最好的那个模型。定制产品有更大的优化空间，但每个客户的交付成本也更高。\n核心区别在于：SaaS 产品靠自动化规模化，定制产品靠深度优化差异化。产品设计的时候要先把这条线想清楚，再决定往哪个方向投入资源。\n这一点在 ToC 产品中尤其明显。用户对 AI 产品的耐心比传统产品更低，因为期望更高。前 30 秒的体验决定了用户会不会继续用下去。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n算法优化的边际效益递减是客观规律，这在学术界也有大量讨论。简单来说，越接近理论上限，每提升一个百分点所需的算力、数据和时间都会指数级增长。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n这个原则其实不只适用于 AI 产品。任何需要用户输入的场景，选择题都优于填空题。但 AI 产品尤其需要重视，因为用户对\u0026quot;智能\u0026quot;的期望更高，对操作成本更敏感。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n这种思路在推荐系统中也很常见：不是靠算法精准预测用户想要什么，而是给出一个候选集让用户自己选。把最终决定权交给用户，既降低了算法压力，又提升了用户感知。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n数据科学领域有一句老话：\u0026ldquo;Garbage in, garbage out。\u0026ldquo;算法的上限往往不是模型架构决定的，而是数据质量决定的。在投入大量资源优化模型之前，先看看数据有没有洗干净。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2025-09-15","externalUrl":null,"permalink":"/posts/2025/09/ai-product-design-fallback/","section":"所有文章","summary":"","title":"AI 产品设计法则：当算法不够好时怎么办","type":"posts"},{"content":"","date":"2025-09-15","externalUrl":null,"permalink":"/en/tags/interaction-design/","section":"Tags","summary":"","title":"Interaction Design","type":"tags"},{"content":"","date":"2025-09-15","externalUrl":null,"permalink":"/en/tags/product-design/","section":"Tags","summary":"","title":"Product Design","type":"tags"},{"content":"","date":"2025-09-15","externalUrl":null,"permalink":"/en/tags/user-experience/","section":"Tags","summary":"","title":"User Experience","type":"tags"},{"content":"","date":"2025-09-15","externalUrl":null,"permalink":"/tags/%E4%BA%A7%E5%93%81%E8%AE%BE%E8%AE%A1/","section":"标签","summary":"","title":"产品设计","type":"tags"},{"content":"","date":"2025-09-15","externalUrl":null,"permalink":"/tags/%E4%BA%A4%E4%BA%92%E8%AE%BE%E8%AE%A1/","section":"标签","summary":"","title":"交互设计","type":"tags"},{"content":"","date":"2025-09-15","externalUrl":null,"permalink":"/tags/%E7%94%A8%E6%88%B7%E4%BD%93%E9%AA%8C/","section":"标签","summary":"","title":"用户体验","type":"tags"},{"content":"","date":"2025-07-15","externalUrl":null,"permalink":"/en/tags/algorithm-optimization/","section":"Tags","summary":"","title":"Algorithm Optimization","type":"tags"},{"content":"","date":"2025-07-15","externalUrl":null,"permalink":"/en/tags/evaluation/","section":"Tags","summary":"","title":"Evaluation","type":"tags"},{"content":"","date":"2025-07-15","externalUrl":null,"permalink":"/en/tags/model-selection/","section":"Tags","summary":"","title":"Model Selection","type":"tags"},{"content":" 前言 # 选了大模型之后怎么评估效果？上线之后怎么持续追踪？这两个问题一直困扰着我。大模型赛道现在卷得飞起，每个月都有新模型发布，各种榜单排名满天飞。但榜单上的分数和你的实际业务效果之间，往往隔着一道鸿沟。模型上线了，准确率只有 60%，问题出在哪？是模型不行、数据不行、还是标注有问题？\n我花了很长时间把评测选型和算法效果追踪这两件事梳理清楚，发现它们其实是一条完整链路上的两个阶段：先通过系统化评测选出最适合业务的模型，再通过持续的效果追踪确保模型在线上真正发挥作用。今天把这套方法论完整记录下来，从评测框架、指标体系到持续优化的闭环，一口气讲透。\n评测的起点：不是找最好的，而是找最合适的 # 先明确一个前提：评测的目的不是找\u0026quot;最好的模型\u0026quot;，而是找\u0026quot;最适合业务场景的模型\u0026quot;。这两件事完全不同。\n一个模型在通用能力上碾压全场，但在你的垂直领域（比如法律合同审查、医疗问诊）表现拉胯，那它对你来说就不是好模型。反过来，一个小模型在你的场景上微调得很好，推理成本还低，那它就是最佳选择。\n所以评测的核心逻辑是：用最低成本，选出最适合业务场景的模型。 这需要一套系统化的评测方案。\nSuperCLUE 榜单：便捷但不够透明的标尺 # AI 领域广泛关注的评测榜单 SuperCLUE1，用 660 道题目从图像质量、一致性、创造力、复杂适应性等维度对模型打分。对产品经理来说，它的价值在于低成本获取客观评测数据，支撑初步判断。你不需要自己跑评测，看看榜单就能对主流模型的能力有个大致了解。\n但它有一个明显的局限性：评测数据不开源。 你只能看到最终排名，看不到模型在每个具体问题上的表现。这意味着你无法拆解某个模型在你关心的维度上到底是强还是弱，写技术报告时缺乏具体案例佐证，也无法做归因分析。\n打个比方，SuperCLUE 就像大学排行榜。你知道清华北大排名靠前，但无法判断清华的计算机系和北大的计算机系哪个更适合你。要做精准选型，必须深入到具体维度。\n开源评测方案：EvalScope 实战 # SuperCLUE 看不到细节怎么办？那就上开源评测方案。开源评测的核心价值是可复现、可拆解、可归因。使用业内公认的开源数据集，所有评测流程透明，每个问题模型的回答都能追溯。\n但开源评测有一个痛点：操作门槛高。涉及大量数据集、统计脚本和指标计算，光是跑通评测流程就要折腾很久。\nEvalScope[^2]（基于 FlagEval 开源框架）就是来解决这个痛点的。它提供三项关键能力：\n自动化评测。 整个流程被封装得很简洁：安装依赖包、配置文本编码器、选择数据集、运行脚本。不需要手动写评测代码，也不需要一个个数据集去下载和处理。\n可视化报告。 跑完评测大概 20 分钟，就能拿到一份带诊断结论的可视化报告。不再是冷冰冰的数字表格，而是图形化的对比分析，哪个模型在哪个维度强、哪个维度弱，一目了然。\n归因分析。 这是最有价值的部分。不只是告诉你模型得了多少分，而是告诉你为什么得了这个分。是在哪类问题上答错了？是推理能力不够还是知识覆盖不全？是中文表现差还是英文表现差？\n有了归因分析，才能做出有针对性的决策：是需要换模型，还是针对特定场景做微调，还是调整 Prompt 策略。举个具体例子：假设你在做一款法律问答产品，评测发现模型 A 总得分比模型 B 高，但归因分析显示模型 A 在\u0026quot;法律推理\u0026quot;类问题上错误率高达 40%，而模型 B 在这类问题上错误率只有 15%。如果只看总分，会选模型 A；但结合归因分析，模型 B 才是最优选择。\n评测实战：四步走 # 讲完工具，说几条实用的建议。\n明确评测维度 # 不要上来就跑全量评测。先想清楚业务场景最看重什么：\n准确率优先（医疗问诊、法律咨询）：重点看知识准确性、推理能力 流畅性优先（内容创作、营销文案）：重点看语言质量、创意能力 稳定性优先（客服对话）：重点看输出一致性、格式遵守能力 成本优先（高并发场景）：重点看 Token 消耗、推理速度 构建业务评测集 # 通用榜单只能做参考。真正有说服力的评测，必须用你自己的业务数据。 从真实用户问题中抽取一批代表性样本，人工标注标准答案，做成专属评测集。这比任何通用榜单都有说服力。\n多维度对比 # 一个模型在 BLEU[^3]（翻译指标）上得分高，不代表它在实际业务中表现就好。综合多个指标，结合人工测评，做 A/B 测试上线对比。效果才是真理。\n持续评测 # 模型在迭代，业务在变化，用户需求也在演进。评测不是一次性的事情，而是需要持续进行。建立一个评测流水线，每次模型更新或业务调整后自动跑一轮评测。\n评测的终点不是报告，而是决策。好的评测应该能直接回答：这个模型在核心场景上表现如何？相比现有方案提升多少？推理成本是否在可接受范围内？有没有特定的短板需要通过 Prompt 优化或微调来弥补？\n从选型到追踪：算法效果跟进的通用流程 # 模型选好了，效果追踪怎么做？这里有一个三步通用流程：提出需求、效果跟踪、持续优化。\n提出算法需求 # 明确输入和输出：算法工程师需要知道你给什么、他返回什么 明确业务指标和上线阈值：「准确率达到 90% 才能上线」，这个数字不能模糊 把需求文档交给研发工程师：白纸黑字，避免口头理解偏差 如果公司有算法中台，先看中台现有能力能不能满足，别上来就造轮子；如果是复杂场景，可能需要算法工程师提前介入评估可行性。\n效果跟踪 # 需求提出去只是开始：\n持续和算法工程师沟通，了解他们打算用什么方式实现 提供标注数据（这一点太重要了，后面单独讲） 持续跟踪实验结果，每次模型迭代的指标变化都要记录 多元数据验证，验证通过后才能上线 持续优化 # 模型上线了不代表工作结束。上线后持续收集 Bad Case，配合算法工程师用 Bad Case 训练模型，一轮一轮提升业务指标。\n下面用一个具体的案例来展开。\n案例详解：情感识别算法 # 在客服机器人场景中，我们发现一个很影响体验的问题：用户已经明显带着情绪了，机器人还在就事论事地回复。\n看这个例子：\n用户：\u0026ldquo;要命了，我贷款申请提交七天了，还没审批结果吗？我要投诉了！\u0026rdquo; 机器人回复：\u0026ldquo;您的贷款申请已收到，正在审批中。\u0026rdquo;\n用户看完什么感受？火上浇油。明明已经急得不行了，机器人冷冰冰甩一句「正在审批中」，感觉像在对着一堵墙说话。\n我们期望的效果是：先安抚再给答案。 同样是处理贷款审批，先安抚用户的情绪，再告知审批进度，体验完全不同。要实现这个效果，需要一个情感识别算法：输入用户的问题，输出情绪值（正面/负面/中性），预期指标是情绪识别准确率达到 90% 以上。\n分类算法与数据标注 # 情感识别用的核心技术是分类算法。二分类就像判断一只羊是黑羊还是白羊，结果只有两种可能。多分类就像把用户的问题分成三堆：正面的放一堆，负面的放一堆，中性的放一堆。情感识别就是一个典型的多分类任务。\n训练过程可以一句话概括：人工标注数据，算法学习标注数据，训练出模型，模型判断新句子的类别。 就像教小朋友认动物：先给他看一堆猫的照片并告诉他「这是猫」，看多了之后小朋友就能认出没见过的猫了。\nAI 产品经理为什么要懂分类算法 # 看这个例子：\n句子 A：\u0026ldquo;我抓不空了，我要投诉\u0026rdquo; 句子 B：\u0026ldquo;到底行不行啊，我很着急\u0026rdquo;\n作为人，一眼就能看出来 A 比 B 更负面。A 都说了「投诉」。但在分类算法眼里，这两个句子打出的负面分数可能差不多。为什么？因为分类算法本质上是看词频和统计特征，它不太能理解「投诉」比「着急」有更强的负面信号。\n如果不了解这个技术限制，就会觉得算法工程师「能力不行」。但如果了解，就知道可以加一条关键词规则来补充。这就是「技术不够，产品来补」的思路。\n数据标注：模型效果的天花板 # 讲到算法效果，数据标注是绕不开的话题。核心结论是：标注数据的准确率是模型效果的上限值。 如果你标注了 20000 条语句，其中 95% 标注正确，5% 标注不正确或模棱两可，那模型不管怎么调优，最高准确率也就 95% 左右。因为模型学到的「标准答案」本身就有 5% 是错的，它怎么可能超越自己的教材？\nAI 的三大基石是数据、算力和算法，但数据在其中的权重占到 70%[^4]。一个很能说明问题的例子：让一个刚毕业的新人和一个资深算法工程师用同样的数据训练分类模型，准确率差别也就 1%-2%。这意味着数据的质量远比算法工程师的经验重要。\n建议的做法是：同一批语料至少由两个人标注，取两人一致的交集；不一致的部分再由第三个人或两人一起核对校验。\n混淆矩阵：读懂算法的「成绩单」 # 模型训练完了，算法工程师会给一份「成绩单」。要读懂它，需要理解混淆矩阵[^5]。\n数据集怎么分 # 数据不是全部拿来训练的。标准的分法是：训练集 80% 的数据（比如 20000 条中的 16000 条），用来让模型学习；验证/测试集 20% 的数据（4000 条），用来考模型学得怎么样。就像学生做题：16000 道是练习题，4000 道是考试题。考试题模型没见过，才能真实反映效果。\n混淆矩阵长什么样 # 以「正向情绪预测」为例：\n预测值：正向 预测值：负向/中性 真实值：正向 TP（真正例） FN（假负例） 真实值：负向/中性 FP（假正例） TN（真负例） 翻译一下：\nTP（真正例）：本来就是正向，模型也判正向，判对了 FN（假负例）：本来是正向，模型判成负向，漏掉了 FP（假正例）：本来是负向，模型判成正向，误判了 TN（真负例）：本来是负向，模型也判负向，判对了 三个核心指标 # 基于混淆矩阵，可以算出三个核心指标：\n精确率（Precision）：模型说「这是正向」的样本中，有多少确实是对的。通俗说就是：模型声称找出来的正向情绪，有多少是真的正向。\n召回率（Recall）：所有真实为正向的样本中，模型找回了多少。通俗说就是：实际的正向情绪，模型有没有漏掉。\nF1 值：精确率和召回率的调和平均数，是一个综合指标。\n精确率和召回率往往此消彼长。要求精确率高，模型就会更谨慎，可能漏掉一些正向的（召回率下降）；要求召回率高，模型就会更激进，可能误判一些负向为正向（精确率下降）。F1 值就是帮你在两者之间找平衡的。 这三个指标越高越好，但不要追求完美，根据业务场景找到合适的平衡点更重要。\n多元数据验证：上线前的最后一道关卡 # 模型在测试集上效果不错，就可以上线了吗？还不够。需要做多元数据验证，验证模型在不同场景下的表现是否一致：\n不同业务线：从售前和售后各抽测试样本，看效果是否稳定 不同渠道用户：APP 端和网页端用户的表达方式可能不同 不同表达方式：同一件事，有人说「提前还款」有人说「不想贷了」，模型都能识别吗 为什么要这么做？因为模型可能在某类数据上表现特别好，但在另一类上表现很差。如果只看整体指标，这个短板会被平均值掩盖。如果验证发现不同场景下效果差异明显，可能需要按场景或行业分别训练模型，而不是用一个通用模型打天下。\n持续优化：一个永不停止的闭环 # 模型上线后，持续优化是一个永不停止的过程。主要关注两件事。\n收集线上 Bad Case # Bad Case 就是模型判断错误的案例。比如明明是负面情绪被识别成了正面。这些案例是金矿，把它们收集回来，重新标注，重新训练，模型的准确率就会一步步提升。\n定期更新训练数据 # 业务是持续变化的。三个月前用户的常用话术和三个月后可能完全不同。如果模型一直用老数据，效果会逐渐衰减。所以需要定期收集线上新语料进行标注，更新模型。\n这两个动作形成一个持续运转的飞轮：发现问题，收集案例，标注数据，训练模型，上线验证，发现新问题。这个飞轮转得越快，产品就越好。\n评测和效果追踪不是技术团队的专属工作，而是必须参与的决策环节。不管是选模型还是优化模型，始终记住一条原则：评测的终点不是报告，而是决策。 报告写得再漂亮，如果它不能帮你做出更好的产品决策，那就是无效评测。\n参考文献 # SuperCLUE EvalScope SuperCLUE 是中文通用大模型综合性测评基准，定期发布评测榜单，覆盖多个维度和场景。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2025-07-15","externalUrl":null,"permalink":"/posts/2025/07/llm-evaluation-tracking/","section":"所有文章","summary":"","title":"大模型评测与算法效果追踪：从选型到持续优化","type":"posts"},{"content":"","date":"2025-07-15","externalUrl":null,"permalink":"/tags/%E6%A8%A1%E5%9E%8B%E9%80%89%E5%9E%8B/","section":"标签","summary":"","title":"模型选型","type":"tags"},{"content":"","date":"2025-07-15","externalUrl":null,"permalink":"/tags/%E8%AF%84%E6%B5%8B/","section":"标签","summary":"","title":"评测","type":"tags"},{"content":"","date":"2025-07-15","externalUrl":null,"permalink":"/tags/%E7%AE%97%E6%B3%95%E4%BC%98%E5%8C%96/","section":"标签","summary":"","title":"算法优化","type":"tags"},{"content":" 前言 # 最近 AI 生图越来越火，从 Midjourney 到 Stable Diffusion，生成的图片质量已经到了让人惊叹的程度。但我一直很好奇，这背后的技术到底是什么？为什么 AI 能凭空「画」出一张图？后来花了一些时间研究 Diffusion 模型、UNet、CLIP 这些核心组件，发现整个技术栈比想象中更加巧妙。这篇文章就把我的理解整理出来，从底层原理到实际应用中的风格控制和控图方案，尽量讲清楚。\n扩散模型：一杯墨水的故事 # AI 生图的底层技术叫扩散模型（Diffusion Model）。核心思路用一个墨水的比喻就能讲明白1。\n想象一杯清水，代表一张清晰的原始图片。\n第一步：不断往水里滴墨水。 一滴、两滴、三滴……水越来越浑浊，图片变得越来越模糊。滴到足够多的时候，清水变成了纯墨水，图片变成了满是噪点的雪花屏。这个过程叫加噪（Forward Diffusion）。\n第二步：反过来，把墨水从水里抽出来。 一点一点地抽，雪花屏上的噪点逐步消失，模糊的画面逐渐变得清晰。最终，墨水抽干了，一张全新的清晰图片诞生了。这个过程叫去噪（Reverse Diffusion）。\n扩散模型的核心逻辑就是这两步：\n训练阶段：让模型学习「加墨水」的过程，知道滴了多少步之后图像会变成什么样，建立噪声规律的知识。 生成阶段：反向操作，从一张纯噪声的雪花图开始，一步步把「墨水」抽出来，最终得到一张全新图片。 所以 AI 「画画」的过程，不是从白纸开始画，而是从一团乱麻中「还原」出一幅画。这个思路最早来自物理学中的非平衡热力学，后来被 Sohl-Dickstein 等人引入到生成模型领域2。\nUNet 和 CLIP：主力工程师与翻译官 # 知道了扩散模型的核心思路，接下来认识两个关键角色。\nUNet：流水线上的主力工程师 # UNet 是一个 U 型的图像处理网络，负责预测每一步该「抽多少墨水」[^3]。\n它的工作方式像一条流水线：编码器把图片压缩，抓住核心特征；解码器再一步步还原出清晰的图。中间有一条记忆通道（残差连接），保证信息在压缩和还原过程中不丢失。\n没有 UNet，扩散模型就不知道每一步该怎么去噪，生成过程就无法进行。\nCLIP：把人话翻译成机器语言的翻译官 # UNet 能干活，但它只认识数字，不认识你写的「一只戴帽子的狗」这种文字。\nCLIP 就是那个翻译官。它在海量「图片 - 文字描述」对上训练过，能把文字和图片的含义对齐到同一种「语言」里[^4]。「一只戴帽子的狗」这段文字被编码成特征向量后，和真实戴帽子的狗图片编码后的含义非常接近。\nCLIP 把你的文字指令翻译成 UNet 能理解的特征向量，UNet 再根据这些特征去做去噪。两个角色配合，才实现了「你说什么，它画什么」。\nStable Diffusion 的天才优化：潜空间扩散 # 早期的扩散模型有一个致命问题：直接在像素空间上做加噪和去噪，计算量巨大，速度很慢。一张高清图动辄几百万像素，每一步都要处理这么多数据，生成一张图要等很久。\nStable Diffusion 的解决方案非常巧妙[^5]：\n先训练一个自编码器（VAE），把高清图压缩成很小的低维潜在特征。就像把一桶原料浓缩成一小瓶精华液。 所有的扩散过程（加噪和去噪）都在这个小「精华液」空间里进行。 UNet 处理的「图」变小了很多倍，计算量大幅下降。 最后一步，解码器把处理好的小精华液「解压」回高清图。 效果：生成图片的速度大幅提升。 这也是为什么 Stable Diffusion 能成为开源生图领域的主流方案。\n用一张表把整个文生图技术栈串起来：\n组件 比喻 功能 扩散模型 核心生产线 「先弄乱再还原」的学习逻辑 UNet 流水线上的智能机器人 一步步净化精华液 CLIP 翻译官 把用户指令翻译成机器能懂的暗号 注意力机制 实时对讲机 让翻译官随时告诉机器人这一步重点处理什么 潜空间扩散 把原料压缩成精华液后再加工 省时省力 风格指定：关键词够用吗？ # 了解了原理，来看看实际使用中的问题。\n几乎所有的 AI 生图模型都支持在提示词中加入风格关键词：电影写真、赛博朋克、日系动漫、水墨画、油画等等。一些产品甚至把常见风格做成下拉菜单，方便用户直接选择。\n但问题在于：即使你指定了风格，不同模型对同一风格关键词的理解也千差万别。 同样写「赛博朋克」，有的模型生成偏漫画风，有的偏现实画风。而且你提示词里对五官、表情、服饰、动作等细节的描述，还会干扰最终的风格效果。\n如果你的业务要求每张图都是统一画风（比如品牌素材、金融产品宣传图），单纯靠关键词是不够的。\n微调方案：让模型固定在某种风格 # 解决方案是对基础模型进行微调，把它固定在某种特定风格上。\n像 LibLib AI 这样的 AIGC 内容创作平台上，有大量创作者开源的微调模型（通常是 LoRA）。搜索特定风格（比如赛博朋克），能找到一系列基于不同基础模型（Flux.1、SDXL 等）做二次微调的模型。\n使用时有几个技巧：\n进入微调模型主页查看版本信息和作者说明。 prompt 中带上该微调模型的「口令」关键词，确保模型按微调后的风格生成。 如果对公开模型不放心，可以自己准备一批风格完全符合要求的训练数据，用自备数据集微调模型。 经过特定风格微调的模型，生成结果的稳定性基本能得到保障。\n风格迁移的效果边界 # 风格迁移是另一个常见需求：用照片做微信头像（转漫画风），或者对已生成的图像改变画风。\n操作方式是把原始图像和风格迁移指令一起提供给多模态生图模型。\n但要特别注意它的效果边界：\n人物迁移效果相对较好。 构图复杂的大场景画面迁移效果较差，图像元素越多越复杂，轮廓细节越难还原。 容易出现明显的细节崩坏。 目前风格迁移只能作为辅助技能，期待不能太高。建议亲自测试不同画风、不同结构复杂度、不同主题图像的迁移效果差异，摸清边界后再决定是否用于生产环境。\n控图方案：产品视角的核心课题 # 用户端的三大困境 # 困境一：提示词门槛太高。 用户用「人话」描述需求（如「可爱日系少女、大眼睛、双马尾、微笑表情、光影柔和」），效果往往不理想。因为现阶段视觉生成模型对自然语言的理解和执行能力远远不够精准，只能靠用户迁就模型，用关键词堆砌的方式把细节塞进去。而且很多隐藏经验普通用户根本不知道，比如「4K 画质」比「高清画质」更容易被模型识别。\n困境二：陷入恶性循环。 用户拼命堆词，生成结果还是像开盲盒，然后反复「抽卡」重试。体验很差。\n困境三：有些需求根本无法用文字描述。 看到一张超有氛围的风景照，想按此风格生成写真，但「氛围」和「风格」这种主观性强的东西，用文字说不清楚。在金融产品宣传图等设计类场景里更是如此，用户只能靠参考图，纯文本生成完全行不通。\n产品经理的课题 # 这三大困境指向同一个课题：如何让 AI 生图的生成过程更可控？\n具体来说，产品经理需要思考：\n如何降低用户写出高质量提示词的门槛？（比如智能提示词推荐、风格预设模板） 如何帮助用户把「说不清的感觉」转化为有效的生成指令？（比如以图生图、风格参考图上传） 如何在保持创意自由度的同时增加可控性？ 控图方案是生图产品经理必须重点关注的方向。谁在这个问题上做得好，谁就能在 AI 生图产品的竞争中脱颖而出。\nAI 生图技术已经从「能看」进化到「好看」，下一步要解决的是「可控」。这个方向上，机会还很多。\n参考文献 # Denoising Diffusion Probabilistic Models, Ho et al. 2020 Deep Unsupervised Learning using Nonequilibrium Thermodynamics, Sohl-Dickstein et al. 2015 Learning Transferable Visual Models From Natural Language Supervision, Radford et al. 2021 High-Resolution Image Synthesis with Latent Diffusion Models, Rombach et al. 2022 扩散模型的核心论文由 Ho et al. 于 2020 年提出，题为 \u0026ldquo;Denoising Diffusion Probabilistic Models\u0026rdquo;，奠定了现代 AI 生图的理论基础。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nSohl-Dickstein 等人的论文 \u0026ldquo;Deep Unsupervised Learning using Nonequilibrium Thermodynamics\u0026rdquo; 首次将非平衡热力学引入生成模型。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2025-05-15","externalUrl":null,"permalink":"/posts/2025/05/ai-image-generation-deep-dive/","section":"所有文章","summary":"","title":"AI 生图技术解析：从 Diffusion 模型到 Stable Diffusion","type":"posts"},{"content":"","date":"2025-05-15","externalUrl":null,"permalink":"/tags/diffusion/","section":"标签","summary":"","title":"Diffusion","type":"tags"},{"content":"","date":"2025-05-15","externalUrl":null,"permalink":"/en/tags/image-generation/","section":"Tags","summary":"","title":"Image Generation","type":"tags"},{"content":"","date":"2025-05-15","externalUrl":null,"permalink":"/tags/stable-diffusion/","section":"标签","summary":"","title":"Stable Diffusion","type":"tags"},{"content":"","date":"2025-05-15","externalUrl":null,"permalink":"/tags/%E5%9B%BE%E5%83%8F%E7%94%9F%E6%88%90/","section":"标签","summary":"","title":"图像生成","type":"tags"},{"content":"","date":"2025-04-15","externalUrl":null,"permalink":"/tags/graphrag/","section":"标签","summary":"","title":"GraphRAG","type":"tags"},{"content":" 前言 # 在做 RAG 项目的过程中，我发现传统向量检索在处理跨文档关联问题时效果一直不太理想。比如用户问「这家公司这几年的战略方向有什么变化」，系统返回的往往是几段零散的文字片段，前后逻辑对不上。后来了解到 GraphRAG 这个方向，它在传统 RAG 基础上引入了知识图谱，能够理解实体之间的关系，对宏观性问题的回答质量有明显提升。这篇文章就来详细聊聊 GraphRAG 的核心机制和实际应用中的取舍。\n传统 RAG 的瓶颈 # 传统 RAG 在过去两年确实有效缓解了大模型的幻觉问题，但随着应用场景越来越复杂，三个局限越来越明显。\n只能检索「点」，看不到「线」 # 传统 RAG 只能找到零散的文本片段，无法理解实体间的关系。比如知识库里有张三的信息，也有李四的信息，但系统不知道张三是李四的直属上级，也不知道他们正在合作同一个项目。它能检索到「点」，但看不到「线」和「面」1。\n宏观问题回答质量差 # 当你问一些概括性、主题性的问题，比如「这篇年报的核心观点是什么」，传统 RAG 只能给你缺乏逻辑联系的零散段落。就像让一个只看过电影片段的人给你讲剧情大纲，他只能复述画面，讲不出主线。\n无法处理多步推理 # 需要多步推理的查询，传统 RAG 基本束手无策。比如「张三负责的部门中，哪个部门的业绩增长最快」，这需要先找到张三，再找到他负责的部门，再比较各部门业绩。每一步都依赖上一步的结果，传统 RAG 没有这种推理链条。\nGraphRAG 的核心：从文本检索到知识图谱查询 # GraphRAG 将传统基于文本的检索，升级为基于知识图谱的查询模式。这个升级主要通过三个关键环节实现。\n知识图谱构建 # 这是 GraphRAG 和传统 RAG 最大的区别，也是整个系统的基础。构建过程分三步。\n第一步是实体抽取。 通过大模型自动识别文档中的各类实体：人物、机构、事件、地点等。相当于从文本中把「谁」和「什么」挑出来。\n第二步是关系构建。 构建实体-关系-实体的三元组。比如：\n(张三) --[汇报给]--\u0026gt; (李四) (市场部) --[负责]--\u0026gt; (品牌推广) 这一步建立的是实体之间的连接线。\n第三步是知识融合。 将不同来源的同一个实体进行合并。比如年报第一章提到「公司 CEO 张三」，第三章又提到「张总」，系统需要识别出这是同一个人。\n每个节点和关系都带有语义描述，通过这些描述文本生成 Embedding，供后续检索阶段使用。最终形成完整的知识图谱，相当于给知识库建了一个「认知中枢」2。\n社区划分 # 光有图谱还不够，还需要对整个图谱进一步划分为不同的社区（即子图）。这个过程也分三步。\n社区发现算法：识别哪些节点之间联系程度高，让它们聚在一起构成社区。就像在一个公司里，同一个部门的员工日常联系更紧密，自然形成一个小圈子。 社区摘要生成：让大模型为每个社区生成内容摘要。比如某个社区的摘要可能是「某公司 2025 年 Q1 财报情况」，另一个社区可能是「产品线组织架构」。 社区 Embedding：用摘要文本得到子图的向量表达。 到这里，整个图谱建设完成。你拥有的不再是一堆散乱的文本片段，而是一个有结构、有层次、有关系的知识网络 3。\n两种检索策略 # GraphRAG 提供了两种检索策略，分别应对不同类型的问题。\n局部检索（Local Search） # 局部检索从用户问题中提取关键实体出发，收集直接相关的事实信息。流程如下：\n识别问题中的关键实体（比如「微软」） 通过问题 Embedding 与节点 Embedding 的语义匹配，将实体与图谱节点链接 以候选节点为锚点，沿关系边向外扩展（跳数越多，知识结构越复杂丰富） 将遍历中收集的节点和关系边形成子图，作为上下文输入大模型 打个比方：局部检索就像从地图上的一个点出发，沿着道路不断向外探索，走过的路和路边的建筑都会被记录下来。\n适用场景：简单事实查询（「微软总部在哪里」）和多跳复杂推理。\n全局检索（Global Search） # 全局检索基于已划分好的社区子图展开。流程是：\n将用户问题编码为向量 与所有社区的 Embedding 做相似度匹配 选出最相关的若干社区子图 将子图摘要文本、关键实体列表、关系列表作为检索结果输入大模型 打个比方：全局检索就像先看整本书的目录，找到最相关的几个章节，然后把这几个章节的摘要给你看。\n适用场景：宏观性、主题性问题（「这篇文章讨论了哪些话题」），或者没有明确实体指向的概括性问题 4。\nGraphRAG 的现实挑战 # GraphRAG 很强，但也不是万能的。在实际项目中，它面临三个实实在在的挑战。\n计算成本高 # 实体抽取和关系抽取都依赖大模型来做，需要大量计算资源和处理时间。传统 RAG 建个知识库可能几小时搞定，GraphRAG 可能要跑好几天。对于数据量大的场景，这个时间成本必须考虑进去。\n知识质量存疑 # 完全依赖大模型来抽取实体和关系，一旦抽取结果有问题，错误就会被固化到图谱中。这比散落的幻觉更可怕，因为结构化的错误更容易被当真 5。所以在一些对准确性要求极高的场景中，可能还需要人工审核或额外的质量校验机制。\n知识更新困难 # 更新一个知识点不是改一个文档那么简单，而是需要重新跑知识抽取和融合流程，难以实现实时更新。对于需要频繁更新知识的场景，这个代价太大了。\n实践建议：两者结合才是最优解 # GraphRAG 和传统 RAG 各有适用场景，在实际项目中一般将两者结合使用：\nGraphRAG 适合推理深度和全局理解要求高的场景，比如金融研报分析、法律条文关联推理、企业知识管理等 传统 RAG 在简单检索和实时性要求高的场景中仍有优势，比如客服 FAQ、产品使用手册查询等 比较平衡的做法是先用传统 RAG 处理简单的检索查询，遇到需要复杂推理或全局理解的问题时再调用 GraphRAG。也可以把两者作为互补的检索通道，结果合并后再交给大模型生成。这种混合架构在工程实现上并不复杂，但在回答质量上会有明显提升。\n传统 RAG 基于向量相似度检索，本质上只能捕捉语义层面的「近邻关系」，无法建模实体间的结构化关联。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n知识图谱中的三元组（Triple）是语义网的基础数据模型，由主语（Subject）、谓语（Predicate）、宾语（Object）组成。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n社区发现算法常用的包括 Louvain 算法和 Leiden 算法，它们通过最大化模块度（Modularity）来识别图中紧密连接的子结构。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nGraphRAG 的全局检索策略参考了 Map-Reduce 的思想：先将问题分发给各相关社区生成局部答案，再汇总为最终回答。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n这种现象在学术上被称为「结构化幻觉」（Structured Hallucination），即错误信息因为被组织成结构化形式而更具欺骗性。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2025-04-15","externalUrl":null,"permalink":"/posts/2025/04/graphrag-deep-dive/","section":"所有文章","summary":"","title":"GraphRAG 深度解析：知识图谱增强的检索生成","type":"posts"},{"content":"","date":"2025-04-15","externalUrl":null,"permalink":"/en/tags/knowledge-graph/","section":"Tags","summary":"","title":"Knowledge Graph","type":"tags"},{"content":"","date":"2025-04-15","externalUrl":null,"permalink":"/tags/%E7%9F%A5%E8%AF%86%E5%9B%BE%E8%B0%B1/","section":"标签","summary":"","title":"知识图谱","type":"tags"},{"content":"","date":"2025-02-15","externalUrl":null,"permalink":"/en/tags/document-chunking/","section":"Tags","summary":"","title":"Document Chunking","type":"tags"},{"content":"","date":"2025-02-15","externalUrl":null,"permalink":"/tags/embedding/","section":"标签","summary":"","title":"Embedding","type":"tags"},{"content":" 前言 # 之前写过一篇 RAG 基础文章，把分片、索引、召回、重排、生成的完整链路梳理了一遍。文章里提到分片粒度需要权衡，但当时没有展开。后来在实际项目中，我发现切片策略对最终效果的影响远比想象中大。同样的文档、同样的 Embedding 模型，换一种切法，检索准确率可以差出一倍。这才意识到，RAG 系统回答质量的好坏，在你还没开始检索之前就已经决定了。这篇文章就来把文档切片这个环节彻底说透，对比五种主流策略的优劣和适用场景。\n为什么切片策略这么关键 # RAG 系统的核心是把知识文档变成向量，再跟用户问题做匹配。而决定这个匹配效果的基础，就是你一开始怎么把文档切成块的。\n切得不好，一个完整的语义单元会被打散到多个片段里。检索时可能只命中了其中一部分，另一半关键信息丢了。就像切蛋糕，你想吃到完整的草莓，结果草莓被切成碎末分布在五六块蛋糕里，拿到任何一块都尝不到完整的草莓味。\n切片的好坏直接决定了 Embedding 的编码效果和检索的准确性。 这是整个 RAG 链路中最容易被忽视，却最值得投入精力的环节。\n五种切片策略对比 # 固定大小分块（Fixed-size Chunking） # 最直觉的方案：按预设的字符数、单词数或 Token 数切分。比如每 500 个 Token 切一块。\n# LangChain 中的固定大小切片示例 from langchain.text_splitter import RecursiveCharacterTextSplitter splitter = RecursiveCharacterTextSplitter( chunk_size=500, chunk_overlap=50, # 相邻块之间保留重叠 length_function=len, ) chunks = splitter.split_text(document) 关键技巧是引入滑动窗口：相邻文本块之间保留部分重叠内容（比如重叠 50 个 Token），缓解语义被切断的问题。\n优点很明显：实现最简单，文本块大小统一，方便批量化处理。适合作为基线方案或快速验证。\n缺点也很明显：会切断句子和段落，导致重要信息分散到不同文本块中。一句话被切成两半，检索时只匹配到一半，上下文丢了。\n能跑通，但回答质量不高。适合入门和快速验证，不建议作为最终方案。\n语义分块（Semantic Chunking） # 固定大小分块的问题在于它完全不看内容。语义分块的思路是：按照语义边界来切割。\n具体做法分四步：\n先把文档按有意义的单元（句子、段落、主题章节）做预划分 为每个单元生成 Embedding 向量 计算相邻单元的余弦相似度 相似度高的合并，迭代直到相似度显著下降（意味着语义发生了转变） 就像听人讲话，当他从一个话题切换到另一个话题时，你能感觉到「嗯，他在说别的事了」。语义分块就是让机器来做这种判断。\n优点是保持了语义的自然连贯性，文本块信息更丰富，检索准确率更高。\n缺点是依赖相似性阈值的设定。阈值设高了，块太大；设低了，块太碎。这个阈值很吃经验，不同类型的文档可能需要不同的阈值。\n在更多场景中表现相对优异，是性价比很高的选择。\n递归分块（Recursive Chunking） # 这是一种「层层递进」的切法：\n先按段落或章节做预分块（用简单的分隔符就行） 判断每个文本块是否超过最大尺寸限制 如果超过了，在段落内进一步切分 重复迭代，直到每个段落都小于预设阈值 # LangChain 中的递归切片示例 from langchain.text_splitter import RecursiveCharacterTextSplitter splitter = RecursiveCharacterTextSplitter( chunk_size=1000, chunk_overlap=100, separators=[\u0026#34;\\n\\n\u0026#34;, \u0026#34;\\n\u0026#34;, \u0026#34;。\u0026#34;, \u0026#34;！\u0026#34;, \u0026#34;？\u0026#34;, \u0026#34;.\u0026#34;, \u0026#34; \u0026#34;, \u0026#34;\u0026#34;], ) chunks = splitter.split_text(document) separators 参数定义了优先使用的分隔符层级：先尝试按双换行（段落）切，切不动再按单换行（行）切，再切不动按句号切，依此类推。\n递归分块既保证了语义的自然流畅度，也确保了语义单元的完整性。是对固定大小分块和语义分块的一种折中优化。缺点是复杂度和计算开销相对较高，需要设计好递归的终止条件和每层的切分策略。\n适合结构相对规整但段落长度差异大的文档。\n基于文档结构的分块（Structure-based Chunking） # 充分利用文档现有的结构特征：标题、章节、段落来划分文本块的边界。如果文档有清晰的「第一章」「1.1」「1.1.1」这样的层级结构，就按这个结构来切。\n# LangChain 中按 Markdown 标题结构切片 from langchain.text_splitter import MarkdownHeaderTextSplitter headers_to_split_on = [ (\u0026#34;#\u0026#34;, \u0026#34;Header 1\u0026#34;), (\u0026#34;##\u0026#34;, \u0026#34;Header 2\u0026#34;), (\u0026#34;###\u0026#34;, \u0026#34;Header 3\u0026#34;), ] splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers_to_split_on) chunks = splitter.split_text(markdown_document) 语义完整性最好，因为切出来的块天然符合文档的逻辑结构。\n但前提是文档必须具备清晰的层次结构。现实是很多文档结构杂糅：标题乱用、层级混乱、格式不统一。而且按结构切出来的文本块长度参差不齐，某个标题下可能就两行字，另一个标题下可能上千字1。\n文档结构清晰时效果很好，但现实中「结构清晰」这个前提经常不成立。\n基于大模型的分块（LLM-based Chunking） # 通过设计 Prompt 让大语言模型自动生成语义独立、内容完整的文本块。相当于让 AI 来读文档，自己判断该怎么切。\n# 概念示例：用 LLM 做语义切分 prompt = \u0026#34;\u0026#34;\u0026#34; 请将以下文本按照语义主题进行切分。 要求： 1. 每个片段必须是语义完整的主题单元 2. 不要在句子中间切断 3. 返回 JSON 格式：[{\u0026#34;chunk\u0026#34;: \u0026#34;...\u0026#34;, \u0026#34;topic\u0026#34;: \u0026#34;...\u0026#34;}] 文本内容： {document} \u0026#34;\u0026#34;\u0026#34; 语义准确性最高。大模型真正理解上下文和语义关联性，能做出最接近人类判断的切分。\n但计算资源需求也最高。每个文档都要过一遍大模型，成本和耗时都不小2。\n效果最好但最贵，适合对质量要求极高、文档量可控的场景。\n五种策略速查表 # 策略 核心思路 语义完整性 实现复杂度 计算成本 适用场景 固定大小分块 按 Token 数等分 低 低 低 快速验证、基线方案 语义分块 按语义边界切割 高 中 中 通用场景，性价比最优 递归分块 层层递进切分 中高 中 中 段落长度差异大的文档 结构化分块 按文档层级切割 高 低 低 结构清晰的文档（如法律条文） LLM 分块 大模型理解后切割 最高 高 最高 高质量要求、文档量可控 实战中不是单选题 # 讲了五种策略，到底选哪个？答案是：可以组合使用。\n实际落地中，切片策略通常经历这样的演进过程：\n第一版： 先用固定大小切片跑通，验证基本流程没问题。这时候回答质量可能不高，但至少系统能运转。\n第二版： 针对问题优化。比如加入人工规则，用正则表达式匹配专有名词，确保不被切断。如果文档涉及医学、法律等有大量专业术语的领域，这一步尤其重要。\n最终形成三类策略的组合：\n策略类型 适用场景 方法 通用切片 通用知识类文档 设定固定 Token 长度直接分割 专业切片 涉及专有术语的学科文档 设定专门策略，确保不切断专有名词 粗精分结合 复杂文档 先粗分（如 5000 Token 一组），再精细化小片段切割 粗精分结合的思路特别值得说一说。先按大段落粗分，保证每个大块在同一个主题范围内。然后在每个大块内部再做精细化的小片段切割，兼顾了主题完整性和检索精度3。\nToken 大小的权衡 # 不管用哪种策略，都有一个绕不开的参数：每个文本块的 Token 大小。\nToken 过大： 语义理解困难，一个段落涵盖的信息太多，无法精准匹配到用户问题的具体关注点。就像问一个人「北京明天天气怎么样」，结果他给你念了一整周的天气预报。\nToken 过小： 能精准命中最相关的句子，但切块数量暴增，相似度计算量增大，系统性能下降。而且碎片化太严重，上下文信息丢失。\n核心原则是：Token 大小需要在语义完整性和计算性能之间取得平衡。没有一个万能的最优值，需要根据具体的文档类型和使用场景来调4。\n选择策略时要注意什么 # 每种方案都有独特优势和局限性，不可能用一种通用方案解决所有问题。方案之间也不冲突，可以组合使用。比如递归分块时，对长段落转用固定大小分块或语义分块5。\n技术选型需要综合考虑内容特性、大模型能力和计算资源等多重因素。最重要的是先跑通再优化，别一上来就追求最完美的方案，先用简单策略把流程跑通，再根据实际问题迭代优化。\n结构化分块在 Markdown、HTML 等格式文档上效果最好，因为这类文档的标题层级是显式标注的。对于扫描件 PDF 或纯文本，需要先做结构化抽取。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nLLM 分块的成本可以通过小模型（如 GPT-4o-mini）来降低，但语义理解能力也会相应下降。实际中可以在关键文档上用大模型，普通文档用更轻量的方案。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n粗精分结合的思路在 LlamaIndex 的 HierarchicalNodeParser 中有对应的实现，它将文档构建为树状结构，父节点是粗分块，子节点是细分块。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n一般来说，通用知识问答场景下 256～512 Token 是一个不错的起点。法律、医学等专业领域可能需要更大的块（512～1024 Token）来保持上下文完整。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nLangChain 和 LlamaIndex 都提供了多种内置的 Text Splitter，可以按需组合。建议先通读文档了解每种 Splitter 的设计思路，再决定怎么搭配。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2025-02-15","externalUrl":null,"permalink":"/posts/2025/02/rag-chunking-strategies/","section":"所有文章","summary":"","title":"RAG 进阶：文档切片策略深度对比","type":"posts"},{"content":"","date":"2025-02-15","externalUrl":null,"permalink":"/en/tags/vector-retrieval/","section":"Tags","summary":"","title":"Vector Retrieval","type":"tags"},{"content":"","date":"2025-02-15","externalUrl":null,"permalink":"/tags/%E6%96%87%E6%A1%A3%E5%88%87%E7%89%87/","section":"标签","summary":"","title":"文档切片","type":"tags"},{"content":"","date":"2025-02-15","externalUrl":null,"permalink":"/tags/%E5%90%91%E9%87%8F%E6%A3%80%E7%B4%A2/","section":"标签","summary":"","title":"向量检索","type":"tags"},{"content":"","date":"2025-01-12","externalUrl":null,"permalink":"/tags/prd/","section":"标签","summary":"","title":"PRD","type":"tags"},{"content":"","date":"2025-01-12","externalUrl":null,"permalink":"/en/categories/product-manager/","section":"Categories","summary":"","title":"Product Manager","type":"categories"},{"content":"","date":"2025-01-12","externalUrl":null,"permalink":"/en/tags/product-manager/","section":"Tags","summary":"","title":"Product Manager","type":"tags"},{"content":"","date":"2025-01-12","externalUrl":null,"permalink":"/categories/%E4%BA%A7%E5%93%81%E7%BB%8F%E7%90%86/","section":"分类","summary":"","title":"产品经理","type":"categories"},{"content":"","date":"2025-01-12","externalUrl":null,"permalink":"/tags/%E4%BA%A7%E5%93%81%E7%BB%8F%E7%90%86/","section":"标签","summary":"","title":"产品经理","type":"tags"},{"content":" 前言 # 最近在负责一个 Agent 产品，PRD 写到一半的时候我意识到一件事：以前那套写法，用在 Agent 上有点像拿螺丝刀拧钉子，不是完全不行，但就是使不上劲。功能列表、页面结构、交互流程这些老朋友依然在列，但 Agent 真正需要定义的东西，它们覆盖不到。\n于是我把整份 PRD 推倒重写了一遍，把思路从「描述功能」切换到「描述决策」。这篇文章聊聊我为什么要换写法，以及换完之后的结构是什么样的。\n传统 PRD 的盲区 # 传统 PRD 的底层假设是系统行为可预期，你定义好每个状态的流转路径就行了。这套方法在确定性的产品里很好使，但 Agent 产品天生带着不确定性：同样的用户输入，在不同上下文下可能对应完全不同的处理路径。\n传统 PRD 在面对 Agent 时有几个明显的盲区：\n意图理解没有定义。 传统产品里用户通过按钮、菜单、表单来表达意图，意图是显式的。Agent 产品里用户说的是自然语言，意图是隐式的[^1]。「帮我查一下上个月的数据」可能是想看总览，可能是想对比环比，也可能是要导出给老板。如果不把意图空间先拆清楚，后面的设计就是空中楼阁。\n工具调用的判断条件缺失。 很多 Agent PRD 会列一串工具能力：「支持知识库检索、支持搜索、支持调用工单系统。」但真正决定体验的是这些工具在什么条件下该调、按什么顺序调、调失败了怎么办[^2]。工具调用本身就是一个产品决策，不能丢给研发自己去判断。\n边界条件被当成异常处理。 传统产品里异常处理是兜底，放在文档最后补一段就行。但 Agent 产品里，信息不足、工具失败、数据冲突、幻觉风险这些情况不是小概率事件，而是每天都在发生的主流程的一部分[^3]。把它们放到「异常处理」的框架下去写，本身就是一种误判。\n换个思路：从决策流开始写 # 意识到这些问题之后，我把 PRD 的起点从「功能模块」换成了「决策流」。\n核心思路是这样的：用户说了一句话之后，系统先判断意图是否清晰，不清晰就追问；清晰之后判断需不需要调工具；调完工具之后判断是不是高风险动作，是的话必须让用户确认。整条链路就是一系列判断节点。\n我用 Mermaid[^4] 画了一下这个逻辑：\ngraph TD A([用户输入]) --\u003e B{是否识别清晰意图} B -- 否 --\u003e C[追问补充信息] B -- 是 --\u003e D{是否需要外部工具} D -- 否 --\u003e E[直接生成结果] D -- 是 --\u003e F[调用知识库/搜索/业务系统] F --\u003e G{是否高风险动作} G -- 是 --\u003e H[请求用户确认] G -- 否 --\u003e I[执行并返回结果] 这不是一个放之四海皆准的模板，不同业务场景肯定要调整分支和判断条件。但它给了 PRD 一个骨架：每个菱形节点都是一个需要明确定义的判断规则，每个矩形节点都是一个需要写清楚的行为描述。\n我怎么写意图拆解 # 意图拆解我一般用结构表来写，不是简单列几个意图名称就完事。每个意图至少要覆盖这些字段：意图名称、典型表达、真实目标、优先级、是否允许自动执行、是否需要二次确认。\n举个实际例子。同样是「我无法登录」，背后至少有四种意图：忘记密码、账号被锁、新员工未开通、SSO 配置异常。这四种意图对应的处理路径完全不同。如果 PRD 只写一句「系统自动识别问题类别并创建工单」，研发在实现的时候只能靠猜。\n拆完意图之后还有一件事很重要：信息不足时怎么办。用户可能一句话说了两个诉求，也可能关键信息完全没给。这时候是追问一轮直接给答案，还是追问到底？追问两轮还是不够信息，是降级回答还是报错？这些规则要提前定死，不能留到线上再调。\n我怎么写工具调用 # 工具调用我不会写成能力清单，而是写成条件判断：\n用户的问题是否涉及内部数据？涉及的话先查知识库，不涉及的话直接回答 知识库返回的结果是否足够回答用户的问题？不够的话再调搜索补全 是否涉及写操作（创建、修改、删除）？涉及的话必须经过用户确认 工具调用超时或返回异常，系统是降级回答、报错、还是保存草稿等用户重试 多个工具返回的结果互相矛盾，优先信哪个数据源 这里面每一个条件都是一个产品决策。写得越明确，研发实现的时候偏离就越小，上线后扯皮也越少。\n每多调一个工具，就多一层延迟、多一个失败点。所以工具不是越多越好，能不调就不调。一句话能答明白的事情，非要绕三个工具再总结一遍，体验只会更差。\n我怎么写边界条件 # 边界条件我现在的做法是和主流程同等对待，不是放在文档最后的补充章节，而是嵌入到每个判断节点里。\n具体来说，我会关注几类：\n信息不足：追问策略是什么，追问几轮后怎么收束 工具失败：每个工具挂了之后的降级方案 高风险动作：哪些操作必须确认、哪些可以自动执行、哪些永远不能自动执行 数据冲突：多个信息源结果矛盾时信谁的，还是展示给用户自己判断 幻觉控制：模型在没有充分依据时能不能生成内容，不能的话怎么告知用户 这些规则看起来枯燥，但它们才是 Agent 产品「可控」和「失控」的分界线。\n完整的 PRD 结构 # 踩完一轮之后，我现在的 Agent PRD 大致按这个结构来写：\n场景定义：为什么这个场景需要 Agent 而不是传统功能？如果一件事规则极稳定、输入极结构化、不需要多轮判断，其实不一定要上 Agent[^3]。这一段可以过滤掉不少伪需求。\n意图拆解：结构表，把每个场景下可能的意图、触发条件、优先级、处理方式列出来。\n决策路径：就是上面那张 Mermaid 图展开的内容，每个判断节点要写清楚判断条件和分支走向。\n工具调用规则：触发条件、禁止条件、调用顺序、失败降级。\n边界条件：和主流程同等重要，嵌入到每个节点而不是单独成章。\n结果定义：什么叫完成、什么叫部分完成、什么叫失败但有可用中间结果。\n评估指标：意图识别准确率、工具调用成功率、任务完成率、人工接管率、结果采纳率，不能只看 DAU。\n写在最后 # 回头看，传统 PRD 和 Agent PRD 最根本的区别在于：传统 PRD 描述的是系统在不同状态下的表现，Agent PRD 描述的是系统在不同判断下的行为。一个是静态的页面和流程，一个是动态的意图和决策。\n想通这件事之后，写法自然就变了。如果你也在做 Agent 产品，不妨拿自己的 PRD 检查一下：意图拆清楚了吗？工具调用的判断条件写了吗？边界条件覆盖全了吗？如果有一个答不上来，问题大概不在文档，而在产品设计本身还没想透。\n参考文献 # Prompt Engineering Guide Claude Agents and Tools Building Effective Agents Mermaid ","date":"2025-01-12","externalUrl":null,"permalink":"/posts/2025/01/agent-prd-writing/","section":"所有文章","summary":"","title":"写 Agent PRD 踩过的坑，以及我现在的写法","type":"posts"},{"content":"","date":"2024-12-15","externalUrl":null,"permalink":"/tags/ltsc/","section":"标签","summary":"","title":"LTSC","type":"tags"},{"content":"","date":"2024-12-15","externalUrl":null,"permalink":"/en/categories/tinkering/","section":"Categories","summary":"","title":"Tinkering","type":"categories"},{"content":"","date":"2024-12-15","externalUrl":null,"permalink":"/tags/windows/","section":"标签","summary":"","title":"Windows","type":"tags"},{"content":" 前言 # 我的日常主力是 Mac，但手上还有一台 Linux 笔记本，跑了双系统：一边是 NixOS，另一边装了个 Windows 11 IoT LTSC。这台机器上的 Windows 用途比较轻量，主要处理一些 macOS 和 Linux 上不方便做的事情。\n为什么选 LTSC？我就是想要一个干净的 Windows。没有 Microsoft Store、没有 Copilot、没有一堆预装 UWP 应用，系统占用也比普通版低不少。如果你也喜欢从零开始定制系统，LTSC 是个不错的选择。\n这篇文章记录一下我的安装和配置过程，仅供参考。\n安装前要知道的事 # LTSC 和 IoT LTSC 怎么选 # LTSC（Long-Term Servicing Channel）1 是 Windows 企业版的特殊分支，面向对稳定性和连续性有极高要求的设备。普通 LTSC 提供 5 年支持，IoT LTSC 提供 10 年。功能上两者几乎一致，主要区别在于：\nIoT LTSC 支持数字权利许可证激活 IoT LTSC 默认不开启 BitLocker 加密 IoT LTSC 支持不满足 TPM 要求的设备安装 不过有一点需要注意：IoT LTSC 默认只有英文，安装完需要手动下载中文语言包。如果你嫌麻烦，普通 LTSC 自带多语言，装完直接是中文，省一步。\n备份数据 # 重装前备份好所有数据、软件配置和个人文件，这是老生常谈了。因为是双系统，我还要确保 NixOS 的分区和引导配置不被破坏，建议先记清楚分区布局。\n准备基础程序 # 提前下载好以下东西，避免装完系统之后网卡驱动都没有、连不上网的尴尬：\n以太网和 Wi-Fi 网卡驱动 系统激活工具（HEU2 即可） 下载和安装 # 下载镜像 # Windows 11 IoT LTSC 的官方下载渠道需要填写企业信息，流程比较繁琐。更方便的方式是通过 MAS 提供的直链下载3，免注册，直接拿到 ISO。\n制作启动盘 # 我习惯用 Ventoy4，把 ISO 直接扔进 U 盘就行，省得每次用 Rufus 写入。\n安装过程 # 进 PE 格式化目标分区，然后直接启动 Windows 镜像安装。安装过程没什么特别的，跟着向导走就行。双系统用户注意选对分区，别覆盖了 Linux 那边的数据。装完后如果 GRUB 引导被覆盖，用 NixOS 的安装 U 盘修复一下就行。\n系统配置 # 装完系统之后，才是真正折腾的开始。\n添加中文语言包 # IoT LTSC 默认只有英文，安装完进入系统设置，连接网络后下载中文语言包，把显示语言和区域格式都改过来。如果觉得这步麻烦，可以考虑装普通 LTSC，自带中文。\n禁用 Windows 保留存储 # Windows 默认会预留一部分磁盘空间给系统更新，如果不需要可以关掉：\nDISM.exe /Online /Set-ReservedStorageState /State:Disabled 删除休眠文件 # 如果不使用休眠功能，休眠文件会白白占用几个 GB 的磁盘空间。在管理员终端中执行：\npowercfg -h off 禁用 Windows Defender # Windows Defender 本身是不错的杀毒软件，但误报率太高，而且经常吃资源让风扇起飞。我用的是联想知识库提供的工具来禁用，Windows 10/11 通杀。不过禁用之后建议装一个靠谱的第三方杀毒软件，裸奔还是不太安全。\n卸载 Edge 浏览器 # Edge 本身是个还行的 Chromium 浏览器，但它实在太烦了：访问 ChatGPT 弹 Copilot 横幅、下载 Chrome 推 Edge 横幅、小组件还时不时冒出来。用 Remove-MS-Edge5 可以卸载，注意保留 WebView2，只选第一个选项卸载浏览器本体就行。\n更新 WebView2 # 很多第三方应用依赖 WebView26，卸载 Edge 之后记得更新到最新稳定版，从微软开发者网站下载离线安装包。\n安装 VC++ 运行库 # 大部分 Windows 应用都需要 VC++ 运行库，从微软官网下载 VC++ 2015-2022 合集安装。\n安装 Microsoft Store（可选） # 如果你需要 UWP 应用，用管理员权限打开 PowerShell 执行：\nwsreset -i 没有进度条，装完会有通知弹出来。\nWindows Update # 连接网络后先跑一次完整的系统更新。下载慢的话可以临时开启传递优化（P2P 加速），更新完记得关掉。\n禁用硬件驱动自动更新 # 建议在完成一次完整更新之后禁用驱动自动更新，这样显卡之类的驱动就不会被 Windows Update 覆盖，可以自己去官网下载 WHQL 认证版本安装。\n写在最后 # 整体折腾下来，IoT LTSC 给我的感觉就是干净和稳定。没有乱七八糟的预装应用，没有 Copilot 强推，IoT 版默认连 AI 相关组件都不带，系统资源占用也比普通版低。如果需要打游戏，安装对应的运行库和组件就行，LTSC 本身不会有什么限制。\n对我来说，从一个干净的系统开始，按自己的需求一步步装软件、做配置，这个过程本身就是折腾的乐趣所在。\n参考文献 # Windows 11 IoT LTSC 下载链接 - MAS Windows 11 Enterprise 官方介绍 LTSC 概述 - Microsoft Learn HEU KMS Activator - GitHub Remove-MS-Edge - GitHub Ventoy 官网 WebView2 - Microsoft Developer LTSC（Long-Term Servicing Channel）是 Windows 企业版的特殊分支。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nHEU 是一个 Windows 系统激活工具。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nMAS 是一个提供 Windows 系统下载链接的项目网站。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nVentoy 是一个制作启动盘的工具，支持直接复制 ISO 到 U 盘启动。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nRemove-MS-Edge 是一个用于卸载 Edge 浏览器的工具。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nWebView2 是微软提供的嵌入式 Web 控件，许多 Windows 应用依赖它来显示 Web 内容。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2024-12-15","externalUrl":null,"permalink":"/posts/2024/12/windows-11-iot-ltsc-guide/","section":"所有文章","summary":"","title":"Windows 11 IoT LTSC 安装指南","type":"posts"},{"content":"","date":"2024-12-15","externalUrl":null,"permalink":"/categories/%E6%8A%98%E8%85%BE%E8%AE%B0%E5%BD%95/","section":"分类","summary":"","title":"折腾记录","type":"categories"},{"content":"","date":"2024-11-06","externalUrl":null,"permalink":"/tags/macos/","section":"标签","summary":"","title":"MacOS","type":"tags"},{"content":"","date":"2024-11-06","externalUrl":null,"permalink":"/tags/mlx/","section":"标签","summary":"","title":"MLX","type":"tags"},{"content":" 前言 # 最近有音频转录的需求，正好刷到 Awni Hannun 发的一条推文，最新的 MLX Whisper 速度更快了，M2 Ultra 上 12.3 秒就能转录 12 分钟的音频，接近 60 倍实时速度：\nThe latest MLX Whisper is even faster.\nWhisper v3 Turbo on an M2 Ultra transcribes ~12 minutes in 12.3 seconds. Nearly 60x real-time.\npip install -U mlx-whisper pic.twitter.com/DcKE0TRcbv\n\u0026mdash; Awni Hannun (@awnihannun) November 1, 2024 刚好手上有 Mac，就想试试这个方案。研究了一下发现用起来非常简单，记录一下过程。\n安装 # MLX Whisper 是基于 Apple MLX 框架的 Whisper 实现，跑在 Apple Silicon 上效率很高。安装方式有两种。\n常规方式 # pip install -U mlx-whisper 用 uv 安装 # 我个人有点系统洁癖，不太喜欢往全局环境里装东西。uv 是一个 Python 包管理工具，它的 uv tool install 可以把命令行工具安装到隔离环境中，不会污染系统 Python。如果你也有类似习惯，推荐这种方式：\nuv tool install mlx-whisper 安装完之后 mlx_whisper 命令就可以直接用了。\n使用 # 命令行直接用 # mlx_whisper audio.mp3 --model mlx-community/whisper-large-v3-turbo 写个脚本批量处理 # 我写了一个小脚本，支持一次传入多个音频文件，转录完会自动保存为同名的 .txt 文件：\nimport sys import os import mlx_whisper if len(sys.argv) \u0026lt; 2: print(\u0026#34;用法: python whisper.py \u0026lt;音频文件\u0026gt; [音频文件...]\u0026#34;) sys.exit(1) model = \u0026#34;mlx-community/whisper-large-v3-turbo\u0026#34; for file in sys.argv[1:]: print(f\u0026#34;正在转录: {file}\u0026#34;) result = mlx_whisper.transcribe(file, path_or_hf_repo=model) output = \u0026#34;\\n\\n\u0026#34;.join(seg[\u0026#34;text\u0026#34;].strip() for seg in result[\u0026#34;segments\u0026#34;]) txt_path = os.path.splitext(file)[0] + \u0026#34;.txt\u0026#34; with open(txt_path, \u0026#34;w\u0026#34;, encoding=\u0026#34;utf-8\u0026#34;) as f: f.write(output + \u0026#34;\\n\u0026#34;) print(f\u0026#34;已保存: {txt_path}\u0026#34;) print() 用法很简单，把音频文件拖进去就行。因为我是用 uv 管理的，脚本也直接用 uv 运行，--with 会自动临时安装依赖：\nuv run --with mlx-whisper python whisper.py 音频1.mp3 音频2.m4a 模型存储位置 # 模型会通过 Hugging Face Hub 自动下载，默认缓存在：\n~/.cache/huggingface/hub/models--mlx-community--whisper-large-v3-turbo/ 如果以后不想用了，直接删掉这个目录就行，不留残余。\n小结 # MLX Whisper 配合 Apple Silicon 确实很快，基本就是丢个文件进去等几秒出结果。安装和清理也都很干净，推荐有 Mac 的朋友试试。\n参考文献 # uv - Astral 出品的 Python 包管理工具 ","date":"2024-11-06","externalUrl":null,"permalink":"/posts/2024/11/mlx-whisper-mac-transcription/","section":"所有文章","summary":"","title":"MLX Whisper：Mac 极速音频转录","type":"posts"},{"content":"","date":"2024-11-06","externalUrl":null,"permalink":"/tags/python/","section":"标签","summary":"","title":"Python","type":"tags"},{"content":"","date":"2024-11-06","externalUrl":null,"permalink":"/tags/whisper/","section":"标签","summary":"","title":"Whisper","type":"tags"},{"content":"","date":"2024-09-15","externalUrl":null,"permalink":"/en/tags/fine-tuning/","section":"Tags","summary":"","title":"Fine-Tuning","type":"tags"},{"content":"","date":"2024-09-15","externalUrl":null,"permalink":"/en/tags/large-language-model/","section":"Tags","summary":"","title":"Large Language Model","type":"tags"},{"content":"","date":"2024-09-15","externalUrl":null,"permalink":"/tags/lora/","section":"标签","summary":"","title":"LoRA","type":"tags"},{"content":"","date":"2024-09-15","externalUrl":null,"permalink":"/tags/%E5%A4%A7%E6%A8%A1%E5%9E%8B/","section":"标签","summary":"","title":"大模型","type":"tags"},{"content":" 前言 # 最近在做项目时需要微调一个大模型，在选型过程中对比了全参数微调和 LoRA，顺便把微调相关的实战经验梳理了一遍。大模型开箱即用听起来很美好，但实际落地时会发现预训练模型只会续写，不会对话。要让模型真正按指令办事，指令微调（SFT）是绕不过去的一步。这篇文章就把微调的核心原理、方案选型和一些实战经验整理出来，希望能帮到同样在做模型落地的同学。\n预训练模型为什么不够用 # 先搞清楚一个前提：预训练后的模型只会续写，不会对话。\n这不是 bug，是设计如此。预训练的目标就是让模型学会「给定上文，预测下一个字」。所以你输入「今天天气真好」，它接一句「适合出去散步」。续写能力很强，但不会「听指令办事」1。\n打个比方，预训练就像让一个人读了万卷书，知识渊博，但没有人教过他怎么跟人对话。你问他「能帮我总结一下吗？」，他的反应可能是「好啊好啊，你给我看呗」，而不是真的动手去总结。因为他根本不知道「总结」是一个需要执行的指令，而不是一句客套话。\nSFT（Supervised Fine-Tuning，有监督微调）就是用有标签的指令数据对模型进行训练，让它学会理解和执行人类指令。没有这一步，模型就是一个高级续写器；有了这一步，模型才是一个真正的助手。\n而且 SFT 还有一个意外惊喜：泛化能力。你用摘要、情感分析、翻译三种指令数据训练模型，它可能突然就会做推理和改写了。这种「举一反三」的能力，是大模型最迷人的地方。\n全参数微调 vs 参数高效微调 # 微调方式主要分两大阵营：\n类型 说明 适用场景 全参数微调 对模型所有参数进行微调 高敏感场景（医疗、金融等），预算充足 参数高效微调 只微调部分参数 一般性任务，预算有限 在参数高效微调里，又有几个具体方案：\n方案 特点 推荐程度 LoRA 效果最稳定，最接近全参数微调 首选推荐 Prompt Tuning 效果不太稳定 不推荐首选 Prefix Tuning Prompt Tuning 的一种变体 不推荐首选 Adapter 推理会变慢 不推荐首选 LoRA 的核心原理 # LoRA（Low-Rank Adaptation）的核心思想很优雅：冻结预训练模型的原始权重，只在每一层旁边加一个低秩分解矩阵来学习增量变化 2。\n具体来说，假设模型某一层的权重矩阵是 W，LoRA 把它改写成：\nW\u0026#39; = W + ΔW = W + B × A 其中 B 是 d×r 的矩阵，A 是 r×d 的矩阵，r 远小于 d。训练时只更新 A 和 B，原始权重 W 保持不变。这样需要训练的参数量从 d² 降到 2×d×r，大幅减少。\n打个比方，全参数微调就像把一个员工送回学校重新读一遍学位，什么都重新学一遍，效果当然好但成本极高。LoRA 微调就像给员工安排一个为期两周的专项培训班，只学跟岗位直接相关的技能，大部分情况下够用了 3。\n# 以 LLaMA-Factory 为例，使用 LoRA 微调的典型配置 llamafactory-cli train \\ --model_name_or_path meta-llama/Llama-2-7b-hf \\ --finetuning_type lora \\ --lora_rank 8 \\ --lora_target q_proj,v_proj \\ --dataset alpaca_zh \\ --output_dir ./output/lora_model 一句话总结：有钱任性可以全参数微调；预算不足优选 LoRA，效果几乎接近全参数微调。\n三条实战经验 # 理论讲完了，来看一些经过验证的实战经验。以下经验来自一个翻译大模型项目的实际测试结果，每一条都挺反直觉的。\n经验一：示例数量宁少勿多 # 很多人直觉上觉得，给模型的示例越多，效果越好。实际测试结果直接打脸：\n方案 效果 无示例（零样本） 输出不稳定 1 个示例（单样本） 输出稳定，效果最佳 多个示例（多样本） 性能不再提升，反而可能下降 示例宁少勿多，加一个示例效果最好。多了不仅没用，还可能干扰模型。这就像教一个人做菜，给一份食谱他能照着做，给十份食谱他反而不知道该听谁的了 4。\n经验二：数据质量远胜于数据数量 # 这条经验更加反直觉。在这个翻译项目中，团队从 20 万条中英句子中按质量筛选出前 25%（约 5 万条），用这 5 万条高质量数据训练的模型，效果竟然比用全部 20 万条数据训练的更好。\n原因很简单：全量数据中包含大量低质量样本，这些「噪声」反而把模型带偏了。\n所以做微调项目的时候，数据采集的第一步不应该是「找更多数据」，而应该是「定义什么是好数据」。先定好质量标准，按标准筛选，宁缺毋滥 5。\n经验三：模型参数规模与成本的权衡 # 不同参数规模的训练成本差异巨大：\n模型参数 硬件资源 训练时间 38B 128 张昇腾芯片 11 天 2.6B 8 张 A100 显卡 9 天 模型规模仍然有用，但必须考虑成本。高敏感场景（医疗、金融）用大模型加全参数微调；一般性任务（客服、内容生成）用小模型加 LoRA，成本低、迭代快。不是所有场景都需要请「大牛」出马，合适才最重要。\n跨语言微调的一个经典思路 # 假设你的模型只懂中英文，现在需要让它学会一种新的语言（比如泰语），而泰语数据少，词表中根本没有泰语词。一个泰语句子被切分成一堆乱七八糟的片段，模型完全看不懂。\n怎么解决？一个经过验证的方案是扩展词表加增量微调，具体分四步：\n训练目标语言的分词器，让系统先认识这种语言的基本单位 将新语言的词加入原 Token 词表，给模型的「字典」里加新词 用混合数据做增量微调，让模型在已有知识基础上补充学习 使用 LoRA 进行高效参数微调，低成本完成最后的适配 效果对比非常明显。优化前，一个句子被切分成一堆毫无意义的片段；优化后，同一句子被合理切分为 21 个 Token，模型终于「看懂」了。\n这个思路可以推广到更多场景。比如你的模型要支持某个垂直行业（医疗、法律），可以先往词表里补充行业专有术语，再用行业数据做增量微调。不必推倒重来，在已有能力的基础上做加法就行。\n评估体系：别只看学术指标 # 最后说一个容易被忽视的点：评估方式。\nBLEU、ROUGE 这些学术指标只能做参考，它们衡量的是文本表面的相似度，不代表实际使用效果。一个翻译结果 BLEU 分数很高，但读起来可能完全不自然。一个摘要 ROUGE 分数很高，但可能遗漏了关键信息。\n真正有效的评估方式是人工测评加 A/B 测试。线上部署两套模型，让真实用户来评判哪个更好。效果才是真理，用户满意才是最终的评价标准。\n预训练模型本质上是一个条件语言模型，P(xt | x_1, \u0026hellip;, x{t-1})。它擅长的是概率意义上的文本续写，而不是理解指令意图。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nLoRA 论文由 Hu 等人于 2021 年发表，全称「LoRA: Low-Rank Adaptation of Large Language Models」。论文证明在多数任务上，秩 r=4 或 r=8 的 LoRA 就能达到接近全参数微调的效果。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n参数量的差异是巨大的。以 7B 模型为例，全参数微调需要训练 70 亿个参数，而 LoRA（r=8）通常只需要训练不到 1% 的参数。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n这种现象在 Few-Shot Learning 的研究中也有类似发现，被称为「demonstration sensitivity」，即模型对示例的选择和数量非常敏感。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n「LIMA: Less Is More for Alignment」这篇论文也验证了这一结论：仅用 1000 条高质量数据训练的模型，效果可以媲美用数十万条数据训练的模型。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2024-09-15","externalUrl":null,"permalink":"/posts/2024/09/llm-fine-tuning-practice/","section":"所有文章","summary":"","title":"大模型微调实战：全参数微调与 LoRA 的选择","type":"posts"},{"content":"","date":"2024-09-15","externalUrl":null,"permalink":"/tags/%E5%BE%AE%E8%B0%83/","section":"标签","summary":"","title":"微调","type":"tags"},{"content":"","date":"2024-06-10","externalUrl":null,"permalink":"/en/tags/networking/","section":"Tags","summary":"","title":"Networking","type":"tags"},{"content":" 前言 # 在 2022 年 1 月份，我在小黄鱼上购入了一台搭载 J4125 处理器的 2.5G 软路由，从此开启了我的折腾之旅。2 月份猫棒开始流行，原本打算也跟风购买一个来替代家里的光猫，后来听说发热量比较大，温度过高后就会断流，于是就打消了念头。\n2024 年的上半年，中兴的 F7015tv3/F7005tv3 突然火了起来，有着很低的功耗，带有一个 2.5G 网口、一个 IPTV 口、两个千兆网口。两款光猫的区别在于 F7015tv3 多了一个电话口。由于家里宽带送了电话（虽然一年也接不到几个电话），最后在小黄鱼上购买了 F7015tv3，到手后发现是真的小巧，跟我的 iPhone 13 mini 一样长。\n硬件信息 # # cat /proc/capability/boardinfo system:LINUX cpufac:ZXIC cpumod:ZX279133 2gwlmod:MTK 5gwlmod:MTK cpufre:1000MHZ cpunum:2 flshcap:256MB ddrcap:512MB 准备工作 # 打开老光猫的后台，使用超级管理员账户登录。这里提供北京联通的登录方法：打开 192.168.1.1，按下 F12 键，在 Console 里输入以下内容。\ndocument.getElementById(\u0026#34;loginfrm\u0026#34;).setAttribute(\u0026#34;method\u0026#34;, \u0026#34;get\u0026#34;); document.getElementById(\u0026#34;username\u0026#34;).value = \u0026#34;CUAdmin\u0026#34;; document.getElementById(\u0026#34;password\u0026#34;).value = \u0026#34;CUAdmin\u0026#34;; document.getElementById(\u0026#34;loginfrm\u0026#34;).submit(); submitFrm(); 记录一下 SN/MAC 地址、设备号以及连接配置 VLAN 等信息（截图或拍照保存下来）。\n配置新光猫 1 # 使用光猫的超级账户 useradmin，密码 nE7jA%5m 登录后台，点击管理 → 上行方式，切换 XGPON 或者 XEPON，这里按照自己的宽带类型操作。\n开启光猫的 Telnet2，电脑网卡的 MAC 地址要修改为 000729553357，然后执行下面的命令，提示 reboot 重启后等待光猫自动重启即可。\nzteOnu --telnet 删除原有的网络配置： sendcmd 1 DB p WANC sendcmd 1 DB delr WANC 0 sendcmd 1 DB delr WANC 1 有几个删几个，修改 WANC 后的数字即可。\n新建网络连接，这里按照旧光猫上的配置操作即可。\n使用 Telnet 修改光猫参数。setmac show2 查看系统参数。\n切换区域 # # cat /etc/init.d/regioncode 200:Jiangsu 201:Xinjiang 202:Hainan 203:Tianjin 204:Anhui 205:Shanghai 206:Chongqing 207:Beijing 208:Sichuan 209:Shandong 210:Guangdong 211:Hubei 212:Fujian 214:Zhejiang 215:Shanxi 216:Hunan 217:Yunnan 218:Xizang 219:Heilongjiang 220:Guizhou 221:Shanxi2 222:Hebei 223:Ningxia 224:Guangxi 225:Jiangxi 226:Gansu 227:Qinghai 229:Liaoning 230:Jilin 231:Neimeng 232:Henan 234:TelecomInstitute # 切换北京区域 upgradetest sdefconf 207 修改 MAC 地址 # setmac 1 32769 12:34:56:78:90:12 修改设备码前 6 位 # setmac 1 768 xxxxxx 修改设备码后 17 位数字 # setmac 1 512 xxxxxxxxxx ITMS 欺骗 # sendcmd 1 DB set PDTCTUSERINFO 0 Status 0 sendcmd 1 DB set PDTCTUSERINFO 0 Result 1 sendcmd 1 DB save 修改完毕后，连接光纤，重启即可。\n参考文献 # 北京联通光猫改 MAC 改地区教程 zteOnu 工具 使用超级账户登录光猫后台，点击管理 → 上行方式切换 XGPON 或 XEPON，具体操作按宽带类型决定。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n开启光猫的 Telnet 需要将电脑网卡 MAC 地址修改为 000729553357，然后执行 zteOnu --telnet 命令。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2024-06-10","externalUrl":null,"permalink":"/posts/2024/06/beijing-unicom-2.5g-modem/","section":"所有文章","summary":"","title":"记一次北京联通更换 2.5G 光猫","type":"posts"},{"content":"","date":"2024-06-10","externalUrl":null,"permalink":"/tags/%E7%BD%91%E7%BB%9C/","section":"标签","summary":"","title":"网络","type":"tags"},{"content":"","date":"2024-05-20","externalUrl":null,"permalink":"/en/tags/data-annotation/","section":"Tags","summary":"","title":"Data Annotation","type":"tags"},{"content":"","date":"2024-05-20","externalUrl":null,"permalink":"/en/tags/data-quality/","section":"Tags","summary":"","title":"Data Quality","type":"tags"},{"content":"","date":"2024-05-20","externalUrl":null,"permalink":"/tags/%E6%95%B0%E6%8D%AE%E6%A0%87%E6%B3%A8/","section":"标签","summary":"","title":"数据标注","type":"tags"},{"content":" 前言 # 之前一直觉得算法模型是 AI 的核心，后来才发现数据才是真正决定效果的天花板。看过不少项目，算法团队用的是同一套开源模型，但因为数据准备的差距，最终效果天差地别。这让我开始认真审视数据这件事，从标注规范到知识图谱，越深入越觉得这里面的门道远比想象中多。这篇文章把我的理解整理下来，算是给\u0026quot;数据驱动 AI\u0026quot;这个命题一个比较完整的回答。\n数据标注：远不止\u0026quot;打个标签\u0026quot; # 简单的标注任务确实没什么门槛。判断一条评论是正面还是负面，标注人员看一眼就能决定，可能连文档都不需要。\n但复杂场景就完全不同了。比如你在做一个医疗问诊系统，需要标注症状与疾病的对应关系。这不是凭直觉就能搞定的事，你需要输出一份完整的标注文档，里面至少包括这些内容：\n标注目标：要标注什么？为什么这么定义？ 标注规范：什么情况标 A，什么情况标 B，边界 case 怎么处理？ 审核流程：标注完怎么质检？抽检比例多少？不一致的怎么仲裁？ 标注文档的质量直接决定训练数据的质量，而训练数据的质量直接决定模型的上限1。这一点怎么强调都不为过。\n微调数据：要学会\u0026quot;看表\u0026quot; # 大模型微调过程中，有一项容易被忽视但非常关键的工作：检查微调数据的 Schema。具体来说就是检查用户意图的标注是否和意图体系一致。\n举个例子：意图体系里有\u0026quot;贷款提前还款\u0026quot;这个类目，但标注数据里出现了大量\u0026quot;想提前结清贷款\u0026quot;\u0026ldquo;能不能少还点\u0026quot;\u0026ldquo;提前还款怎么操作\u0026quot;被标成了不同的意图。这就是标注不一致，需要及时纠正。\n另外就是数据质量评判。数据有没有异常值？有没有大量缺失？有没有重复样本？这些看似是数据工程师的活，但如果你连数据质量都判断不了，就没办法确保模型训练出来的效果能达到预期。\n一句话：数据质量直接决定模型性能。 这不是口号，是铁律。\n数据驱动的模型迭代 # 很多人有个误区，觉得模型上线就万事大吉了。恰恰相反，模型上线才是迭代的起点。\n模型在真实环境中的表现和测试环境往往差异很大。你需要通过数据来衡量实际效果，找到问题所在，指导下一步迭代。具体来说，要把模型表现拆解成可量化的指标：\n用户意图识别准确率：模型有没有正确理解用户意图？ 转人工率：用户是不是因为模型回答不好才转人工的？转人工率有没有下降？ 错误意图占比：哪些意图被频繁识别错误？是有规律的还是随机的？ 举个实际例子。假设你负责一个银行贷款智能客服产品，上线一周后数据看板显示\u0026quot;还款方式变更\u0026quot;意图的识别准确率只有 65%，远低于整体平均的 85%。深挖数据发现，大量用户说\u0026quot;我想把贷款转成等额本息\u0026rdquo;，模型没有把它识别为\u0026quot;还款方式变更\u0026quot;意图。\n找到问题后怎么办？针对性的数据补充和策略调整：增加这类表述的训练样本，或者在意图识别前加一层同义词映射。\n这就是数据驱动迭代。不是拍脑袋说\u0026quot;我觉得效果不好\u0026rdquo;，而是用数据精确告诉你哪里不好、差多少、怎么改2。\n还有一个实操建议：构建数据看板。 不要每次都跑 SQL 拉数据，做一个研发、产品、运营都能看的看板，让各方对齐目标。数据看板不是为了汇报好看，而是为了让所有人都基于同一组数据做决策。\n数据颗粒度：AI 分析精度的决定因素 # 这是最容易被忽略的一点。\n为什么很多企业落地 AI 效果很差？不是因为模型不够强，而是因为数据颗粒度不够细。\n某金融机构想用大模型分析贷款审批的瓶颈。部门负责人说：\u0026ldquo;我们上 AI，让大模型告诉我们怎么提升贷款审批效率。\u0026ldquo;结果大模型给出的答案是：\u0026ldquo;建议更换风控团队负责人。\u0026rdquo;\n能怪大模型吗？不能。因为喂给模型的数据只有\u0026quot;贷款审批平均耗时增加 15%\u0026ldquo;这种粗粒度信息。模型能分析出的结论只能是宏观层面的\u0026quot;风控流程有问题\u0026rdquo;。\n要让模型给出有价值的分析，你需要什么粒度的数据？\n具体是哪个贷款产品、哪个审批环节、哪个风控节点出了问题？ 客户资质如何？授信额度是多少？担保方式是什么？ 什么时候开始出现审批积压？持续了多久？ 风控参数（逾期阈值、征信评分、负债率）有没有波动？ 数据颗粒度越细，模型分析出来的结论就越精准。当你有了上述细粒度数据，大模型才有可能告诉你：\u0026ldquo;个人消费贷的三方审批环节在 2 月 15 日后平均耗时上升 12%，原因是该批次客户征信评分普遍偏低导致人工复核增多，建议优化自动审批规则或调整征信评分阈值。\u0026rdquo;\n企业数据基础是使用 AI 的前提条件3。很多企业连数据都没沉淀好，就急着上 AI，结果自然是\u0026quot;垃圾进、垃圾出\u0026rdquo;。\n知识图谱：让 AI 输出\u0026quot;靠谱\u0026quot;的结构化利器 # 聊到数据能力，就不能不提知识图谱。\n大模型的幻觉问题 # 大模型是从互联网公开数据训练出来的。这些数据有准确的也有错误的，有新的也有过时的。这就导致大模型有时候会一本正经地胡说八道，学术上叫\u0026quot;幻觉\u0026rdquo;（Hallucination）4。\n闲聊场景下幻觉顶多是闹个笑话。但在医疗、金融等高风险场景中，幻觉是不能容忍的。不准确的答案可能导致严重的健康问题甚至生命危险。\n知识图谱如何解决幻觉 # 知识图谱本质上是一个结构化的知识库。它不是让模型自由发挥，而是给模型画了一条\u0026quot;围栏\u0026quot;，只能在已知的事实关系里做推理。在实际应用中，知识图谱通常和 RAG 结合使用，也就是常说的 GraphRAG5。\n用医疗场景理解知识图谱 # 假设有一个医疗知识图谱，包含以下关系：\n药品与疾病的关系 药品与成分的关系 成分与器官的关系 药品与副作用的关系 症状与疾病的关系 用户问：\u0026ldquo;高血压患者服用波罗菲安全吗？\u0026rdquo;\n没有知识图谱的情况下，大模型可能根据训练数据中零散的信息给出模糊的回答，甚至编造不存在的事实。\n有知识图谱的情况下，推理链是这样的：\n波罗菲 → 释放化氧化酶 → 影响前列腺 → …… → 损伤肾 → 导致肾病 结合大模型的推理能力和语言生成能力，系统最终给出准确的风险提示：高血压患者服用波罗菲存在肾脏损伤风险，不建议使用。\n知识图谱在这里发挥了三个关键作用：\n精准关联复杂关系：从药物成分到器官影响，每一步都有据可查。 支持多跳推理：不需要人工预设\u0026quot;A 和 B 有关系\u0026quot;，系统能自动从 A 推到 B，再从 B 推到 C，直到得出结论。 提升可解释性：不是告诉用户一个结论，而是展示完整的推理链路，增强信任感。 知识图谱的应用远不止医疗 # 社交领域：人与人之间的关系网络，预测隐藏关系，推荐潜在人脉。 公安系统：通过通话记录、位置信息等数据构建图谱，用于案件分析。 教育领域：知识点之间的关系图谱，用于个性化学习路径推荐。 税务领域：发票流转分析，构建知识图谱，识别逃税漏税行为。 知识图谱的六个能力维度 # 想用好知识图谱，至少要了解它的六个维度：\n知识建模：定义图谱的结构，有哪些实体、哪些关系。 知识抽取：从非结构化数据中提取实体和关系。 知识融合：把来自不同源的知识合并，解决冲突。 知识可视化：让复杂的图谱关系直观可理解。 知识计算：基于图谱做推理和分析。 知识应用：把图谱能力落地到具体的产品场景中。 不需要每个维度都精通，但至少要知道每个维度在做什么、为什么重要，这样才能在项目中做出正确的决策。\n业界有一句广泛流传的话：\u0026ldquo;Garbage in, garbage out.\u0026rdquo; 数据质量是机器学习效果的上限，算法只是在逼近这个上限。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n数据驱动迭代的思路和传统互联网产品的 A/B 测试一脉相承，核心都是用数据替代主观判断。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nMcKinsey 2023 年的报告指出，企业 AI 落地最大的障碍不是技术，而是数据基础设施的成熟度。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n大模型幻觉（Hallucination）是当前 LLM 领域最活跃的研究方向之一，根治方案目前仍在探索中。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nGraphRAG 是微软在 2024 年提出的方法，将知识图谱与检索增强生成结合，显著提升了复杂问答的准确性。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2024-05-20","externalUrl":null,"permalink":"/posts/2024/05/data-annotation-knowledge-engineering/","section":"所有文章","summary":"","title":"数据标注与知识工程：AI 的隐形基础设施","type":"posts"},{"content":"","date":"2024-05-20","externalUrl":null,"permalink":"/tags/%E6%95%B0%E6%8D%AE%E8%B4%A8%E9%87%8F/","section":"标签","summary":"","title":"数据质量","type":"tags"},{"content":"","date":"2024-03-15","externalUrl":null,"permalink":"/en/tags/classification-algorithms/","section":"Tags","summary":"","title":"Classification Algorithms","type":"tags"},{"content":"","date":"2024-03-15","externalUrl":null,"permalink":"/en/tags/intent-recognition/","section":"Tags","summary":"","title":"Intent Recognition","type":"tags"},{"content":"","date":"2024-03-15","externalUrl":null,"permalink":"/tags/nlp/","section":"标签","summary":"","title":"NLP","type":"tags"},{"content":"","date":"2024-03-15","externalUrl":null,"permalink":"/tags/%E5%88%86%E7%B1%BB%E7%AE%97%E6%B3%95/","section":"标签","summary":"","title":"分类算法","type":"tags"},{"content":"","date":"2024-03-15","externalUrl":null,"permalink":"/tags/%E6%84%8F%E5%9B%BE%E8%AF%86%E5%88%AB/","section":"标签","summary":"","title":"意图识别","type":"tags"},{"content":" 前言 # 最近在研究智能客服相关的技术方案，深入梳理之后发现，意图识别这件事远比想象中复杂。它不是一个简单的分类问题，而是一整套从单条 Case 分析到全局体系构建的工程方法论。这篇文章把我的思考整理出来，希望对同样在做对话式产品的朋友有帮助。\n意图识别到底是什么 # 先说一个核心观点：意图是目的，不是表达。\n举个生活中的例子。朋友约你吃饭，你不想去，说了句\u0026quot;改天吧\u0026quot;。你说的是\u0026quot;改天\u0026quot;，但你的目的是拒绝。如果对方追问\u0026quot;那改到哪天？\u0026quot;，你会觉得这人怎么这么不懂事。你明明是在拒绝，对方却在字面意思上较真。\nAI 产品的核心能力，就是根据用户的各种行为数据来推测意图。用户不会按你设计的模板说话，真实的表达往往极度口语化、随意、甚至充满情绪。一条用户消息，可能有四种甚至更多的解读方向，你需要有能力精准判断到底哪一个是正确的。\n我把意图分析浓缩成一个公式：\n意图分析 = 目的 + 承接动作\n这个公式有两层含义。第一，分析意图不是分析用户那句话本身，而是搞清楚两件事：用户有什么目的？用什么动作去承接更合适？第二，意图识别的最终目标是承接。每一类意图都应该对应一组具体的承接动作，如果识别了一个意图却没有任何动作可以接住它，那这个识别就毫无意义1。\n理解了这个公式，我们来看一个经典案例。\n\u0026ldquo;贷款审批怎么还没通过\u0026rdquo;：一个 Case 讲透意图分析 # 这是一个银行客服场景。用户发来一条消息：\n\u0026ldquo;我贷款审批怎么还没通过，都提交好几天了\u0026rdquo;\n拿到这条消息，用户的意图是什么？别急，先列出四种可能的解读。\n解读一：进度咨询。 用户只是在问审批进度，想知道还要等多久。如果系统识别为\u0026quot;进度咨询\u0026quot;，承接动作就是告知当前审批状态和预计完成时间。但结果呢？客服回复\u0026quot;您的贷款正在审批中，请耐心等待\u0026quot;，用户可能更焦虑了。我都等了好几天了，你就让我继续等？\n解读二：情绪宣泄。 用户明显有些焦虑和不耐烦，提交了好几天还没结果，心里不踏实。如果系统判断为情绪宣泄，走安抚路线。但光说\u0026quot;理解您的心情\u0026quot;有什么用？用户的核心诉求不是被安慰，而是想知道到底什么时候能批下来。\n解读三：紧急催办。 用户可能急需这笔贷款，等着用钱。走催办流程，联系审批部门加急处理。但问题来了，第一，有些贷款本身审批周期就长，催办也不一定能加速；第二，用户可能只是随口一问，并不真的急，盲目催办反而打乱审批节奏。\n解读四：贷款方案调整。 用户等了很久没通过，可能是因为当前贷款产品审批严格或额度受限。与其被动等待，不如主动帮用户评估是否可以更换贷款产品、调整贷款金额或期限。走贷款方案调整流程，用户要么获得更合适的方案，要么明确知道当前审批卡在哪里、还需要多久，同时客服可以给出具体的下一步建议。\n只有第四种解读，用户才会满意。\n对比一下四种解读的承接效果：\n解读 承接动作 用户满意度 进度咨询 告知审批状态 不满意，等了几天只得到\u0026quot;请耐心等待\u0026quot; 情绪宣泄 安抚话术 不满意，光安慰不能解决实际需求 紧急催办 催办审批部门 不满意，不一定能加速，且可能打乱审批节奏 贷款方案调整 方案调整流程 满意，用户获得更合适的方案或明确进度预期 这个案例说明了一个关键点：意图分析的核心不是分析文字表面，而是倒推。用什么承接动作能满足用户的需求，就去定义什么意图。 贷款方案调整流程能完美承接，因为它既解决了用户的实际需求（获得更合适的方案或明确进度），又能倒查审批卡点，安抚也更加具体、有依据。\n这就是意图分析公式的威力：根据承接动作倒推意图2。\n再来看两个练习场景：\n理财产品咨询： 用户问\u0026quot;你们那个年化 4% 的理财产品现在还能买吗？\u0026ldquo;如果识别为简单问答，查产品状态返回结果。如果产品确实在售，那还行。但如果已经售罄了呢？用户可能接下来就要把钱转到别的银行了。有没有更好的承接方式？比如识别为\u0026quot;理财需求咨询\u0026rdquo;，提前给用户推荐收益相近的替代产品。\n信用卡账单查询： 用户说\u0026quot;我这个月账单怎么这么多\u0026quot;，查账意图。查询流程怎么接更合适？是直接报总金额？还是先列明细再标注异常交易？不同承接方式，用户体验天差地别。\n意图标注：从单条 Case 到全局视图 # 单个 Case 会分析了，但现实是：算法同学不会只拿一条 Case 来找你。他们会问：这种意图在业务里占比多少？优化这个意图能解决多大的业务价值？\nAI 团队通常背负核心业务指标，压力很大。你拿出一个意图优化方案，但如果占比只有 0.5%，算法和业务团队根本不会配合你做。你必须找出占比最大的意图，优先优化，才能撬动资源。\n怎么知道各意图的占比？靠的就是意图标注，通过大规模评估画出意图的全景图。\n不夸张地说，做 AI 产品的人 30% 到 40% 的时间，其实都花在分析用户意图和各类评估上。评估涉及随机抽样、数据选取、标准制定等一系列方法论，一个环节出错，结论就全偏了。\n怎么做一次靠谱的评估 # 第一步：确定评估集。\n评估集就是用来做标注的数据集，这一步决定了整个评估的根基。几个关键点：\n规模要够，最少 1000 条。如果在大型商业银行、股份制银行这种量级的业务，最少 10000 条。达不到规模，随机数据可能缺失，占比就不能反映真实情况。\n时间要选对，一定要尽量模拟真实环境。去除大促、节假日等特殊时期的数据。大促期间用户需求量大但相对特殊，节假日咨询量可能极少。这些数据混进来，占比就会失真。不能模拟真实环境是非常致命的3。\n抽法有讲究。随机不去重、随机去重、分层随机，各有适用场景。在比较正常的环境下，随机不去重最能代表真实情况。为什么？因为真实环境中高频问题本来就会出现多次，去重反而歪曲了占比。\n对话条数要够。银行客服场景一般抽 3 到 5 条对话，保证有上下文。只抽一条可能没有上下文，根本看不懂用户在说什么。\n第二步：输出评估文档。\n评估文档不是摆设，它的目的是让所有参与评估的人知道\u0026quot;什么 Case 标什么意图，什么 Case 不标什么意图\u0026quot;。\n每种意图需要包含三部分：\n意图介绍： 用文字说清楚这个意图是什么。比如\u0026quot;贷款方案调整是用户因审批延迟、额度不满足等原因申请更换贷款产品或调整贷款条件的意图\u0026quot;。 代表问： 最能代表该意图的 3 到 5 种问法，包括 query 的方式。 相似问： 与代表问相关但不那么典型的问法，符合条件也归为该意图。 假设有 10 种意图，就把每种意图的这三部分都列出来。文档初期不完善是正常的，随着评估越来越多，不断补充和修正，文档会越来越完善。\n第三步：执行评估。\n参与人员方面，最少 2 到 3 人交叉评估。意图评估是主观判断的过程，不同人理解不同，所以必须交叉。更重要的是，算法同学和业务运营至少各有一人参与，这是拉齐 AI 项目中不同角色对意图认知的好机会4。\n计算方式上，每个独立 Case 评完意图后，每类 Case 的数量除以评估总数，就是该意图的占比。比如评估 1000 条，其中 100 条是贷款方案调整，那贷款方案调整占比 10%。\n最终产出就是一张意图全景图。这张图会让你心里有底，你到底在为什么样的用户做产品。\n评估中的三个大坑 # 做了很多次评估之后，总结了最容易踩的三个坑：\n坑一：随机取数逻辑有误。 这是最隐蔽的坑。每个人对\u0026quot;随机\u0026quot;的理解不一样，加上各种限定条件之后，取出来的数据可能完全不能代表真实分布。1000 条都评完了，结果占比是错的，白干。务必在取数之前确认随机逻辑。\n坑二：评估人不用心，质量低。 把评估任务分发下去，大家各自评各自的，拖到最后一天赶工，质量惨不忍睹。怎么解决？找一个下午，定一个会议室，所有人坐在一起评。有问题当场讨论，有分歧当场解决。这个投入绝对值得。\n坑三：评估标准不一致。 算法觉得应该标 A，业务觉得应该标 B，产品觉得应该标 C，三方争论不休。怎么办？个例先跳过。但当多个 Case 出现分歧时，必须有人站出来拍板给出方案。这是 AI 产品经理的责任，作为那个最理解用户的人，也是要为最终效果负责的人。\n建立意图体系：从混沌到结构 # 做完一轮标注，会面对一个现实问题：意图太多了。\n中等复杂度的智能客服业务，最少也有 200 到 300 个意图。大型复杂业务，比如大型商业银行、股份制银行这种级别，意图数量轻松过千。这么多的意图，如果不做组织和管理，后续的维护、迭代、模型训练都会非常痛苦。\n但为什么要建意图体系，很多人给出的理由其实只说对了一半：\n\u0026ldquo;分层之后增删改查更方便\u0026rdquo;，对，但不够本质。 \u0026ldquo;有一级大类、二级大类，新人更容易理解\u0026rdquo;，对，但也不够本质。 \u0026ldquo;能查漏补缺，发现缺失的意图\u0026rdquo;，对，还是不够本质。 真正的根本原因：提升识别准确率的容错能力 # 用一个例子说清楚。假设你在搜索一个精准地址：XX 城市 XX 区 XX 街道 XX 门牌号。\n方案 A： 搜索系统只认门牌号这一层。搜不到门牌号？返回空结果。用户得到的是一片空白。\n方案 B： 搜索系统有层级结构，城市、区、街道、门牌号。搜不到门牌号？返回街道级别的结果。用户到了街道，可以自己找、可以问路人、可以继续缩小范围。\n两种方案都没有完美满足\u0026quot;精准到门牌号\u0026quot;的需求，但方案 B 明显更好。因为它有容错能力。\n意图体系也是同一个道理：\n当意图能精准识别时，有没有体系、有没有父子结构，差别不大，模型直接命中，皆大欢喜。 但当意图不能精准识别时（这才是常态），有体系和无体系的差别巨大。没有体系，模型只能给出一个完全不相关的结果，也就是\u0026quot;答非所问\u0026quot;。有体系，模型至少能识别到父类意图，给出的结果虽然不精准，但方向是对的。 所以，建立意图体系的根本原因是提升识别准确率的容错能力。 凡是跟识别准确率有关系的事情，一定要做。\n另外，意图体系对后续做启发式对话（引导对话）也非常有用。有了层级结构，就能在识别不准时引导用户缩小范围，而不是直接给出一个不靠谱的答案5。\n搭建意图体系的三个原则 # 原则一：每个意图用代表问和相似问圈定范围。 在意图分析阶段，已经为每个意图梳理了代表问和相似问。在搭建体系时，这些代表问和相似问就是每个意图的\u0026quot;边界线\u0026quot;。范围的概念非常关键，代表问和相似问定义得越清晰，这个意图的边界就越明确。\n原则二：每个意图的边界要清晰。 当意图超过 100 个时，会明显感到某些意图之间非常相似、重叠部分很高。人都会搞混的情况下，模型识别时更容易把 A 误判为 B。一旦误判，B 对应的承接动作满足不了用户的需求，就会造成答非所问。在客服场景中，这是非常严重的事故。所以必须明确：这个意图就是管这一圈事情，那个意图就是管另外一圈事情，边界要划得清清楚楚。\n原则三：同类问题的不同情况归为同一层级。 比如：贷款提前还款、利率变更还款、逾期补缴，这三个看起来是三个不同的意图，但本质上是\u0026quot;还款异常处理\u0026quot;这个问题的三种不同情况。判断标准很简单：它们是不是同一个问题的不同种情况？如果是，归为同一层级。\n补充一点：意图层级越少越好。层级越多，管理和维护成本就越大。能两层解决的事情，不要搞三层。\n意图体系是长出来的，不是画出来的 # 一套意图体系不是坐在会议室里画个脑图就能定下来的。通常需要半年到一年的时间才能逐渐稳定。\n迭代过程中会不断遇到这些问题：标注标准不统一，同一类 Case 上周标 A 这周标 B；意图重复冗余，发现两个意图其实是一回事；意图遗漏，用户问了新问题，现有意图覆盖不了。\n这些都非常正常。意图体系本身就是一个极度复杂、需要持续迭代的过程。心态不要是\u0026quot;我要一次性设计完美\u0026quot;，而是\u0026quot;我先搭个框架，然后在实战中不断修正\u0026quot;。\n一条完整路径 # 把三个步骤串起来：\n用户那样问，TA 有什么目的，用什么动作去满足。 这是意图分析，解决单个 Case 的问题。\n现在有多少种意图，每种意图占比多少。 这是意图标注，解决全局认知的问题。\n哪些意图应该归在一起，层级结构长什么样。 这是意图体系，解决结构化管理的问题。\n三者串联起来，得到的就是一张完整的意图体系，也就是用户需求全景图。\n有了这张全景图之后，不管用传统方式建 QA 库做匹配，还是用生成式模型做 RAG，都有了坚实的地基。识别需求、满足需求、迭代优化，雪球就能滚起来。\n意图识别不是一个纯粹的技术问题，而是一个\u0026quot;你到底有多懂用户\u0026quot;的问题。你对用户的理解有多深，意图体系就有多靠谱，AI 产品就有多好用。\n在自然语言理解（NLU）领域，意图识别通常被建模为文本分类任务。但产品视角下，识别本身不是目的，找到正确的承接动作才是。这与传统的 accuracy-only 评估思路有很大区别。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n这种\u0026quot;从承接动作倒推意图\u0026quot;的思路，本质上是逆向工程用户需求。在推荐系统中类似的思路是\u0026quot;从目标行为反推特征重要性\u0026quot;。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n数据分布偏移（Distribution Shift）是机器学习中非常经典的问题。评估集与真实环境分布不一致，会导致离线指标很高但线上效果很差。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n交叉评估在标注领域叫做 Inter-Annotator Agreement（IAA），常用的指标有 Cohen\u0026rsquo;s Kappa 和 Fleiss\u0026rsquo; Kappa。IAA 分数越高，说明标注标准越一致。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n这种层级化的意图结构在学术界被称为 Hierarchical Intent，相关研究可以参考用于任务型对话系统的层次化意图建模方法。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2024-03-15","externalUrl":null,"permalink":"/posts/2024/03/intent-recognition-deep-dive/","section":"所有文章","summary":"","title":"意图识别技术拆解：从需求分析到算法落地","type":"posts"},{"content":"","date":"2024-01-15","externalUrl":null,"permalink":"/en/tags/vector-database/","section":"Tags","summary":"","title":"Vector Database","type":"tags"},{"content":" 前言 # 公司最近在筹划做智能客服，调研了一圈发现 RAG 这个词出现的频率非常高。企业知识助手、文档问答、智能客服，几乎都绕不开它。既然要用，就得先搞懂底层原理，于是我花时间把 RAG 的完整链路梳理了一遍，从数据准备到最终生成答案，涉及分片、Embedding、向量数据库、召回、重排等环节。这篇文章记录一下我的理解，希望对你也有帮助。\n为什么需要 RAG # 假设你想做一个智能客服，让它回答公司产品相关的问题。最直觉的做法是选一个大模型，然后把产品手册连同用户问题一起发给它。\n乍一看没问题，但实际落地时会遇到几个硬伤：\n上下文窗口有限：每个模型能接收的输入长度是有上限的。产品手册动辄上百页，塞不进去就是塞不进去，强行塞只会导致模型读了后面忘了前面，回答质量直线下降。 推理成本高：输入越长，token 消耗越多。每次提问都带上一整本手册，一天下来账单会很壮观。 推理速度慢：输入越多，模型需要消化的内容越多，用户等一个回答可能要等半天。 核心矛盾在于：我们希望模型看到所有相关信息，但又不能把所有信息都塞给它。那怎么办？\nRAG 的思路是：与其把整个文档都发给模型，不如只发和问题相关的部分。具体来说，先把文档切成多个片段，在用户提问后，从中检索出最相关的几段，再把这些片段连同问题一起交给大模型来生成答案。\nRAG 的整体流程 # RAG 的流程分为两大部分：\n数据准备阶段（用户提问前）：包含分片（Chunking）和索引（Indexing）两个环节。 回答阶段（用户提问后）：包含召回（Retrieval）、重排（Reranking）和生成（Generation）三个环节。 下面逐个拆解。\n1. 分片（Chunking） # 分片就是把一份长文档切分成多个小片段。切分方式有很多：按字数切、按段落切、按章节切、按页码切，甚至还有基于语义的智能切分。\n切分粒度是一个需要权衡的问题。片段太大，包含的无关信息就多，相当于把噪音也带进来了；片段太小，可能丢失上下文，模型看到的内容缺乏连贯性。实际项目中往往需要反复试验才能找到合适的粒度。\n2. 索引（Indexing） # 索引是整个数据准备阶段的核心。它做的事情只有两步：\n通过 Embedding 模型把每个片段文本转换成向量。 把片段文本和对应的向量一起存入向量数据库。 听起来简单，但要真正理解这两步，需要先弄清三个概念：向量、Embedding 和向量数据库。\n向量 # 向量是数学中的基本概念，表示一个既有大小又有方向的量。在程序里用一个数组来表示，数组中数字的个数就是向量的维度。\n一维向量画在一维坐标轴上，二维向量画在平面坐标系里，三维向量需要三维坐标系。但我们在 RAG 中使用的向量维度通常是几百甚至几千维，无法直观可视化。维度越高，向量能承载的语义信息越丰富，检索的准确性也越高。\nEmbedding # Embedding 是把文本转换成向量的过程。它的关键特性是：语义相近的文本，转换后得到的向量也相近。\n举个例子，假设用二维向量来表示：\n文本 向量 Python 是一门编程语言 (2, 3) 编程语言 Python 简介 (2, 2) 今天适合去爬山 (-3, -1) 前两个句子的向量非常接近，而第三个则离得很远。这正是因为前两个句子语义相近，而第三个完全不相关。\n有了这个特性，当用户问「Python 是什么」时，我们先把问题做 Embedding 转换成向量，然后通过向量相似度找到与之接近的其他向量，就能定位到相关的文本片段了。\n需要注意的是，Embedding 操作是由专门的 Embedding 模型完成的，不是 GPT-4 Turbo 这类生成式模型。MTEB 排行榜1对各种 Embedding 模型做了评测和排名，可以参考。\n向量数据库 # 向量数据库是专门用来存储和查询向量的数据库，针对向量存储做了优化，并提供了计算向量相似度等函数。\n值得强调的是，存入向量数据库的不仅有向量，还有原始文本。因为最终要交给大模型的是原始文本，向量只是检索过程中的中间产物。所以向量数据库中通常至少有两列：原始文本和对应向量。\n回到索引环节，整个流程就是：对所有分片后的片段逐一做 Embedding，然后把文本和向量一起写入向量数据库，直到所有片段处理完毕。\n3. 召回（Retrieval） # 召回是用户提问后的第一个环节，目的是从所有片段中找出与问题最相关的一批片段。\n流程如下：\n把用户的问题发给 Embedding 模型，转换为向量。 将这个向量发送给向量数据库。 向量数据库计算该向量与所有已存储片段向量的相似度，返回相似度最高的若干片段（比如 10 个）。 向量相似度计算 # 向量数据库是如何判断哪些片段与用户问题最相关的？答案是计算向量相似度。常见的计算方法有三种：\n余弦相似度：计算两个向量之间夹角的余弦值。夹角越小，相似度越高。 欧式距离：计算两个向量之间的直线距离。距离越小，相似度越高。 点积（Dot Product）：同时考虑向量的方向和长度。方向一致且长度越大，点积值越大，相似度越高。 向量数据库会把用户问题向量与每个片段向量逐一计算相似度，然后排序，返回前 N 个。\n4. 重排（Reranking） # 重排的作用是从召回阶段返回的片段中，进一步筛选出最相关的几个（比如 3 个）。\n你可能会问：既然最终只要 3 个，为什么不在召回阶段就直接取 3 个？为什么要分两步？\n原因在于两个阶段使用的相似度计算方式不同，准确率和成本的权衡也不同：\n阶段 计算方式 成本 耗时 准确率 作用 召回 向量相似度（余弦、欧式距离等） 低 短 较低 粗筛 重排 Cross Encoder 模型 高 长 高 精选 可以类比公司招聘：召回就像 HR 筛简历，从成千上万份简历中快速挑出 10 份看起来不错的；重排就像部门面试，对这 10 个候选人深入考察，最终选出最合适的 3 个。两轮筛选各司其职，效率和准确率都得到了保障。\nCross Encoder2 是一种专门用于文本对相似度判断的模型，它同时接收两个文本作为输入，直接输出相似度分数，准确率比单纯的向量相似度高很多，但计算成本也更高，所以只对少量候选片段使用。\n5. 生成（Generation） # 最后一步就水到渠成了。我们有了用户的问题，也有了经过重排筛选出的 3 个最相关片段，把这两部分组装成一个 Prompt 发给大模型（比如 GPT-4 Turbo），让它根据片段内容来回答用户的问题。\n这一步的 Prompt 工程也有一些技巧，比如如何组织上下文、如何引导模型只基于提供的片段来回答、如何处理「找不到答案」的情况等，不过这些属于进阶话题，本文先不展开。\n完整流程回顾 # 提问前的数据准备：\n将相关文档进行分片。 通过 Embedding 模型将每个片段转换为向量。 将片段文本和向量存入向量数据库。 提问后的回答流程：\n将用户问题通过 Embedding 模型转换为向量。 在向量数据库中查询与问题最相似的 10 个片段（召回）。 用 Cross Encoder 对这 10 个片段重新排序，选出最相关的 3 个（重排）。 将这 3 个片段连同用户问题一起发给大模型，生成最终答案（生成）。 这就是 RAG 的完整工作流程。理解了每个环节的原理，后续不管是选型 Embedding 模型、调优分片策略，还是优化检索效果，都会有更清晰的方向。\nMTEB（Massive Text Embedding Benchmark）是一个对 Embedding 模型进行综合评测的排行榜，涵盖分类、聚类、检索等多个维度。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nCross Encoder 是一种将两个文本拼接后 jointly 编码的模型结构，相比 Bi-Encoder（分别编码再计算相似度），它能捕捉更深层的语义关系，但无法预计算向量，因此速度较慢。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2024-01-15","externalUrl":null,"permalink":"/posts/2024/01/understanding-rag/","section":"所有文章","summary":"","title":"理解 RAG：智能客服背后的检索增强生成","type":"posts"},{"content":"","date":"2024-01-15","externalUrl":null,"permalink":"/tags/%E5%90%91%E9%87%8F%E6%95%B0%E6%8D%AE%E5%BA%93/","section":"标签","summary":"","title":"向量数据库","type":"tags"},{"content":"","date":"2023-12-03","externalUrl":null,"permalink":"/tags/disko/","section":"标签","summary":"","title":"Disko","type":"tags"},{"content":"","date":"2023-12-03","externalUrl":null,"permalink":"/tags/nixos/","section":"标签","summary":"","title":"NixOS","type":"tags"},{"content":" 前言 # NixOS1 是一个声明式配置的系统，整个系统都可以使用声明式的方式来配置。2023 年刚接触 NixOS 的时候，我还不知道 Disko2，系统分区等操作还是需要通过手动执行一些命令来完成。后来我发现了 tmpfs as root 这种玩法，对于我这种有严重强迫症的人来说是极好的，于是把本地的设备都用上了 tmpfs as root。体验非常好之后，我又想着把手里的服务器也换成 NixOS，于是就遇到了这个困扰我几个月的问题。\n由于我本地的设备都是 UEFI + systemd-boot 的组合，使用起来一直很正常。但云服务器一般都是 BIOS 启动的，systemd-boot 对于 BIOS 来说有一些问题3，最后选择了 BIOS + GRUB 的组合，这与我本地的配置大不相同。\n问题 # 当我执行 rebuild 后就会出现如下报错：\n... updating GRUB 2 menu... updating GRUB 2 menu... updating GRUB 2 menu... Failed to get blkid info (returned 512) for / on tmpfs at /nix/store/nvycxmg4g2q5jyqdxfvkgi95sqs48iw3-install-grub.pl line 212. warning: error(s) occurred while switching to the new configuration 在 GitHub 上搜索相关问题，尝试了很多次也没有解决。经过一个多月的摸索最终找到了解决办法。\n解决办法 # 编辑 hardware-configuration.nix 文件，添加如下代码：\nboot.loader.grub.enable = true; boot.loader.grub.efiSupport = true; boot.loader.grub.efiInstallAsRemovable = true; 保存后重新 rebuild，发现已经正常了。\n参考文献 # NixOS 官网 Disko - NixOS 分区工具 systemd/issues/25963 NixOS 是一个使用声明式配置的 Linux 发行版，整个系统都可以通过配置文件来管理。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nDisko 是 NixOS 上的一个分区管理工具，支持通过配置文件来定义分区布局。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nsystemd-boot 在 BIOS 模式下有一些兼容性问题，这是选择 GRUB 的原因。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2023-12-03","externalUrl":null,"permalink":"/posts/2023/12/nixos-bios-boot-using-disko-to-manage-partitions/","section":"所有文章","summary":"","title":"在 BIOS 启动的 NixOS 系统中使用 Disko 管理分区","type":"posts"},{"content":"","date":"2023-11-15","externalUrl":null,"permalink":"/tags/avif/","section":"标签","summary":"","title":"AVIF","type":"tags"},{"content":" 前言 # 在上一篇中，我介绍了 AVIF 格式以及它的使用方法。当时觉得这是一个非常有潜力的图片格式，压缩率很高，画质也很好。但在实际把博客图片替换为 AVIF 之后，我发现了不少兼容性问题，最终还是决定换回 WebP。\n这篇就来聊聊我为什么要换回 WebP。\n兼容性问题 # 先看 AVIF 在 Can I use 上的兼容性数据。\n可以看到，Edge 浏览器从 121 版本才开始支持 AVIF。即便到了这篇文章发表时，应该还有不少人没有更新到最新版浏览器。更麻烦的是手机端，大部分移动浏览器也是刚刚支持。比如 QQ 浏览器，如果在 QQ 或微信中打开博客，很有可能看不到图片。虽然我的博客图片并不多，但这也很影响阅读体验。\n其次是设备对 AV1 硬件解码的支持。在维基百科中可以查看各 CPU 和 SoC 的支持情况，这里列出一些常见的：\nPC 端 # Intel # 11 代及以后：从第 11 代 Tiger Lake 处理器开始，Intel 集成了 AV1 硬件解码支持。 AMD # Ryzen 6000 系列及以后：基于 Zen 3+ 架构，开始支持 AV1 硬件解码。 Apple Silicon # M3 及以后：M3、M3 Pro、M3 Max 开始支持 AV1 硬件解码1。 移动端 # Qualcomm # 从 Snapdragon 888 开始支持 AV1 硬件解码。 iOS # 从 A17 Pro 开始支持 AV1 硬件解码2。 吐槽 # 苹果果然是牙膏厂，2023 年底发布的芯片才支持 AV1 硬件解码。虽然可以通过软件解码来弥补，但前提是系统或浏览器已经更新支持。在不支持硬解的设备上走软件解码，会导致 CPU 占用过高、设备发热，在性能较弱的旧设备上还会出现卡顿。\nWebP 介绍 # WebP3 是由谷歌开发的一种图片格式，旨在加快图片加载速度。它的核心优势是体积更小，在质量相同的情况下，WebP 格式图像的体积比 JPEG 小约 40%，大约只有 JPEG 的三分之二，可以节省大量的服务器带宽和存储空间。\n同样在 Can I use 查看 WebP 的支持情况。\n相比 AVIF，WebP 的支持广泛得多。除了早已被微软放弃的 IE 浏览器（应该没有人在用了吧），主流浏览器全部支持。而且 WebP 已经存在多年，生态成熟，使用它基本不用担心兼容性问题。\n如何将图片转换为 WebP 格式 # 使用 FFmpeg4 即可完成转换：\n# JPEG → WebP ffmpeg -i input.jpg output.webp # PNG → WebP ffmpeg -i input.png output.webp 调整输出质量，使用 -qscale 参数（0–100，值越大质量越高，文件也越大）：\nffmpeg -i input.jpg -qscale 75 output.webp 调整压缩级别，使用 -compression_level 参数（0–6，值越大压缩率越高，速度越慢）：\nffmpeg -i input.jpg -compression_level 4 output.webp 组合使用，同时指定质量和压缩级别：\nffmpeg -i example.png -qscale 80 -compression_level 4 example.webp 参考文献 # WebP - 维基百科 AV1 硬件支持列表 - Wikipedia CPU/SoC AV1 支持列表 - Reddit Apple M3 芯片新闻稿 Apple A17 Pro AV1 支持讨论帖 WebP 官方介绍 FFmpeg 官方网站 苹果 M3 系列芯片发布时宣布支持 AV1 硬件解码\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n社区讨论确认 A17 Pro 支持 AV1 硬件解码\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n谷歌开发的图片格式，主打体积小\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n多平台多媒体编码工具\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2023-11-15","externalUrl":null,"permalink":"/posts/2023/11/avif-vs-webp/","section":"所有文章","summary":"","title":"AVIF 如此好，为什么我还是选择了 WebP","type":"posts"},{"content":"","date":"2023-11-15","externalUrl":null,"permalink":"/tags/ffmpeg/","section":"标签","summary":"","title":"FFmpeg","type":"tags"},{"content":"","date":"2023-11-15","externalUrl":null,"permalink":"/tags/webp/","section":"标签","summary":"","title":"WebP","type":"tags"},{"content":"","date":"2023-05-11","externalUrl":null,"permalink":"/tags/android/","section":"标签","summary":"","title":"Android","type":"tags"},{"content":"","date":"2023-05-11","externalUrl":null,"permalink":"/tags/arch/","section":"标签","summary":"","title":"Arch","type":"tags"},{"content":"","date":"2023-05-11","externalUrl":null,"permalink":"/tags/cloud/","section":"标签","summary":"","title":"Cloud","type":"tags"},{"content":"","date":"2023-05-11","externalUrl":null,"permalink":"/tags/docker/","section":"标签","summary":"","title":"Docker","type":"tags"},{"content":"","date":"2023-05-11","externalUrl":null,"permalink":"/tags/linux/","section":"标签","summary":"","title":"Linux","type":"tags"},{"content":"","date":"2023-05-11","externalUrl":null,"permalink":"/tags/ubuntu/","section":"标签","summary":"","title":"Ubuntu","type":"tags"},{"content":" 前言 # 当前市面上已经有很多云手机厂商，虽然比较稳定，但把数据全都放在别人手里感觉不是很安全。谁知道每次进行操作时厂商会不会进行录屏等行为呢。\nGithub1 上有一个开源项目 Redroid2，可以使用 Docker 运行安卓容器。我已经使用了很长时间，运行很稳定。\n准备环境 # 需要一台服务器，AMD64 和 ARM64 架构都可以。\n支持的系统如下，点击即可查看各个发行版的部署操作：\nAlibaba-Cloud-Linux Amazon-Linux Arch-Linux CentOS Debian Deepin Fedora Gentoo Kubernetes LXC Mint OpenEuler PopOS Ubuntu WSL 如果你是新手，推荐使用 Ubuntu、Arch Linux 或者 NixOS。其中 Ubuntu 需要加载内核模块，Arch Linux 和 NixOS 直接更换 zen 内核即可。\n操作部分 # 这里只介绍上面所说的三个操作系统，其他的系统可自行阅读官方文档。\nUbuntu # 建议不要使用最新的 Ubuntu，可能会有各种小问题。我测试 20.04 和 22.04 版本没有问题，执行下面命令加载内核模块：\nsudo apt install linux-modules-extra-`uname -r` sudo modprobe binder_linux devices=\u0026#34;binder,hwbinder,vndbinder\u0026#34; sudo modprobe ashmem_linux 使用一键脚本安装 Docker3：\ncurl -sSL https://get.docker.com/ | sh 或者使用包管理器安装 Docker 以及 Docker Compose：\nsudo apt install docker docker-compose Arch Linux # 安装 linux-zen 内核：\n# 更新系统 sudo pacman -Syu # 安装 linux-zen 内核 sudo pacman -S linux-zen linux-zen-headers # 更新 GRUB 引导配置 sudo grub-mkconfig -o /boot/grub/grub.cfg # 重启系统 sudo reboot # 验证内核版本，可以看到内核带有 zen 字样 uname -r 使用包管理器安装 Docker 以及 Docker Compose：\nsudo pacman -S docker docker-compose NixOS # 安装 linux-zen 内核：\n# 编辑 /etc/nixos/configuration.nix 增加一行 boot.kernelPackages = pkgs.linuxPackages_zen # rebuild 更新配置 sudo nixos-rebuild switch # 重启系统 sudo reboot # 验证内核版本，可以看到内核带有 zen 字样 uname -r 安装 Docker 以及 Docker Compose：\n# 编辑 /etc/nixos/configuration.nix 添加一行 virtualisation.docker.enable = true; # docker-compose environment.systemPackages = with pkgs; [ docker-compose ]; # rebuild 更新配置 sudo nixos-rebuild switch 运行 Redroid 容器 # 直接启动 # sudo docker run -itd --rm --privileged \\ --pull always \\ -v ~/data:/data \\ -p 5555:5555 \\ redroid/redroid:11.0.0-latest 使用 Docker Compose 启动 # # docker-compose.yaml version: \u0026#34;3\u0026#34; services: redroid: stdin_open: true tty: true privileged: true pull_policy: always volumes: - ~/data:/data ports: - 5555:5555 image: redroid/redroid:11.0.0-latest # 启动 sudo docker-compose up -d 其他说明 # 连接设备 # 可以通过安装 adb4 和 scrcpy5 使用鼠标来操作安卓。\nmacOS 和 Linux 环境直接使用包管理器安装 android-platform-tools 和 scrcpy 即可：\n# macOS brew install --cask android-platform-tools scrcpy # Debian \u0026amp; Ubuntu sudo apt install android-platform-tools scrcpy # Arch Linux sudo pacman -S android-platform-tools scrcpy # NixOS environment.systemPackages = with pkgs; [ android-tools scrcpy ]; 如果你想使用网页进行操作，可以尝试 ws-scrcpy 这个项目。\n如果不想配置环境，可以使用我打包的镜像 aaronyes/ws-scrcpy。\n直接运行：\nsudo docker run --name ws-scrcpy -d -p 8000:8000 aaronyes/ws-scrcpy 或者使用 Docker Compose 启动：\nversion: \u0026#34;3\u0026#34; services: ws-scrcpy: container_name: ws-scrcpy ports: - 8000:8000 image: aaronyes/ws-scrcpy 启动后执行 adb 命令连接安卓：\nsudo docker exec ws-scrcpy adb connect ip:5555 如果使用 Docker Compose，可以把两个服务放在一起：\nversion: \u0026#34;3\u0026#34; services: redroid: container_name: redroid stdin_open: true tty: true privileged: true pull_policy: always volumes: - ~/data:/data ports: - 5555:5555 image: redroid/redroid:11.0.0-latest ws-scrcpy: container_name: ws-scrcpy ports: - 8000:8000 image: aaronyes/ws-scrcpy 这时可以直接使用容器名连接：\ndocker exec ws-scrcpy adb connect redroid:5555 没有应用商店怎么安装软件 # 可以使用 adb 安装，在电脑上下载好 apk 安装包，然后执行 adb install \u0026lt;apk路径\u0026gt;\n推荐安装一个 Via 浏览器，体积很小，轻量无广告，后续可以通过浏览器下载安装其他应用\n安装 Magisk # 可以参考 Magisk 安装指南。\nGMS 支持 # 参考 Redroid GMS 支持说明。\n参考文献 # Via 浏览器 ws-scrcpy - Web 端安卓投屏工具 Magisk 安装指南 GitHub 是一个代码托管平台\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nRedroid 是可以在 Docker 中运行安卓的开源项目\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nDocker 官方安装脚本\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nAndroid 调试桥，用于与安卓设备通信\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n开源的安卓投屏工具\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2023-05-11","externalUrl":null,"permalink":"/posts/2023/05/build-your-own-cloud-phone/","section":"所有文章","summary":"","title":"打造自己的云手机","type":"posts"},{"content":"","date":"2023-03-20","externalUrl":null,"permalink":"/tags/api/","section":"标签","summary":"","title":"API","type":"tags"},{"content":"","date":"2023-03-20","externalUrl":null,"permalink":"/tags/chatgpt/","section":"标签","summary":"","title":"ChatGPT","type":"tags"},{"content":" 前言 # 2023 年初，ChatGPT 火得一塌糊涂，但用起来有几个明显的痛点：Web 端体验不够灵活，没法接入自己的应用；官方 API1 按 Token 收费，对于重度使用者来说成本不低；而且 Web 端和 API 是两套完全独立的系统，ChatGPT Plus 订阅用户的 GPT-4 额度没法通过 API 消耗。\n于是我萌生了一个想法：能不能把 ChatGPT Web 端的接口逆向出来，包装成标准 OpenAI API 格式来用？这样既能用上 Web 端的无限额度，又能接入自己的工具链。\n这篇文章记录一下我从抓包分析到最终实现的完整过程。\n先搞清楚 ChatGPT Web 端的技术架构 # 动手之前，先用浏览器 DevTools 的 Network 面板把 ChatGPT Web 端的请求链路摸清楚。\n认证链路 # ChatGPT 使用 Auth02 作为 OAuth2 认证提供商。正常的 Web 登录流程是：\n浏览器 → chat.openai.com/auth/login → 跳转到 auth0.openai.com（Auth0 托管的登录页） → 用户输入邮箱密码 → Auth0 回调返回 access_token → 前端把 token 存到 session 中 但这个流程是给浏览器设计的，有多次 302 重定向、Cookie 传递、JavaScript 渲染。命令行工具没法直接走这个流程。\n对话链路 # 登录后，前端发消息用的接口长这样：\nPOST https://chat.openai.com/backend-api/conversation Authorization: Bearer \u0026lt;access_token\u0026gt; Content-Type: application/json Accept: text/event-stream 请求体：\n{ \u0026#34;action\u0026#34;: \u0026#34;next\u0026#34;, \u0026#34;messages\u0026#34;: [ { \u0026#34;id\u0026#34;: \u0026#34;uuid\u0026#34;, \u0026#34;role\u0026#34;: \u0026#34;user\u0026#34;, \u0026#34;content\u0026#34;: { \u0026#34;content_type\u0026#34;: \u0026#34;text\u0026#34;, \u0026#34;parts\u0026#34;: [\u0026#34;你好\u0026#34;] } } ], \u0026#34;model\u0026#34;: \u0026#34;text-davinci-002-render-sha\u0026#34;, \u0026#34;parent_message_id\u0026#34;: \u0026#34;uuid\u0026#34; } 响应是 SSE（Server-Sent Events）3 格式，逐字输出：\ndata: {\u0026#34;message\u0026#34;: {\u0026#34;content\u0026#34;:{\u0026#34;parts\u0026#34;:[\u0026#34;你\u0026#34;]}, ...}} data: {\u0026#34;message\u0026#34;: {\u0026#34;content\u0026#34;:{\u0026#34;parts\u0026#34;:[\u0026#34;你好\u0026#34;]}, ...}} data: [DONE] 这里有个关键差异：Web 端的对话是一棵消息树（每条消息有 parent_message_id，支持分支和重新生成），而 OpenAI API 是线性的 messages 数组。后面做协议转换的时候需要处理这个差异。\n逆向 Auth0 认证流程 # 这是整个过程中最核心也最有趣的一步。\n思路转变：从 Web 到 iOS # 一开始我尝试直接模拟浏览器的登录流程，但很快就发现行不通：Auth0 的登录页有大量反爬措施，JavaScript 校验、浏览器指纹检测、reCAPTCHA 都有。\n换个思路：移动端的认证流程通常比 Web 端简单。于是抓包分析了 ChatGPT 的 iOS 客户端，发现它也用 Auth0，但走的是 OAuth2 + PKCE（Proof Key for Code Exchange）4 扩展，不需要浏览器环境。\nPKCE 简述 # PKCE 是 OAuth2 的安全扩展，原本是为无法安全存储 client_secret 的移动端和桌面端应用设计的。流程很简单：\n客户端生成 code_verifier（随机字符串） 客户端计算 code_challenge = SHA256(code_verifier) 授权请求中带上 code_challenge 回调时带上原始的 code_verifier 服务端验证 SHA256(code_verifier) == code_challenge，发放 Token 好处是：即使授权码被截获，没有 code_verifier 也无法换取 Token。\n拆解出的认证流程 # 通过分析 iOS 客户端的网络请求，我把完整的认证流程拆解成了 7 步：\n获取 preauth_cookie 拼装 authorize URL，模拟 iOS 客户端参数 访问 authorize URL，提取 state 参数并保存 Cookie 提交邮箱 提交密码 处理回调或 MFA 验证 如果需要 MFA，提交验证码后回到第 6 步 最终用授权码换取 Access Token 有几个值得注意的细节：\ncode_verifier 为什么可以硬编码？ 因为 iOS 客户端是可以反编译的，code_verifier 和 code_challenge 这对值写死在客户端里，所有 iOS 用户共用同一对。PKCE 在这种场景下保护的是传输链路（授权码泄露不会导致 Token 泄露），而不是客户端本身。\nclient_id 是哪来的？ 同样来自 iOS 客户端反编译。这是 OpenAI 在 Auth0 注册的 iOS 应用 ID。\nredirect_uri 为什么是 com.openai.chat://...？ 这是 iOS 的 URL Scheme，用于 Auth0 授权完成后跳回 App。在我们的实现里不需要真正跳转，只需要从响应的 Location 头里提取 code 参数。\nPython 实现大致长这样：\nclass Auth0: def auth(self, login_local=False) -\u0026gt; str: return self.__part_one() if login_local else self.get_access_token_proxy() def __part_one(self): # Step 1: get preauth def __part_two(self): # Step 2: build authorize URL def __part_three(self): # Step 3: follow authorize def __part_four(self): # Step 4: submit email def __part_five(self): # Step 5: submit password def __part_six(self): # Step 6: handle callback/MFA def __part_seven(self): # Step 7: MFA OTP def get_access_token(self): # Final: code → token 实现 SSE 流式对话代理 # 拿到 Access Token 后，下一步是调 ChatGPT 的对话接口。\n请求构造比较直观：每条消息需要一个 UUID 作为 id，parent_message_id 指向上一条消息形成对话链，首次对话不带 conversation_id，服务端会创建并返回。action 可以是 next（新消息）、variant（重新生成）、continue（继续输出）。\n难点在于 SSE 响应的处理。Python 的 Flask 是同步框架，但 SSE 需要异步消费流式响应。我的解决方案是异步线程 + 阻塞队列 + Generator 桥接：\ndef _request_sse(self, url, headers, data): queue, event = block_queue.Queue(), threading.Event() t = threading.Thread(target=asyncio.run, args=(self._do_request_sse(url, headers, data, queue, event),)) t.start() return queue.get(), queue.get(), self.__generate_wrap(queue, t, event) 为什么要绕这么一圈？因为 httpx5 的流式 API 是 async 的（async with client.stream('POST', url) 需要在 async 上下文中），但上层是同步代码（Flask 的路由处理函数、CLI 的 readline 循环都是同步的），又不想把全局架构从 Flask 改成 aiohttp/uvicorn，改动太大。\n所以用一个线程跑异步事件循环，通过 queue.Queue 把异步世界的数据搬运到同步世界，对外暴露一个标准 Generator，上层代码完全无感。\n还有一个细节：threading.Event 用于中断保护。如果客户端断开连接触发了 GeneratorExit，Event 被置位，异步线程检测到后主动关闭 httpx 连接，避免线程泄漏。\nWeb API 到 OpenAI API 的协议转换 # 这是把 ChatGPT Web 接口包装成标准 OpenAI API 的关键步骤。两种 API 格式差异很大：\n维度 ChatGPT Web API OpenAI Public API 认证 Bearer access_token Bearer sk-xxx（API Key） 请求格式 消息树（parent_message_id） messages 数组 响应格式 SSE + 消息树节点 SSE + choices 数组 会话管理 服务端存储 conversation_id 无状态 请求转换 # 我的做法是维护一个本地的消息树，把 OpenAI 格式的 messages 数组转换成树形结构，支持多轮对话和 regenerate：\ndef talk(self, content, model, message_id, parent_message_id, ...): if conversation_id: parent = conversation.get_prompt(parent_message_id) else: parent = conversation.add_prompt(Prompt(parent_message_id)) parent = conversation.add_prompt(SystemPrompt(self.system_prompt, parent)) conversation.add_prompt(UserPrompt(message_id, content, parent)) user_prompt, gpt_prompt, messages = conversation.get_messages(message_id, model) 响应转换 # Web 端每次返回的是全量文本（parts[0] 越来越长），而 OpenAI API 返回的是增量文本。需要做差值计算：\n# Web 端返回 {\u0026#34;message\u0026#34;: {\u0026#34;content\u0026#34;: {\u0026#34;parts\u0026#34;: [\u0026#34;完整文本\u0026#34;]}, \u0026#34;author\u0026#34;: {\u0026#34;role\u0026#34;: \u0026#34;assistant\u0026#34;}}} # 转换为 OpenAI 格式 data: {\u0026#34;choices\u0026#34;: [{\u0026#34;delta\u0026#34;: {\u0026#34;content\u0026#34;: \u0026#34;增量文本\u0026#34;}, \u0026#34;finish_reason\u0026#34;: null}]} data: {\u0026#34;choices\u0026#34;: [{\u0026#34;delta\u0026#34;: {}, \u0026#34;finish_reason\u0026#34;: \u0026#34;stop\u0026#34;}]} data: [DONE] Token 超限裁剪 # OpenAI API 有 Token 上限（gpt-3.5-turbo 是 4096，gpt-4 是 8192）。对话历史过长时需要本地裁剪：\ndef __reduce_messages(self, messages, model, token=None): max_tokens = self.FAKE_TOKENS[model] if self.__is_fake_api(token) else self.MAX_TOKENS[model] while gpt_num_tokens(messages) \u0026gt; max_tokens - 200: if len(messages) \u0026lt; 2: raise Exception(\u0026#39;prompt too long\u0026#39;) messages.pop(1) # 从第 2 条开始删，保留 system prompt 和最新对话 return messages 裁剪策略：保留 messages[0]（system prompt）和最新的几轮对话，从最早的用户消息开始删。- 200 是给模型回复留的余量。\n从技术验证到实际交付 # 接口跑通之后，接下来的问题是怎么让身边的同事和朋友也能用上。\n批量注册 # 接口跑通之后，实际用起来发现 ChatGPT 有单账号频率限制，请求量一大就报错。要解决这个问题，最直接的办法就是多账号。于是我写了个注册机，使用自己的域名邮箱批量注册了 200 个 ChatGPT 账号。其中两个开了 Plus 订阅（只有 Plus 才能用 GPT-4），费用跟几个朋友均摊。剩下的账号都走免费的 GPT-3.5，日常使用完全够用。\nToken 管理与持久化 # Access Token 有效期 14 天，到期后需要重新认证刷新。我把所有账号的 Token 存到了 PostgreSQL 数据库中，写了个定时任务自动检测过期时间并批量刷新，保证 Token 池始终可用。\n负载均衡 # 200 个账号不能只用一个。我在代理服务层加了一层简单的负载均衡：每次请求过来，从数据库中轮询选取一个可用的 Token 去调用 ChatGPT 接口。这样既避免了单账号频率限制，也分散了各账号的请求压力。\n最终的效果是：对外暴露一个标准的 OpenAI API 地址，同事和朋友只需要在自己的应用里把 API Base URL 指向我的服务就行，完全感知不到背后是 200 个账号在轮转。GPT-4 的请求路由到 Plus 账号池，GPT-3.5 的请求路由到免费账号池。\n参考文献 # OpenAI API 文档 Auth0 官方网站 Auth0 - Authorization Code Flow with PKCE Server-Sent Events 文档 | MDN PKCE RFC 7636 规范 Python httpx 库文档 OpenAI 官方提供的 API 接口文档\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nChatGPT 使用的 OAuth2 认证提供商\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n一种基于 HTTP 的单向数据推送协议\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nOAuth2 的安全扩展，用于防止授权码被滥用\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nPython 的异步 HTTP 客户端库\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2023-03-20","externalUrl":null,"permalink":"/posts/2023/03/chatgpt-web-to-api/","section":"所有文章","summary":"","title":"ChatGPT Web 逆向 API 包装实战","type":"posts"},{"content":"","date":"2023-03-20","externalUrl":null,"permalink":"/en/tags/reverse-engineering/","section":"Tags","summary":"","title":"Reverse Engineering","type":"tags"},{"content":"","date":"2023-03-20","externalUrl":null,"permalink":"/tags/%E9%80%86%E5%90%91/","section":"标签","summary":"","title":"逆向","type":"tags"},{"content":"","date":"2023-01-17","externalUrl":null,"permalink":"/tags/fastapi/","section":"标签","summary":"","title":"FastAPI","type":"tags"},{"content":"","date":"2023-01-17","externalUrl":null,"permalink":"/tags/jwt/","section":"标签","summary":"","title":"JWT","type":"tags"},{"content":" 前言 # 在开发 Web 应用时，用户认证是一个避不开的话题。最近在用 FastAPI 做后端项目的时候，接触到了 JWT1 这种认证方式，觉得挺有意思的，于是做了一些研究，在这里记录一下。\nJWT 是什么 # JWT 的全称是 JSON Web Token，简单来说它是一种令牌格式。令牌的内容是编码后的 JSON，且常用在 Web 领域的身份校验，所以称之为 JWT。\n一般来说，用户先用自己的用户名和密码（OAuth 之类的第三方认证暂时略去）发送到服务器，服务器验证通过后给用户签发一个 Token。这个 Token 中包含了必要的信息，比如签发人、主题（一般是用户 ID）、有效时间等。之后服务器便不再需要用户名和密码，单靠这个 Token 就可以确认用户的身份。\nJWT 解决了什么问题 # 很多人可能会问：既然用户已经有了用户名和密码，何必还要多此一举？先生成 Token 再使用，而不是直接用用户名密码？就好像 HTTP Basic Authentication2 那样，简单粗暴，靠 HTTPS 也能保证基本的安全。\n主要有两个原因：第一个是安全，第二个是压力。\n安全比较好理解。与经常把明文的用户名和密码放在请求里传输相比，可以带有失效机制的 JWT 显然更加安全。\n第二个原因我觉得更为主要，那就是压力。我们不妨先想想用户名和密码的鉴权过程是什么样的：\n用户发出请求，在 HTTP 请求中带上 Base64 编码后的用户名和密码。 服务器解码请求，将用户名、密码与数据库中的内容（一般密码部分只存 Hash）作比对，返回通过或不通过。 如果每秒只有几个请求，这样的设计当然没问题。但如果这是一个每秒请求量数万的服务，仅做鉴权校验就会让数据库承受非常大的压力。而且目前相对成熟的数据库往往是单点的（涉及到事务的东西，即使能扩容也要脱一层皮）。\n那有没有可能，我们签发一个令牌给用户，以后不管是本服务器的其他副本（Replica），还是跨服务器，都能进行验证，而且验证的过程不需要查数据库？这样的话，服务器就真正变成了无状态的。熟悉分布式系统的小伙伴都知道，无状态可太省事了。\nJWT 的安全性是如何保障的 # 既然就是 Base64 编码过的 JSON，那用户自己捏一个骗服务器说这是 Token 可以吗？\n当然不可以。JWT 会进行数字签名，最后一部分会使用服务器独有的密钥和前两部分的内容一起生成签名。由于这个密钥只有服务器才有，而且通过内容也几乎不可能反推出密钥，所以攻击者无法伪造 JWT。\nJWT 的结构详解 # JWT 的结构分为三块：\n头部 Header 载荷 Payload 签名 Signature 三个部分都是 Base64 编码的字符串，用 . 连接起来。一个典型的 JWT 如下所示：\neyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.cThIIoDvwdueQB468K5xDc5633seEFoqwxjF_xSJyQQ 其中头部解码后为：\n{ \u0026#34;alg\u0026#34;: \u0026#34;HS256\u0026#34;, \u0026#34;typ\u0026#34;: \u0026#34;JWT\u0026#34; } 载荷解码后为：\n{ \u0026#34;sub\u0026#34;: \u0026#34;1234567890\u0026#34;, \u0026#34;name\u0026#34;: \u0026#34;John Doe\u0026#34;, \u0026#34;iat\u0026#34;: 1516239022 } 最后一部分是签名，用于校验前两部分是否被篡改。\n如何使用 JWT # HTTP Header # 理论上，只要把 JWT 传给服务器就行了。RFC 75193 也没有规定必须在哪个位置使用 JWT。但考虑到它是个 Token，一般的使用方式是放在 Request Header 里：\nAuthorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.cThIIoDvwdueQB468K5xDc5633seEFoqwxjF_xSJyQQ 以 FastAPI 为例 # 这里用的是 python-jose4，直接 pip install \u0026quot;python-jose[cryptography]\u0026quot; 即可。也可以使用其他库，详见 https://jwt.io/libraries。\n出于简洁考虑，这里略去 FastAPI 的框架代码，只展示核心部分。\n首先创建一个 Endpoint 来签发 Token：\nfrom fastapi.security import HTTPBasic, HTTPBasicCredentials http_basic = HTTPBasic() def create_jwt_access_token( data: dict, expires_delta: timedelta, ) -\u0026gt; str: to_encode = data.copy() to_encode.update({\u0026#34;exp\u0026#34;: datetime.utcnow() + expires_delta}) return jwt.encode( to_encode, \u0026#34;jwt_secret\u0026#34;, # 请替换为你自己的密钥 algorithm=\u0026#34;HS256\u0026#34;, ) @app.post(\u0026#34;/auth/issue-new-token\u0026#34;) def issue_new_token( credentials: HTTPBasicCredentials = Depends(http_basic), ): username = basic_authentication(credentials) # 验证用户名密码 access_token = create_jwt_access_token( data={\u0026#34;sub\u0026#34;: username}, expires_delta=timedelta(seconds=1234), ) return {\u0026#34;access_token\u0026#34;: access_token, \u0026#34;token_type\u0026#34;: \u0026#34;bearer\u0026#34;} 这是一个极简版的 JWT 生成接口。需要注意的是，这里既没有 Rate Limit，也没有针对错误进行处理，只是一个基本的演示。\n接下来是验证 JWT 的函数：\nfrom fastapi.security import HTTPBearer, HTTPAuthorizationCredentials http_bearer = HTTPBearer() def jwt_authentication( credentials: HTTPAuthorizationCredentials = Depends(http_bearer), ): credentials_exception = HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail=\u0026#34;Could not validate credentials\u0026#34;, headers={\u0026#34;WWW-Authenticate\u0026#34;: \u0026#34;Bearer\u0026#34;}, ) try: if credentials.scheme.lower() != \u0026#34;bearer\u0026#34;: raise credentials_exception return jwt.decode( credentials.credentials, \u0026#34;jwt_secret\u0026#34;, # 请替换为你自己的密钥 algorithms=[\u0026#34;HS256\u0026#34;], ) except JWTError: raise credentials_exception 在需要认证的接口中加入依赖即可：\n# 方式一：只验证，不获取 payload @app.get(\u0026#34;/api/protected\u0026#34;, dependencies=[Depends(jwt_authentication)]) def protected_api(): ... # 方式二：获取 payload 做进一步处理 @app.get(\u0026#34;/api/protected\u0026#34;) def protected_api(jwt_payload: dict = Depends(jwt_authentication)): # 处理 jwt_payload ... 前端使用思路 # 由于很多组件都会依赖后端的 API，且这些 API 需要 JWT，所以一般会用 Context 来管理 JWT，以便于跨组件传递。\n在制作 AuthStateContext.Provider 的时候，我的思路是：\n由于用了 OAuth，JWT 会从后端通过 query string 传入，首先检查 query string 有没有新的 Token 如果有，提取出来存到 localStorage 如果没有，进入下一步 检查 localStorage 里有没有存储的 Token 如果有，检查是否有效 如果没有，或上一步的结果为无效，进入下一步 调用 \u0026ldquo;who am I\u0026rdquo; 接口，让后端校验 JWT（这一步可选，但可以增加可靠性） 如果所有校验都通过，将 JWT 状态通过 Provider 传递给其他组件 否则跳转到相应页面提示用户 其他组件使用 useContext(AuthStateContext) 即可获取已验证的 Token。\nJWT 的几个缺陷 # JWT 还是有几个小缺陷的，在设计系统时需要考虑：\nJWT 一旦签发就难以撤回，这是无状态带来的代价。 考虑到第一点，往往需要设置较短的有效期，客户端需要维护 Token 的刷新。 JWT 中的信息默认不加密，任何人都可以读取，所以不要把敏感信息放在里面。 JWT 推荐在 HTTPS 环境下使用。 参考资料 # JSON Web Token - Wikipedia JWT 官方库列表 JWT（JSON Web Token）是一种开放的 RFC 7519 标准，用于在各方之间安全地传输信息。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nHTTP Basic Authentication 是最简单的 HTTP 认证方式，直接在请求头发送用户名和密码。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nRFC 7519 是 JWT 的正式规范文档。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\npython-jose 是一个 Python 库，用于创建和验证 JWT。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2023-01-17","externalUrl":null,"permalink":"/posts/2023/01/jwt-guide/","section":"所有文章","summary":"","title":"JWT 入门与实践","type":"posts"},{"content":"","date":"2022-11-20","externalUrl":null,"permalink":"/en/tags/requirements-document/","section":"Tags","summary":"","title":"Requirements Document","type":"tags"},{"content":"","date":"2022-11-20","externalUrl":null,"permalink":"/en/tags/work-method/","section":"Tags","summary":"","title":"Work Method","type":"tags"},{"content":"","date":"2022-11-20","externalUrl":null,"permalink":"/tags/%E5%B7%A5%E4%BD%9C%E6%96%B9%E6%B3%95/","section":"标签","summary":"","title":"工作方法","type":"tags"},{"content":"","date":"2022-11-20","externalUrl":null,"permalink":"/tags/%E9%9C%80%E6%B1%82%E6%96%87%E6%A1%A3/","section":"标签","summary":"","title":"需求文档","type":"tags"},{"content":" 前言 # 需求文档写得全不全面，其实跟经验没太大关系。写得多的人照样漏，写得少的人反而因为小心所以想得周全。区别往往在于有没有一套检查机制，把该想的点落到纸面上，而不是靠脑子硬记。\n这份清单来自一次又一次的教训。评审时当场答不上来、上线后发现少考虑了兼容性、甚至漏掉过数据埋点。每踩一次坑就补一个检查项，慢慢地清单就变长了。现在写之前会过一遍，写完还会再过一遍，算是给自己的一道防线。\n如果你也在经历类似的困扰，希望这份清单对你有用。\n基本要素 # 开篇先把文档的基础信息补全。\n文档形式：用 Word、Markdown、还是直接在高保真原型上标注，提前确定好，团队统一。\n版本命名：大版本改第一位（如 1.0 → 2.0），小迭代改中间位（如 1.1 → 1.2），Bug 修复改最后位（如 1.1 → 1.1.1）。这个规范通常团队统一，但写进文档里让相关方心里有数。\n需求目录：本期要做什么，分模块列出来。这就像一本书的目录，读者扫一眼就知道整体结构，后续也能快速定位。\n更新记录：谁在什么时间改了什么是，每次更新都要同步。开发在用哪个版本，全靠这个记录来对齐。\n全局位置：这个功能在产品全局里处于什么位置，跟其他模块是什么关系。有时候单独看一个需求没问题，放进全局就发现跟现有逻辑冲突了。\n业务说明 # 这块容易被跳过。很多产品新人直接跳到功能细节，结果开发做完发现这不是自己想要的。\n用一段简洁的文字说清楚：这个功能是什么、为谁解决什么问题、为什么现在要做、未来会怎么演进。\n如果功能涉及跨角色协作（比如运营要在后台配置什么），把各方的职责边界也写明白。开发理解了业务目的，自己就能做很多判断，不用事事来问产品。\n业务流程图是标配。一图胜千言，流程图能大幅降低需求理解偏差。宁可多画一张图，也不要用一大段文字描述一个流程。\n规范与全局说明 # 新项目启动前，通常会制定一套设计规范。后续迭代直接引用，不用每次重复定义。\n这份规范里通常包括：\n状态名称与状态流转：有哪些状态，各自之间怎么转换，异常状态怎么处理 数据字典：字段含义、枚举值、业务规则 全局通用组件：弹窗样式、按钮状态、提示文案、加载样式、错误提示、动画效果，这些全局统一的交互规则提前定好 有了规范，需求文档可以更简洁，直接写「按规范执行」就行，不用每个页面重复描述同样的交互细节。\n需求正文 # 这是文档的核心部分。常见格式是图文穿插：截图旁边配文字说明。如果截图要素较多，先用线框图标注序号，再逐条说明。\n页面描述 # 开篇先说清楚这个页面是什么、属于哪个业务、处于哪个步骤的节点上。上下文清晰了，读者才能正确理解后续的细节。\n前置条件 # 这是最容易遗漏的部分。展示这个页面需要什么前提？\n权限：是否需要登录？是否有功能权限或数据权限的限制？ 数据来源：数据是后台配置的还是接口返回的？如果是后台配置，后台是否已具备这个模块？ 数据加载：是后端推送还是前端拉取？加载方式是什么（预加载、懒加载、实时加载）？加载的条数和触发时机是什么？加载中、加载成功、加载失败的展示分别是什么？ 这些细节不写清楚，开发只能靠猜。猜错了，返工的是整个团队。\n展示问题 # 页面上的数据从哪里来、怎么展示，需要说明白的点包括：\n数据范围：是否有时间范围、权限范围的限定？展示的是哪个数据表的哪些字段？ 空状态：数据为空时展示什么？是完全空白还是给引导文案？ 字段展示：字段长度超限怎么处理？特殊字符是否需要过滤或转义？计量单位怎么标注？ 元素标注：页面上的每一个元素都需要说明，尤其是信息密度高的列表页，可以用指示线或角标来标注序号，逐一解释。 排序与缓存 # 排序的默认规则是什么？哪些操作会触发排序变更？\n缓存的策略呢？哪些数据需要缓存？缓存什么时候删除、什么时候更新？\n交互设计 # 前置交互：用户在哪里触发、触发的方式是什么（点击、滑动、长按）、热区多大、是否容易误触。获取焦点时的样式是什么？禁用状态下又是什么样子？\n校验规则：是实时校验还是提交时校验？校验失败的提示是什么？有没有引导性的提示帮助用户修正？\n联动逻辑：选项之间是否有联动关系？选 A 会带出 B，选 C 则不会，这些条件需要一一列举。\n后置交互：操作成功或失败后的提示是什么？是否有加载状态或倒计时？点击后的跳转逻辑是什么，是返回上一页、返回首页、还是生成一条记录但留在当前页？比如支付未完成通常会生成一条「未支付订单」而不是简单返回购物车。\n后置条件 # 操作完成后，数据怎么流转？\n数据落库：新增、修改、删除分别涉及哪些表、哪些字段？是软删除还是硬删除？是否需要生成操作日志？ 数据处理：是实时处理还是异步处理？有没有时效性要求？涉及什么算法？结果如果有异常怎么报错？ 后置页面：操作完成后用户看到的是什么页面？这个页面如果要继续操作，需要重新走一遍前置条件。 常见功能 # 搜索、筛选、排序是高频出现的功能，也最容易遗漏细节。\n搜索：数据来源是本地还是云端？支持精确搜索还是模糊搜索？搜索范围是否需要限定？\n筛选：复合筛选还是单项筛选？筛选的默认值和可选项有哪些？\n排序：默认排序规则是什么？是否支持千人千面？\n其他注意事项 # 以下这些点容易被忽略，但一旦漏掉，上线后会很麻烦：\n版本限制：是否有最低版本要求？是否需要强制更新？新版本和旧数据是否兼容？\n新手引导：是否需要启动页或功能引导？\n数据埋点：需要记录哪些事件？这个在 PRD 阶段就要想清楚，不能等上线了再补。\n消息推送：推送内容、推送时间、推送渠道（厂商通道还是自建通道）。\n手机本地能力：相机、陀螺仪、通讯录、备忘录等原生能力的调用。\n外部跳转：跳转到其他 App 还是 H5 页面？有没有缓存清理机制？\n夜间模式：是否需要适配深色模式？\n衍生产品：如果未来要做桌面小组件、浏览器插件等衍生能力，当前版本的技术方案是否留有余地？\n","date":"2022-11-20","externalUrl":null,"permalink":"/posts/2022/11/demand-document-checklist/","section":"所有文章","summary":"","title":"需求文档自查清单","type":"posts"},{"content":" 前言 # 七月份，我正式开始做产品经理了。说实话，刚入行的时候，连 PRD 是什么都只有模糊的概念。\n以前看别人写的文档，觉得也就那样，不就是画个原型写写功能吗？等自己真正上手才发现，问题远比想象中复杂。设计出来的功能漏这漏那，跟开发评审的时候被问得哑口无言，上线之后才发现埋点没做好、数据取不到。\n痛定思痛，我开始认真研究 PRD 到底应该怎么写。今天这篇文章，就是我对 PRD 写作方法的一次总结，也是给刚入行的自己一个交代。\nPRD 的三种形态 # 在开始讲怎么写之前，先说一个很多新人容易犯的错：不是所有需求都要写 PRD。\n随着现在开发节奏越来越快，有些简单的改动根本没必要创建专门的文档。比如只是改几个文字、调一下按钮颜色，这种直接在任务管理系统里备注清楚就行了。我把这种方式叫做 in-task design，就是在任务卡片里把需求说清楚。\n但还有一种情况恰恰相反，当你面对一个复杂的产品从零开始，比如做一个电商 App、做一个社区产品，这时候单个 PRD 根本装不下所有的设计细节，可能需要多个产品经理协作，共同完成一整套 产品家族（Product Suite）。这种情况下，就需要有一个更顶层的 PRD 来做总纲，各个模块再拆分成独立的 PRD。\n我们日常最常写的，其实是中间这一层：面向单个用户故事（User Story）的 PRD。本文主要讲的就是这种。\nPRD 的标准结构 # 一份合格的 PRD，在我看来应该包含以下六个部分：\n版本说明 背景与目标 故事介绍 概要设计 详细设计 交付设计 下面逐一展开。\n版本说明 # 这个最好理解，就是记录你每次更新的内容。但要注意一点：版本说明不是每次改一点就写一次，而是跟着评审（Review）走的。每一次 Review 之后发生了哪些变化，你需要记录下来。这样下次参与评审的同学就能快速了解：这个 PRD 跟上一次相比，有什么调整。\n背景与目标 # 这部分也很基础，但在很多公司的实际流程中，可能已经有业务团队写的 MRD（Market Requirements Document）来交代背景了。\n背景要回答的问题是：为什么需要这个功能？它在业务上能带来什么？\n这里不需要讲太多细节，关键是说明做这件事的必要性。比如是拓展用户群、提升某项业务指标、还是解决用户反馈强烈的问题。把核心原因说清楚就行。\n目标则是：功能上线后，你期望达成什么样的效果？\n可以是量化的指标，也可以是定性的预期。比如「日活提升 X%」「用户投诉率降低」或者「支撑某类新场景」。目标的价值在于，它让所有阅读 PRD 的人都能理解：这个功能最终要创造什么样的价值。当然，目标怎么达成不用在这里展开，那是后面要做的事。\n故事介绍 # 这是很多产品新人容易遗漏的部分。\n很多人设计功能的时候，打开原型工具就开始画页面。这其实是不对的。作为产品经理，你更需要解释的是：这个功能的意义是什么，它能解决什么问题。\n故事介绍的核心是场景拆解。具体做法很简单：把你想到的用户会做的事，一件一件列出来，一行一条。不用写得很细，关键是把用户旅程的关键节点梳理出来。\n为什么要这么做？因为当你把这些场景列完之后，读者脑子里已经有了「画面感」，他们会联想到自己用过的类似产品，会在脑海里形成对这个功能的初步印象。这种画面感非常重要，接下来的所有设计都是在填充和完善这个画面。\n有了场景之后，还需要做价值分析。简单说就是讲清楚：这个功能上线后，会在哪个环节产生什么正向影响。\n价值分析的颗粒度可以根据实际需要来：有些团队有专门的 MRD 来说这些，产品经理可以简化这部分；有些团队没有，那就需要产品经理自己补上这块逻辑。\n概要设计 # 概要设计对应的是产品设计五张图中的三张：模块图、功能清单图和页面结构图。\n功能清单是核心中的核心。很多人觉得原型最重要，其实不对。一个清晰的功能清单（Feature List） 才是整个 PRD 的目录索引。\n功能清单怎么列？可以按模块来分层组织。比如用 M 开头标记模块（Module），用 F 开头标记功能（Feature）。每个模块下面有哪些功能，全部穷举出来。\n为什么要穷举？因为这能让团队快速了解全貌。开发团队有十几二十号人，每人每个迭代只能做三四个功能，首先得让他们知道整体在做什么。同时，Tech Leader 可以根据功能清单分配任务，测试团队也能据此准备测试用例。功能清单就像一本书的目录，保证任务不遗漏。\n页面结构则是对功能清单的视觉化索引。每个页面对应哪些功能，如果发现页面里承载的功能在清单里找不到，或者清单里有但页面没有，那就要回头检查是不是哪里漏了。这是一种自洽的验证机制。\n详细设计 # 详细设计其实是最不容易出错的部分，因为大家都会做：把每个页面的交互逻辑、字段说明一一写清楚。\n但有一个点必须强调：详细设计里的每一个细节，都要能跟前面的功能清单和页面结构对应上。如果发现写了某个功能但前面没列，就要回头补上；反之亦然。\n几次自洽检查下来，设计会非常完整，也不容易漏逻辑。跟团队的信任感就是这么建立的。\n交付设计 # 这是另一个容易被遗漏的部分。\n交付设计里，我认为最关键的两块是：数据埋点设计 和 上线前提条件。\n数据埋点：很多产品经理等到功能上线了才想起来要分析数据，结果发现埋点没做、数据取不到，只能等下一个版本。这是非常糟糕的情况。\n好的做法是，在 PRD 设计阶段、进入研发之前就想清楚：你需要看什么数据？现在的埋点方案能否满足你的分析需求？如果不能，就要提前改方案。\n数据分析师也是 PRD 的「用户」之一。在测试阶段，他们要验证埋点是否正确、能否生成你需要的报表。如果不能，测试就不算通过。这是一个双保险。\n上线前提条件：这个功能是否依赖其他系统先上线？是否会影响历史数据？需不需要提前备份？这些作为产品经理都要考虑清楚。\n产品上线后 # 我的一个习惯是：功能上线一两周、数据稳定之后，更新 PRD，告诉团队当时设定的目标和实际达成的数据。\n如果设计 10 个 PRD，这 10 个功能上线后的数据都跟预测接近，那你绝对是专家了。刚开始预测不准很正常，不要怕。大胆走出第一步，你就已经超过很多人了。\n评审效率 # 最后说一个提高效率的技巧：PRD 评审会之前，把文档提前一到两天发给开发。\n很多新人因为担心被老程序员吐槽，总是藏着掖着，评审会上才拿出文档让大家现场发挥。这是非常不可取的做法。\n正确的做法是大胆地提前分享，让大家带着问题来开会，很多问题在线下就解决了，效率高得多。\n结语 # 产品经理是一个非常依靠实践的岗位。只有真正动手写、动手做，才能把别人的经验转化为自己的能力。\n共勉。\n","date":"2022-07-20","externalUrl":null,"permalink":"/posts/2022/07/prd-checklist/","section":"所有文章","summary":"","title":"PRD 产品设计文档怎么写","type":"posts"},{"content":" 前言 # 最近在折腾博客图片格式的时候，接触到了 AVIF1 这个比较新的图片格式。了解之后发现它在压缩率上相比传统的 JPEG 有着非常大的优势，于是做了一些研究，在这里记录一下。\n什么是 AVIF # AVIF 是一种基于 AV1 视频编码技术的图片格式。与 JPEG 类似，它使用有损压缩来减小文件大小，但不同之处在于，AVIF 在相同画质下可以把文件体积压缩得更小。\n该格式由开放媒体联盟（AOMedia）开发，该联盟由亚马逊、Netflix、谷歌和 Mozilla 等公司组成。文件使用 AV12 算法压缩，并以 HEIF3 容器格式存储。由于 AV1 压缩技术是免版税的，所以无需支付任何许可费用即可使用。\n你可以在 Can I use 上查看 AVIF 的浏览器支持范围。\nAVIF 对比 JPEG # 与 JPEG 相比，AVIF 最明显的优势是文件尺寸大幅缩小。更小的文件意味着更少的带宽消耗和更快的加载速度，将网页中的 JPEG 替换为 AVIF 后，图像的数据消耗量可能会减少一半。\n颜色深度是 AVIF 优于 JPEG 的另一个方面。JPEG 仅支持 8 位颜色深度，而 AVIF 支持 HDR4，这意味着更丰富的色彩和更多的细节表现。\nNetflix 提供了一些很好的视觉示例，比较了相同图像分别压缩为 JPEG 和 AVIF 的效果。WebP 和 PNG 格式的其他比较也值得一看。\n如何使用 AVIF # 可以使用 FFmpeg5 将其他格式的图片转换为 AVIF 格式。\n简单的转换示例：\n# JPEG → AVIF ffmpeg -i input.jpg output.avif # PNG → AVIF ffmpeg -i input.png output.avif 调整输出质量，使用 -crf 参数（0-63，值越低质量越高）：\nffmpeg -i input.jpg -c:v libaom-av1 -crf 30 -b:v 0 output.avif 调整压缩级别，使用 -cpu-used 参数（0-8，值越大速度越快，但压缩率越低）：\nffmpeg -i input.jpg -c:v libaom-av1 -cpu-used 4 -crf 30 -b:v 0 output.avif 组合使用，同时指定质量和压缩级别：\nffmpeg -i example.png -c:v libaom-av1 -crf 30 -cpu-used 4 -b:v 0 example.avif 参考文献 # Can I Use - AVIF AVIF Has Landed - Jake Archibald WebP vs AVIF Comparison - ctrl.blog Netflix TechBlog - AVIF for Next Generation Image Coding What is an AVIF File - Lifewire AVIF 是基于 AV1 视频编码技术的图片格式，由开放媒体联盟开发。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nHEIF（High Efficiency Image File Format）是一种高效的图像容器格式。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nAV1 是免版税的视频编码标准，由 AOMedia 联盟制定。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nHDR（High Dynamic Range）提供更高的色彩深度和对比度。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nFFmpeg 是强大的音视频处理工具，支持多种格式转换。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2022-05-04","externalUrl":null,"permalink":"/posts/2022/05/avif-usage/","section":"所有文章","summary":"","title":"什么是 AVIF，如何使用它","type":"posts"},{"content":"","date":"2022-03-23","externalUrl":null,"permalink":"/en/categories/blog/","section":"Categories","summary":"","title":"Blog","type":"categories"},{"content":"","date":"2022-03-23","externalUrl":null,"permalink":"/en/tags/font/","section":"Tags","summary":"","title":"Font","type":"tags"},{"content":"","date":"2022-03-23","externalUrl":null,"permalink":"/categories/%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2/","section":"分类","summary":"","title":"个人博客","type":"categories"},{"content":" 前言 # 最近一直在使用「霞鹜文楷」1 这款开源字体，十分喜欢，便想着把博客的字体也换成霞鹜文楷。\n配置 # 阅读字体的 [Issue #24]2，发现已经有大佬通过 ttf2woff2 工具转换好了字体，并提供了在网页上使用霞鹜文楷字体的方法。经过测试，如果直接使用 lxgw-wenkai-webfont 在 Safari 上显示会不正常，使用 lxgw-wenkai-lite-webfont 就不会出现问题。lite 版本精简了部分字重，在兼容性上表现更好。\n通过搜索 3 得知 Congo 主题允许直接将额外的代码插入到模板的 和 部分，只需要创建 layouts/partials/extend-head.html 或 layouts/partials/extend-footer.html 文件即可。创建好 html 文件，将下面的代码贴入其中，保存并刷新网页，博客就已经换好霞鹜文楷字体了。\n\u0026lt;link rel=\u0026#34;stylesheet\u0026#34; href=\u0026#34;https://cdn.jsdelivr.net/npm/lxgw-wenkai-lite-webfont@1.1.0/style.css\u0026#34; /\u0026gt; \u0026lt;style\u0026gt; body { font-family: \u0026#34;LXGW WenKai Lite\u0026#34;, sans-serif; } \u0026lt;/style\u0026gt; 参考文献 # 霞鹜文楷 GitHub 仓库 Congo 主题文档 - Head and Footer lxgw-wenkai-webfont 项目 一款开源的中文字体，风格清新\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nIssue #24 中有人转换好的字体版本\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nCongo 主题允许插入额外代码的位置说明\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2022-03-23","externalUrl":null,"permalink":"/posts/2022/03/change-blog-font/","section":"所有文章","summary":"","title":"为博客更换字体","type":"posts"},{"content":"","date":"2022-03-23","externalUrl":null,"permalink":"/tags/%E5%AD%97%E4%BD%93/","section":"标签","summary":"","title":"字体","type":"tags"},{"content":"","date":"2022-02-11","externalUrl":null,"permalink":"/tags/hugo/","section":"标签","summary":"","title":"Hugo","type":"tags"},{"content":" 前言 # 似乎每年都可以看到类似于\u0026quot;xx 年，还有必要建立自己的博客吗\u0026quot;的讨论。这确实是个值得思考的问题，如今已经是 2022 年了，有着很多成熟的写作与发布平台，对于我来说，它们有利有弊，虽然可以很方便地在上面撰写文章，但也有着各种局限性。\n我第一次开始写博客是在 2015 年，当时用着免费空间部署了自己的第一个网站。2017 年，购买了人生中第一台服务器，接触了 Typecho1 博客系统，购买了 Handsome2 博客主题。2018 年，写作平台更换到了微信公众号，凭借着一周五更、无广告、高质量，吸引了不少读者，阅读占比也十分高。当时的我充满热情，坚持了三年，写了有 700 多篇文章。随着时间的推移，我认为这是一个受各种限制的平台，加上腾讯的各种整活，在 2021 年，我注销了它。\n注销之后，总觉得少了点什么。发朋友圈太碎，写长文又没地方发。思来想去，还是自己搭个博客吧，至少这里我说了算，想写什么就写什么。\n选择 # 博客分为两种：动态博客与静态博客。\n我使用过的动态博客程序：\nWordPress: https://wordpress.com Typecho: https://typecho.org Halo: https://www.halo.run 我使用过的静态博客程序：\nGridea: https://open.gridea.dev Hexo: https://hexo.io Hugo: https://gohugo.io 这些都是很不错的博客程序，它们各有优缺点。动态博客功能丰富、上手简单，但需要维护服务器和数据库；静态博客轻量快速、部署方便，但功能上相对有限。\n开始 # 尝试过多种不同的博客程序后，这次选择了 Hugo。在它的官网3上可以找到四种常见操作系统的安装步骤。\n创建站点 # 在 Hugo 中创建网站文件夹的命令是 hugo new site 网站名字，比如这里我创建一个名为 Blog 的博客文件夹。\nhugo new site Blog cd Blog 这时就可以输入 hugo server 尝试访问默认生成的网页了。\nhugo server Web Server is available at http://localhost:1313/ (bind address 127.0.0.1) Press Ctrl+C to stop 安装主题 # Hugo 有很多不错的主题，可以在 https://themes.gohugo.io 挑选。主题的 Github 仓库或对应的演示站点通常会有安装教程以及配置修改教程，按照说明操作即可。\n撰写文章 # 可以使用命令创建一篇名为 Hello World 的文章。\nhugo new posts/hello-world/index.md hugo server -D 这时你可以访问 http://localhost:1313/posts/hello-world 查看。你可能会发现启动参数加了一个 -D，这是用来预览草稿的参数。新创建的文章默认是 draft: true，除非手动把 true 修改为 false，否则这篇文章不仅在本地无法预览，发布后也不会显示。\nHugo 使用 Markdown4 标记语言来撰写文章，花费 10 分钟就可以学会使用。\n部署 # Hugo 可以部署在很多地方，官网上也有部署教程5，很多都是免费的，门槛非常低。其中我认为托管到 GitHub Pages 和 Cloudflare Pages 是最方便的。\n参考文献 # Typecho 官方主页 Handsome 主题发布页 Hugo 官方安装指南 Markdown 教程 | 菜鸟教程 Hugo 部署文档 一款轻量级的开源博客系统，简洁高效\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n一款优秀的 Typecho 主题，界面美观\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n支持四种常见操作系统的安装步骤\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n一种轻量级标记语言，学习成本低\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n支持多种部署方式\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2022-02-11","externalUrl":null,"permalink":"/posts/2022/02/build-personal-blog/","section":"所有文章","summary":"","title":"我是如何创建个人博客的?","type":"posts"},{"content":"","externalUrl":null,"permalink":"/series/","section":"Series","summary":"","title":"Series","type":"series"},{"content":" 如果本网站的任何内容无意中侵犯了您的版权或利益，请及时与我联系，我将采取适当行动。 除非另有特殊说明，否则 本站 所有内容均在 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 许可下授权。\n以下是该 许可证 的人类可读摘要（而不是替代）。\n您可以自由地： # 共享 — 在任何媒介以任何形式复制、发行本作品 演绎 — 修改、转换或以本作品为基础进行创作 只要你遵守许可协议条款，许可人就无法收回你的这些权利。\n惟须遵守下列条件： # 署名 — 您必须给出 适当的署名，提供指向本许可协议的链接，同时 标明是否（对原始作品）作了修改。您可以用任何合理的方式来署名，但是不得以任何方式暗示许可人为您或您的使用背书。 非商业性使用 — 您不得将本作品用于 商业目的。 相同方式共享 — 如果您再混合、转换或者基于本作品进行创作，您必须基于 与原先许可协议相同的许可协议 分发您贡献的作品。 没有附加限制 — 您不得适用法律术语或者 技术措施 从而限制其他人做许可协议允许的事情。 声明： # 您不必因为公共领域的作品要素而遵守许可协议，或者您的使用被可适用的 例外或限制 所允许。 不提供担保。许可协议可能不会给与您意图使用的所必须的所有许可。例如，其他权利比如 形象权、隐私权 或 人格权 可能限制您如何使用作品。 署名-非商业性使用-相同方式共享 4.0 国际许可证\n","externalUrl":null,"permalink":"/copyright/","section":"Hideaway","summary":"","title":"版权声明","type":"page"},{"content":"👋 你好呀，我是 Aaron。\n","externalUrl":null,"permalink":"/about/","section":"Hideaway","summary":"","title":"关于我","type":"page"},{"content":" 介绍 # 本隐私政策旨在帮助您了解我们对可能从您那里收集的或您提供给我们的任何信息的做法，我们使用这些信息的方式，以及我们如何处理这些信息。\n鉴于我们不收集任何个人数据，我们的做法直截了当，并致力于保护您的隐私。\n信息处理 # 本站是一个非商业性质的个人独立博客站点，我们致力于保护用户隐私。根据这一点，我们确认，在您访问我们的网站时，我们不会收集、存储或处理您的任何个人数据。\n个人数据是指有可能识别你个人的任何信息。由于我们不收集此类信息，所以我们没有可能使用、分享或出售这些数据。\n安全声明 # 虽然我们不收集个人数据，但我们非常重视您在提供任何非个人数据方面的信任，因此我们尽全力使用可接受的方法来保护这些数据、维护我们所使用服务在物理层面或软件层面的安全性。然而，没有任何一种互联网传输方法或电子存储方法是 100% 安全和可靠的，我们不能保证其绝对安全。\n政策变更 # 我们可能会不时地更新我们的隐私政策。因此，我们建议你定期查看本页面以了解任何变化。我们将通过在本页面上发布新的隐私政策来通知您任何变化。这些变化在本页面上公布后立即生效。\n","externalUrl":null,"permalink":"/privacy/","section":"Hideaway","summary":"","title":"隐私政策","type":"page"}]