[{"content":"","date":"June 2, 2026","externalUrl":null,"permalink":"/en/tags/binary-tree/","section":"Tags","summary":"","title":"Binary Tree","type":"tags"},{"content":"","date":"2026年06月02日","externalUrl":null,"permalink":"/","section":"Hassenfeld","summary":"","title":"Hassenfeld","type":"page"},{"content":"","date":"2026年06月02日","externalUrl":null,"permalink":"/series/leetcode/","section":"Series","summary":"","title":"LeetCode","type":"series"},{"content":"","date":"2026年06月02日","externalUrl":null,"permalink":"/tags/leetcode/","section":"Tags","summary":"","title":"LeetCode","type":"tags"},{"content":"二叉树，包含：\n节点元素； 指向子节点的指针Left； 指向另一个子节点的指针Right。 leetcode树表示方法：\nstruct TreeNode { int val; TreeNode *left; TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) {} }; leetcode104.二叉树的最大深度 # int maxDepth(TreeNode* root) { if(root==nullptr){ return 0; } return max(maxDepth(root-\u0026gt;left),maxDepth(root-\u0026gt;right))+1; } 先递归计算左右子树的深度 #递归 max()返回最大深度+1 leetcode226.反转二叉树 # TreeNode* invertTree(TreeNode* root) { if(root==nullptr){ return nullptr; } TreeNode* temp; temp=root-\u0026gt;left; root-\u0026gt;left=root-\u0026gt;right; root-\u0026gt;right=temp; invertTree(root-\u0026gt;left); invertTree(root-\u0026gt;right); return root; } 考虑递归遍历二叉树，交换每个节点的左 / 右子节点，即可生成二叉树的镜像。\n时间复杂度：O(n) 空间复杂度：O(n) leetcode101.对称二叉树 # bool isSymmetric(TreeNode* root) { if(root==nullptr){ return true; } return isMirror(root-\u0026gt;left,root-\u0026gt;right); } bool isMirror(TreeNode* root1,TreeNode* root2){ if(root1==nullptr\u0026amp;\u0026amp;root2==nullptr){ return true; } if(root1==nullptr||root2==nullptr||root1-\u0026gt;val!=root2-\u0026gt;val){ return false; } return(isMirror(root1-\u0026gt;left,root2-\u0026gt;right)\u0026amp;\u0026amp;isMirror(root1-\u0026gt;right,root2-\u0026gt;left)); } 时间复杂度：O(n) 空间复杂度：O(n) 函数isSymmetric() :\n特例处理： 若根节点 root 为空，则直接返回 true ； 返回值： 即 isMirror(root-\u0026gt;left,root-\u0026gt;right) 。 函数isMirror()：\n先判断当前左右两个节点是否满足对称； 再判断这两个节点的子节点L.left和R.right 是否对称，L.right 和 R.left 是否对称。 #递归 ![[对称二叉树.png|697]] ","date":"2026年06月02日","externalUrl":null,"permalink":"/posts/20260602-leetcode.binarytree/","section":"文章","summary":"","title":"LeetCode.二叉树","type":"posts"},{"content":"","date":"2026年06月02日","externalUrl":null,"permalink":"/series/","section":"Series","summary":"","title":"Series","type":"series"},{"content":"","date":"2026年06月02日","externalUrl":null,"permalink":"/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":"","date":"2026年06月02日","externalUrl":null,"permalink":"/tags/%E4%BA%8C%E5%8F%89%E6%A0%91/","section":"Tags","summary":"","title":"二叉树","type":"tags"},{"content":"","date":"2026年6月2日","externalUrl":null,"permalink":"/ja/tags/%E4%BA%8C%E5%88%86%E6%9C%A8/","section":"Tags","summary":"","title":"二分木","type":"tags"},{"content":"单向链表每一个节点(node)就是一个结构体指针，包含：\n节点元素数据； 当前节点的指针地址； 当前节点的指针地址；下一个节点的指针地址。 leetcode链表表示方法：\nstruct ListNode { int val; ListNode *next; ListNode(int x) : val(x), next(nullptr) {} }; leetcode206.反转链表 # 迭代法 # ListNode* reverseList(ListNode* head) { ListNode *head_prev = nullptr, *head_next; while (head) { head_next = head-\u0026gt;next; head-\u0026gt;next = head_prev; head_prev = head; head = head_next; } return head_prev; } 时间复杂度：O(n) 空间复杂度：O(1) 递归法 # ListNode* reverseList(ListNode* head, ListNode* head_prev = nullptr) { if (head == nullptr) { return head_prev; } ListNode* head_next = head-\u0026gt;next; head-\u0026gt;next = head_prev; return reverseList(head_next, head); } 时间复杂度：O(n) 空间复杂度：O(n) return reverseList(head_next, head);\n这是尾递归形式。 #尾递归 #递归 将“下一个要处理的节点” (head_next) 作为新的 head 传进去。 将“当前节点” (head) 作为下一次递归的 head_prev 传进去。 笔记 迭代法中，ListNode *head_prev = nullptr位于函数内部；递归法中，ListNode *head_prev = nullptr位于函数参数内 ·迭代法：它只在这一次函数调用中存在，且一共只调用了一次。 ·递归法：每一个递归层级（每一层函数调用）都有它自己的head_prev。\nleetcode234.回文链表 # 快慢指针法 # 刚做完反转链表的题目，所以立刻想到的是反转后半段链表，然后比较前半段和后半段是否相同。其实把链表放到数组中再进行回文比较显然是代码量更小的方法，但快慢指针的好处也显而易见：避免使用 O(n) 额外空间。\nbool isPalindrome(ListNode* head) { if(head==nullptr||head-\u0026gt;next==nullptr){ return true; } ListNode* head1=head; //head1慢指针 ListNode* head2=head; //head2快指针 while(head2!=nullptr\u0026amp;\u0026amp;head2-\u0026gt;next!=nullptr){ head1=head1-\u0026gt;next; head2=head2-\u0026gt;next-\u0026gt;next; } ListNode* secondHalf=reverseList(head1,nullptr); ListNode* p1=head; ListNode* p2=secondHalf; bool flag=true; while(p2!=nullptr){ if(p1-\u0026gt;val!=p2-\u0026gt;val){ flag=false; break; } p1=p1-\u0026gt;next; p2=p2-\u0026gt;next; } return flag; } ListNode* reverseList(ListNode* head,ListNode *head_prev = nullptr) { ListNode *head_next; if(head==nullptr){ return head_prev; } head_next=head-\u0026gt;next; head-\u0026gt;next=head_prev; return reverseList(head_next,head); } ","date":"2026年06月01日","externalUrl":null,"permalink":"/posts/20260601-leetcode.list/","section":"文章","summary":"","title":"LeetCode.链表","type":"posts"},{"content":"","date":"June 1, 2026","externalUrl":null,"permalink":"/en/tags/list/","section":"Tags","summary":"","title":"List","type":"tags"},{"content":"","date":"2026年6月1日","externalUrl":null,"permalink":"/ja/tags/%E3%83%AA%E3%82%B9%E3%83%88/","section":"Tags","summary":"","title":"リスト","type":"tags"},{"content":"","date":"2026年06月01日","externalUrl":null,"permalink":"/tags/%E9%93%BE%E8%A1%A8/","section":"Tags","summary":"","title":"链表","type":"tags"},{"content":"","date":"2026年05月03日","externalUrl":null,"permalink":"/tags/agent/","section":"Tags","summary":"","title":"Agent","type":"tags"},{"content":" GrantGo 简介 # GrantGo 是一位随时在线的申补小助手，陪你把复杂政策看明白、把申报流程走顺：你可以在这里更省心地完成注册、画像、补贴咨询和材料预审，少走弯路，更快找到适合自己的补贴机会；系统也会把个人画像、政策解读、申报步骤与材料检查整合在同一工作台，帮助你从“看不懂、找不到、怕漏材料”变成“看得懂、做得到、办得快”。\n前往 GrantGo Hassenfeld-hub/GrantGo Vue 0 0 GrantGo 功能 # 登录注册 # 打开首页后，可点击“登录”或“免费体验”进入登录页。 新用户通过邮箱 + 密码注册并登录。 画像完善 # 需完善个人画像：最高学历、毕业年份、当前工作/居住城市、社保月数、是否就业、是否本地户口、婚育状态、创业意向、行业方向。 城市选择为三级联动（省/市/区县），可停留在省级或市级。 点击“保存并开始匹配”后进入工作台，系统会自动触发首次画像匹配。 工作台 # 左侧历史对话：查看、切换、删除会话；支持“新建对话”。 中间智能问答：与 AI 对话，获取补贴匹配建议、政策解释、办理问题解答。 右侧PDF 解析：上传材料进行智能预审，展示匹配度、资料检查、流程进度。 顶部可收起/展开左右侧栏，提升阅读空间。 智能问答 # 高匹配度补贴 系统会结合个人画像返回高匹配度补贴卡片。 补贴卡片中可查看：补贴名称、适用区域、匹配度、金额、条件说明。 点击查看申报指南可进一步生成该补贴的办理指南内容。 申报指南 申报指南中包含指南摘要、申报网址、政务信息网址、咨询电话、所需材料、办理步骤。 点击生成流程图查看可视化办理路径。 点击显示清单勾选材料，跟踪个人准备进度。 自由对话\n随时在对话框中输入你想咨询的内容，GrantGo 会解答你的问题。 PDF预审\n支持上传 PDF 文件（如毕业证、社保证明等）。 上传后系统会进行解析并实时展示状态：上传中、解析中、完成或失败。 解析完成后可查看：匹配度百分比、资料清单检查结果、流程时间线建议。 技术架构 # 前端应用 (Frontend) 核心框架：Vue 3 + Vite + TypeScript 状态管理：Pinia（多会话 Chat Session 管理、用户状态） 路由管理：Vue Router UI \u0026amp; 样式：Tailwind CSS v4 + 纯手写极客风样式（Inter 字体） 特色库：markdown-it, mermaid, dompurify (安全渲染) 业务后端 (Backend - Go) 核心框架：Gin (Go 1.25) 数据库 \u0026amp; ORM：PostgreSQL / SQLite + GORM 实时通信：Gorilla WebSocket (处理流式对话) 身份认证：JWT 认证机制 + CSRF 防护 核心职责：处理用户认证、画像数据、地区工具、后台数据聚合、WebSocket 转发、安全中间件与限流。 AI 智能服务 (Backend - Python) 核心框架：FastAPI + Uvicorn AI 核心库：LangChain, OpenAI API (兼容 DeepSeek 等模型) 文档与向量库：PyMuPDF, ChromaDB, Redis 核心职责：纯 LLM 对话模式、System Prompt 策略控制、格式化 JSON 卡片生成、RAG 检索（备用/兜底链路）及 PDF 解析。 ","date":"2026年05月03日","externalUrl":null,"permalink":"/posts/20260503-grantgo/","section":"文章","summary":"GrantGo AI 技术文章说明","title":"GrantGo AI","type":"posts"},{"content":"","date":"2026年05月03日","externalUrl":null,"permalink":"/tags/grantgo-ai/","section":"Tags","summary":"","title":"GrantGo AI","type":"tags"},{"content":" 信息 以下内容转载自 Anthropic 官方网站技术文章并通过 AI 翻译，完整原文请见：https://www.anthropic.com/engineering/building-effective-agents\n我们曾与各行各业构建 LLM 代理的数十个团队合作。一贯地，最成功的实现通常使用简单、可组合的模式，而不是复杂的框架。\n在过去的一年里，我们与数十个旨在构建大语言模型 (LLM) 代理的团队紧密合作。我们观察到，最成功的案例并非使用了复杂的框架或专门的库，相反，它们往往是基于简单、可组合的模式构建的。\n在本文中，我们将分享从客户合作及自身代理构建过程中总结的经验，并为开发人员提供构建高效代理的实用建议。\n什么是代理？ # “代理 (Agent)”有多种定义方式。一些客户将其定义为完全自主的系统，能够利用各种工具长期独立运行以完成复杂任务。另一些则用该术语描述遵循预定义工作流、更具规定性的实现。在 Anthropic，我们将所有这些变体统称为代理系统 (agentic systems)，但在架构上对工作流 (workflows) 和代理 (agents) 做了重要区分：\n工作流是将 LLM 和工具通过预定义代码路径进行策划的系统。 代理则是 LLM 能够动态指导自身流程和工具使用，并保持对任务完成方式控制权的系统。 下面我们将详细探讨这两类代理系统。在附录 1（“代理的实践”）中，我们描述了客户认为此类系统特别具有价值的两个领域。\n何时（以及何时不）使用代理 # 在构建 LLM 应用程序时，我们建议寻找尽可能简单的解决方案，仅在必要时增加复杂性。这可能意味着根本不需要构建代理系统。代理系统通常以增加延迟和成本为代价来换取更好的任务性能，您应当权衡这种折衷是否值得。\n当需要更高复杂性时，工作流能为定义明确的任务提供可预测性和一致性；而当需要在规模化场景下发挥灵活性和模型驱动的决策能力时，代理则是更好的选择。然而，对于许多应用来说，通过检索和上下文示例优化单个 LLM 调用通常就足够了。\n何时及如何使用框架 # 有许多框架可以简化代理系统的实现，包括：\nClaude Agent SDK； AWS 的 Strands Agents SDK； Rivet，一个拖拽式 GUI LLM 工作流构建器；以及 Vellum，另一个用于构建和测试复杂工作流的 GUI 工具。 这些框架通过简化调用 LLM、定义和解析工具以及链接调用等标准底层任务，让入门变得容易。然而，它们往往会创建额外的抽象层，掩盖底层的 prompt 和响应，使调试变得更加困难。它们还可能诱导开发者在简单的方案就足够时增加不必要的复杂性。\n我们建议开发人员从直接使用 LLM API 开始：许多模式只需几行代码即可实现。如果您确实使用了框架，请确保您理解其底层代码。对“黑盒”功能的错误假设是客户出错的常见原因。\n请参阅我们的 Cookbook 获取一些示例实现。\n构建块、工作流和代理 # 在本节中，我们将探讨生产中常见的代理系统模式。我们将从基础构建块——增强型 LLM 开始，逐步增加复杂性，从简单的组合工作流演进到自主代理。\n基础构建块：增强型 LLM # 代理系统的基本构建块是经过增强（如检索、工具和记忆）的 LLM。我们目前的模型可以主动利用这些能力——生成搜索查询、选择合适的工具并确定要保留的信息。\n增强型 LLM\n我们建议关注实现的两个关键方面：针对特定用例定制这些能力，并确保它们为 LLM 提供简单、文档齐全的接口。虽然实现这些增强有很多方法，但其中一种是使用我们最近发布的 Model Context Protocol (MCP)，它允许开发人员通过简单的客户端实现集成第三方工具生态系统。\n在本文余下部分，我们将假设每个 LLM 调用都可以访问这些增强能力。\n工作流：Prompt 链 (Prompt chaining) # Prompt 链将任务分解为一系列步骤，其中每个 LLM 调用处理前一个步骤的输出。您可以在任何中间步骤添加程序化检查（见下图中的“gate”），以确保流程处于正确轨道。\nPrompt 链工作流\n何时使用此工作流： 此工作流非常适合可以轻松、清晰地分解为固定子任务的情况。其核心目标是通过让每个 LLM 调用处理更简单的子任务来换取更高的准确性。\n示例：\n生成营销文案，然后将其翻译成另一种语言。 编写文档大纲，检查大纲是否符合特定标准，然后根据大纲编写完整文档。 工作流：路由 (Routing) # 路由对输入进行分类，并将其导向专门的后续任务。这种工作流实现了关注点分离，允许构建更专业的 prompt。若没有此工作流，针对一种输入的优化往往会损害模型在其他输入上的表现。\n路由工作流\n何时使用此工作流： 路由适用于存在明显分类的复杂任务，且分类可以由 LLM 或传统分类模型/算法准确处理的情况。\n示例：\n将不同类型的客户服务查询（一般问题、退款请求、技术支持）引导至不同的下游流程、prompt 和工具。 将简单常见的问题路由至低成本模型（如 Claude 4.5 Haiku），而将困难罕见的问题路由至更强大的模型（如 Claude 4.5 Sonnet），以优化整体性能。 工作流：并行化 (Parallelization) # LLM 有时可以同时处理一个任务，并以程序化方式聚合其输出。并行化有两种关键变体：\n分截 (Sectioning)：将任务分解为并行的独立子任务。 投票 (Voting)：多次运行同一任务以获得多样化的输出。 并行化工作流\n何时使用此工作流： 当子任务可以并行化以提高速度，或者需要多个视角/尝试以获得更高可信度的结果时。对于涉及多重考量的复杂任务，当每个考量由独立的 LLM 调用负责时，性能通常更好。\n示例：\n分截：实施护栏系统，一个实例处理用户查询，另一个实例筛查不当内容。这通常比让单个 LLM 同时处理回复和护栏效果更好。 投票：检查代码是否存在漏洞，由多个不同的 prompt 进行审查，如果发现问题则进行标记。 工作流：编排者-工作者 (Orchestrator-workers) # 在编排者-工作者工作流中，中央 LLM 动态分解任务，将其分配给工作者 LLM，并汇总最终结果。\n编排者-工作者工作流\n何时使用此工作流： 适用于无法预知具体子任务的复杂任务（例如在编程中，需要修改的文件数量和性质取决于具体任务）。它与并行化的关键区别在于其灵活性——子任务不是预定义好的，而是由编排者根据输入动态决定的。\n示例：\n针对涉及多个文件复杂变更的编程产品。 需要从多个来源收集并分析信息的搜索任务。 工作流：评估者-优化者 (Evaluator-optimizer) # 在这种工作流中，一个 LLM 调用生成响应，另一个提供评估和反馈，并在循环中进行优化。\n评估者-优化者工作流\n何时使用此工作流： 适用于有明确评估标准且迭代改进具有衡量价值的场景。\n示例：\n文学翻译：译者 LLM 可能无法一次性捕捉所有细微差别，而评估者 LLM 可以提供有用的批评。 复杂搜索任务：需要多轮搜索和分析，由评估者决定是否需要进一步搜索。 代理 (Agents) # 随着 LLM 在理解复杂输入、推理规划、可靠使用工具以及错误恢复等方面能力的成熟，代理正逐渐进入生产环境。代理的工作通常始于用户的指令或交互讨论。任务明确后，代理独立进行规划和操作，并在需要进一步信息或判断时返回询问用户。在执行过程中，代理必须从每一步的运行环境（如工具调用结果或代码执行情况）中获取“事实依据 (ground truth)”以评估进度。代理可以在检查点或遇到障碍时暂停以获取人工反馈。如果是为了维持控制权，通常会包含停止条件（如最大迭代次数）。\n代理可以处理复杂任务，但其实现往往非常简单。它们通常就是在循环中使用工具、根据环境反馈进行决策的 LLM。因此，精心设计工具集及其文档至关重要。我们在附录 2（“为工具进行 Prompt 工程”）中详细介绍了相关最佳实践。\n自主代理\n何时使用代理： 适用于难以预测步骤数量、无法硬编码固定路径的开放式问题。由于代理具有自主性，其成本可能更高且存在错误累积的风险，因此建议在沙盒环境中进行广泛测试并配备相应的护栏。\n示例：\n解决 SWE-bench 任务 的编程代理。 我们的“计算机使用”参考实现。 编程代理的高级流程\n组合与定制模式 # 这些构建块并非一成不变的准则。它们是开发人员可以根据不同用例进行重塑和组合的常见模式。成功的关键在于衡量性能并不断迭代。再次强调：仅在复杂性能够带来显著改进时才考虑增加复杂性。\n总结 # LLM 领域的成功不在于构建最复杂的系统，而在于为您的需求构建正确的系统。从简单的 prompt 开始，通过全面的评估进行优化，仅在简单方案无法满足需求时再引入多步代理系统。\n在实现代理时，我们遵循三个核心原则：\n保持代理设计的简洁性。 通过显式展示代理的规划步骤来优先考虑透明度。 通过详尽的工具文档和测试精心打造您的代理-计算机接口 (ACI)。 框架可以帮助您快速入门，但在进入生产阶段时，不要犹豫去减少抽象层并使用基础组件。遵循这些原则，您可以创建出既强大又可靠、可维护且受用户信赖的代理。\n致谢 # 由 Erik Schluntz 和 Barry Zhang 撰写。\n附录 1：代理的实践 # 由于篇幅关系，附录部分主要探讨了客户支持和编程代理两个最具前景的应用领域。\n附录 2：为工具进行 Prompt 工程 # 无论您构建哪种代理系统，工具都是核心。我们建议：\n给模型足够的 Token 让它在采取行动前进行“思考”。 保持格式接近模型在互联网上自然见到的文本。 确保没有格式“开销”（如要求精确计算数千行代码的行数）。 站在模型的角度思考工具描述是否清晰，并进行 Poka-yoke（防错）设计。 我们在构建 SWE-bench 代理时，发现将相对路径改为绝对路径能显著降低模型的出错率。这对提升代理-计算机接口 (ACI) 的稳定性至关重要。\n","date":"2026年04月28日","externalUrl":null,"permalink":"/posts/20260428-02.buildingeffectiveagents/","section":"文章","summary":"","title":"02.Building Effective Agents","type":"posts"},{"content":"","date":"2026年04月28日","externalUrl":null,"permalink":"/series/anthropic/","section":"Series","summary":"","title":"Anthropic","type":"series"},{"content":"","date":"2026年04月28日","externalUrl":null,"permalink":"/tags/anthropic/","section":"Tags","summary":"","title":"Anthropic","type":"tags"},{"content":"","date":"2026年04月28日","externalUrl":null,"permalink":"/tags/workflow/","section":"Tags","summary":"","title":"Workflow","type":"tags"},{"content":"","date":"2026年04月27日","externalUrl":"https://grantgo.hassenfeld.org/","permalink":"/projects/grantgo/","section":"作品","summary":"GrantGo 你的AI智能申补助手。从精准画像到材料预审，我们全程陪伴。让注册更简单，让咨询更高效，助你少走弯路，快速锁定最适合的补贴机会。","title":"GrantGo","type":"projects"},{"content":"","date":"2026年04月27日","externalUrl":null,"permalink":"/projects/","section":"作品","summary":"","title":"作品","type":"projects"},{"content":" 信息 以下内容转载自 Anthropic 官方网站技术文章并通过 AI 翻译，完整原文请见：https://www.anthropic.com/engineering/contextual-retrieval\n为了让 AI 模型在特定语境下发挥作用，它通常需要访问背景知识。例如，客户支持聊天机器人需要了解其服务的特定业务知识，而法律分析机器人则需要掌握大量的过往案例。\n开发人员通常使用检索增强生成（RAG）来增强 AI 模型。RAG 是一种从知识库中检索相关信息并将其附加到用户提示词中的方法，能显著提升模型的响应质量。然而，传统 RAG 方案在对信息进行编码时会丢失语境，这往往导致系统无法从知识库中检索到最相关的信息。\n在本文中，我们将介绍一种大幅提升 RAG 检索步骤的方法，称为“语境化检索（Contextual Retrieval）”。它结合了两种子技术：语境化嵌入（Contextual Embeddings）和语境化 BM25（Contextual BM25）。该方法可将检索失败率降低 49%，若结合重排序（Reranking）技术，降幅可达 67%。检索准确性的显著提升将直接转化为下游任务中更优质的表现。\n您可以通过 我们的 Cookbook 轻松部署基于 Claude 的语境化检索方案。\n关于直接使用长提示词的建议 # 有时最简单的方案就是最好的。如果您的知识库小于 200,000 个 token（约 500 页材料），您可以直接将整个知识库放入提示词中，无需使用 RAG 或类似方法。\n几周前，我们为 Claude 推出了 提示词缓存（Prompt Caching），使这种方法不仅更快而且更具成本效益。开发人员现在可以在 API 调用之间缓存常用提示词，从而缩短延迟 2 倍以上，并降低成本高达 90%（详情请参阅 提示词缓存 Cookbook）。\n然而，随着知识库的增长，您将需要更具扩展性的方案。而这正是语境化检索的用武之地。\nRAG 入门：扩展至大规模知识库 # 对于无法放入上下文窗口的大型知识库，RAG 是典型的解决方案。RAG 的工作原理是通过以下步骤对知识库进行预处理：\n将知识库（文档语料库）分解为较小的文本块（Chunk），通常不超过几百个 token； 使用嵌入模型（Embedding Model）将这些文本块转换为编码语义的向量嵌入； 将这些嵌入存储在向量数据库中，以便进行语义相似度搜索。 在运行时，当用户输入查询时，向量数据库会查找语义上与查询最相关的文本块。然后，这些最相关的文本块被添加到发送给生成模型的提示词中。\n虽然嵌入模型擅长捕捉语义关系，但它们可能会遗漏关键的精确匹配。幸运的是，有一种较老的技术可以在这种情况下提供帮助。BM25（最佳匹配 25）是一种利用词法匹配查找精确词语或短语匹配的排序函数。对于包含唯一标识符或技术术语的查询，它尤其有效。\nBM25 基于 TF-IDF（词频-逆文档频率）概念构建。TF-IDF 衡量一个词对集合中某个文档的重要性。BM25 通过考虑文档长度并对词频应用饱和函数进行了优化，这有助于防止常用词占据主导地位。\n以下是 BM25 在语义嵌入失败时能够成功的原因：假设用户在技术支持数据库中查询“错误代码 TS-999”。嵌入模型可能会找到关于错误代码的一般内容，但可能错过精确的“TS-999”匹配。而 BM25 会寻找这个特定的字符串来识别相关文档。\n通过结合嵌入技术和 BM25 技术，RAG 解决方案可以更准确地检索到最适用的文本块，步骤如下：\n将知识库（文档语料库）分解为较小的文本块（通常不超过几百个 token）； 为这些文本块创建 TF-IDF 编码和语义嵌入； 使用 BM25 根据精确匹配查找排名靠前的文本块； 使用嵌入根据语义相似度查找排名靠前的文本块； 使用排名融合（Rank Fusion）技术合并来自 (3) 和 (4) 的结果并去重； 将排名靠前的 K 个文本块添加到提示词以生成响应。 通过结合使用 BM25 和嵌入模型，传统的 RAG 系统可以提供更全面准确的结果，平衡了精确词项匹配与广泛的语义理解。\n一个标准的检索增强生成（RAG）系统，结合使用嵌入和 BM25 来检索信息。\n这种方法可以让您以高成本效益扩展到巨大规模的知识库，远超单个提示词所能容纳的范围。但这些传统的 RAG 系统有一个显著的技术局限：它们往往会破坏语境。\n传统 RAG 中的语境困局 # 在传统 RAG 中，为了提高检索效率，文档通常被切分为小块。虽然这种方法适用于许多应用，但当日语块缺乏足够的上下文语境时，就会产生问题。\n例如，假设您的知识库中有财务信息（例如美国 SEC 文件），并且您收到了以下问题：“ACME Corp 在 2023 年第二季度的营收增长是多少？”\n一个相关的文本块可能包含这样的文字：“公司营收较上一季度增长了 3%。” 然而，这个文本块本身并没有说明它指的是哪家公司或哪段时期，这使得检索正确信息或有效利用信息变得非常困难。\n引入语境化检索（Contextual Retrieval） # 语境化检索通过在嵌入（“语境化嵌入”）和创建 BM25 索引（“语境化 BM25”）之前，为每个文本块添加特定于该块的解释性语境来解决这一问题。\n让我们回到 SEC 文件的例子。以下是一个文本块可能被转换后的样子：\noriginal_chunk = \u0026#34;The company\u0026#39;s revenue grew by 3% over the previous quarter.\u0026#34; contextualized_chunk = \u0026#34;This chunk is from an SEC filing on ACME corp\u0026#39;s performance in Q2 2023; the previous quarter\u0026#39;s revenue was $314 million. The company\u0026#39;s revenue grew by 3% over the previous quarter.\u0026#34; 值得注意的是，过去也曾提出过使用语境改善检索的其他方法，包括：为文本块添加通用文档摘要（我们实验后发现收益非常有限）、假设性文档嵌入以及基于摘要的索引（我们评估后发现性能较低）。这些方法与本文提出的方案有所不同。\n实现语境化检索 # 当然，手动为知识库中成千上万、甚至数百万个文本块添加注释的工作量太大了。为了实现语境化检索，我们寻求 Claude 的帮助。我们编写了一个提示词，指导模型提供简练、针对特定文本块的说明，并利用整个文档的内容来解释该文本块。我们使用以下 Claude 3 Haiku 提示词为每个文本块生成语境：\n\u0026lt;document\u0026gt; {{WHOLE_DOCUMENT}} \u0026lt;/document\u0026gt; Here is the chunk we want to situate within the whole document \u0026lt;chunk\u0026gt; {{CHUNK_CONTENT}} \u0026lt;/chunk\u0026gt; Please give a short succinct context to situate this chunk within the overall document for the purposes of improving search retrieval of the chunk. Answer only with the succinct context and nothing else. 生成的语境文本通常为 50-100 个 token，在对文本块进行嵌入及创建 BM25 索引之前，将其前置添加到文本块中。\n以下是实际预处理流程的示意：\n语境化检索是一种提升检索准确性的预处理技术。\n如果您有兴趣使用语境化检索，可以参考 我们的 Cookbook 开始实践。\n使用提示词缓存降低语境化检索成本 # 得益于上文提到的特殊提示词缓存功能，使用 Claude 可以以极低成本实现语境化检索。通过提示词缓存，您无需为每个文本块重复传入参考文档。只需将文档加载到缓存中一次，随后参考已缓存的内容即可。假设文本块为 800 token，文档为 8k token，语境指令为 50 token，每个文本块生成的语境为 100 token，每百万文档 token 生成语境化文本块的一次性成本仅为 1.02 美元。\n","date":"2026年04月25日","externalUrl":null,"permalink":"/posts/20260425-01.introducingcontextualretrieval/","section":"文章","summary":"","title":"01.Introducing Contextual Retrieval","type":"posts"},{"content":"","date":"2026年04月25日","externalUrl":null,"permalink":"/tags/rag/","section":"Tags","summary":"","title":"RAG","type":"tags"},{"content":"","date":"2026年04月20日","externalUrl":null,"permalink":"/posts/20260420-00.introduction/","section":"文章","summary":"","title":"00.Introduction","type":"posts"},{"content":"","date":"2026年04月20日","externalUrl":null,"permalink":"/tags/ai/","section":"Tags","summary":"","title":"AI","type":"tags"},{"content":"","date":"2026年04月20日","externalUrl":null,"permalink":"/tags/llm/","section":"Tags","summary":"","title":"LLM","type":"tags"},{"content":" 关于我 # 我是Hassenfeld，软件工程专业本科生。目前正在不断学习探索新的知识和技术……\n技术栈 # Languages # Frameworks \u0026amp; Libraries # 教育经历 # 本科：大连理工大学，立命馆大学 高中：浙江省杭州第二中学 爱好 # 篮球、排球、自行车、胶佬、摄影、影视爱好者……\n关于本站 # 本站基于 Hugo 静态站点生成器构建，UI 层采用 Blowfish 主题。源代码托管于 GitHub，通过 GitHub Actions驱动的 CI/CD 流动推送到 GitHub Pages 进行静态托管，并由 Cloudflare 承载域名解析。\n","date":"2026年03月25日","externalUrl":null,"permalink":"/about/","section":"Hassenfeld","summary":"","title":"关于","type":"page"},{"content":"","date":"2026年03月25日","externalUrl":null,"permalink":"/posts/","section":"文章","summary":"","title":"文章","type":"posts"},{"content":" 建站 # 我用Hugo\u0026amp;　Blowfish，建立了第一个网站。\n本站基于 Hugo 静态站点生成器构建，UI 层采用 Blowfish 主题。源代码托管于 GitHub，通过 GitHub Actions驱动的 CI/CD 流动推送到 GitHub Pages 进行静态托管，并由 Cloudflare 承载域名解析。\n\u0026mdash;26.3.22\n","date":"2026年03月22日","externalUrl":null,"permalink":"/posts/20260325-stationbuilt/","section":"文章","summary":"","title":"建站","type":"posts"},{"content":"","date":"2026年02月16日","externalUrl":"/works/happySpringFestival2026/","permalink":"/projects/happyspringfestival2026/","section":"作品","summary":"点亮许愿墙，一起许下崭新的新年愿望吧！","title":"2026 新春快乐","type":"projects"},{"content":"","date":"2025年12月31日","externalUrl":"/works/happyNewYear2026/index.html","permalink":"/projects/happynewyear2026/","section":"作品","summary":"指尖轻触，烟花绽放迎新年！","title":"2026 新年快乐","type":"projects"},{"content":"","externalUrl":null,"permalink":"/authors/","section":"Authors","summary":"","title":"Authors","type":"authors"},{"content":"","externalUrl":null,"permalink":"/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"}]