<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>个人博客</title><description>AI 应用开发</description><link>https://blog.miansu.eu.cc/</link><templateTheme>Firefly</templateTheme><templateThemeVersion>6.16.5</templateThemeVersion><templateThemeUrl>https://github.com/CuteLeaf/Firefly</templateThemeUrl><lastBuildDate>2026年8月30日 20:51:32</lastBuildDate><item><title>从“答错了”到“错在哪”：RAG 检索诊断接口设计</title><link>https://blog.miansu.eu.cc/posts/rag-diagnostics-design/</link><guid isPermaLink="true">https://blog.miansu.eu.cc/posts/rag-diagnostics-design/</guid><description>面向考试题库场景，记录如何让缺失材料、召回过程、重排结果和关键短语覆盖变得可检查。</description><pubDate>Mon, 27 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;&lt;p&gt;这篇笔记由 2026 年 7 月考试 RAG Agent 平台的阶段记录整理成文，重点记录诊断思路，不把一次样例结果包装成普遍指标。&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;考试题库是一个很适合暴露 RAG 缺陷的场景。问题常常依赖一段材料、图表或公式；如果材料缺失，模型仍可能凭常识给出流畅答案。用户看到的只是“答错了”，研发人员却需要判断错误发生在存储、解析、召回、重排还是生成阶段。&lt;/p&gt;
&lt;p&gt;我为此设计诊断接口时，目标不是再返回一个答案，而是返回一份可以追踪的检索报告。&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;1. 诊断输入要保留业务语义&lt;a href=&quot;#1-诊断输入要保留业务语义&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;普通搜索接口通常只接收 query。考试场景还需要题干、材料、选项、科目和题型。材料不能默认从题干里推断，而应明确读取业务字段；当材料字段为空时，诊断结果要直接标记，而不是悄悄跳过。&lt;/p&gt;&lt;p&gt;这一点非常重要：接口健康、数据库有题目、知识库有文档，三者都不等于这道题所依赖的材料已经被正确存储。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;2. 返回每个阶段，而不是只返回最终 top-k&lt;a href=&quot;#2-返回每个阶段而不是只返回最终-top-k&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;诊断响应可以分为以下层次：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;request&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;├─ normalized_query&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;├─ material_status&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└─ key_phrases&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;retrieval&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;├─ bm25_candidates&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;├─ vector_candidates&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;├─ rrf_candidates&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└─ reranked_candidates&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;coverage&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;├─ matched_phrases&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└─ missing_phrases&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;context&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└─ final_chunks&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;候选条目至少保留文档标识、片段标识、原文摘要、分数、排名和召回来源。这样，开发者能够看到目标片段是否被 BM25 命中、是否只出现在向量结果中、RRF 后排名如何，以及是否在重排阶段被挤出上下文。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;3. 关键短语覆盖比单一分数更直观&lt;a href=&quot;#3-关键短语覆盖比单一分数更直观&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;相似度分数难以直接解释“为什么这道题缺证据”。我会从题干和材料中提取有区分度的实体、术语和限定条件，再检查最终候选是否覆盖它们。&lt;/p&gt;&lt;p&gt;&lt;code&gt;missing_phrases&lt;/code&gt; 不是新的相关性算法，而是一个面向排障的解释层。例如题目涉及“特定年份”“实验条件”和“图中曲线 B”，最终上下文只覆盖了主题词，却没有覆盖这些限定条件，那么即使相似度很高，回答仍可能错误。&lt;/p&gt;&lt;p&gt;这个字段还能指导下一步动作：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;专有名词缺失：检查 BM25 分词与索引；&lt;/li&gt;
&lt;li&gt;同义表达缺失：检查向量模型与查询改写；&lt;/li&gt;
&lt;li&gt;材料限定条件缺失：检查业务字段和切分；&lt;/li&gt;
&lt;li&gt;候选有覆盖但最终上下文缺失：检查重排、阈值和上下文预算。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;4. 诊断接口不直接替生产参数背书&lt;a href=&quot;#4-诊断接口不直接替生产参数背书&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;一个样例召回成功，不代表当前阈值适用于所有科目。诊断接口的职责是暴露过程，而不是自动把 top-k、权重或阈值写回生产配置。参数调整应该基于覆盖不同题型的评测集，并比较召回、答案正确性、延迟和成本。&lt;/p&gt;&lt;p&gt;同样，RRF 的常数、BM25 与向量召回数量、重排 top-n 都只是起点。它们需要通过真实数据验证，不应被写成脱离数据集的“最佳实践数字”。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;5. 错误路径也要可用&lt;a href=&quot;#5-错误路径也要可用&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;诊断接口遇到材料缺失、知识库未索引、向量服务不可用或重排超时时，不应只返回 500。更有用的响应是保留已经完成的阶段，并通过状态字段说明停止原因。例如 BM25 已得到结果、向量服务失败，报告仍可返回关键词候选，同时标记混合检索未完成。&lt;/p&gt;&lt;p&gt;这让诊断工具本身不会成为新的黑盒，也方便前端把“系统错误”和“证据不足”用不同方式呈现。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;6. 最终收获&lt;a href=&quot;#6-最终收获&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;RAG 系统真正难维护的部分，不是写出一次能回答问题的 Demo，而是在它答错时能快速定位原因。一个好的诊断接口应该沿数据流保留证据、区分存储缺失与检索失败、展示关键短语覆盖，并把参数调优留给评测而不是直觉。&lt;/p&gt;&lt;section&gt;&lt;h3&gt;参考资料&lt;a href=&quot;#参考资料&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.elastic.co/docs/reference/elasticsearch/rest-apis/reciprocal-rank-fusion&quot; target=&quot;_blank&quot;&gt;Elasticsearch：Reciprocal rank fusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.sbert.net/examples/sentence_transformer/applications/retrieve_rerank/README.html&quot; target=&quot;_blank&quot;&gt;Sentence Transformers：Retrieve &amp;amp; Re-Rank&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.ragas.io/&quot; target=&quot;_blank&quot;&gt;Ragas 文档&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Tencent/WeKnora&quot; target=&quot;_blank&quot;&gt;WeKnora 开源仓库&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;/section&gt;</content:encoded></item><item><title>WeKnora 二次开发笔记：把 RAG 拆成可观察的工程链路</title><link>https://blog.miansu.eu.cc/posts/weknora-rag-pipeline/</link><guid isPermaLink="true">https://blog.miansu.eu.cc/posts/weknora-rag-pipeline/</guid><description>从文档解析、索引、召回、重排到引用与生成，记录我在 WeKnora 二次开发中形成的 RAG 排障方法。</description><pubDate>Mon, 06 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;&lt;p&gt;这篇笔记由 2026 年 7 月的 WeKnora 二次开发阶段记录整理成文。WeKnora 是腾讯开源的知识库与 Agent 框架，我的实践基于其开源版本进行扩展与排障。&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;很多 RAG 问题在页面上只有一个现象：“回答不完整”或“没有引用到正确内容”。如果把所有原因都归结为模型，调试会非常低效。一次回答至少经过文档解析、切分、索引、查询处理、召回、重排、上下文组装、模型流式输出和前端渲染。任何一环都可能丢失信息。&lt;/p&gt;
&lt;p&gt;我在 WeKnora 二次开发中形成的核心方法，是把整条链路拆成可以独立确认的证据点。&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;1. 先确认知识是否真的进入系统&lt;a href=&quot;#1-先确认知识是否真的进入系统&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;用户看到原 PDF 里有一段材料，并不等于解析结果里也有。复杂版面、扫描图片、公式和表格都可能让解析器只保留部分文字。排查的第一步不是改 Prompt，而是检查解析后的知识条目：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;原始文件是否完成解析；&lt;/li&gt;
&lt;li&gt;目标段落是否出现在清洗文本中；&lt;/li&gt;
&lt;li&gt;图片或公式是否只有本地资源引用，却没有可检索的文本描述；&lt;/li&gt;
&lt;li&gt;切分后，题干与材料是否被拆到无法建立联系的片段中。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;如果知识在这一层已经缺失，后续无论怎样提高 top-k 都找不回来。此时应该修复解析、OCR、VLM 描述或切分策略。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;2. 再判断是“没召回”还是“召回后被过滤”&lt;a href=&quot;#2-再判断是没召回还是召回后被过滤&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;当目标文本确实存在时，我会保留查询、候选结果、各路分数、融合排名与重排结果。混合检索中，BM25 对专有名词和原句更敏感，向量检索更适合语义改写；RRF 可以融合两路排名，但融合后仍可能因为阈值或重排模型丢掉关键片段。&lt;/p&gt;&lt;p&gt;因此，“检索接口返回 200”只说明接口可用，不说明召回质量正确。真正有价值的诊断输出应回答：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;每一路召回了什么；&lt;/li&gt;
&lt;li&gt;目标片段在哪一步消失；&lt;/li&gt;
&lt;li&gt;查询中的哪些关键短语没有被候选证据覆盖；&lt;/li&gt;
&lt;li&gt;上下文最终向模型发送了什么。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;我倾向于把缺失短语作为诊断字段返回，而不是只展示一个总分。这样，排障人员可以区分“材料根本没有”“关键词没有召回”“重排压低了结果”和“模型忽略了已提供证据”。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;3. 上下文组装必须保留来源&lt;a href=&quot;#3-上下文组装必须保留来源&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;召回片段不能只拼成一个长字符串。每个片段需要携带知识库、文档、页码或位置、召回方式与分数，引用编号也应在进入模型前固定。这样，模型输出中的 citation 才能映射回真实材料，前端点击引用时也能展示正确来源。&lt;/p&gt;&lt;p&gt;对于考试材料或长文问答，材料、题干和选项可能来自不同结构字段。我会在组装阶段显式检查材料字段是否为空，并避免用模型常识“补齐”缺失材料。没有证据时，系统应该诚实地说明缺口，而不是生成一个看似完整的答案。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;4. 流式输出是独立的故障面&lt;a href=&quot;#4-流式输出是独立的故障面&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;即使后端已经正确召回和生成，SSE 连接、模型结束标记或前端状态机处理异常，仍可能让用户只看到半段回答。因此我会分别确认：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;模型端是否完成生成并返回结束原因；&lt;/li&gt;
&lt;li&gt;服务端是否发送完整事件与终止事件；&lt;/li&gt;
&lt;li&gt;浏览器是否收到所有 chunk；&lt;/li&gt;
&lt;li&gt;页面是否因为状态更新或 Markdown 渲染提前停止。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;把“检索质量”和“输出完整性”分开验证，可以避免一看到截断就盲目调整检索参数。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;5. Agent 不应掩盖 RAG 的证据边界&lt;a href=&quot;#5-agent-不应掩盖-rag-的证据边界&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;ReAct Agent 可以根据问题调用知识检索、Web 搜索和 MCP 工具，但工具更多并不自动带来更可靠的答案。每次工具调用仍要留下输入、输出、耗时与来源；最终回答要能说明使用了哪些证据。Agent 的规划状态可以是运行时上下文，却不能代替业务数据和知识库事实。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;6. 一条实用的排障顺序&lt;a href=&quot;#6-一条实用的排障顺序&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;我的固定顺序是：存储内容 → 解析文本 → 切分结果 → 初始召回 → 融合与重排 → 上下文包 → 模型输出 → SSE 事件 → 前端渲染。沿着数据流逐层缩小范围，通常比直接改模型、改 Prompt 或扩大 top-k 更快找到根因。&lt;/p&gt;&lt;section&gt;&lt;h3&gt;参考资料&lt;a href=&quot;#参考资料&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Tencent/WeKnora&quot; target=&quot;_blank&quot;&gt;WeKnora 开源仓库&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/guide/current/relevance-intro.html&quot; target=&quot;_blank&quot;&gt;BM25 与 Elasticsearch 相关性介绍&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://langfuse.com/docs/observability/overview&quot; target=&quot;_blank&quot;&gt;Langfuse Observability&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events&quot; target=&quot;_blank&quot;&gt;MDN：Server-Sent Events&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;/section&gt;</content:encoded></item><item><title>从 Prompt 到 Context Compiler：智能编码 Harness 的上下文工程笔记</title><link>https://blog.miansu.eu.cc/posts/context-engineering-for-harness/</link><guid isPermaLink="true">https://blog.miansu.eu.cc/posts/context-engineering-for-harness/</guid><description>记录我在个人 Harness 实践中对上下文分层、证据检索、状态压缩和工具反馈闭环的理解。</description><pubDate>Tue, 18 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;&lt;p&gt;这是一篇围绕智能编码 Agent 的学习笔记，重点总结上下文如何被组织成可执行输入，而不是把对话历史原样塞给模型。&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;做智能编码工具时，我逐渐意识到：真正决定 Agent 行为质量的，不只是模型和 Prompt，还包括每一轮到底给了模型哪些事实、这些事实来自哪里、是否仍然有效，以及工具执行后怎样把新状态带回下一轮。&lt;/p&gt;
&lt;p&gt;因此，我更愿意把上下文理解成一种&lt;strong&gt;按当前任务动态编译的运行时产物&lt;/strong&gt;。&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;1. 上下文不是聊天记录的同义词&lt;a href=&quot;#1-上下文不是聊天记录的同义词&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;完整聊天记录具有审计价值，但并不适合直接成为每轮推理输入。历史越长，重复信息、过期判断和无关工具输出越多，模型越难抓住真正影响下一步行动的内容。&lt;/p&gt;&lt;p&gt;一个更清晰的上下文可以分成几层：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;系统契约&lt;/strong&gt;：角色、权限、禁止事项和完成标准；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;工作区事实&lt;/strong&gt;：仓库结构、技术栈、项目规则和当前分支状态；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;任务状态&lt;/strong&gt;：目标、已完成步骤、未解决问题和下一步动作；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;检索证据&lt;/strong&gt;：与当前问题直接相关的代码、日志、文档和配置；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;近期交互&lt;/strong&gt;：用户最新补充、关键决策以及刚刚返回的工具结果。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;这些信息的优先级、有效期和可信来源不同，不应该被压成一段没有边界的长文本。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;2. Context Compiler 要解决什么&lt;a href=&quot;#2-context-compiler-要解决什么&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;我把 Context Compiler 理解为一层输入编排器。它不负责替模型思考，而是负责在模型思考前完成几件确定性的工作：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;根据任务阶段选择需要的上下文来源；&lt;/li&gt;
&lt;li&gt;对检索结果去重、排序并保留来源位置；&lt;/li&gt;
&lt;li&gt;区分用户确认的事实、代码事实和模型推测；&lt;/li&gt;
&lt;li&gt;在 token 预算内优先保留高信号信息；&lt;/li&gt;
&lt;li&gt;将工具执行后的新状态写回任务记录，而不是只留在临时对话中。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;压缩也不等于简单摘要。路径、错误码、约束和尚未验证的假设如果在摘要中消失，后续 Agent 就可能基于不完整信息继续行动。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;3. 记忆应该服务任务，而不是替代事实源&lt;a href=&quot;#3-记忆应该服务任务而不是替代事实源&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;对智能编码 Agent 来说，记忆可以保存用户偏好、常用流程和过去解决问题的方法，但代码仓库、测试输出和运行日志仍然是当前事实的权威来源。&lt;/p&gt;&lt;p&gt;我会把信息区分为：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;可以长期复用的工作偏好；&lt;/li&gt;
&lt;li&gt;只在当前任务有效的临时状态；&lt;/li&gt;
&lt;li&gt;必须重新读取的易变事实；&lt;/li&gt;
&lt;li&gt;只能作为候选、不能直接执行的历史经验。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;这样可以减少“记住了旧结论，却没有检查当前代码”的问题。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;4. Harness 的核心是执行闭环&lt;a href=&quot;#4-harness-的核心是执行闭环&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;在个人 Harness 的二次开发实践中，我更关注下面这条循环：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;理解目标 → 编译上下文 → 选择工具 → 执行动作&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↑                              ↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;验证结果 ← 更新任务状态 ← 记录结构化反馈&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;每个工具结果都应该回答：动作是否成功、改变了什么、还缺什么证据。只有把验证结果重新进入上下文，Agent 才不是“调用工具的聊天机器人”，而是一个可以持续修正计划的执行系统。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;5. 我的检查清单&lt;a href=&quot;#5-我的检查清单&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;当前输入里是否混入过期状态？&lt;/li&gt;
&lt;li&gt;关键结论能否回到代码、日志或文档来源？&lt;/li&gt;
&lt;li&gt;工具输出是否经过裁剪，但没有丢失错误信息？&lt;/li&gt;
&lt;li&gt;用户偏好与项目事实是否被分开管理？&lt;/li&gt;
&lt;li&gt;压缩后是否仍保留未完成事项和验证边界？&lt;/li&gt;
&lt;li&gt;下一轮能否明确知道为什么要执行某个动作？&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;对我而言，上下文工程的目标不是让模型“看到更多”，而是让它在正确的时刻看到足够、可信且可行动的信息。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>Agent 上线前的安全边界：权限、确认与可追溯执行</title><link>https://blog.miansu.eu.cc/posts/production-agent-guardrails/</link><guid isPermaLink="true">https://blog.miansu.eu.cc/posts/production-agent-guardrails/</guid><description>整理生产级 Agent 在工具权限、提示注入、人工确认、审计日志和失败恢复方面需要建立的基础边界。</description><pubDate>Wed, 05 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;当 Agent 只能生成文字时，错误回答的影响通常停留在页面里；当它能够修改文件、访问数据库、发送消息或调用业务接口后，模型的一次误判就可能变成真实副作用。&lt;/p&gt;
&lt;p&gt;因此，生产级 Agent 的安全目标不是保证模型永不犯错，而是让错误被限制在可发现、可阻断和可恢复的范围内。&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;1. 把“思考”和“执行”分开&lt;a href=&quot;#1-把思考和执行分开&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;模型可以生成计划和候选动作，但真正执行前应经过确定性校验。执行层至少需要检查：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;工具是否在当前任务的允许列表中；&lt;/li&gt;
&lt;li&gt;参数是否满足类型、范围和业务约束；&lt;/li&gt;
&lt;li&gt;目标资源是否与用户授权一致；&lt;/li&gt;
&lt;li&gt;操作是否会产生不可逆副作用；&lt;/li&gt;
&lt;li&gt;当前身份是否具备所需权限。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;Prompt 中写一句“请谨慎操作”不能代替服务端权限控制。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;2. 按风险设置不同确认级别&lt;a href=&quot;#2-按风险设置不同确认级别&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;所有操作都弹窗会让用户疲劳，所有操作都自动执行又过于危险。更合理的方式是分级：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;搜索、读取和状态检查可以直接执行；&lt;/li&gt;
&lt;li&gt;创建草稿、生成临时产物等可恢复操作可以自动执行并报告；&lt;/li&gt;
&lt;li&gt;覆盖文件、提交代码、发送外部消息需要展示目标和变更摘要；&lt;/li&gt;
&lt;li&gt;删除数据、调整权限、批量迁移等高风险操作需要明确确认和额外校验。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;确认信息要具体说明“将对什么对象做什么”，而不是只问“是否继续”。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;3. 外部内容默认是不可信输入&lt;a href=&quot;#3-外部内容默认是不可信输入&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;网页、邮件、文档和代码注释中都可能包含类似指令的文字。Agent 在检索这些内容后，必须把它们当作数据，而不能自动提升为系统命令。&lt;/p&gt;&lt;p&gt;可以采用的防护包括：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;在上下文中标记内容来源和信任级别；&lt;/li&gt;
&lt;li&gt;不允许检索内容修改系统权限；&lt;/li&gt;
&lt;li&gt;高风险工具只接受结构化参数；&lt;/li&gt;
&lt;li&gt;对敏感字段进行输出过滤；&lt;/li&gt;
&lt;li&gt;在执行前重新校验用户原始目标。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;提示注入无法只靠一句防御 Prompt 解决，它需要上下文隔离、权限最小化和执行层校验共同完成。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;4. 每次工具调用都应可追溯&lt;a href=&quot;#4-每次工具调用都应可追溯&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;审计日志不应该只保存模型最终回复，还应记录：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;调用的工具与时间；&lt;/li&gt;
&lt;li&gt;参数范围与目标标识；&lt;/li&gt;
&lt;li&gt;使用的权限和确认状态；&lt;/li&gt;
&lt;li&gt;工具返回的结构化结果；&lt;/li&gt;
&lt;li&gt;重试、失败与回滚过程；&lt;/li&gt;
&lt;li&gt;最终对外部状态产生的变化。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;涉及隐私和密钥时，日志应做脱敏，不能为了可观测性把敏感数据完整复制一份。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;5. 失败恢复是安全能力的一部分&lt;a href=&quot;#5-失败恢复是安全能力的一部分&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Agent 可能在多步骤任务的中间失败。只记录“任务失败”并不足够，还需要知道哪些步骤已经生效。&lt;/p&gt;&lt;p&gt;对重要工作流，可以考虑：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;为步骤分配稳定 ID，支持幂等重试；&lt;/li&gt;
&lt;li&gt;在执行前保存必要的旧状态；&lt;/li&gt;
&lt;li&gt;使用检查点记录已完成阶段；&lt;/li&gt;
&lt;li&gt;为可逆操作提供补偿动作；&lt;/li&gt;
&lt;li&gt;无法自动恢复时，输出人工接管所需证据。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;6. 上线前检查清单&lt;a href=&quot;#6-上线前检查清单&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;工具是否遵循最小权限原则？&lt;/li&gt;
&lt;li&gt;读操作和写操作是否明确区分？&lt;/li&gt;
&lt;li&gt;高风险动作是否需要确认？&lt;/li&gt;
&lt;li&gt;外部内容是否被标记为不可信数据？&lt;/li&gt;
&lt;li&gt;服务端是否重新校验参数与身份？&lt;/li&gt;
&lt;li&gt;重复调用是否会产生重复副作用？&lt;/li&gt;
&lt;li&gt;是否能追踪每一步改变了什么？&lt;/li&gt;
&lt;li&gt;失败后能否停止、恢复或交给人工处理？&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;对 Agent 来说，能力上限很重要，但可控边界决定了它能否真正进入生产环境。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>具身智能学习笔记：智能体需要怎样的记忆系统</title><link>https://blog.miansu.eu.cc/posts/embodied-agent-memory/</link><guid isPermaLink="true">https://blog.miansu.eu.cc/posts/embodied-agent-memory/</guid><description>从世界状态、情景记忆、语义记忆和技能记忆出发，思考具身智能体如何在持续交互中保留有效经验。</description><pubDate>Sun, 14 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;语言模型面对的大多是离散文本，而具身智能体面对的是持续变化的环境。摄像头、传感器、动作反馈和任务指令会不断产生新信息，智能体既不能保存并反复读取全部原始观测，也不能只依赖当前一帧做决定。&lt;/p&gt;
&lt;p&gt;因此，具身智能中的记忆问题，本质上是：&lt;strong&gt;怎样把连续经验整理成可用于下一次决策的状态。&lt;/strong&gt;&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;1. 当前世界状态不是长期记忆&lt;a href=&quot;#1-当前世界状态不是长期记忆&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;智能体首先需要一个可更新的世界状态，例如物体位置、可交互区域、当前任务阶段和最近动作结果。它强调的是“现在相信环境是什么样”，而不是保存完整历史。&lt;/p&gt;&lt;p&gt;世界状态必须处理不确定性。视觉模型识别到桌上有杯子，不代表杯子一直存在；机器人移动后，相机视角变化，旧坐标也可能失效。因此状态应包含观测时间、来源和置信度，并允许新证据覆盖旧判断。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;2. 四类记忆承担不同职责&lt;a href=&quot;#2-四类记忆承担不同职责&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;我倾向于把具身智能体的记忆拆成四类：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;工作记忆&lt;/strong&gt;：当前目标、最近观测、候选动作和短期计划；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;情景记忆&lt;/strong&gt;：一次任务中发生了什么、采取了什么动作、结果如何；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;语义记忆&lt;/strong&gt;：环境中的稳定知识，例如物体属性、空间规则和用户偏好；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;技能记忆&lt;/strong&gt;：可以复用的操作步骤、策略或控制程序。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;这几类记忆的写入频率和检索方式不同。工作记忆需要高频更新，情景记忆按任务整理，语义记忆需要去重与冲突处理，技能记忆则应经过验证后再复用。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;3. 从原始观测到可检索经验&lt;a href=&quot;#3-从原始观测到可检索经验&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;一段完整经历可以经过下面的处理链路：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;多模态观测 → 状态估计 → 事件切分 → 结果标注&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;→ 经验摘要 → 建立索引 → 按任务检索&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;事件切分很重要。连续视频不适合直接作为记忆单元，而“发现杯子”“尝试抓取”“抓取失败”“调整视角后成功”更适合成为可复用的经验片段。&lt;/p&gt;&lt;p&gt;摘要时要保留动作前提和结果，不能只写“成功拿到杯子”。如果缺少物体位置、抓取姿态和失败尝试，下一次检索到的经验很难指导行动。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;4. 记忆检索要与当前任务相关&lt;a href=&quot;#4-记忆检索要与当前任务相关&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;相似场景不一定需要相同动作。检索条件除了视觉或文本相似度，还应考虑：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;当前任务目标；&lt;/li&gt;
&lt;li&gt;环境约束与可用工具；&lt;/li&gt;
&lt;li&gt;机器人本体能力；&lt;/li&gt;
&lt;li&gt;过去经验的成功条件；&lt;/li&gt;
&lt;li&gt;记忆是否已经过期。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;召回经验后，智能体仍需根据当前状态重新规划，而不能把过去动作序列机械重放。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;5. 记忆中的错误会被持续放大&lt;a href=&quot;#5-记忆中的错误会被持续放大&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;错误观测如果被写成长期事实，后续规划会不断建立在错误前提上。为此，记忆系统需要：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;区分观测、推断和已验证事实；&lt;/li&gt;
&lt;li&gt;为易变状态设置有效期；&lt;/li&gt;
&lt;li&gt;保存冲突信息而不是静默覆盖；&lt;/li&gt;
&lt;li&gt;允许执行反馈修正已有记忆；&lt;/li&gt;
&lt;li&gt;对高风险技能保留人工确认或仿真验证。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;6. 我关注的工程问题&lt;a href=&quot;#6-我关注的工程问题&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;具身记忆并不只是选择向量数据库。真正困难的是决定写什么、何时写、谁有权覆盖、怎样检索，以及记忆如何进入规划模型但不占满上下文。&lt;/p&gt;&lt;p&gt;后续实践中，我希望继续关注多模态事件抽取、世界模型更新、经验检索与行动反馈之间的连接，让记忆成为闭环决策的一部分，而不是附加在 Agent 旁边的聊天记录库。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>MCP 工具设计笔记：从“能调用”到“可安全执行”</title><link>https://blog.miansu.eu.cc/posts/mcp-tool-contract-design/</link><guid isPermaLink="true">https://blog.miansu.eu.cc/posts/mcp-tool-contract-design/</guid><description>总结 MCP 工具的输入契约、读写边界、幂等性、错误返回和权限设计，避免把普通接口直接包装成工具。</description><pubDate>Mon, 18 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;&lt;p&gt;MCP 解决了模型与外部能力之间的连接方式，但协议接通并不代表工具已经适合交给 Agent 使用。&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;第一次设计工具时，很容易把已有 HTTP 接口直接暴露给模型：接口有几个参数，工具就照搬几个参数。实际使用后会发现，面向程序员的 API 与面向模型的工具契约并不完全相同。模型需要更清晰的使用条件、更小的操作范围，以及可以据此修正下一步的结构化反馈。&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;1. 工具名称和描述就是路由条件&lt;a href=&quot;#1-工具名称和描述就是路由条件&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;模型通常先根据名称和描述判断是否调用工具，因此描述不能只写“查询数据”或“更新记录”。它至少要说明：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;工具适合解决什么问题；&lt;/li&gt;
&lt;li&gt;什么时候不应该调用；&lt;/li&gt;
&lt;li&gt;必填参数的业务含义；&lt;/li&gt;
&lt;li&gt;返回结果能证明什么；&lt;/li&gt;
&lt;li&gt;操作是否会修改外部状态。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;如果多个工具的语义高度重叠，模型就容易随机选择。与其提供十个边界模糊的工具，不如提供少量职责清楚的能力。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;2. Schema 要减少无效自由度&lt;a href=&quot;#2-schema-要减少无效自由度&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;工具参数应该尽量使用枚举、明确字段和可验证格式，而不是让模型拼接一段自由文本再由服务端猜测。&lt;/p&gt;&lt;p&gt;例如文件操作可以把 &lt;code&gt;path&lt;/code&gt;、&lt;code&gt;encoding&lt;/code&gt;、&lt;code&gt;overwrite&lt;/code&gt; 分开；查询工具可以明确分页、排序和过滤条件。对于互斥参数，应在说明和服务端校验中同时表达，不能只依赖模型“自觉”。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;3. 先区分只读和写操作&lt;a href=&quot;#3-先区分只读和写操作&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;我倾向于把工具按风险分层：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;只读工具&lt;/strong&gt;：搜索、读取、状态检查，可以自动执行；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可恢复写入&lt;/strong&gt;：创建草稿、生成临时文件，需要返回明确变更；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;重要修改&lt;/strong&gt;：覆盖配置、发送消息、发布内容，需要确认目标；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;破坏性操作&lt;/strong&gt;：删除、批量迁移、权限调整，需要额外审批和范围校验。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;一个名为 &lt;code&gt;manage_project&lt;/code&gt; 的万能工具很难设置安全边界。把读与写拆开，既有利于权限控制，也能让执行日志更容易审计。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;4. 返回错误要能指导下一步&lt;a href=&quot;#4-返回错误要能指导下一步&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;仅返回 &lt;code&gt;success: false&lt;/code&gt; 对 Agent 帮助很小。更实用的错误结构应该包含：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;code&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;RESOURCE_NOT_FOUND&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;message&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;未找到目标文件&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;retryable&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;details&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;path&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;src/config/app.ts&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;Agent 可以据此判断是修改参数、重新检索，还是停止并请求用户确认。对限流、超时和临时网络故障，还应明确是否适合重试，避免模型在永久错误上反复调用。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;5. 写工具要考虑幂等性&lt;a href=&quot;#5-写工具要考虑幂等性&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Agent 可能因为超时或响应丢失而重复调用。创建订单、发送消息或提交任务等操作，如果没有幂等键，就可能产生重复副作用。&lt;/p&gt;&lt;p&gt;因此写操作最好支持：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;请求级幂等标识；&lt;/li&gt;
&lt;li&gt;执行前的目标状态检查；&lt;/li&gt;
&lt;li&gt;明确的变更摘要；&lt;/li&gt;
&lt;li&gt;可查询的操作结果；&lt;/li&gt;
&lt;li&gt;在条件允许时提供撤销路径。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;6. 一份实用检查清单&lt;a href=&quot;#6-一份实用检查清单&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;工具是否只有一个主要职责？&lt;/li&gt;
&lt;li&gt;描述能否让模型知道“不该什么时候用”？&lt;/li&gt;
&lt;li&gt;输入是否可以在服务端完整校验？&lt;/li&gt;
&lt;li&gt;读写权限是否明确分离？&lt;/li&gt;
&lt;li&gt;重复调用是否会造成额外副作用？&lt;/li&gt;
&lt;li&gt;错误是否能指导重试、改参或停止？&lt;/li&gt;
&lt;li&gt;返回值是否包含可验证的目标和状态？&lt;/li&gt;
&lt;li&gt;日志能否追踪调用者、参数范围与执行结果？&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;MCP 工具的价值不只是让模型“连接更多系统”，而是用稳定契约把模型的不确定推理与外部系统的确定执行隔离开。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>RAG 评估笔记：把召回质量与回答质量分开检查</title><link>https://blog.miansu.eu.cc/posts/rag-evaluation-checklist/</link><guid isPermaLink="true">https://blog.miansu.eu.cc/posts/rag-evaluation-checklist/</guid><description>从评测集、召回证据、引用正确性和失败归因出发，整理一套适合迭代 RAG 系统的检查方法。</description><pubDate>Wed, 22 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;RAG 系统的回答出现错误时，最常见的处理方式是继续改 Prompt 或更换模型。但如果正确证据根本没有进入上下文，生成模型再强也只能在缺失信息上进行猜测。&lt;/p&gt;
&lt;p&gt;因此，评估 RAG 的第一原则是：&lt;strong&gt;把检索阶段和生成阶段分开测，再检查它们之间的上下文组装。&lt;/strong&gt;&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;1. 先建立可以解释的评测集&lt;a href=&quot;#1-先建立可以解释的评测集&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;评测问题不应该只有问题和标准答案，还应保留：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;问题依赖的原始文档；&lt;/li&gt;
&lt;li&gt;能支持答案的证据片段；&lt;/li&gt;
&lt;li&gt;容易被误召回的相似片段；&lt;/li&gt;
&lt;li&gt;问题类型，例如事实查询、跨段归纳、表格读取或多跳推理；&lt;/li&gt;
&lt;li&gt;无法从知识库回答的拒答样例。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;只有知道“证据应该在哪里”，召回率和引用正确性才有可解释的依据。否则评估很容易退化成用另一个模型判断回答“看起来像不像对”。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;2. 检索阶段关注证据是否到场&lt;a href=&quot;#2-检索阶段关注证据是否到场&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;对每个问题，我会记录 BM25、向量检索、融合排序和重排后的候选列表，并检查：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;正确证据是否进入候选集合；&lt;/li&gt;
&lt;li&gt;它在哪个阶段被提升或过滤；&lt;/li&gt;
&lt;li&gt;专有名词、数字和原句是否更依赖关键词检索；&lt;/li&gt;
&lt;li&gt;语义改写是否更依赖向量检索；&lt;/li&gt;
&lt;li&gt;top-k 增大后，噪声是否淹没有效片段。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;如果候选阶段没有正确证据，应优先检查解析、切块、索引和查询改写；如果候选中有、重排后消失，则问题更可能出在融合策略、阈值或重排模型。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;3. 上下文组装也需要单独检查&lt;a href=&quot;#3-上下文组装也需要单独检查&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;检索结果正确，不代表最终 Prompt 中仍然正确。常见问题包括：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;片段被截断，关键限定条件丢失；&lt;/li&gt;
&lt;li&gt;多个来源合并时顺序混乱；&lt;/li&gt;
&lt;li&gt;表格行列关系被转成无法理解的纯文本；&lt;/li&gt;
&lt;li&gt;引用编号与实际片段错位；&lt;/li&gt;
&lt;li&gt;总 token 超限后，最重要的证据被后加入的内容挤出。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;我会保存最终送入模型的上下文快照，并让引用编号与文档、页码或块 ID 建立稳定映射。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;4. 生成阶段不只看答案相似度&lt;a href=&quot;#4-生成阶段不只看答案相似度&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;回答评估至少可以拆成：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;正确性&lt;/strong&gt;：结论是否与证据一致；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;完整性&lt;/strong&gt;：问题要求的要点是否覆盖；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;忠实度&lt;/strong&gt;：是否加入证据中不存在的断言；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;引用正确性&lt;/strong&gt;：引用是否真正支持对应句子；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;拒答能力&lt;/strong&gt;：证据不足时是否明确说明边界。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;开放式问题不适合只使用字符串匹配。可以结合规则、人工抽查和模型评审，但模型评审也应看到问题、证据、回答和清晰的评分标准。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;5. 用失败类型推动迭代&lt;a href=&quot;#5-用失败类型推动迭代&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;我更关心错误分布，而不是孤立的总分：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;解析缺失 → 切块断裂 → 未召回 → 重排丢失&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;→ 上下文截断 → 生成偏离 → 引用错位&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;每一类错误对应不同的修复位置。把所有问题都交给 Prompt，会让短期样例看起来改善，却难以形成稳定、可重复的工程进步。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;6. 迭代时的最小记录&lt;a href=&quot;#6-迭代时的最小记录&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;评测集版本和知识库版本；&lt;/li&gt;
&lt;li&gt;embedding、切块和索引配置；&lt;/li&gt;
&lt;li&gt;各阶段候选与最终上下文；&lt;/li&gt;
&lt;li&gt;模型、Prompt 和采样参数；&lt;/li&gt;
&lt;li&gt;失败分类与人工复核记录。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;RAG 评估的目的不是得到一个好看的数字，而是让每次改动都能回答：它改善了哪类问题，又可能牺牲了什么。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>用 LangGraph 设计可修复的 AI 面试工作流</title><link>https://blog.miansu.eu.cc/posts/langgraph-interview-workflow/</link><guid isPermaLink="true">https://blog.miansu.eu.cc/posts/langgraph-interview-workflow/</guid><description>从简历解析、面试规划到问题生成和回答评价，记录显式状态机比单次 Prompt 更适合产品化的原因。</description><pubDate>Mon, 16 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;&lt;p&gt;这篇笔记由 2026 年 3 月 AI Interview Studio 的开发记录整理而来。对应仓库为 &lt;a href=&quot;https://github.com/yeliheng-010/ai-interview-studio&quot; target=&quot;_blank&quot;&gt;ai-interview-studio&lt;/a&gt;。&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;AI 面试产品表面上像一个“上传简历，生成问题”的功能，但真正做成可反复使用的产品后，流程会迅速变复杂：PDF 可能抽不出文本，简历信息需要归纳，目标 JD 会改变提问方向，问题之间会重复，某个难度可能数量不足，用户还希望重生成单题并评价自己的回答。&lt;/p&gt;
&lt;p&gt;如果把这些逻辑塞进一次模型调用，Prompt 会同时承担解析、规划、生成、校验和修复，任何一步失败都只能整体重来。于是我选择把它建模成显式工作流。&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;1. 先区分确定性逻辑与模型推理&lt;a href=&quot;#1-先区分确定性逻辑与模型推理&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;上传 PDF 后，系统先在本地完成文本提取、空白清理与质量校验。只有通过基本检查的纯文本才会进入模型调用。这条边界带来三个好处：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;文件解析失败可以给出明确错误，而不是让模型面对乱码；&lt;/li&gt;
&lt;li&gt;PDF 页面图像不会无必要地发送给模型；&lt;/li&gt;
&lt;li&gt;清洗规则可以通过普通代码测试，不依赖模型稳定性。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;模型更适合承担简历归纳、面试策略、问题生成和答案评价。数据库则只负责用户、简历、面试集、收藏和练习记录等业务事实。工作流状态不代替持久化，持久化也不侵入每个节点的推理细节。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;2. 把中间状态写进类型&lt;a href=&quot;#2-把中间状态写进类型&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;工作流状态包含原始文本、清洗文本、简历摘要、JD 上下文、面试策略、不同难度的问题列表、错误信息和最终结果。每次模型调用都返回结构化对象，再由 Pydantic 校验后进入下游节点。&lt;/p&gt;&lt;p&gt;这样做的意义不只是“防止 JSON 格式错误”。类型把节点间契约写清楚了：生成节点不必知道数据库表结构，修复节点只接收问题列表与缺口信息，最终节点负责把内部状态转换成 API 响应。某个节点替换模型或 Prompt 时，不会让整条链路一起变化。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;3. 生成之后必须有修复路径&lt;a href=&quot;#3-生成之后必须有修复路径&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;一次生成常见的问题包括：题目语义重复、难度分布不符、答案视角不统一、题目脱离简历证据。工作流在初次生成后执行确定性的归一化与去重，再计算每个难度的缺口。缺少的部分进入修复节点补齐，而不是把全部题目推倒重来。&lt;/p&gt;&lt;p&gt;单题重生成也使用独立工作流：先读取原问题、简历摘要、JD 和同组题目，生成候选题，再检查它与现有题目的重复度。如果候选结果不合法，系统保留旧题或返回可解释错误。这比“点一下按钮，再赌一次模型输出”更接近可靠产品。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;4. 评价是另一条工作流&lt;a href=&quot;#4-评价是另一条工作流&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;用户练习回答时，输入不是只有“问题 + 用户答案”。评价节点还需要参考答案、简历背景和评分维度。输出包含结构化评分、做得好的部分、缺失信息和一个改进版本。&lt;/p&gt;&lt;p&gt;我没有把评价接在生成流程后面，而是设计成单独的图。原因是两条流程的触发时机、状态、成本和失败恢复完全不同。生成关注题目集合的完整性，评价关注单个回答的证据与表达。拆开后，两者都更容易维护。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;5. 流式进度应该对应真实节点&lt;a href=&quot;#5-流式进度应该对应真实节点&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;长工作流如果只显示一个转圈动画，用户不知道系统是卡住还是仍在处理。项目后续为生成过程加入阶段进度：文本提取、简历分析、策略规划、不同难度生成、修复与落库。前端显示的是实际节点推进，而不是伪造的百分比。&lt;/p&gt;&lt;p&gt;这也改善了排障：当请求在“困难题生成”阶段失败，日志和 UI 都能指向同一节点，不必从一个巨大请求里猜测问题发生在哪里。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;6. 我的结论&lt;a href=&quot;#6-我的结论&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;LangGraph 的价值不是把函数画成图，而是把状态、边界、条件分支和恢复路径变成系统的一等公民。对于包含多个模型步骤、需要校验修复、又必须接入真实业务数据的 AI 应用，显式工作流比线性 Prompt 链更容易解释、测试和持续演进。&lt;/p&gt;&lt;section&gt;&lt;h3&gt;参考资料&lt;a href=&quot;#参考资料&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.langchain.com/oss/python/langgraph/graph-api&quot; target=&quot;_blank&quot;&gt;LangGraph Graph API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.pydantic.dev/latest/concepts/models/&quot; target=&quot;_blank&quot;&gt;Pydantic Models&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://fastapi.tiangolo.com/&quot; target=&quot;_blank&quot;&gt;FastAPI 官方文档&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nextjs.org/docs/app&quot; target=&quot;_blank&quot;&gt;Next.js App Router&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;/section&gt;</content:encoded></item><item><title>从混合检索到 Agentic GraphRAG：代码仓库问答的设计笔记</title><link>https://blog.miansu.eu.cc/posts/agentic-graphrag-learning-notes/</link><guid isPermaLink="true">https://blog.miansu.eu.cc/posts/agentic-graphrag-learning-notes/</guid><description>记录我如何把 Tree-sitter、BM25、向量检索、符号图和 LangGraph 工作流组合成可追踪的代码仓库问答系统。</description><pubDate>Fri, 06 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;&lt;p&gt;这篇笔记由 2026 年 3 月的项目阶段记录整理成文，项目代码与时间线可在 &lt;a href=&quot;https://github.com/yeliheng-010/CodeRag&quot; target=&quot;_blank&quot;&gt;CodeRag&lt;/a&gt; 查看。&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;做代码仓库问答时，我很快发现：只把代码切块后放进向量库，能够回答“这段代码大概在做什么”，却很难稳定回答“谁调用了它”“一条请求怎样穿过多个模块”“修改这个符号会影响哪里”。原因并不神秘——代码的核心信息不只存在于文本相似度里，还存在于结构关系里。&lt;/p&gt;
&lt;p&gt;因此，这个项目的目标不是再做一个聊天壳，而是构建一条能够在文本证据与图关系之间主动切换的检索链路。&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;1. 先把代码变成两种可检索表示&lt;a href=&quot;#1-先把代码变成两种可检索表示&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;第一种表示是文本。仓库文件经过读取、清洗和切分后，分别进入 BM25 与向量索引：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;BM25 擅长精确命中函数名、类名、配置键与报错文本；&lt;/li&gt;
&lt;li&gt;向量检索擅长处理自然语言问题和代码描述之间的语义差异；&lt;/li&gt;
&lt;li&gt;两路结果通过 RRF（Reciprocal Rank Fusion）合并，减少对某一路分数尺度的依赖。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;第二种表示是符号图。Tree-sitter 负责从不同语言的语法树中抽取函数、类、方法与导入关系，再将 &lt;code&gt;CALLS&lt;/code&gt;、&lt;code&gt;IMPORTS&lt;/code&gt;、&lt;code&gt;OWNS&lt;/code&gt;、&lt;code&gt;HERITAGE&lt;/code&gt; 等边写入 KuzuDB。这样，系统不需要让大模型“猜”调用关系，而是可以执行邻居扩展和路径搜索。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;2. 为什么需要 Agent 路由&lt;a href=&quot;#2-为什么需要-agent-路由&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;并不是每个问题都需要 GraphRAG。比如“README 中如何启动服务”通常用关键词检索就够了；“解释 &lt;code&gt;handle_request&lt;/code&gt; 到 &lt;code&gt;save_order&lt;/code&gt; 的调用路径”才需要符号消歧和图搜索。&lt;/p&gt;&lt;p&gt;我把流程拆成多个职责清晰的节点：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;&lt;code&gt;router&lt;/code&gt; 判断是直接回答、普通 RAG、GraphRAG 还是调用路径问题；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;symbol_suggest_tool&lt;/code&gt; 对短名进行前缀与模糊匹配，避免同名符号误判；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;retrieve_tool&lt;/code&gt; 执行语义或混合检索；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;expand_graph_tool&lt;/code&gt; 沿调用、导入和归属边扩展上下文；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;call_path_tool&lt;/code&gt; 查询两个符号之间的路径；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;answer&lt;/code&gt; 只基于整理后的 &lt;code&gt;context_pack&lt;/code&gt; 生成回答；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;verify&lt;/code&gt; 检查证据是否足够，必要时改写查询并重试一次。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;这里最重要的不是节点数量，而是“决策可以被观察”。前端能够看到 route、attempts、节点调用顺序、引用片段和原始 trace。调试时，我可以分辨到底是路由选错、符号消歧失败、检索召回不足，还是回答阶段没有遵守证据。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;3. 上下文不是越多越好&lt;a href=&quot;#3-上下文不是越多越好&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;图扩展很容易带来新的问题：一个高连接度符号可能在两跳内扩出大量节点。如果把所有结果交给模型，既浪费上下文，也会稀释真正相关的证据。&lt;/p&gt;&lt;p&gt;我的处理方式是给扩展设置明确边界：限制深度、限制每类边的数量、保留初始命中与扩展命中的来源，并在组装 &lt;code&gt;context_pack&lt;/code&gt; 时优先保留能直接回答问题的符号与代码片段。换句话说，图不是为了“查得更多”，而是为了“补齐文本检索缺失的关系”。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;4. 自验证应检查证据，而不是检查文风&lt;a href=&quot;#4-自验证应检查证据而不是检查文风&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;第一次实现验证节点时，很容易写成“你觉得回答好不好”。这类检查太主观，也不能驱动下一步动作。更有效的方式是检查结构化条件：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;路径类问题是否真的得到路径结果；&lt;/li&gt;
&lt;li&gt;回答中的关键结论是否有对应 citation；&lt;/li&gt;
&lt;li&gt;符号存在歧义时是否已经完成消歧；&lt;/li&gt;
&lt;li&gt;检索结果是否覆盖问题中的关键实体。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;只有检查失败时才触发一次重试，并提高检索范围或改写查询。有限循环比无限“反思”更容易控制延迟与成本。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;5. 这次实践留下的结论&lt;a href=&quot;#5-这次实践留下的结论&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;代码 RAG 的质量不只取决于模型。解析质量、符号命名、混合检索、图扩展边界、引用格式和运行轨迹共同决定了系统是否可信。Agent 的价值也不是让每个问题都经过复杂流程，而是让系统能够根据问题类型选择最小充分路径，并在证据不足时知道下一步该做什么。&lt;/p&gt;&lt;section&gt;&lt;h3&gt;参考资料&lt;a href=&quot;#参考资料&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://tree-sitter.github.io/tree-sitter/&quot; target=&quot;_blank&quot;&gt;Tree-sitter 官方文档&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.kuzudb.com/&quot; target=&quot;_blank&quot;&gt;KuzuDB 文档&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.langchain.com/oss/python/langgraph/overview&quot; target=&quot;_blank&quot;&gt;LangGraph 概览&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://plg.uwaterloo.ca/~gvcormac/cormacksigir09-rrf.pdf&quot; target=&quot;_blank&quot;&gt;Reciprocal Rank Fusion 原始论文&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;/section&gt;</content:encoded></item></channel></rss>