<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Origin Blog</title><description>Notes on technology, learning, and everyday life.</description><link>https://www.originblog.xyz/</link><language>zh</language><item><title>在信息之外，留一页空白</title><link>https://www.originblog.xyz/blog/chatter_1790012226310/</link><guid isPermaLink="true">https://www.originblog.xyz/blog/chatter_1790012226310/</guid><description>个人博客的意义，或许就在这种慢里。它没有替我保留一个固定的形象，只是给我一块可以反复修改的地方。页面会变，文章会增加，很多想法也会被后来的人推翻。但只要这块地方还在，我就还能从某个旧页面出发，继续认识自己正在成为谁。</description><content:encoded>&lt;blockquote&gt;
&lt;p&gt;如果计算资源能够持续转化为探索能力和判断能力，这也带来了一个令人不安的未来问题：拥有大量算力的人或组织，会不会获得远超普通人的智力资源？这是对未来可能性的追问，并非已经发生的终局。&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;对于正在学习的我们，可以从眼前开始改变做事方式：让模糊愿望获得可检查的表达，把有效经验沉淀成可复用的方法，在需要探索时主动扩展方向，又用证据筛选结果。提示词的价值，最终要落在这些行动如何改变任务的结果上。&lt;/p&gt;
&lt;p&gt;——Yanyan’s Wiki，《提示词与上下文工程》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;我很认同这段话。提示词的价值并不在于把句子写得多么漂亮，而在于能不能把一个模糊的愿望变成可以执行、检查和修正的行动。对我来说，博客也有类似的意义：它不只是保存结果，还保存一个人如何形成判断的过程。&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;这一周我都在着手用AI重构我的个人博客，但是我不太想把这次博客的二次优化写成一份版本说明。页面换了什么颜色，按钮增加了什么效果，当然可以列出来，但那只是最容易被看见的部分。真正让我停下来想一想的，是一个更简单的问题：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;在今天这个什么都被放进信息流的时代，我为什么还要做个人博客？&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;现在的社交平台让表达变得非常方便。注册一个账号，写几句话，配一张图，剩下的事情交给推荐、热搜和通知。内容被看见的机会更多了，但它也越来越像一种即时消费。昨天写下的东西很快沉下去，今天的情绪必须比昨天更响，才有可能得到一点回应。我们渐渐习惯用浏览量、点赞和转发来确认一段文字是否值得留下，甚至让平台替我们决定什么应该被记住。&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;而个人博客的节奏要慢得多。这里没有热榜，也没有谁提醒我“再发一条”。页面打开以后，访客需要自己选择去哪里；一篇文章写完了，也不一定有人马上读到。可正因为它不负责替我制造注意力，我才可以在这里保存一些还没有想清楚的东西。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;它不是展示一个已经完成的我，而是允许尚未完成的人留下修改的痕迹。&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;所以这次优化不只是审美变化。我希望页面更安静一些，让黑白墨色把视觉从装饰里收回来；圆角让交互不要像在操作一张冷冰冰的表格。原来的栏目、文章、音乐和相册已经构成了这个博客的记忆。如果为了追求“新”而把它们全部抹掉，得到的可能只是一个更漂亮、却不再属于我的页面。&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;一个好的界面不需要不停提醒自己存在。借海德格尔关于“上手状态”的说法，工具在顺手工作时会退到背景，只有失灵或不合适时才会重新显形。对博客来说，界面大概也应该保持这样的分寸：让人感觉到它在工作，而不是一直要求被观看。对我而言，网页最重要的功能不是让别人觉得它完成得很好，而是让我&lt;/p&gt;
&lt;p&gt;愿意下次再打开它。&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;我想起司马迁在《报任安书》中写下的那句话：“藏之名山，传之其人。”这当然是史家的理想，和我的博客不能相提并论，但两者之间仍然有一点相似：记录从来不只是把内容放在那里，它还意味着对时间的抵抗。今天的想法如果不留下，明天很可能连自己都无法确认曾经这样想过。&lt;/p&gt;
&lt;p&gt;博客也从来不是中性的仓库。什么被保存、什么被删去、用什么顺序保存，都会影响后来的人如何理解过去。公共平台上的记忆往往按照流量排列，而个人博客可以暂时采用另一套尺度：一篇没有得到回应的文章，和一篇被很多人读过的文章，都有资格留在同一份目录里；一次失败的尝试，也不必因为不够体面而立刻消失。&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;正如开头那段话所说，知识最终要落在行动改变任务结果的地方。放在今天的 AI 浪潮里，思考也成了我最快捷的消遣之一。我可以在一个问题上不断追问，让模糊的念头慢慢变得具体；也可以和 Agent 讨论生活、哲学和人文历史。&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;有时，我会在它的回答里找到一个暂时站得住的答案；有时，它只是让我意识到自己原本问错了问题。读到人类在历史长河中留下的思想，会让我开心很久。我会被一瞬间出现的灵感震撼，也会因为某个时代的命运不公而惋惜。那些已经离开的人，仍然可以通过文字和今天的我进行一场延迟很久的谈话。&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;但这件事也有令人不安的一面。也许在不久的将来，AI 能够在某种意义上读懂人的思想和感情；也许有一天，它不仅在效率上远远超过人类，还能在策略和框架的精细程度上全面胜过人类。那或许才是社会真正被迫接受 AI 的时刻，因为人们接受的将不再是一件工具，而是一种无法绕开的基础能力。&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;我也不因此觉得人类会走上“没有工作、没有钱途”的道路，世界的一切偶然都是必然。回看上世纪中叶以来计算机的发展，从最初的理论设想，到今天渗透进生活的各个角落，许多变化在当时都像偶然，后来却成为了新的必然。世界没有什么东西是一成不变的，人类也总能在变化中重新创造适合当下的工具、制度和生活方式。&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;是的，世界会迎来巨变，这是无疑的。但我不认为接受变化就等于赞成变化，也不认为恐惧变化能够阻止变化。与其假装时代不会改变，不如尽量在变化发生时保留判断，找到属于自己的位置。这可能就是我对生命在当下的理解。&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;上大学以来，我有过很多后悔的事情，生活整体也算不上特别充实。有时思考到一半的事情，因为没有记下来，转眼就忘得一干二净。我把太多事想得简单，又把太多事想得复杂；一方面过于功利，另一方面又想显得自己无欲无求，最后反而越来越不自在，越来越迷茫。&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;更让我难受的是，很多想法总是在事情过去几天，甚至几年之后才突然出现。我会忍不住想：为什么我当时没有想到？如果那时候就明白，现在是不是会不一样？可一旦这样想下去，又会觉得现在才开始做，是不是已经太晚了，以后的路是不是只会越来越难走。&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;小时候的我畏惧死亡，现在的我更畏惧时间。死亡至少有一个明确的终点，时间却没有声音，也不会提前通知你。等我意识到它已经过去时，青春可能早就只剩下回忆。&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;后来我慢慢发现，我经常在用今天的自己审判过去的自己。现在的我拥有更多经验，所以当然比几年前看得远，但那时的我并没有今天的这些信息，也不可能提前知道答案。那个“如果早点开始就好了”的自己，只存在于想象里。他没有经历真实的迟疑、代价和迷茫，所以看起来永远比现实中的我更聪明。&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;未来的路可能确实会更难走。责任会增加，机会成本会变高，有些选择也不会像以前那样轻易重新开始。但我也会拥有过去没有的东西：对风险的判断，对自己的了解，以及知道哪些事情不值得再重复的经验。路也许会变窄一些，却不一定只会变坏。&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;我现在仍然会后悔，但我不想让后悔只证明自己来晚了。它至少说明某件事对我很重要，也说明我终于看见了以前看不见的部分。问题不应该一直停留在“为什么前几年没有想到”，而应该继续问下去：既然我现在已经想到了，接下来准备怎样处理它？&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;时间当然无法追回，但我可以把现在的思考留下来。博客无法让我回到过去，却能让未来的我知道，自己曾经在什么时刻感到迷茫，又是怎样试着走出迷茫的。&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;在大学里，生活大部分时候由课程、代码、实验、截止日期和一些没有整理好的念头组成。现在回头看，这种不稳定也许正是值得记录的地方。等以后真的走过一段路，再回来看这些页面，我未必会认同其中的每一句话，但至少能知道自己曾经怎样理解技术、时间和自己。&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;这更像是一次我对生活的重新确认：我仍然愿意为一个不被算法催促的空间花时间，仍然愿意把界面、文章和那些并不重要却很具体的细节慢慢整理好。&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;个人博客的意义，或许就在这种慢里。它没有替我保留一个固定的形象，只是给我一块可以反复修改的地方。页面会变，文章会增加，很多想法也会被后来的人推翻。但只要这块地方还在，我就还能从某个旧页面出发，继续认识自己正在成为谁。&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;唯一重要的是，永恒的现在。&lt;/strong&gt;&lt;/p&gt; &lt;blockquote&gt;This rendering was automatically generated by Frosti Feed and may have formatting issues. For the best experience, please visit: &lt;a href=&quot;https://www.originblog.xyz/blog/chatter_1790012226310/&quot;&gt;https://www.originblog.xyz/blog/chatter_1790012226310/&lt;/a&gt;&lt;/blockquote&gt;</content:encoded><dc:creator>Origin Blog</dc:creator><pubDate>Sun, 20 Sep 2026 00:00:00 GMT</pubDate></item><item><title>应用视角的操作系统</title><link>https://www.originblog.xyz/blog/post_1789796421/</link><guid isPermaLink="true">https://www.originblog.xyz/blog/post_1789796421/</guid><content:encoded>&lt;h2 id=&quot;1-本讲要回答的问题&quot;&gt;1. 本讲要回答的问题&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;h2 id=&quot;2-核心知识地图&quot;&gt;2. 核心知识地图&lt;/h2&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;link rel=&quot;https://www.originblog.xyz/stylesheet&quot; href=&quot;https://www.originblog.xyz/_astro/ec.vdc5k.css&quot;&gt;&lt;script type=&quot;module&quot; src=&quot;https://www.originblog.xyz/_astro/ec.0vx5m.js&quot;&gt;&lt;/script&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;flowchart TD&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;S[源代码 C] --&gt; T[简化为 SimpleC]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;T --&gt; I[解释执行：逐条语句改变状态]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;T --&gt; C[编译：转换为机器指令]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;C --&gt; M[CPU 状态机：取指、译码、执行]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;M --&gt;|syscall / ecall / svc| OS[操作系统]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;OS --&gt; O[文件、进程、内存、设备等对象]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;OS --&gt;|返回结果或改变状态| M&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;M --&gt; X[exit / exit_group]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;flowchart TD    S[源代码 C] --&gt; T[简化为 SimpleC]    T --&gt; I[解释执行：逐条语句改变状态]    T --&gt; C[编译：转换为机器指令]    C --&gt; M[CPU 状态机：取指、译码、执行]    M --&gt;|syscall / ecall / svc| OS[操作系统]    OS --&gt; O[文件、进程、内存、设备等对象]    OS --&gt;|返回结果或改变状态| M    M --&gt; X[exit / exit_group]&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;3-程序一个状态机&quot;&gt;3. 程序：一个状态机&lt;/h2&gt;
&lt;h3 id=&quot;31-什么是状态&quot;&gt;3.1 什么是“状态”&lt;/h3&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;每个栈帧中的参数、局部变量和下一步执行位置（PC）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;执行一条语句，状态就发生一次迁移。程序运行，就是不断重复“读取当前状态 → 执行一步 → 得到新状态”。&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;初始状态 → 执行语句 → 新状态 → 执行语句 → 新状态 → …&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;初始状态 → 执行语句 → 新状态 → 执行语句 → 新状态 → …&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3 id=&quot;32-函数调用与栈帧&quot;&gt;3.2 函数调用与栈帧&lt;/h3&gt;
&lt;p&gt;调用函数时，程序会：&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;p&gt;所以递归本质上不是“函数自己调用自己”这么一句话，而是&lt;strong&gt;调用栈不断压入新的函数实例&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&quot;33-为什么函数调用顺序很重要&quot;&gt;3.3 为什么函数调用顺序很重要&lt;/h3&gt;
&lt;p&gt;数学函数通常只关心输入和输出；C/C++ 函数还可能产生副作用，例如打印、写文件、修改全局变量。存在副作用时，调用顺序可能改变最终结果。&lt;/p&gt;
&lt;p&gt;例如：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;printf(&quot;A&quot;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;printf(&quot;B&quot;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;printf(&amp;#x22;A&amp;#x22;);printf(&amp;#x22;B&amp;#x22;);&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;交换两句，输出就从 &lt;code&gt;AB&lt;/code&gt; 变成 &lt;code&gt;BA&lt;/code&gt;。因此，程序的“语义”不仅是算出了什么，还包括对外部世界做了什么、按什么顺序做。&lt;/p&gt;
&lt;h2 id=&quot;4-解释执行编译与编译正确性&quot;&gt;4. 解释执行、编译与编译正确性&lt;/h2&gt;
&lt;h3 id=&quot;41-simplec把复杂语句拆小&quot;&gt;4.1 SimpleC：把复杂语句拆小&lt;/h3&gt;
&lt;p&gt;C 语言包含循环、分支、函数调用和复杂表达式。为了更容易观察程序运行，可以把它改写成每行只做一件事的简化形式，例如把循环改写为条件跳转，把函数调用拆成明确的保存和跳转。&lt;/p&gt;
&lt;p&gt;这样可以像模拟 CPU 一样单步执行源程序：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;fetch_statement()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;decode_and_execute(statement)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;fetch_statement()decode_and_execute(statement)&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3 id=&quot;42-编译器做什么&quot;&gt;4.2 编译器做什么&lt;/h3&gt;
&lt;p&gt;编译器把高级语言程序转换成更低级的程序表示，最终得到 CPU 能执行的机器指令。&lt;/p&gt;
&lt;p&gt;编译后的程序不需要保留源代码的结构。只要对于相同输入，它的可观察行为与原程序等价，就可以认为编译是正确的。&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;系统调用和其他外部 API 的行为；&lt;/li&gt;
&lt;li&gt;对文件、设备、网络等外部对象的影响。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;43-优化与死代码消除&quot;&gt;4.3 优化与死代码消除&lt;/h3&gt;
&lt;p&gt;如果某段计算不会影响任何外部可观察结果，编译器可以删除它，这叫 &lt;strong&gt;DCE（Dead Code Elimination，死代码消除）&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;因此，“编译正确”不等于“机器代码看起来像逐行翻译”；正确性关注的是外部行为，而不是内部写法。&lt;/p&gt;
&lt;h2 id=&quot;5-最小应用程序从-_start-到-exit&quot;&gt;5. 最小应用程序：从 &lt;code&gt;_start&lt;/code&gt; 到 &lt;code&gt;exit&lt;/code&gt;&lt;/h2&gt;
&lt;h3 id=&quot;51-_start-与-main&quot;&gt;5.1 &lt;code&gt;_start&lt;/code&gt; 与 &lt;code&gt;main&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;普通 C 程序通常从 &lt;code&gt;main&lt;/code&gt; 开始，但操作系统加载可执行文件后，首先进入的往往是 &lt;code&gt;_start&lt;/code&gt;。运行库会在 &lt;code&gt;_start&lt;/code&gt; 中完成初始化，再调用 &lt;code&gt;main&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;操作系统加载 ELF&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;进入 _start&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;运行库初始化&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;调用 main&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;调用 exit / _exit&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;操作系统加载 ELF    ↓进入 _start    ↓运行库初始化    ↓调用 main    ↓调用 exit / _exit&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3 id=&quot;52-程序如何退出&quot;&gt;5.2 程序如何退出&lt;/h3&gt;
&lt;p&gt;CPU 的普通指令没有“把当前程序从系统中删除”这一能力。程序要退出，必须请求操作系统服务：&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;p&gt;架构 指令 x86-64 &lt;code&gt;syscall&lt;/code&gt; RISC-V &lt;code&gt;ecall&lt;/code&gt; AArch64 &lt;code&gt;svc&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&quot;6-系统调用应用与操作系统的边界&quot;&gt;6. 系统调用：应用与操作系统的边界&lt;/h2&gt;
&lt;p&gt;系统调用不是普通的库函数调用，而是一次控制权和权限的交接。&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;sequenceDiagram&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;participant P as 应用程序&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;participant CPU as CPU&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;participant K as 操作系统&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;P-&gt;&gt;CPU: 执行 syscall / ecall / svc&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;CPU-&gt;&gt;K: 切换到操作系统入口&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;K-&gt;&gt;K: 检查参数、访问对象、执行服务&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;K--&gt;&gt;CPU: 返回结果或改变进程状态&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;CPU--&gt;&gt;P: 应用继续运行、阻塞或终止&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;sequenceDiagram    participant P as 应用程序    participant CPU as CPU    participant K as 操作系统    P-&gt;&gt;CPU: 执行 syscall / ecall / svc    CPU-&gt;&gt;K: 切换到操作系统入口    K-&gt;&gt;K: 检查参数、访问对象、执行服务    K--&gt;&gt;CPU: 返回结果或改变进程状态    CPU--&gt;&gt;P: 应用继续运行、阻塞或终止&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;系统调用可以用于：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;fork&lt;/code&gt;、&lt;code&gt;execve&lt;/code&gt;、&lt;code&gt;exit&lt;/code&gt;：进程管理；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;open&lt;/code&gt;、&lt;code&gt;close&lt;/code&gt;、&lt;code&gt;read&lt;/code&gt;、&lt;code&gt;write&lt;/code&gt;：文件和设备访问；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mmap&lt;/code&gt;、&lt;code&gt;brk&lt;/code&gt;：内存和地址空间管理；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;send&lt;/code&gt;、&lt;code&gt;recv&lt;/code&gt;：网络通信；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ptrace&lt;/code&gt;：观察或控制其他进程。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;本讲的直观理解是：应用程序大部分时间在自己的状态机中计算；遇到系统调用时，暂时把控制权交给操作系统。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://bu.dusays.com/2026/09/19/6aae1ffc54c0e.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;配图提示：小黑卡在“系统调用”的服务舱口，把请求递给操作系统，再接回返回结果。它表达的是权限和控制权的交接，不是普通函数调用。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;7-应用世界程序看起来不同本质相同&quot;&gt;7. 应用世界：程序看起来不同，本质相同&lt;/h2&gt;
&lt;h3 id=&quot;71-三类程序&quot;&gt;7.1 三类程序&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Applications&lt;/strong&gt;：浏览器、编辑器、播放器、IDE；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Utilities&lt;/strong&gt;：Shell、&lt;code&gt;gcc&lt;/code&gt;、&lt;code&gt;vim&lt;/code&gt;、&lt;code&gt;curl&lt;/code&gt;、&lt;code&gt;ffmpeg&lt;/code&gt;、包管理器；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Daemons&lt;/strong&gt;：后台服务，如网络服务、设备管理、安全组件和定时任务。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;它们都可以抽象成：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;计算 + 调用操作系统 API + 继续计算 + 退出&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;计算 + 调用操作系统 API + 继续计算 + 退出&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3 id=&quot;72-elf可执行文件是初始状态的描述&quot;&gt;7.2 ELF：可执行文件是初始状态的描述&lt;/h3&gt;
&lt;p&gt;Linux 原生程序通常使用 ELF 格式。ELF 文件中包含：&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;因此，ELF 不是“只有指令的二进制文件”，而是操作系统建立程序初始运行状态所需的信息集合。&lt;/p&gt;
&lt;h3 id=&quot;73-复杂应用也只是-api-的组合&quot;&gt;7.3 复杂应用也只是 API 的组合&lt;/h3&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;它们的功能虽然复杂，但底层仍然由计算和操作系统 API 组合而成。&lt;/p&gt;
&lt;h2 id=&quot;8-用工具观察程序&quot;&gt;8. 用工具观察程序&lt;/h2&gt;
&lt;h3 id=&quot;81-strace&quot;&gt;8.1 &lt;code&gt;strace&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;strace&lt;/code&gt; 用来观察程序执行过哪些系统调用，是理解“应用—操作系统边界”的重要工具。&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;strace ./a.out&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;strace -f gcc hello.c&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;strace ls |&amp;#x26; grep -e read -e write&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;strace ./a.outstrace -f gcc hello.cstrace ls |&amp;#x26; grep -e read -e write&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;看到 &lt;code&gt;openat&lt;/code&gt;、&lt;code&gt;read&lt;/code&gt;、&lt;code&gt;write&lt;/code&gt;、&lt;code&gt;mmap&lt;/code&gt;、&lt;code&gt;exit_group&lt;/code&gt; 等调用时，可以把它们理解成程序和操作系统之间的一次次对话。&lt;/p&gt;
&lt;h3 id=&quot;82-工具分工&quot;&gt;8.2 工具分工&lt;/h3&gt;
&lt;p&gt;工具 主要用途 &lt;code&gt;gcc&lt;/code&gt; / &lt;code&gt;clang&lt;/code&gt; 编译程序 &lt;code&gt;gdb&lt;/code&gt; 单步执行、查看栈和变量 &lt;code&gt;strace&lt;/code&gt; 查看系统调用 &lt;code&gt;readelf&lt;/code&gt; 查看 ELF 文件结构 &lt;code&gt;objdump&lt;/code&gt; 反汇编和查看二进制内容 profiler 分析时间和性能开销&lt;/p&gt;
&lt;h2 id=&quot;9-术语速查用一句话理解&quot;&gt;9. 术语速查：用一句话理解&lt;/h2&gt;
&lt;p&gt;术语 通俗解释 状态机 有明确状态和变化规则的运行模型 状态迁移 执行一步后从旧状态到新状态 栈帧 一次函数调用保存参数、局部变量和返回位置的区域 调用栈 当前还没有返回的函数调用层层叠加形成的结构 PC Program Counter，下一步执行位置 形式语义 用严格规则说明程序“到底是什么意思” 解释器 直接读取并执行程序表示的软件 编译器 把程序转换成另一种等价表示的软件 Transpiler 在相近语言之间转换的程序，例如 C → SimpleC 副作用 除了返回值，还改变了外部可观察状态 指令集 CPU 支持的机器指令及其规则 寄存器 CPU 内部少量但高速的存储单元 系统调用 应用请求操作系统提供服务的入口 API 应用可以使用的接口和约定 进程 一个正在运行的程序实例 ELF Linux 常见的可执行文件格式 &lt;code&gt;_start&lt;/code&gt; 可执行文件通常实际开始执行的位置 &lt;code&gt;main&lt;/code&gt; C 运行库通常交给程序员使用的入口 &lt;code&gt;execve&lt;/code&gt; 用新程序替换当前进程的内容 daemon 在后台长期运行并提供服务的程序 Trace 记录程序从开始到结束的执行过程 DCE 删除不会影响结果的代码&lt;/p&gt;
&lt;h2 id=&quot;10-最容易混淆的地方&quot;&gt;10. 最容易混淆的地方&lt;/h2&gt;
&lt;h3 id=&quot;程序可执行文件进程&quot;&gt;程序、可执行文件、进程&lt;/h3&gt;
&lt;ul&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;h3 id=&quot;api库函数系统调用&quot;&gt;API、库函数、系统调用&lt;/h3&gt;
&lt;p&gt;API 是接口约定；库函数是对接口的某种封装；系统调用是进入操作系统的机制。一个库函数可能执行一次、多个或零个系统调用。&lt;/p&gt;
&lt;h3 id=&quot;编译不是逐行翻译&quot;&gt;编译不是“逐行翻译”&lt;/h3&gt;
&lt;p&gt;编译器可以重排、合并或删除内部计算。只要外部可观察行为保持等价，内部结构不同也属于正确编译。&lt;/p&gt;
&lt;h3 id=&quot;系统调用不是普通函数调用&quot;&gt;系统调用不是普通函数调用&lt;/h3&gt;
&lt;p&gt;普通函数调用仍在应用自己的权限和地址空间中进行；系统调用会切换到操作系统控制的执行路径，并可能改变进程状态。&lt;/p&gt;
&lt;h2 id=&quot;11-检查&quot;&gt;11. 检查&lt;/h2&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;code&gt;_start&lt;/code&gt; 和 &lt;code&gt;main&lt;/code&gt; 分别处在启动流程的什么位置？&lt;/li&gt;
&lt;li&gt;编译器为什么可以删除一段“看起来执行了计算”的代码？&lt;/li&gt;
&lt;li&gt;&lt;code&gt;strace&lt;/code&gt; 观察到的是程序的哪些行为？&lt;/li&gt;
&lt;li&gt;浏览器、编译器、任务管理器为什么都可以被看成“计算 + 系统调用”？&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;12-实验&quot;&gt;12. 实验&lt;/h2&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;# 1. 编译一个最小 C 程序&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;gcc hello.c -o hello&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;# 2. 观察它与操作系统的交互&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;strace ./hello&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;# 3. 查看可执行文件的 ELF 信息&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;readelf -h ./hello&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;# 4. 查看反汇编结果&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;objdump -d ./hello&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;# 1. 编译一个最小 C 程序gcc hello.c -o hello# 2. 观察它与操作系统的交互strace ./hello# 3. 查看可执行文件的 ELF 信息readelf -h ./hello# 4. 查看反汇编结果objdump -d ./hello&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;建议重点观察：程序如何被加载、何时申请内存、怎样写出 &lt;code&gt;Hello World&lt;/code&gt;，以及最后由哪个退出系统调用结束。&lt;/p&gt;
&lt;h2 id=&quot;13-资料来源&quot;&gt;13. 资料来源&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1KNPHzPEra&quot; rel=&quot;https://www.originblog.xyz/nofollow&quot;&gt;B 站课程视频：02 - 应用视角的操作系统&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jyywiki.cn/OS/2026/lect2.md&quot; rel=&quot;https://www.originblog.xyz/nofollow&quot;&gt;JYY Wiki：应用视角的操作系统（2026）&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jyywiki.cn/OS/2026/&quot; rel=&quot;https://www.originblog.xyz/nofollow&quot;&gt;南京大学操作系统原理（2026）课程主页&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;‍&lt;/p&gt; &lt;blockquote&gt;This rendering was automatically generated by Frosti Feed and may have formatting issues. For the best experience, please visit: &lt;a href=&quot;https://www.originblog.xyz/blog/post_1789796421/&quot;&gt;https://www.originblog.xyz/blog/post_1789796421/&lt;/a&gt;&lt;/blockquote&gt;</content:encoded><dc:creator>Origin Blog</dc:creator><pubDate>Sat, 19 Sep 2026 00:00:00 GMT</pubDate></item><item><title>幻觉治理四机制（借鉴 J-Space）—— 分四阶段实施</title><link>https://www.originblog.xyz/blog/post_1786883135/</link><guid isPermaLink="true">https://www.originblog.xyz/blog/post_1786883135/</guid><content:encoded>&lt;h2 id=&quot;总原则ux-安全&quot;&gt;&lt;strong&gt;总原则（UX 安全）&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;全部改动落在&lt;strong&gt;离线质量脚本 + prompt 层 + 判定输出结构&lt;/strong&gt;，不触碰交互路径延迟：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/submit&lt;/code&gt;、&lt;code&gt;/feedback&lt;/code&gt;、&lt;code&gt;/assess/stream&lt;/code&gt; 的耗时零增加（判定→再生只发生在离线脚本 &lt;code&gt;run_quality_test.py&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;工作流重试改造是&lt;strong&gt;同延迟更聪明&lt;/strong&gt;（同样的 9 次 LLM 调用，但带上诊断 hint），不加调用&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;阶段一-判定幻觉--携带诊断的定向再生最高优先级&quot;&gt;&lt;strong&gt;阶段一 ②：判定幻觉 → 携带诊断的定向再生（最高优先级）&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;11-_generate_one-支持修正-hintcontent_generatorpy&quot;&gt;&lt;strong&gt;1.1&lt;/strong&gt; &lt;code&gt;_generate_one&lt;/code&gt; &lt;strong&gt;支持修正 hint（&lt;/strong&gt;&lt;code&gt;content_generator.py&lt;/code&gt;&lt;strong&gt;）&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;_build_generation_prompt(node, theory_level, content_type, correction_hint=&quot;&quot;)&lt;/code&gt; 新增可选参数；非空时 system 消息追加”【上轮判定修正要求】{hint}。重点修正上述问题，其余保持原结构。”&lt;/li&gt;
&lt;li&gt;&lt;code&gt;_generate_one(node, theory_level, content_type, correction_hint=&quot;&quot;)&lt;/code&gt; 透传&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;12-新文件-backendappagentsquality_regenpy&quot;&gt;&lt;strong&gt;1.2 新文件&lt;/strong&gt; &lt;code&gt;backend/app/agents/quality_regen.py&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;🐍&lt;/p&gt;
&lt;p&gt;python&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;link rel=&quot;https://www.originblog.xyz/stylesheet&quot; href=&quot;https://www.originblog.xyz/_astro/ec.vdc5k.css&quot;&gt;&lt;script type=&quot;module&quot; src=&quot;https://www.originblog.xyz/_astro/ec.0vx5m.js&quot;&gt;&lt;/script&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;def regenerate_flagged(resources, hallucination_result, kg) -&gt; dict:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;def regenerate_flagged(resources, hallucination_result, kg) -&gt; dict:&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;# verdict==&quot;hallucinated&quot; 的资源 → kg.get_node(target_node_id) 取节点&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# → adaptation_profile 反映射 theory_level (beginner→2/intermediate→3/advanced→5)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# → _generate_one(node, level, content_type, correction_hint=verdict.reason)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# → 原位替换（保持顺序），节点缺失/生成失败保留原资源&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# ThreadPoolExecutor 并行 (min(CONTENT_GEN_CONCURRENCY, n))&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# 返回 {resources, regenerated_count, regen_indexes, failures}&lt;/code&gt;&lt;/p&gt;
&lt;h3 id=&quot;13-工作流盲重试--定向重试content_generatorpy&quot;&gt;&lt;strong&gt;1.3 工作流盲重试 → 定向重试（&lt;/strong&gt;&lt;code&gt;content_generator.py&lt;/code&gt;&lt;strong&gt;）&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;_node_body&lt;/code&gt; 开头：&lt;/p&gt;
&lt;p&gt;🐍&lt;/p&gt;
&lt;p&gt;python&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;retry_hint = &quot;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;retry_hint = &amp;#x22;&amp;#x22;&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;if state.get(&quot;content_phase_entered&quot;) and isinstance(state.get(&quot;review_results&quot;), dict):&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;retry_hint = (state[&quot;review_results&quot;].get(&quot;retry_hint&quot;) or &quot;&quot;).strip()&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;tasks 元组带上 hint 传入 &lt;code&gt;_generate_one&lt;/code&gt;（reviewer 产出的 retry_hint 首次被消费；首轮 content_phase_entered=False 不注入，语义安全）&lt;/p&gt;
&lt;h3 id=&quot;14-离线脚本闭环scriptsrun_quality_testpy&quot;&gt;&lt;strong&gt;1.4 离线脚本闭环（&lt;/strong&gt;&lt;code&gt;scripts/run_quality_test.py&lt;/code&gt;&lt;strong&gt;）&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;measure_one&lt;/code&gt; 里 judge 后：&lt;code&gt;hallucinated &gt; 0&lt;/code&gt; → &lt;code&gt;regenerate_flagged&lt;/code&gt; → 只对被替换资源重跑 judge → 汇总：&lt;/p&gt;
&lt;p&gt;🐍&lt;/p&gt;
&lt;p&gt;python&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;independent = {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;independent = {&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;&quot;hallucination&quot;: &amp;#x3C;第一轮&gt;,&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&quot;regen&quot;: {&quot;regenerated&quot;: n, &quot;reasons&quot;: [...], &quot;after&quot;: {rate, hallucinated, total}} | None,&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&quot;adaptation&quot;: ...&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;}&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--no-regen&lt;/code&gt; 开关（默认开）；artifacts 的 resources 更新为再生后（—judge-only 复跑看到的是修复后状态）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;aggregate()&lt;/code&gt; 增加 independent 再生聚合（before_rate / after_rate）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;write_markdown&lt;/code&gt; 增加”幻觉定向再生闭环”小节（before→after 对比表）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;15-测试&quot;&gt;&lt;strong&gt;1.5 测试&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;新 &lt;code&gt;backend/tests/test_quality_regen.py&lt;/code&gt;：只替换 flagged / hint 透传捕获 / 节点缺失保原 / 无幻觉 no-op / 非 dict 资源跳过&lt;/li&gt;
&lt;li&gt;扩展 &lt;code&gt;test_content_generator_unit.py&lt;/code&gt;：correction_hint 注入 prompt；retry_hint 状态流（state 带 content_phase_entered+retry_hint → _generate_one 收到 hint；首轮不注入）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;阶段二-生成时认识状态标记unverified_claims&quot;&gt;&lt;strong&gt;阶段二 ①：生成时认识状态标记（unverified_claims）&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;_build_generation_prompt&lt;/code&gt; / &lt;code&gt;_generate_feedback_one&lt;/code&gt; 的 JSON schema 增加： &lt;code&gt;&quot;unverified_claims&quot;: [&quot;图谱事实之外的类比/背景性陈述, 每条一句; 完全锚定为空数组&quot;]&lt;/code&gt; system 说明：自声明用于定向审计（类比允许，但要浮出水面），不是禁止&lt;/li&gt;
&lt;li&gt;&lt;code&gt;_generate_one&lt;/code&gt; / &lt;code&gt;_generate_feedback_one&lt;/code&gt;：&lt;code&gt;data.setdefault(&quot;unverified_claims&quot;, [])&lt;/code&gt; + 非 list 强转 &lt;code&gt;[]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;judge_hallucination&lt;/code&gt;：&lt;code&gt;_build_hallucination_prompt(content, facts_text, unverified=None)&lt;/code&gt; 追加”## 资源自声明待验证补充”块；从 &lt;code&gt;r.get(&quot;unverified_claims&quot;)&lt;/code&gt; 读&lt;/li&gt;
&lt;li&gt;&lt;code&gt;run_quality_test.py&lt;/code&gt;：per_run + aggregate 统计 &lt;code&gt;unverified_claims&lt;/code&gt; 总数（审计覆盖率证据）&lt;/li&gt;
&lt;li&gt;测试：setdefault 兜底、prompt 含字段、judge prompt 含自声明块、空数组不追加&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;阶段三-先锚定后展开bridge-before-conclusion&quot;&gt;&lt;strong&gt;阶段三 ③：先锚定后展开（bridge-before-conclusion）&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;_build_generation_prompt&lt;/code&gt; system 增加： “【先锚定后展开】资源的第一小节必须先用 1-3 句复述节点 summary/key_points 已给事实（不加新信息），随后的展开只能阐释已锚定事实；结论不得先于其图谱依据出现。”&lt;/li&gt;
&lt;li&gt;correction_hint 再生路径天然强化（修正要求 + 先锚定）&lt;/li&gt;
&lt;li&gt;同步 &lt;code&gt;data/prompts/04_content_generator_agent.txt&lt;/code&gt; 设计稿（新增同名小节）&lt;/li&gt;
&lt;li&gt;测试：prompt 字符串含规则；04 spec 文件含小节&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;阶段四-验证方式--覆盖双记录&quot;&gt;&lt;strong&gt;阶段四 ④：验证方式 + 覆盖双记录&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;data/prompts/judge_hallucination.txt&lt;/code&gt; 输出 JSON 增加 &lt;code&gt;&quot;coverage&quot;: &quot;full|partial|none&quot;&lt;/code&gt;（内容对节点 key_points 的锚定覆盖度）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;quality_judge.py&lt;/code&gt;：verdict dict 读入 &lt;code&gt;evidence_node_ids&lt;/code&gt;（现在被忽略）+ &lt;code&gt;coverage&lt;/code&gt;（非法值兜底 “none”）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;quality_metrics.py&lt;/code&gt; 新纯函数：&lt;/p&gt;
&lt;p&gt;🐍&lt;/p&gt;
&lt;p&gt;python&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;def compute_anchor_coverage(verdicts) -&gt; {&quot;full&quot;: n, &quot;partial&quot;: n, &quot;none&quot;: n, &quot;rate_full&quot;: float}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;def compute_anchor_coverage(verdicts) -&gt; {&amp;#x22;full&amp;#x22;: n, &amp;#x22;partial&amp;#x22;: n, &amp;#x22;none&amp;#x22;: n, &amp;#x22;rate_full&amp;#x22;: float}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;run_quality_test.py&lt;/code&gt;：independent 聚合带 coverage 统计；markdown 判定表加”锚定覆盖”行；verdicts 留档 evidence_node_ids（验证依据可追溯）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;测试：judge 读入两字段、非法兜底、compute_anchor_coverage 纯函数&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;‍&lt;/p&gt; &lt;blockquote&gt;This rendering was automatically generated by Frosti Feed and may have formatting issues. For the best experience, please visit: &lt;a href=&quot;https://www.originblog.xyz/blog/post_1786883135/&quot;&gt;https://www.originblog.xyz/blog/post_1786883135/&lt;/a&gt;&lt;/blockquote&gt;</content:encoded><dc:creator>Origin Blog</dc:creator><pubDate>Sun, 16 Aug 2026 00:00:00 GMT</pubDate></item><item><title>项目图谱深度增强：实体丰富化 + 技术栈检测 + LLM 分析 + 联网搜索</title><link>https://www.originblog.xyz/blog/post_1786808863/</link><guid isPermaLink="true">https://www.originblog.xyz/blog/post_1786808863/</guid><content:encoded>&lt;h2 id=&quot;问题根因&quot;&gt;&lt;strong&gt;问题根因&lt;/strong&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;数据断层&lt;/strong&gt;：后端 &lt;code&gt;parse_project&lt;/code&gt; 已提取 docstring/params/return_type/bases/decorators/external_calls/source_code 等丰富属性，但前端 &lt;code&gt;normalizeGraphResponse&lt;/code&gt; 只保留 6 个字段（id/name/kind/qualified_name/line_start/line_end），其余全丢弃&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;无技术栈检测&lt;/strong&gt;：&lt;code&gt;external_calls&lt;/code&gt; 已捕获未解析外部调用名（&lt;code&gt;requests.get&lt;/code&gt;/&lt;code&gt;pandas.DataFrame&lt;/code&gt;），但从未聚合分类&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;无 LLM 分析&lt;/strong&gt;：&lt;code&gt;parse_project&lt;/code&gt; 纯 AST，无语义层面的架构解读&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;无联网搜索&lt;/strong&gt;：&lt;code&gt;search_web&lt;/code&gt; + &lt;code&gt;learningResources&lt;/code&gt; store 已就绪，但 ProjectGraphView 完全未接入&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stats bug&lt;/strong&gt;：后端返回 &lt;code&gt;function_count&lt;/code&gt;，前端读 &lt;code&gt;function&lt;/code&gt;，头部计数永远 0&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;设计两段式ast-自动--llm-按需&quot;&gt;&lt;strong&gt;设计：两段式（AST 自动 + LLM 按需）&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;AST 能做的（技术栈检测、实体属性展示）自动执行零成本；LLM 分析 + 联网搜索按需触发（按钮），不拖慢自动解析。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;phase-1修复数据断层--实体详情丰富化零-llm-自动&quot;&gt;&lt;strong&gt;Phase 1：修复数据断层 + 实体详情丰富化（零 LLM, 自动）&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;11-apiprojectjsnormalizegraphresponse-传递完整属性&quot;&gt;&lt;strong&gt;1.1&lt;/strong&gt; &lt;code&gt;api/project.js``normalizeGraphResponse&lt;/code&gt; &lt;strong&gt;传递完整属性&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;entities 增加：docstring, params, return_type, bases, decorators, external_calls, module_name, source_code&lt;/li&gt;
&lt;li&gt;stats key 映射：&lt;code&gt;function_count&lt;/code&gt;-&gt;&lt;code&gt;function&lt;/code&gt;, &lt;code&gt;class_count&lt;/code&gt;-&gt;&lt;code&gt;class&lt;/code&gt;, &lt;code&gt;method_count&lt;/code&gt;-&gt;&lt;code&gt;method&lt;/code&gt;, &lt;code&gt;module_count&lt;/code&gt;-&gt;&lt;code&gt;module&lt;/code&gt;（修复头部计数 bug）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;12-projectgraphviewvue-实体详情面板丰富化&quot;&gt;&lt;strong&gt;1.2&lt;/strong&gt; &lt;code&gt;ProjectGraphView.vue&lt;/code&gt; &lt;strong&gt;实体详情面板丰富化&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;显示 docstring（如有，折叠展示）&lt;/li&gt;
&lt;li&gt;显示参数列表 &lt;code&gt;params&lt;/code&gt;（名称:类型）&lt;/li&gt;
&lt;li&gt;显示外部依赖 &lt;code&gt;external_calls&lt;/code&gt;（去重后的 top-level 模块名）&lt;/li&gt;
&lt;li&gt;显示继承基类 &lt;code&gt;bases&lt;/code&gt;、装饰器 &lt;code&gt;decorators&lt;/code&gt;（如有）&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;phase-2技术栈自动检测ast-零-llm-自动&quot;&gt;&lt;strong&gt;Phase 2：技术栈自动检测（AST, 零 LLM, 自动）&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;21-新建-frontendsrcutilstechstackjs&quot;&gt;&lt;strong&gt;2.1 新建&lt;/strong&gt; &lt;code&gt;frontend/src/utils/techStack.js&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;detectTechStack(entities)&lt;/code&gt;: 扫描所有实体的 &lt;code&gt;external_calls&lt;/code&gt;，提取 top-level 模块名（&lt;code&gt;.&lt;/code&gt; 前的部分），映射到技术分类&lt;/li&gt;
&lt;li&gt;内置 ~30 个常见 Python 包映射表（flask-&gt;Web 框架, pandas-&gt;数据分析, requests-&gt;HTTP 客户端, pytest-&gt;测试 等）&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;[{name, category, count}]&lt;/code&gt;（按引用次数降序）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;22-projectgraphviewvue-头部展示技术栈-badges&quot;&gt;&lt;strong&gt;2.2&lt;/strong&gt; &lt;code&gt;ProjectGraphView.vue&lt;/code&gt; &lt;strong&gt;头部展示技术栈 badges&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;在 stats 行下方加一行技术栈 tag（&lt;code&gt;el-tag&lt;/code&gt; per tech），点击可触发联网搜索该技术&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;phase-3llm-深度分析--联网搜索按需&quot;&gt;&lt;strong&gt;Phase 3：LLM 深度分析 + 联网搜索（按需）&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;31-后端新建-post-apiprojectanalyze&quot;&gt;&lt;strong&gt;3.1 后端新建&lt;/strong&gt; &lt;code&gt;POST /api/project/analyze&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;新文件&lt;/strong&gt; &lt;code&gt;backend/app/agents/project_analyzer.py&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;analyze_project(project_id, kg, tavily_key)&lt;/code&gt;: 从 Neo4j 取项目图谱 -&gt; LLM 分析架构（模式/入口点/关键模块/复杂度/学习建议）-&gt; 对检测到的技术栈逐个 &lt;code&gt;search_web&lt;/code&gt; 搜教程/文档&lt;/li&gt;
&lt;li&gt;LLM prompt 包含：实体清单（名称/类型/调用关系摘要）+ 技术栈 + 要求输出 JSON &lt;code&gt;{summary, architecture:{pattern, entry_points, key_modules}, recommendations:[...]}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;复用 &lt;code&gt;app/agents/llm.py&lt;/code&gt; 的 LLM 调用基础设施&lt;/li&gt;
&lt;li&gt;复用 &lt;code&gt;app/utils/web_search.py&lt;/code&gt; 的 &lt;code&gt;search_web&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;路由&lt;/strong&gt; &lt;code&gt;backend/app/api/project.py&lt;/code&gt; 加 &lt;code&gt;POST /analyze&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;入参 &lt;code&gt;{project_id, tavily_key?}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;503 if Neo4j 未就绪；404 if 项目图谱不存在&lt;/li&gt;
&lt;li&gt;返回 &lt;code&gt;{summary, architecture, tech_stack, web_resources:[{title,url,snippet,tech}]}&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;32-apiprojectjs-加-analyzeprojectprojectid-tavilykey&quot;&gt;&lt;strong&gt;3.2&lt;/strong&gt; &lt;code&gt;api/project.js&lt;/code&gt; &lt;strong&gt;加&lt;/strong&gt; &lt;code&gt;analyzeProject(projectId, tavilyKey)&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;POST /api/project/analyze&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;33-projectgraph-store-加-analyze-action&quot;&gt;&lt;strong&gt;3.3&lt;/strong&gt; &lt;code&gt;projectGraph store&lt;/code&gt; &lt;strong&gt;加&lt;/strong&gt; &lt;code&gt;analyze()&lt;/code&gt; &lt;strong&gt;action&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;analyzing&lt;/code&gt; ref (bool) + &lt;code&gt;analysis&lt;/code&gt; ref (null|{summary, architecture, tech_stack, web_resources})&lt;/li&gt;
&lt;li&gt;调 &lt;code&gt;analyzeProject&lt;/code&gt; -&gt; set analysis -&gt; web_resources 流入 &lt;code&gt;learningResources&lt;/code&gt; store&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;34-projectgraphviewvue-加深度分析按钮--结果面板&quot;&gt;&lt;strong&gt;3.4&lt;/strong&gt; &lt;code&gt;ProjectGraphView.vue&lt;/code&gt; &lt;strong&gt;加”深度分析”按钮 + 结果面板&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;工具栏加”深度分析”按钮（loading 状态）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;侧栏新增”项目分析”卡片（analysis 非空时显示）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;架构摘要（summary）&lt;/li&gt;
&lt;li&gt;架构模式 + 入口点 + 关键模块&lt;/li&gt;
&lt;li&gt;学习建议&lt;/li&gt;
&lt;li&gt;联网资源列表（web_resources，点击打开浏览器）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;技术栈 badge 点击 -&gt; 自动调 web_search 搜该技术 -&gt; 结果流入 learningResources&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;phase-4测试--收尾&quot;&gt;&lt;strong&gt;Phase 4：测试 + 收尾&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;测试&quot;&gt;&lt;strong&gt;测试&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;utils/techStack.test.js&lt;/code&gt;: detectTechStack 映射 + 去重 + 空输入&lt;/li&gt;
&lt;li&gt;&lt;code&gt;api/project.test.js&lt;/code&gt;（或扩展 project-graph-store.test.js）: normalizeGraphResponse 传递丰富字段 + stats key 映射&lt;/li&gt;
&lt;li&gt;后端 &lt;code&gt;test_project_analyzer.py&lt;/code&gt;: mock LLM + search_web, 验证返回结构&lt;/li&gt;
&lt;li&gt;全量 vitest + pytest 回归&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;‍&lt;/p&gt; &lt;blockquote&gt;This rendering was automatically generated by Frosti Feed and may have formatting issues. For the best experience, please visit: &lt;a href=&quot;https://www.originblog.xyz/blog/post_1786808863/&quot;&gt;https://www.originblog.xyz/blog/post_1786808863/&lt;/a&gt;&lt;/blockquote&gt;</content:encoded><dc:creator>Origin Blog</dc:creator><pubDate>Sat, 15 Aug 2026 00:00:00 GMT</pubDate></item><item><title>修复&quot;新领域动态建域后图谱无法生成&quot;</title><link>https://www.originblog.xyz/blog/post_1786789626/</link><guid isPermaLink="true">https://www.originblog.xyz/blog/post_1786789626/</guid><content:encoded>&lt;p&gt;Kmatch项目中用户对于新领域的知识点答题后，无法生成知识图谱&lt;br&gt;
&lt;strong&gt;已确认根因&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;动态域会话 submit 组装的 &lt;code&gt;learning_path&lt;/code&gt; 含重复 node_id（实测 AI 域 12 节点中 AI-003/004/010 ×2）→ 前端 &lt;code&gt;useGraphData.g6Nodes&lt;/code&gt; 直接喂 G6 → “Node already exists” 渲染崩溃。后端 BFS 结果循环是唯一无防重的口子（弱项补丁分支已有 &lt;code&gt;path_ids&lt;/code&gt; 防重）。工作区已有的 &lt;code&gt;seen_ids&lt;/code&gt; 修复方向正确，但&lt;strong&gt;从未验证&lt;/strong&gt;（后端未重启过），且注释归因（“按 (start,n) 分组”）与 Cypher 语义矛盾，需实库取证修正——若库里真有同 id 双实体还需清库。&lt;/p&gt;
&lt;h2 id=&quot;执行步骤&quot;&gt;&lt;strong&gt;执行步骤&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;1-起环境--实库取证&quot;&gt;&lt;strong&gt;1. 起环境 + 实库取证&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;docker-compose up -d&lt;/code&gt; 起 Neo4j，等健康&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;cypher-shell 只读查询：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;MATCH (n:KnowledgeNode) WITH n.id AS id, count(*) AS c WHERE c&gt;1 RETURN id, c&lt;/code&gt; — 同 id 双实体？&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SHOW CONSTRAINTS&lt;/code&gt; — &lt;code&gt;kb_unique_id&lt;/code&gt; 唯一约束是否存在&lt;/li&gt;
&lt;li&gt;直跑 BFS Cypher（known_ids=AI-001..007）数每个 id 的返回行数 — 实锤重复行来源&lt;/li&gt;
&lt;li&gt;节点总数（期望 252）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;2-根因处置&quot;&gt;&lt;strong&gt;2. 根因处置&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;若有双实体：清重复（保留 &lt;code&gt;updated_at&lt;/code&gt; 最新）+ 确保 &lt;code&gt;kb_unique_id&lt;/code&gt; 约束在&lt;/li&gt;
&lt;li&gt;依据取证结果&lt;strong&gt;改写 engine.py 修复注释&lt;/strong&gt;的归因描述（删掉未经证实的”按 (start,n) 分组”说法，写实测结论）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;3-验证后端修复&quot;&gt;&lt;strong&gt;3. 验证后端修复&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;重启 uvicorn（加载含 &lt;code&gt;seen_ids&lt;/code&gt; 的 engine.py）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;POST /api/graph/path&lt;/code&gt;（known=AI-001..007, weak=AI-001..010, level=1, max_nodes=12，复现 20:44 场景）→ 断言返回路径 node_id 唯一、难度 ≤3&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;4-前端防御治本加固&quot;&gt;&lt;strong&gt;4. 前端防御（治本加固）&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;frontend/src/composables/useGraphData.js&lt;/code&gt;：&lt;code&gt;g6Nodes&lt;/code&gt; 按 &lt;code&gt;node_id&lt;/code&gt; 去重（保首个）——G6 对重复 id 直接抛错，前端单点兜底后任何后端回归都不再炸渲染&lt;/li&gt;
&lt;li&gt;新增 &lt;code&gt;frontend/src/__tests__/useGraphData.test.js&lt;/code&gt;（重复 node_id → g6Nodes 唯一）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;5-回归测试&quot;&gt;&lt;strong&gt;5. 回归测试&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;后端：&lt;code&gt;pytest tests/test_engine_learning_path.py -q&lt;/code&gt; → 全量 &lt;code&gt;pytest -q&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;前端：&lt;code&gt;npm run test&lt;/code&gt;（vitest 全量，281 个）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;6-e2e-浏览器验证browser-use&quot;&gt;&lt;strong&gt;6. E2E 浏览器验证（browser-use）&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;浏览器打开学习会话，输入”人工智能”（已入库，秒级命中复用）→ 答题（全错模拟零基础）→ 提交 → 断言第 4 阶段”专属知识图谱”正常渲染（无 Node already exists，节点可点）&lt;/li&gt;
&lt;li&gt;不再新建全新域：动态建域本身已 3 次 E2E 验证过（JV/CS/AI），本次坏的是路径组装+渲染&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;7-文档--提交&quot;&gt;&lt;strong&gt;7. 文档 + 提交&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;devlog 追加到 &lt;code&gt;docs/devlogs/A_后端/2026-08-14_动态建域.md&lt;/code&gt;：实锤根因 + 修复 + 前端防御 + E2E 结果&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;分 3 个 commit 推送：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;fix(graph): assemble_learning_path BFS 重复节点 seen_ids 去重 - 修动态域 G6 &quot;Node already exists&quot; 渲染失败&lt;/code&gt;（engine.py + 测试 + devlog）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fix(ide): useGraphData g6Nodes 按节点 id 去重防御 - 后端任何回归不再炸图谱渲染&lt;/code&gt;（useGraphData.js + 测试）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;chore(data): AI 动态域演示数据入库 (E2E 实测 10 节点 20 题, source=llm_generated 可识别)&lt;/code&gt;（_manual_nodes.json + AI 题目文件）&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;备注&quot;&gt;&lt;strong&gt;备注&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;若用户实际在&lt;strong&gt;安装包&lt;/strong&gt;（sidecar exe）里复现：sidecar 是旧代码打的包，需重跑 PyInstaller 才含修复（本次不打包，验证以 dev 模式为准）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;‍&lt;/p&gt; &lt;blockquote&gt;This rendering was automatically generated by Frosti Feed and may have formatting issues. For the best experience, please visit: &lt;a href=&quot;https://www.originblog.xyz/blog/post_1786789626/&quot;&gt;https://www.originblog.xyz/blog/post_1786789626/&lt;/a&gt;&lt;/blockquote&gt;</content:encoded><dc:creator>Origin Blog</dc:creator><pubDate>Sat, 15 Aug 2026 00:00:00 GMT</pubDate></item><item><title>差点见不到我的博客</title><link>https://www.originblog.xyz/blog/chatter_1786716136/</link><guid isPermaLink="true">https://www.originblog.xyz/blog/chatter_1786716136/</guid><description>被自己蠢哭了</description><content:encoded>&lt;p&gt;原本是说想给博客的歌曲做个排序功能的，结果最后搞来搞去差点把后台搞崩了&lt;br&gt;
&lt;strong&gt;因为我的后台跑的是”过期的快照”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;my-blog-manager&lt;/code&gt; 后台不是用 &lt;code&gt;npm run dev&lt;/code&gt; 在跑，而是被 &lt;code&gt;launcher.py&lt;/code&gt; 用&lt;strong&gt;生产模式&lt;/strong&gt;启动的：它检测到 &lt;code&gt;.next/standalone/server.js&lt;/code&gt; 存在，就执行 &lt;code&gt;node server.js&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;而文章页面是（&lt;code&gt;app/posts/[slug]/page.tsx&lt;/code&gt;、&lt;code&gt;app/timeline/page.tsx&lt;/code&gt;）：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;link rel=&quot;https://www.originblog.xyz/stylesheet&quot; href=&quot;https://www.originblog.xyz/_astro/ec.vdc5k.css&quot;&gt;&lt;script type=&quot;module&quot; src=&quot;https://www.originblog.xyz/_astro/ec.0vx5m.js&quot;&gt;&lt;/script&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;const postsDirectory = path.join(process.cwd(), &apos;posts&apos;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;const postsDirectory = path.join(process.cwd(), &amp;#x27;posts&amp;#x27;);&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;生产模式下 &lt;code&gt;process.cwd()&lt;/code&gt; = &lt;code&gt;.next/standalone&lt;/code&gt;，所以后台读的是 &lt;code&gt;.next/standalone/posts/&lt;/code&gt;——&lt;strong&gt;都没有更新这个 standalone 快照&lt;/strong&gt;。TAT\&lt;/p&gt;
&lt;p&gt;目前的解决办法就是：&lt;br&gt;
删掉或改名 &lt;code&gt;.next/standalone/server.js&lt;/code&gt;，让 &lt;code&gt;launcher.py&lt;/code&gt; 自动回退到 &lt;code&gt;npm run dev&lt;/code&gt; 模式。这样后台读的就是 &lt;code&gt;my-blog-manager/posts/&lt;/code&gt; 实时数据&lt;/p&gt; &lt;blockquote&gt;This rendering was automatically generated by Frosti Feed and may have formatting issues. For the best experience, please visit: &lt;a href=&quot;https://www.originblog.xyz/blog/chatter_1786716136/&quot;&gt;https://www.originblog.xyz/blog/chatter_1786716136/&lt;/a&gt;&lt;/blockquote&gt;</content:encoded><dc:creator>Origin Blog</dc:creator><pubDate>Fri, 14 Aug 2026 00:00:00 GMT</pubDate></item><item><title>嵌入式图存储：打包版免 Docker/Neo4j</title><link>https://www.originblog.xyz/blog/post_1786708802/</link><guid isPermaLink="true">https://www.originblog.xyz/blog/post_1786708802/</guid><description>让Kmatch的知识题库随安装包开箱即用</description><content:encoded>&lt;h2 id=&quot;背景与结论&quot;&gt;&lt;strong&gt;背景与结论&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;现状：我的Kmatch 的知识库 JSON 真相源（222 节点 + 324 题）&lt;strong&gt;已经随安装包分发&lt;/strong&gt;（electron-builder extraResources 带 &lt;code&gt;data/&lt;/code&gt;），但运行时所有图查询/向量检索只走 Neo4j，用户必须自己 &lt;code&gt;docker&lt;/code&gt; 起 Neo4j 再手动跑导入脚本。全部 Cypher 收口在 &lt;code&gt;backend/app/graph/engine.py&lt;/code&gt; 一个类（932 行 ~30 方法），其余代码只依赖 &lt;code&gt;KnowledgeGraph&lt;/code&gt; 方法签名 —— 替换点单一，是理想收口。&lt;/p&gt;
&lt;p&gt;方案：新写 &lt;code&gt;EmbeddedKnowledgeGraph&lt;/code&gt;（内存图 + SQLite 持久化动态数据 + 分层 embedding 缓存），&lt;code&gt;KMATCH_GRAPH_STORE=embedded|neo4j&lt;/code&gt; env 切换；打包版默认 embedded，开发/Docker 链路保留 Neo4j 不动（488 存量测试均 mock driver，不受影响）。&lt;/p&gt;
&lt;h2 id=&quot;实施步骤&quot;&gt;&lt;strong&gt;实施步骤&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;1-embedding-预生成缓存--backendscriptsbuild_embedding_cachepy&quot;&gt;&lt;strong&gt;1. embedding 预生成缓存 —&lt;/strong&gt; &lt;code&gt;backend/scripts/build_embedding_cache.py&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;复用 &lt;code&gt;import_knowledge_base.py&lt;/code&gt; 的 embedding 逻辑，一次性算全部节点向量，输出 &lt;code&gt;data/knowledge_base/embeddings.json&lt;/code&gt;（node_id → 1536 维向量，保留 4 位小数，~几 MB），提交仓库随 extraResources 分发。&lt;/li&gt;
&lt;li&gt;构建期跑一次即可，装完即有完整离线语义检索。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;2-核心新模块--backendappgraphembeddedpy650-行工作量主体&quot;&gt;&lt;strong&gt;2. 核心新模块 —&lt;/strong&gt; &lt;code&gt;backend/app/graph/embedded.py&lt;/code&gt;&lt;strong&gt;（~650 行，工作量主体）&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;与 Neo4j 版同方法面的 &lt;code&gt;EmbeddedKnowledgeGraph&lt;/code&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;加载&lt;/strong&gt;：启动时经 &lt;code&gt;kb_store&lt;/code&gt; 读 &lt;code&gt;KB_DIR&lt;/code&gt; 全部节点/题目 JSON → 内存 &lt;code&gt;nodes&lt;/code&gt; dict + REQUIRES 正反向邻接表 + &lt;code&gt;questions_by_node&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;图遍历&lt;/strong&gt;：纯 Python BFS 实现 &lt;code&gt;get_prerequisites / get_dependents / get_reachable&lt;/code&gt;（对应 &lt;code&gt;REQUIRES*1..N&lt;/code&gt; 变长路径）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;学习路径&lt;/strong&gt;：&lt;code&gt;assemble_learning_path&lt;/code&gt; 的 Cypher 分层 BFS（&lt;code&gt;MATCH path ... min(length(path))&lt;/code&gt;）重写为 Python 多目标 BFS；&lt;strong&gt;弱项补丁/配额逻辑（&lt;/strong&gt;&lt;code&gt;_WEAK_PATCH_LIMIT&lt;/code&gt; &lt;strong&gt;等）已是纯 Python，原样复用&lt;/strong&gt;，用小 fixture 与 Neo4j 版对拍。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;向量检索&lt;/strong&gt;：numpy（新增依赖）cosine 打分 top-k；embeddings 三层合并：shipped &lt;code&gt;embeddings.json&lt;/code&gt; 为底 + state.db 覆盖层（见下）+ 内存新算。无向量节点跳过语义检索、保留图召回（沿用现有纯图降级语义）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CRUD 同步&lt;/strong&gt;：&lt;code&gt;upsert/delete_knowledge_node&lt;/code&gt;、&lt;code&gt;upsert/delete_question&lt;/code&gt; 只更新内存索引 + 触发增量 embedding（JSON 真相源写回已由 &lt;code&gt;api/kb.py&lt;/code&gt; 完成，不重复）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;回答「题库会变」&lt;/strong&gt;：增删改节点时若 embedding client 可用 → 单点补算写入覆盖层；不可用 → 该节点退出语义检索、仍在图检索里。内置 222 节点开箱即有向量。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;3-动态状态持久化--sqlitekmatch_state_dirstatedb&quot;&gt;&lt;strong&gt;3. 动态状态持久化 — SQLite（&lt;/strong&gt;&lt;code&gt;KMATCH_STATE_DIR/state.db&lt;/code&gt;&lt;strong&gt;）&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;node_status&lt;/code&gt; 表：&lt;code&gt;update_node_status / get_node_status&lt;/code&gt;（掌握状态，比现在写在 Neo4j 全局覆盖式还更干净）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;project_entities / project_relations / project_meta&lt;/code&gt; 三表：&lt;code&gt;write/get/delete_project_graph&lt;/code&gt;、&lt;code&gt;annotate_risk&lt;/code&gt;、&lt;code&gt;link_entity_to_knowledge&lt;/code&gt;（场景二项目图谱）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;embedding_overlays&lt;/code&gt; 表：CRUD 产生的向量变更（node_id, vec blob, text_hash；文本没变沿用缓存）。&lt;/li&gt;
&lt;li&gt;数据量小、后端单进程，全内存 + 写穿 SQLite，无并发难题。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;4-工厂--启动接线&quot;&gt;&lt;strong&gt;4. 工厂 + 启动接线&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;backend/app/config.py&lt;/code&gt;：新增 &lt;code&gt;KMATCH_GRAPH_STORE&lt;/code&gt;（默认 &lt;code&gt;neo4j&lt;/code&gt;）、&lt;code&gt;KMATCH_STATE_DIR&lt;/code&gt;、&lt;code&gt;KMATCH_SEED_KB_DIR&lt;/code&gt; 三个配置。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;main.py&lt;/code&gt; lifespan：按 store 类型实例化 embedded 或 Neo4j 版（duck-typing 共用方法签名）；&lt;code&gt;/api/health&lt;/code&gt; 改报 &lt;code&gt;graph_store: &quot;embedded&quot; | &quot;neo4j connected/unavailable&quot;&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可写性&lt;/strong&gt;：打包版 resources/data 在 Program Files 只读，KB CRUD 写 JSON 会失败。后端启动时若 &lt;code&gt;KMATCH_SEED_KB_DIR&lt;/code&gt; 设置且 state 目录无 knowledge_base → 整体拷贝一份（~几 MB）到 &lt;code&gt;&amp;#x3C;state&gt;/knowledge_base&lt;/code&gt; 并把 &lt;code&gt;KB_DIR&lt;/code&gt; 指过去；资源目录保持只读。开发模式无该变量，行为不变。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;5-electron-注入--electronmainbackend-sidecarjs&quot;&gt;&lt;strong&gt;5. Electron 注入 —&lt;/strong&gt; &lt;code&gt;electron/main/backend-sidecar.js&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;生产分支 spawn 时注入 &lt;code&gt;KMATCH_GRAPH_STORE=embedded&lt;/code&gt;、&lt;code&gt;KMATCH_STATE_DIR=&amp;#x3C;userData&gt;/state&lt;/code&gt;、&lt;code&gt;KMATCH_SEED_KB_DIR=&amp;#x3C;resourcesPath&gt;/data/knowledge_base&lt;/code&gt;；更新头注释（删「Neo4j 仍由用户 Docker 起」）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;6-依赖与打包&quot;&gt;&lt;strong&gt;6. 依赖与打包&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;backend/requirements.txt&lt;/code&gt; 加 &lt;code&gt;numpy&gt;=1.26&lt;/code&gt;（PyInstaller hook 自带，spec 预计无需手动 collect，打包时验证）。&lt;/li&gt;
&lt;li&gt;README/AGENTS.md：安装包章节改为「开箱即用；Neo4j 为开发/服务端可选模式」。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;7-测试与验证&quot;&gt;&lt;strong&gt;7. 测试与验证&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;新增 &lt;code&gt;backend/tests/test_embedded_engine.py&lt;/code&gt;：加载计数与 import 脚本一致 / BFS 手算小图正确性 / 学习路径与 Neo4j 版对拍 / 假向量（one-hot 1536 维）top-k 断言 / 项目图谱与掌握状态 SQLite roundtrip（tmp_path）/ embedding overlay 增删。&lt;/li&gt;
&lt;li&gt;回归：全量 &lt;code&gt;pytest&lt;/code&gt;（488 后端测试）。&lt;/li&gt;
&lt;li&gt;手工：&lt;code&gt;KMATCH_GRAPH_STORE=embedded&lt;/code&gt; 起后端跑学习会话阶段①②、图谱视图、KB 管理增改、场景二项目图谱，再打一次 NSIS 包验证 userData 拷贝与首启流程。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;8-文档&quot;&gt;&lt;strong&gt;8. 文档&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;devlog 新阶段条目 + AGENTS.md 架构速览/打包节更新 + CONTEXT.md + ADR-0007（双后端存储决策）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;风险点&quot;&gt;&lt;strong&gt;风险点&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;assemble_learning_path&lt;/code&gt; Cypher BFS 语义复刻是唯一有对拍风险的点，用小 fixture 双实现对照兜底。&lt;/li&gt;
&lt;li&gt;embeddings.json 体积与 numpy 进 PyInstaller 产物，打包时实测确认。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;‍&lt;/p&gt; &lt;blockquote&gt;This rendering was automatically generated by Frosti Feed and may have formatting issues. For the best experience, please visit: &lt;a href=&quot;https://www.originblog.xyz/blog/post_1786708802/&quot;&gt;https://www.originblog.xyz/blog/post_1786708802/&lt;/a&gt;&lt;/blockquote&gt;</content:encoded><dc:creator>Origin Blog</dc:creator><pubDate>Fri, 14 Aug 2026 00:00:00 GMT</pubDate></item><item><title>龙芯嵌入式 Linux 开发实训第一阶段总结</title><link>https://www.originblog.xyz/blog/post_1783926984/</link><guid isPermaLink="true">https://www.originblog.xyz/blog/post_1783926984/</guid><content:encoded>&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;实训平台&lt;/strong&gt;：龙芯 2K0300 久久派开发板（LoongArch64 架构）&lt;br&gt;
&lt;strong&gt;实训环境&lt;/strong&gt;：Ubuntu 22.04 虚拟机 + VMware + MobaXterm + LoongOS&lt;br&gt;
&lt;strong&gt;实训周期&lt;/strong&gt;：2026年7月&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;一实训背景与目标&quot;&gt;&lt;strong&gt;一、实训背景与目标&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;本次计算机实训的主题是&lt;strong&gt;龙芯（LoongArch）嵌入式 Linux 系统开发&lt;/strong&gt;。实训使用的硬件平台是龙芯 2K0300”久久派”开发板——这是一款搭载龙芯自主研发的 LoongArch64 架构处理器（LA264 核心）的嵌入式开发平台，运行 LoongOS 嵌入式 Linux 系统（内核版本 4.19.190+，含 PREEMPT_RT rt79 实时抢占补丁），固件层使用龙芯平台标准的 PMON 引导程序。&lt;/p&gt;
&lt;p&gt;与常见的 x86 或 ARM 嵌入式开发不同，龙芯平台有自己独特的技术生态：专属的交叉编译工具链（&lt;code&gt;loongarch64-linux-gnu-gcc&lt;/code&gt; 8.3.0）、定制化的板级支持包（BSP）、PMON 固件体系、以及尚在完善中的开源社区支持。这些特性使得本次实训不仅是一次技术学习，更是一次国产自主平台的实践探索。&lt;/p&gt;
&lt;p&gt;实训的目标非常明确——在龙芯 2K0300 平台上，从零开始完整走通嵌入式 Linux 开发的核心链路：从最上层的用户空间应用编程（Shell/Python），到交叉编译环境搭建和 C 语言程序部署，再到 Linux 内核模块驱动开发和设备树配置，最后触及系统固件构建（OpenWrt）。五个模块逐层深入，每一层都涉及不同的工具链、编程范式、和调试方法。&lt;/p&gt;
&lt;p&gt;实训全程在 Ubuntu 22.04 虚拟机（VMware 运行于 Windows 11 宿主机）上进行代码编写和交叉编译，使用 MobaXterm 通过串口连接开发板进行操作，文件传输依赖 U 盘（因网络始终未调通）。整个过程中借助 Claude Code AI 进行实时错误诊断和原理讲解——这也是本次实训的一个特色：在 AI 辅助下，问题排查的效率得到了数量级的提升。&lt;/p&gt;
&lt;p&gt;实训最大的特点是 &lt;strong&gt;“困难驱动学习”&lt;/strong&gt;——五个实验模块下来，累计遇到了大大小小 20 余个实际问题，涵盖命令语法错误、编译工具缺失、内核版本不匹配、设备树语法错误、文件系统兼容性、USB 控制器驱动缺陷、内核启动 panic 等方方面面。每个问题都迫使我深入理解背后的机制，而这种”先踩坑、再理解”的方式，比顺利地走完一遍实验指导书要深刻得多。以下将按照实际实验顺序，以遇到的问题和解决过程为主线展开叙述。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;二实验环境详情&quot;&gt;&lt;strong&gt;二、实验环境详情&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;21-硬件配置&quot;&gt;&lt;strong&gt;2.1 硬件配置&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;组件规格说明开发板&lt;/strong&gt;龙芯 2K0300 久久派龙芯中科推出的嵌入式开发平台，面向 IoT 和工业控制场景&lt;strong&gt;处理器&lt;/strong&gt;LoongArch64 LA264 核心龙芯自主设计的 64 位 RISC 指令集架构，区别于 x86/ARM/MIPS&lt;strong&gt;板载存储&lt;/strong&gt;eMMC 8GB（&lt;code&gt;/dev/mmcblk0&lt;/code&gt;）PMON 固件中识别为 &lt;code&gt;emmc0&lt;/code&gt;，通过 &lt;code&gt;ls2k_sdio&lt;/code&gt; 驱动访问，支持 HS200 高速模式&lt;strong&gt;板载内存&lt;/strong&gt;DDR3/DDR4（约 512MB 可用）内核启动日志显示 &lt;code&gt;Memory: 457496K/524288K available&lt;/code&gt;&lt;strong&gt;网络接口&lt;/strong&gt;千兆以太网（Synopsys DWMAC1000 / stmmac 驱动）实训期间因网络配置问题未调通&lt;strong&gt;USB 接口&lt;/strong&gt;dwc2 OTG 控制器用于 U 盘数据传输、USB 转串口&lt;strong&gt;串口&lt;/strong&gt;UART，参数 115200/8/N/1，无流控通过 USB 转串口线连接 PC，是实验期间与开发板交互的主要通道&lt;strong&gt;扩展引脚&lt;/strong&gt;GPIO 83（GPA80-3）等连接 LED，低电平有效（active-low）接线方式&lt;/p&gt;
&lt;h3 id=&quot;22-软件环境&quot;&gt;&lt;strong&gt;2.2 软件环境&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;组件规格详细说明目标操作系统&lt;/strong&gt;LoongOSYocto 项目构建的嵌入式 Linux，根文件系统高度精简&lt;strong&gt;内核版本&lt;/strong&gt;&lt;code&gt;Linux 4.19.190+&lt;/code&gt;含 &lt;code&gt;rt79&lt;/code&gt; PREEMPT_RT 实时抢占补丁，编译器为 GCC 8.3.0&lt;strong&gt;内核编译者&lt;/strong&gt;&lt;code&gt;zhangruijie@zhangruijie-virtual-machine&lt;/code&gt;说明该内核是由同一实验环境编译的&lt;strong&gt;固件&lt;/strong&gt;PMON龙芯平台标准固件，相当于 x86 的 BIOS/UEFI&lt;strong&gt;主机 OS&lt;/strong&gt;Ubuntu 22.04 LTS（VMware 虚拟机）用户名 &lt;code&gt;zhangruijie&lt;/code&gt;，NAT 网络模式&lt;strong&gt;宿主机 OS&lt;/strong&gt;Windows 11 家庭中文版 10.0.26200运行 VMware 和 MobaXterm&lt;strong&gt;交叉编译器&lt;/strong&gt;&lt;code&gt;loongarch64-linux-gnu-gcc&lt;/code&gt; 8.3.0LoongArch GNU toolchain rc1.6 (20241115)&lt;strong&gt;工具链安装路径&lt;/strong&gt;&lt;code&gt;/usr/local/gcc-13.2.0-loongarch64-linux-gnu/&lt;/code&gt;虽然目录名写的是 13.2.0，实际装的是 8.3 版本&lt;strong&gt;串口终端&lt;/strong&gt;MobaXterm同时支持 SSH、SFTP、串口三种连接方式&lt;strong&gt;AI 辅助工具&lt;/strong&gt;Claude Code（deepseek-v4-pro 模型）运行在 Windows Git Bash 环境下&lt;/p&gt;
&lt;h3 id=&quot;23-三机协作的物理拓扑&quot;&gt;&lt;strong&gt;2.3 三机协作的物理拓扑&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;整个实训的物理连接关系如下：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;link rel=&quot;https://www.originblog.xyz/stylesheet&quot; href=&quot;https://www.originblog.xyz/_astro/ec.vdc5k.css&quot;&gt;&lt;script type=&quot;module&quot; src=&quot;https://www.originblog.xyz/_astro/ec.0vx5m.js&quot;&gt;&lt;/script&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;┌─────────────────┐     USB 转串口     ┌──────────────────┐&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  Windows 11 PC  │ ◄────────────────► │  龙芯 2K0300     │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  (宿主机)        │   115200/8/N/1     │  久久派开发板     │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│                  │                    │  LoongOS 4.19    │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  运行:           │                    │  IP: 192.168.1.3 │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  - VMware        │                    └──────────────────┘&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  - MobaXterm     │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  - Claude Code   │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;└──┬───────────────┘&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│ VMware 虚拟网络 (NAT)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;┌──▼──────────────────┐&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  Ubuntu 22.04 VM    │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  (编译环境)          │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│                      │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  - 交叉工具链        │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  - 内核源码          │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  - 编译产物          │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  IP: 192.168.42.x   │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;└─────────────────────┘&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot; ┌─────────────────┐     USB 转串口     ┌──────────────────┐ │  Windows 11 PC  │ ◄────────────────► │  龙芯 2K0300     │ │  (宿主机)        │   115200/8/N/1     │  久久派开发板     │ │                  │                    │  LoongOS 4.19    │ │  运行:           │                    │  IP: 192.168.1.3 │ │  - VMware        │                    └──────────────────┘ │  - MobaXterm     │ │  - Claude Code   │ └──┬───────────────┘    │    │ VMware 虚拟网络 (NAT)    │ ┌──▼──────────────────┐ │  Ubuntu 22.04 VM    │ │  (编译环境)          │ │                      │ │  - 交叉工具链        │ │  - 内核源码          │ │  - 编译产物          │ │  IP: 192.168.42.x   │ └─────────────────────┘&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;需要注意的关键约束：Ubuntu 虚拟机使用 NAT 模式，IP 在 192.168.42.0/24 网段；宿主机连校园 Wi-Fi，IP 在 10.135.51.0/24 网段；开发板在 192.168.1.0/24 网段。三者之间两两不在同一子网，网络完全不通。因此文件传输只能通过 U 盘物理搬运——这是实训期间最大的工程约束之一。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;三实验过程--以困难与解决为主线&quot;&gt;&lt;strong&gt;三、实验过程 —— 以困难与解决为主线&lt;/strong&gt;&lt;/h2&gt;
&lt;hr&gt;
&lt;h3 id=&quot;模块一gpio-用户空间-led-控制&quot;&gt;&lt;strong&gt;模块一：GPIO 用户空间 LED 控制&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;实验目标&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;掌握 Linux sysfs 接口在用户空间操作 GPIO 的方法，先后使用 Shell 脚本和 Python 脚本控制 GPIO 83 号引脚驱动 LED 闪烁。这是嵌入式 Linux 开发的”Hello World”——从最熟悉的应用层入手，建立对 Linux 硬件抽象机制的第一印象。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;实验原理&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Linux 内核为用户空间提供了一套优雅的 GPIO 控制接口——sysfs。它的设计哲学是”一切皆文件”：把硬件引脚的操作抽象为对普通文件的读写。整个操作流程分为标准的四步：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第一步：导出 GPIO。&lt;/strong&gt; 向 &lt;code&gt;/sys/class/gpio/export&lt;/code&gt; 写入引脚编号（如 &lt;code&gt;83&lt;/code&gt;），内核会为该 GPIO 分配资源并在 sysfs 中创建对应的控制目录 &lt;code&gt;/sys/class/gpio/gpio83/&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第二步：设置方向。&lt;/strong&gt; 向 &lt;code&gt;gpio83/direction&lt;/code&gt; 写入 &lt;code&gt;&quot;out&quot;&lt;/code&gt;（输出模式，用于控制 LED）或 &lt;code&gt;&quot;in&quot;&lt;/code&gt;（输入模式，用于读取按键等）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第三步：读写电平。&lt;/strong&gt; 向 &lt;code&gt;gpio83/value&lt;/code&gt; 写入 &lt;code&gt;&quot;1&quot;&lt;/code&gt;（高电平，典型 3.3V）或 &lt;code&gt;&quot;0&quot;&lt;/code&gt;（低电平，0V）。这里有一个容易混淆的点——LED 的实际亮灭取决于硬件接线方式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;高电平驱动（active-high）&lt;/strong&gt;：LED 阳极接 GPIO，阴极接地，写 &lt;code&gt;&quot;1&quot;&lt;/code&gt; = 亮，写 &lt;code&gt;&quot;0&quot;&lt;/code&gt; = 灭&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;低电平驱动（active-low）&lt;/strong&gt;：LED 阳极接 VCC，阴极经电阻接 GPIO，写 &lt;code&gt;&quot;0&quot;&lt;/code&gt; = 亮，写 &lt;code&gt;&quot;1&quot;&lt;/code&gt; = 灭&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们的开发板 GPIO 83 是低电平驱动方式，所以 Python 代码中 &lt;code&gt;write(&quot;0&quot;)&lt;/code&gt; 才是”点亮”。如果搞反了这个逻辑，调试时会出现程序逻辑和别人代码对不上的困惑。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第四步：释放 GPIO。&lt;/strong&gt; 程序退出前向 &lt;code&gt;/sys/class/gpio/unexport&lt;/code&gt; 写入引脚编号，内核回收资源。这一步很重要——如果程序异常退出（如 &lt;code&gt;Ctrl+C&lt;/code&gt; 中断）而没有 unexport，GPIO 会一直处于导出状态，下次运行时可能遇到”Device or resource busy”错误。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Python 脚本的模块化设计&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Shell 脚本版本很简单，就是顺序执行四步操作。Python 版本做了更完善的设计：使用函数封装每个步骤（&lt;code&gt;gpio_export()&lt;/code&gt;、&lt;code&gt;gpio_write()&lt;/code&gt;、&lt;code&gt;blink()&lt;/code&gt;、&lt;code&gt;gpio_unexport()&lt;/code&gt;），支持两种运行模式——固定次数闪烁（默认 5 次，执行完自动退出并清理）和无限闪烁（&lt;code&gt;Ctrl+C&lt;/code&gt; 中断后通过 &lt;code&gt;try/finally&lt;/code&gt; 确保 GPIO 被正确释放）。Python 版本还用 &lt;code&gt;time.sleep(1)&lt;/code&gt; 控制 1 秒的闪烁间隔，用 &lt;code&gt;print()&lt;/code&gt; 输出当前运行状态方便观察。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;遇到的困难&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;第一个实验就让我深刻体会到了嵌入式开发与桌面开发的一个关键区别：&lt;strong&gt;你以为理所当然存在的东西，在嵌入式系统里可能都没有。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;困难一：权限错误 —— 但原因不是权限&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;我以 root 用户登录开发板后，自信地输入了第一条命令：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;echo 83 &gt; /sys/class/gpio/gpio83&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot; echo 83 &gt; /sys/class/gpio/gpio83&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Shell 直接返回 &lt;code&gt;Permission denied&lt;/code&gt;。我反复 &lt;code&gt;whoami&lt;/code&gt; 确认自己是 root，一度怀疑是文件系统挂载成了只读。后来才知道这里有两个错误同时存在：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;GPIO 83 根本没有被 export，&lt;code&gt;/sys/class/gpio/gpio83/&lt;/code&gt; 这个目录还不存在——我试图往一个不存在的路径写数据&lt;/li&gt;
&lt;li&gt;就算目录存在，&lt;code&gt;gpio83&lt;/code&gt; 是一个目录名，不能直接往里写数据，需要写到 &lt;code&gt;gpio83/value&lt;/code&gt; 或 &lt;code&gt;gpio83/direction&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;正确的第一步是 &lt;code&gt;echo 83 &gt; /sys/class/gpio/export&lt;/code&gt;。这个错误暴露了一个思维定式：&lt;strong&gt;我把 Linux 错误信息的字面意思当成了真实原因&lt;/strong&gt;。&lt;code&gt;Permission denied&lt;/code&gt; 在这里是”目标路径不存在且无法创建”的误导性表达——有时候错误信息只是表象，需要结合上下文深入分析。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;困难二：vim 困境 —— Linux 新手的必经之路&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;通过 MobaXterm SSH 连接开发板后，我输入 &lt;code&gt;vim led_blink.py&lt;/code&gt; 想编辑文件。屏幕上瞬间出现了满屏的波浪号（&lt;code&gt;~&lt;/code&gt; 表示空行）和底部一行看不清的模式提示。我尝试了 Esc、&lt;code&gt;q&lt;/code&gt;、&lt;code&gt;Ctrl+C&lt;/code&gt;、&lt;code&gt;Ctrl+Z&lt;/code&gt;，甚至直接关闭 MobaXterm 窗口重新连接——终端里还残留着 vim 的界面。这是典型的”不知道自己在哪个模式”的 vim 新手困境。最终在 AI 指导下输入 &lt;code&gt;:q!&lt;/code&gt;（冒号 q 感叹号 回车）强制退出。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;困难三：系统精简 —— nano 去哪了&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;逃离 vim 后我决定换个友好的编辑器：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;nano led_blink.py&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot; nano led_blink.py&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;-sh: nano: command not found&lt;/code&gt;。LoongOS 是 Yocto 构建的高度裁剪系统，只保留了最基础的 vi，nano、vim、gedit 等通通没有。解决方案是使用 shell 的 heredoc 语法直接在终端写文件：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;cat &gt; led_blink.py &amp;#x3C;&amp;#x3C; &apos;EOF&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;# 代码写在这里&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;EOF&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot; cat &gt; led_blink.py &lt;&lt; &amp;#x27;EOF&amp;#x27; # 代码写在这里 EOF&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;后来 MobaXterm 的 SFTP 侧边栏文件拖拽功能调通后，文件传输才变得轻松。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;困难四：Python 环境阉割 —— pathlib 消失&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;脚本写好后执行 &lt;code&gt;python3 led_blink.py&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;ModuleNotFoundError: No module named &apos;pathlib&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot; ModuleNotFoundError: No module named &amp;#x27;pathlib&amp;#x27;&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;pathlib&lt;/code&gt; 是 Python 3.4 以后的标准库，桌面上从来不会缺。但 LoongOS 的 Python 显然是去掉了”不必要”模块的精简编译版本。我将所有 &lt;code&gt;Path()&lt;/code&gt; 路径操作替换为 &lt;code&gt;open()&lt;/code&gt; + 字符串拼接，去掉了 pathlib 依赖。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;困难五：终端粘贴缩进毁坏&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;用 heredoc 方式粘贴多行 Python 代码时，终端经常出现缩进随机偏移——有的行多了两格，有的行少了两格，导致 &lt;code&gt;IndentationError: unexpected indent&lt;/code&gt;。这是因为 MobaXterm 在传输大段文本时，对换行符和空格的处理偶尔不稳定。解决办法没有捷径，只能逐行用 vi 检查和修正缩进。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;最终成果与反思&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;两个脚本最终都成功运行，LED 以 1 秒间隔规律闪烁。这次实验让我第一次感受到：&lt;strong&gt;嵌入式系统是一个”减法世界”&lt;/strong&gt;——桌面 Linux 上理所当然的一切（nano、完整的 Python 标准库、tab 补全）在嵌入式环境里都是”可选配置”，有时候甚至被裁剪掉了。从”加法思维”（我需要装什么）转变为”减法思维”（系统有什么我就用什么），是嵌入式开发入门的第一个门槛。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;模块二交叉编译工具链搭建与-c-语言-led-程序部署&quot;&gt;&lt;strong&gt;模块二：交叉编译工具链搭建与 C 语言 LED 程序部署&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;实验目标&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在 x86_64 Ubuntu 虚拟机上搭建 LoongArch64 交叉编译环境，用 C 语言重写 LED 闪烁程序（相比 Python/Shell 有更好的性能和更小的运行依赖），通过 Makefile 管理本地编译和交叉编译两种构建目标，最后将交叉编译产物通过 U 盘部署到开发板并成功运行。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;实验原理 —— 交叉编译的本质&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;交叉编译（Cross Compilation）&lt;/strong&gt; 是嵌入式开发最核心的概念之一。它的定义很简单：编译平台（Build Platform）和目标运行平台（Target Platform）是不同的 CPU 架构。在我们的实验中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Build Platform：x86_64 Ubuntu（&lt;code&gt;uname -m&lt;/code&gt; 输出 &lt;code&gt;x86_64&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;Target Platform：LoongArch64（龙芯 2K0300 处理器）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;交叉编译器 &lt;code&gt;loongarch64-linux-gnu-gcc&lt;/code&gt; 本身是一个运行在 x86_64 上的程序，但它输出的二进制文件是按照 LoongArch64 指令集编码的，只能在龙芯 CPU 上执行。这种”在 A 平台上编译给 B 平台用”的能力是嵌入式开发的基础设施——因为嵌入式设备通常性能有限、存储空间小，不适合直接在板子上跑编译器和链接器。&lt;/p&gt;
&lt;p&gt;实验要求同时进行&lt;strong&gt;本地编译&lt;/strong&gt;和&lt;strong&gt;交叉编译&lt;/strong&gt;，通过对比来直观理解两者的区别：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;led_blink_native&lt;/code&gt;：用 &lt;code&gt;gcc&lt;/code&gt; 本地编译 → &lt;code&gt;ELF 64-bit LSB executable, x86-64&lt;/code&gt; → 能在 Ubuntu 上运行（但没有 GPIO 硬件，运行会失败）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;led_blink_cross&lt;/code&gt;：用 &lt;code&gt;loongarch64-linux-gnu-gcc&lt;/code&gt; 交叉编译 → &lt;code&gt;ELF 64-bit LSB executable, LoongArch&lt;/code&gt; → 必须在龙芯开发板上运行&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;通过 &lt;code&gt;file&lt;/code&gt; 命令查看 ELF 头中的架构标识，可以一目了然地验证交叉编译是否成功。这种”看一眼 ELF 头”的习惯在以后的嵌入式工作中会经常用到。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;遇到的困难&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这个模块是整个实训中”工程琐事”最多的环节。C 代码本身不到 60 行，但从下载工具链到程序在开发板上跑起来，耗费了大量时间在与代码无关的事情上。这恰恰反映了嵌入式开发的真实面貌——&lt;strong&gt;很多时候瓶颈不在编程能力，而在工程环境的搭建和调试。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;阶段一：工具链下载 —— 一场”找源”之旅&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;实验指导书给出的第一步是下载工具链，但没说从哪里下载。于是开始了漫长的尝试：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;尝试 1：一键安装脚本。&lt;/strong&gt; 指导书给了一个 gitcode.com 的脚本链接。下载后 &lt;code&gt;cat install_gcc13.sh&lt;/code&gt;，发现内容竟然是 HTML 登录页面——gitcode.com 的 raw 文件需要认证才能访问。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;尝试 2：GitHub。&lt;/strong&gt; 搜索到龙芯官方的 GitHub 仓库有工具链发布。&lt;code&gt;wget https://github.com/loongson/...&lt;/code&gt;，连接超时。在国内网络环境下访问 GitHub 极其不稳定，下载速度经常降到零。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;尝试 3：apt 包管理器。&lt;/strong&gt; &lt;code&gt;sudo apt install gcc-loongarch64-linux-gnu&lt;/code&gt;。返回 &lt;code&gt;E: Unable to locate package&lt;/code&gt;。Ubuntu 22.04 官方仓库没有收录龙芯交叉编译器。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;尝试 4：龙芯官方 FTP。&lt;/strong&gt; 最终在 &lt;code&gt;ftp.loongnix.cn&lt;/code&gt;（龙芯社区软件源）找到了工具链的直接下载链接：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;wget http://ftp.loongnix.cn/toolchain/loongson-gnu-toolchain-8.3-x86_64-loongarch64-linux-gnu-rc1.6.tar.xz&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot; wget http://ftp.loongnix.cn/toolchain/loongson-gnu-toolchain-8.3-x86_64-loongarch64-linux-gnu-rc1.6.tar.xz&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;下载成功。这个经历让我认识到国产平台的软件分发现状——不像 ARM 的 &lt;code&gt;gcc-arm-none-eabi&lt;/code&gt; 那样 &lt;code&gt;apt install&lt;/code&gt; 就搞定，龙芯的工具链需要从官网或社区源手动获取。版本方面也做了妥协，指导书要求的是 GCC 13.2.0，而 FTP 上稳定版是 8.3.0，（涉及 BSP 版本兼容性问题——BSP 1.0 用 GCC 8.3 + Linux 5.10，BSP 2.0 用 GCC 13.3 + Linux 6.12，开发板跑的是 BSP 1.0 内核）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;阶段二：命令行操作的一连串小问题&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;下载完成后还有一系列操作上的坑：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;目录双层嵌套&lt;/strong&gt;：执行 &lt;code&gt;mv toolchain /usr/local/gcc-13.2.0&lt;/code&gt; 时，目标目录已存在，mv 的行为是把源目录&lt;strong&gt;放入&lt;/strong&gt;目标目录内部而非替换它，结果路径变成 &lt;code&gt;/usr/local/gcc-13.2.0/toolchain/bin/...&lt;/code&gt;。&lt;code&gt;PATH&lt;/code&gt; 环境变量配的路径完全找不到可执行文件。解决方法是 &lt;code&gt;tar -xvf xxx.tar.xz --strip-components=1 -C /usr/local/gcc-13.2.0&lt;/code&gt; 直接解压到目标位置。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;命令被终端换行截断&lt;/strong&gt;：复制粘贴多行命令时，MobaXterm 有时会在不该换行的地方截断，导致命令参数不完整。这个问题的根源是 Windows 和 Linux 的换行符差异（&lt;code&gt;\r\n&lt;/code&gt; vs &lt;code&gt;\n&lt;/code&gt;），以及终端缓冲区对长命令的处理方式。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;阶段三：Makefile 的 Tab 地狱 —— 不可见字符的陷阱&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;按照实验指导书编写好 Makefile 后，自信地敲下 &lt;code&gt;make&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;Makefile:2: *** missing separator.  Stop.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot; Makefile:2: *** missing separator.  Stop.&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;我把第 2 行反复看了不下五遍，语法完全正确。搜索后才知道：&lt;strong&gt;Makefile 每个 target 下的命令行（recipe）必须以 Tab 字符开头，不能用空格缩进。&lt;/strong&gt; 在编辑器里，Tab 和 4 个空格看起来一模一样，但 &lt;code&gt;make&lt;/code&gt; 程序是通过 ASCII 码来区分的——Tab = &lt;code&gt;\t&lt;/code&gt; = 0x09，空格 = 0x20。&lt;/p&gt;
&lt;p&gt;为什么我会用空格？因为实验指导书的代码是通过网页浏览器复制粘贴的，浏览器渲染时把 Tab 替换成了空格显示，复制时 Tab 就永久丢失了。解决办法是在编辑器中开启”显示空白字符”（VS Code 中是 &lt;code&gt;Editor: Render Whitespace&lt;/code&gt;），或者用命令修复：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;sed -i &apos;s/^    /\t/&apos; Makefile&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot; sed -i &amp;#x27;s/^    /\t/&amp;#x27; Makefile&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;这个坑几乎每个第一次写 Makefile 的人都会遇到。它还揭示了一个更普遍的规律：&lt;strong&gt;编程工具链对”不可见字符”的依赖是新手陷阱的重灾区&lt;/strong&gt;。Python 的缩进（空格和 Tab 混用）、Makefile 的 recipe 行、Shell 脚本的换行符（&lt;code&gt;\r\n&lt;/code&gt; vs &lt;code&gt;\n&lt;/code&gt;），都是看起来一样但实际上不同的东西。学会区分和检测不可见字符是程序员的基本功。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;阶段四：VMware USB 控制器兼容性 —— 硬件虚拟化也有坑&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;编译完成后，需要把可执行文件从 Ubuntu 虚拟机传输到开发板。网络不通（后面会讲到），只能用 U 盘。在 VMware 菜单中点击”连接 U330 USB 设备”后，Ubuntu 里执行 &lt;code&gt;lsblk&lt;/code&gt;——空空如也。&lt;/p&gt;
&lt;p&gt;查看内核日志 &lt;code&gt;sudo dmesg | tail -20&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;usb 1-1: Invalid ep0 maxpacket: 9&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;usb 1-1: unable to enumerate USB device&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot; usb 1-1: Invalid ep0 maxpacket: 9 usb 1-1: unable to enumerate USB device&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;VMware 的 USB 2.0（EHCI）控制器和这个朗科 U 盘不兼容。这是 VMware 已知的 USB 直通（passthrough）兼容性问题。解决方法：关闭虚拟机 → 虚拟机设置 → USB 控制器 → 从 USB 2.0 改为 USB 3.0（xHCI）→ 启动虚拟机 → 重新连接 U 盘。&lt;code&gt;lsblk&lt;/code&gt; 这次正确显示了 &lt;code&gt;/dev/sdb&lt;/code&gt;（58.6GB）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;阶段五：文件系统兼容性 —— 开发板不认识 FAT32&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;U 盘在 Ubuntu 上挂载、复制文件、卸载，插到开发板上，执行：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;mount /dev/sda1 /mnt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot; mount /dev/sda1 /mnt&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;返回 &lt;code&gt;unknown filesystem type &apos;vfat&apos;&lt;/code&gt;。尝试加载内核模块：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;modprobe vfat&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot; modprobe vfat&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;返回 &lt;code&gt;FATAL: Module vfat not found&lt;/code&gt;。这意味着开发板内核编译时完全没有包含 vfat/FAT32 支持——既没有编译进内核，也没有编译为模块。&lt;/p&gt;
&lt;p&gt;大多数 U 盘出厂默认 FAT32 格式是为了跨平台兼容（Windows/Mac/Linux 都能读写），但嵌入式 Linux 为了精简内核体积，只保留了 ext4 等最必需的文件系统驱动。解决方案是将 U 盘重新格式化为 ext4：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;sudo mkfs.ext4 /dev/sdb1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot; sudo mkfs.ext4 /dev/sdb1&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;风险提示&lt;/strong&gt;：这会清空 U 盘上所有数据！格式化前务必确认 U 盘里没有需要保留的文件。格式化后还需重新拔插 U 盘让内核识别新的分区表和文件系统签名。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;阶段六：U 盘设备名漂移 —— 没有持久化绑定&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;每次拔插 U 盘后，它的设备名都在变化：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;sda → sdb → sdc → sde → ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot; sda → sdb → sdc → sde → ...&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;这是因为 Linux 内核按照 USB 设备枚举的先后顺序动态分配块设备节点名（&lt;code&gt;/dev/sd*&lt;/code&gt;），没有任何持久化绑定（不像 &lt;code&gt;/dev/disk/by-uuid/&lt;/code&gt; 或 &lt;code&gt;/dev/disk/by-id/&lt;/code&gt; 那样稳定）。开发板上连 &lt;code&gt;lsblk&lt;/code&gt; 命令都没有（系统裁剪 again），只能用 &lt;code&gt;ls /dev/sd*&lt;/code&gt; 和 &lt;code&gt;fdisk -l&lt;/code&gt; 来确认当前设备名。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;阶段七：ext4 超级块损坏&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;格式化 ext4 后挂载时又出错：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;EXT4-fs (sdb1): couldn&apos;t mount as ext3 due to feature incompatibilities&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot; EXT4-fs (sdb1): couldn&amp;#x27;t mount as ext3 due to feature incompatibilities&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;文件系统超级块（superblock）在格式化后出现了不一致。&lt;code&gt;fsck.ext4 -y /dev/sdb1&lt;/code&gt; 修复后正常。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;阶段八：网络困境 —— 为什么放弃 SCP&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;整个实训期间，Ubuntu 虚拟机和开发板的网络始终不通。经过详细排查：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ubuntu 虚拟机：VMware NAT 模式，IP 为 &lt;code&gt;192.168.42.x&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Windows 宿主机：连校园 Wi-Fi，IP 为 &lt;code&gt;10.135.51.x&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;开发板：静态度 &lt;code&gt;192.168.1.3&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;三者之间两两不在同一子网，&lt;code&gt;ping&lt;/code&gt; 全部返回 &lt;code&gt;Network is unreachable&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;没有路由设备连接这三个网段&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;尝试将 VMware 网络模式改为桥接（Bridged），但桥接后虚拟机获取到的是校园网 DHCP 地址（10.135.51.x），仍然和开发板的 192.168.1.x 不在同一网段。想要打通需要额外配置路由规则或 NAT 转发，在实训时间有限的情况下不现实。最终决定全程使用 U 盘物理传输——每次多花几分钟拔插操作，但它不依赖任何网络配置，在隔离的嵌入式开发环境中反而最可靠。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;阶段九：开发板目录结构差异&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在开发板上执行 &lt;code&gt;mkdir -p /root &amp;#x26;&amp;#x26; cp /mnt/led_blink_cross /root/&lt;/code&gt; 时报错 &lt;code&gt;/root&lt;/code&gt; 目录不存在。标准 Linux 发行版中 root 用户的家目录是 &lt;code&gt;/root&lt;/code&gt;，但 LoongOS 的根文件系统中省略了这个目录（root 直接使用 &lt;code&gt;/&lt;/code&gt; 作为家目录）。改为 &lt;code&gt;cp /mnt/led_blink_cross ~/&lt;/code&gt; 后正常。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;最终成果与反思&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;# Ubuntu 上交叉编译&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;$ make cross&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;loongarch64-linux-gnu-gcc -o led_blink_cross led_blink.c&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;$ file led_blink_cross&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;led_blink_cross: ELF 64-bit LSB executable, LoongArch, version 1 (SYSV),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;dynamically linked, interpreter /lib64/ld.so.1, for GNU/Linux 4.15.0, not stripped&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;​&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;# 开发板上运行&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;# ./led_blink_cross&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;LED blinking finished!   # LED 实际闪烁了 5 次，每次 1 秒亮、1 秒灭&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot; # Ubuntu 上交叉编译 $ make cross loongarch64-linux-gnu-gcc -o led_blink_cross led_blink.c $ file led_blink_cross led_blink_cross: ELF 64-bit LSB executable, LoongArch, version 1 (SYSV), dynamically linked, interpreter /lib64/ld.so.1, for GNU/Linux 4.15.0, not stripped ​ # 开发板上运行 # ./led_blink_cross LED blinking finished!   # LED 实际闪烁了 5 次，每次 1 秒亮、1 秒灭&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;C 语言 LED 程序在龙芯开发板上成功运行。这个模块耗费时间最长的部分不是编写 C 逻辑，而是九个”非代码”问题的逐一排查。它让我深刻体会到：&lt;strong&gt;嵌入式开发的瓶颈往往不在编程能力，而在工程环境——从哪里下载工具链、文件怎么过墙（物理的和网络的）、设备为什么不被识别、文件系统为什么挂不上。&lt;/strong&gt; 这些问题在 x86 桌面开发中几乎不存在（&lt;code&gt;apt install&lt;/code&gt; + 本地运行搞定一切），但在嵌入式领域却是每天的日常。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;模块三pmon-固件基础操作&quot;&gt;&lt;strong&gt;模块三：PMON 固件基础操作&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;实验目标&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;了解龙芯平台 PMON 固件的基本功能、命令体系、启动流程、和自动启动配置方法。能够从 PMON 命令行手动加载 Linux 内核并引导系统启动。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;实验原理 —— PMON 在系统启动中的位置&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;PMON 是龙芯平台的系统固件，在系统启动链中处于最底层的位置。它在系统中的角色完全等同于 x86 PC 的 BIOS 或 UEFI。当按下电源键后，SoC 首先执行固化在 ROM/Flash 中的 PMON 代码：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;上电自检（POST）&lt;/strong&gt;：初始化 CPU 核心、DDR 内存控制器、基础时钟树&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;外设枚举&lt;/strong&gt;：扫描并初始化 eMMC、USB Host、以太网等外设，为它们分配 PMON 内部设备名（如 &lt;code&gt;emmc0&lt;/code&gt;、&lt;code&gt;usb0&lt;/code&gt;、&lt;code&gt;syn0&lt;/code&gt;、&lt;code&gt;syn1&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;查找启动配置&lt;/strong&gt;：按优先级顺序查找——先检查根目录是否有 &lt;code&gt;boot.cfg&lt;/code&gt; 文件，没有则读取 &lt;code&gt;autocmd&lt;/code&gt; 环境变量，再没有则使用 &lt;code&gt;al&lt;/code&gt;/&lt;code&gt;al1&lt;/code&gt;/&lt;code&gt;rd&lt;/code&gt;/&lt;code&gt;append&lt;/code&gt; 变量组合&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;加载内核&lt;/strong&gt;：根据配置中的路径，从指定设备加载内核 ELF 文件（vmlinuz）到内存的指定地址&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;加载 initrd&lt;/strong&gt;（可选）：如果配置了 ramdisk，将其加载到内存&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;传递设备树（DTB）&lt;/strong&gt;：将 &lt;code&gt;.dtb&lt;/code&gt; 二进制文件加载到内存并告知内核其位置&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;设置内核命令行参数&lt;/strong&gt;：&lt;code&gt;console=&lt;/code&gt;、&lt;code&gt;root=&lt;/code&gt;、&lt;code&gt;rdinit=&lt;/code&gt;、&lt;code&gt;rootdelay=&lt;/code&gt; 等&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;跳转执行&lt;/strong&gt;：CPU 程序计数器跳转到内核入口地址，此后由 Linux 内核接管一切&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果在启动过程中按了空格键，或者自动启动配置缺失/失败，就会进入 PMON 交互命令行，提示符为 &lt;code&gt;PMON&gt;&lt;/code&gt;。这时管理员可以手动执行 &lt;code&gt;load&lt;/code&gt;、&lt;code&gt;initrd&lt;/code&gt;、&lt;code&gt;g&lt;/code&gt;（go）等命令来加载和启动系统。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PMON 核心命令速查&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;命令语法示例功能说明&lt;/strong&gt;&lt;code&gt;hh&lt;/code&gt;显示帮助信息和可用命令列表&lt;code&gt;versvers&lt;/code&gt;查看 PMON 固件版本号&lt;code&gt;devlsdevls&lt;/code&gt;列出所有检测到的设备及其 PMON 设备名&lt;code&gt;loadload (usb0,0)/boot/vmlinuz&lt;/code&gt;从指定设备的指定路径加载 ELF 格式内核到内存&lt;code&gt;initrdinitrd (usb0,0)/boot/initrd.img&lt;/code&gt;加载 ramdisk（initramfs）镜像&lt;code&gt;gg console=ttyS0,115200 root=/dev/mmcblk0p1&lt;/code&gt;设置内核命令行参数并从入口地址开始执行&lt;code&gt;setset autocmd &quot;load (usb0,0)/boot/vmlinuz;g&quot;&lt;/code&gt;设置环境变量（注意值中不能有空格）&lt;code&gt;unsetunset autocmd&lt;/code&gt;删除环境变量&lt;code&gt;floadfload (usb0,0)/pmon.bin&lt;/code&gt;烧录/更新 PMON 固件本身&lt;code&gt;pingping 192.168.1.1&lt;/code&gt;测试网络连通性&lt;code&gt;ifconfigifconfig syn0 192.168.1.100&lt;/code&gt;配置网络接口 IP&lt;code&gt;fdtfdt (usb0,0)/boot/loongson_2k0300.dtb&lt;/code&gt;手动加载设备树文件&lt;code&gt;blbl&lt;/code&gt;列出启动配置信息&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PMON 与 Linux 的设备命名对照&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;一个关键认知是：&lt;strong&gt;PMON 的设备命名和 Linux 内核的设备命名是两套完全独立的体系&lt;/strong&gt;。它们在同一个硬件上使用不同的名称：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;硬件实体PMON 中的名称Linux 内核中的名称&lt;/strong&gt;板载 eMMC 存储&lt;code&gt;emmc0/dev/mmcblk0&lt;/code&gt;（块设备），&lt;code&gt;mmc0&lt;/code&gt;（控制器）USB 大容量存储&lt;code&gt;usb0/dev/sda&lt;/code&gt;（或 sdb/sdc，取决于枚举顺序）千兆以太网&lt;code&gt;syn0&lt;/code&gt;, &lt;code&gt;syn1eth0&lt;/code&gt;, &lt;code&gt;eth1&lt;/code&gt;（或 &lt;code&gt;stmmac&lt;/code&gt;）回环设备&lt;code&gt;loopdev0/dev/loop0&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;理解这个对应关系在后续的内核启动调试中起到了关键作用——当内核 panic 报 &lt;code&gt;Cannot open root device &quot;mmcblk0p1&quot;&lt;/code&gt; 时，我们可以回到 PMON 用 &lt;code&gt;devls&lt;/code&gt; 确认 &lt;code&gt;emmc0&lt;/code&gt; 硬件是否被检测到，从而判断问题是出在硬件层面还是内核的设备树配置层面。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;遇到的困难与反思&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这个实验以理论学习为主，最大的困难不是技术操作，而是&lt;strong&gt;思维模式的切换&lt;/strong&gt;。在前两个模块中，我们一直在 Linux 操作系统内部工作——有熟悉的 bash shell、&lt;code&gt;ls&lt;/code&gt; / &lt;code&gt;cd&lt;/code&gt; / &lt;code&gt;cat&lt;/code&gt; 命令、tab 补全、命令历史、文件系统层级结构。但 PMON 是裸机固件：没有文件系统的概念（&lt;code&gt;(usb0,0)/boot/vmlinuz&lt;/code&gt; 是 PMON 自己定义的一种设备路径语法，不是 Linux 路径），没有 tab 补全，输错参数就要全部重打，命令总数也就十几个。&lt;/p&gt;
&lt;p&gt;这种切换的本质是从”操作系统提供的丰富抽象”降到”固件提供的极简抽象”。在操作系统里你习惯了 &lt;code&gt;cd /boot &amp;#x26;&amp;#x26; ls -la&lt;/code&gt;，在 PMON 里你得用完全不同的语法 &lt;code&gt;devls&lt;/code&gt; 先看有哪些设备，然后凭记忆输入完整的内核路径。如果忘记设备名或路径，只能重启再看。&lt;/p&gt;
&lt;p&gt;另外，&lt;strong&gt;设备树的传递过程&lt;/strong&gt;值得特别理解：PMON 负责从存储设备读取 DTB 文件，将其加载到内存的指定位置，并在跳转到内核前把 DTB 的内存地址告知内核。如果 DTB 文件损坏（后面实验会遇到的 &lt;code&gt;dtb chsum err!!!&lt;/code&gt;），或者 PMON 没有正确传递地址，内核就无法识别平台设备（GPIO 控制器、MMC 控制器等），导致启动失败。这个机制在后面实验四的内核 panic 排查中成为了分析问题的关键线索。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;模块四linux-内核模块驱动开发--gpio-led-驱动&quot;&gt;&lt;strong&gt;模块四：Linux 内核模块驱动开发 —— GPIO LED 驱动&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;实验目标&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这是本次实训最核心、难度最高、耗时最长的实验。目标是：获取龙芯定制 Linux 4.19 内核源码，编写完整的 GPIO LED 驱动模块，通过交叉编译生成 &lt;code&gt;.ko&lt;/code&gt;（Kernel Object）文件，在开发板上使用 &lt;code&gt;insmod&lt;/code&gt; 加载驱动，驱动加载时自动点亮 LED 并通过 &lt;code&gt;printk&lt;/code&gt; 内核日志输出学号。实验还涉及修改内核 Kconfig/Makefile 构建系统将自定义驱动集成进去，以及修改设备树（DTS）文件为 GPIO 引脚添加正确的 pinctrl 配置。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;实验原理&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;内核模块 vs 用户空间程序的本质区别&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;前面模块一和模块二中，我们通过 sysfs 接口从&lt;strong&gt;用户空间&lt;/strong&gt;控制 GPIO。调用链是：用户程序 → &lt;code&gt;write()&lt;/code&gt; 系统调用 → VFS 虚拟文件系统 → sysfs 文件系统 → GPIO 子系统 → 硬件寄存器。每次 GPIO 操作都要穿越用户态到内核态的边界（系统调用开销），数据要经过多层抽象转换。&lt;/p&gt;
&lt;p&gt;内核模块则完全不同——它直接运行在&lt;strong&gt;内核空间&lt;/strong&gt;，享有最高的 CPU 特权级（Ring 0），调用的是内核内部 API（&lt;code&gt;gpio_request()&lt;/code&gt;、&lt;code&gt;gpio_direction_output()&lt;/code&gt;、&lt;code&gt;gpio_set_value()&lt;/code&gt; 等），直接操作硬件寄存器，没有系统调用开销。内核模块没有 &lt;code&gt;main()&lt;/code&gt; 函数，取而代之的是 &lt;code&gt;module_init(fn)&lt;/code&gt; 和 &lt;code&gt;module_exit(fn)&lt;/code&gt; 两个宏——前者指定模块加载时执行的初始化函数，后者指定模块卸载时执行的清理函数。&lt;/p&gt;
&lt;p&gt;编译后的内核模块是 &lt;code&gt;.ko&lt;/code&gt;（Kernel Object）文件，通过 &lt;code&gt;insmod&lt;/code&gt; 命令加载、&lt;code&gt;rmmod&lt;/code&gt; 卸载、&lt;code&gt;lsmod&lt;/code&gt; 查看已加载模块列表、&lt;code&gt;dmesg&lt;/code&gt; 查看内核日志（包括 &lt;code&gt;printk&lt;/code&gt; 输出）。模块必须是 GPL 兼容许可证才能使用内核的 GPL-only 符号。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kconfig/Makefile 构建系统&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Linux 内核使用一套精密的配置和构建系统来管理数千个驱动和功能选项。要向内核添加一个自定义驱动，需要修改两个文件：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kconfig&lt;/strong&gt;：定义配置选项的元数据。它告诉 &lt;code&gt;make menuconfig&lt;/code&gt; 界面应该显示什么选项、该选项的依赖关系、以及它对应什么编译行为（&lt;code&gt;tristate&lt;/code&gt; 表示可以选 Y/M/N）：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;config LEDS_2K0300_GPIO&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;tristate &quot;LED Support for Loongson 2K0300 GPIO&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;depends on LEDS_CLASS &amp;#x26;&amp;#x26; GPIOLIB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;help&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;This option enables LED driver for Loongson 2K0300&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;using GPIO 83.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot; config LEDS_2K0300_GPIO     tristate &amp;#x22;LED Support for Loongson 2K0300 GPIO&amp;#x22;     depends on LEDS_CLASS &amp;#x26;&amp;#x26; GPIOLIB     help       This option enables LED driver for Loongson 2K0300       using GPIO 83.&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Makefile&lt;/strong&gt;：定义编译规则。它告诉内核构建系统当某个配置项被选中时应该编译哪些源文件：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;obj-$(CONFIG_LEDS_2K0300_GPIO) += leds-2k0300-gpio.o&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot; obj-$(CONFIG_LEDS_2K0300_GPIO) += leds-2k0300-gpio.o&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;这行代码的妙处在于 &lt;code&gt;$(CONFIG_LEDS_2K0300_GPIO)&lt;/code&gt; 会根据 &lt;code&gt;.config&lt;/code&gt; 中的配置值自动展开为 &lt;code&gt;y&lt;/code&gt;、&lt;code&gt;m&lt;/code&gt;、或空——分别对应编译进内核（&lt;code&gt;obj-y&lt;/code&gt;）、编译为模块（&lt;code&gt;obj-m&lt;/code&gt;）、或不编译。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;设备树（Device Tree）机制详解&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;x86 PC 上的硬件很多是”可枚举”的——PCI 总线、USB 总线等都有标准的设备发现协议，内核启动时能自动扫描并识别外接设备。但嵌入式 SoC 平台不一样：大量的外设（GPIO 控制器、I²C、SPI、UART、MMC/SDIO 控制器等）是直接挂在芯片内部的内存映射总线上的，&lt;strong&gt;没有标准的自动发现机制&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;设备树（Device Tree）就是为了解决这个问题。它是一种数据结构（源文件 &lt;code&gt;.dts&lt;/code&gt; 编译为二进制 &lt;code&gt;.dtb&lt;/code&gt;），在系统启动时由固件（PMON）传递给内核，告诉内核：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;这个 SoC 上有哪些硬件（设备节点）&lt;/li&gt;
&lt;li&gt;它们挂载在哪个总线上（&lt;code&gt;compatible&lt;/code&gt; 属性用于匹配驱动）&lt;/li&gt;
&lt;li&gt;它们的寄存器基地址、中断号、时钟源是什么（&lt;code&gt;reg&lt;/code&gt;、&lt;code&gt;interrupts&lt;/code&gt;、&lt;code&gt;clocks&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;GPIO 引脚的多功能复用如何配置（&lt;code&gt;pinctrl&lt;/code&gt; 子系统）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在我们的实验中，设备树的样子大概是：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;&amp;#x26;gpio {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;led_pin: led {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;         &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;gpios = &amp;#x3C;&amp;#x26;gpio 83 GPIO_ACTIVE_HIGH&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;         &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;label = &quot;heartbeat&quot;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot; &amp;#x26;gpio {     led_pin: led {         gpios = &lt;&amp;#x26;gpio 83 GPIO_ACTIVE_HIGH&gt;;         label = &amp;#x22;heartbeat&amp;#x22;;     }; };&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;&amp;#x26;gpio&lt;/code&gt; 是引用（label reference）已定义的 GPIO 控制器节点，&lt;code&gt;gpios = &amp;#x3C;&amp;#x26;gpio 83 ...&gt;&lt;/code&gt; 指定使用该控制器的第 83 号引脚。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;内核模块加载的三道 ABI 校验机制&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Linux 内核为模块加载设下了层层校验，确保模块与当前运行内核的二进制接口（ABI）完全兼容。理解这三道锁是成功加载模块的关键：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第一道：vermagic（版本魔术字符串）&lt;/strong&gt;。编译模块时，构建系统会自动在 &lt;code&gt;.ko&lt;/code&gt; 文件的 &lt;code&gt;.modinfo&lt;/code&gt; 段中嵌入一个”版本魔术字符串”，包含：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;vermagic: 4.19.190+ SMP preempt mod_unload modversions LOONGARCH64&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot; vermagic: 4.19.190+ SMP preempt mod_unload modversions LOONGARCH64&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;insmod&lt;/code&gt; 首先将模块中的 vermagic 与运行中内核的 vermagic 做字符串比对，任何差异都会直接导致加载失败。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第二道：module_layout 符号&lt;/strong&gt;。这不是一个普通函数，而是一个数据结构校验和。它编码了内核核心数据结构 &lt;code&gt;struct module&lt;/code&gt; 的大小和关键成员的内存偏移量。如果正在运行的内核和编译模块时使用的内核在抢占模型（PREEMPT vs PREEMPT_RT）、SMP 配置、内存管理选项等方面有差异，&lt;code&gt;struct module&lt;/code&gt; 的布局就会不同，module_layout 的 CRC 值就会变化。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第三道：CONFIG_MODVERSIONS（符号级 CRC 校验）&lt;/strong&gt;。启用后，内核为每个导出的符号（函数、变量）计算 CRC32 校验值。模块编译时会记录所有引用符号的 CRC 值，&lt;code&gt;insmod&lt;/code&gt; 加载时将这些 CRC 与运行内核的实际情况逐一比对。即使内核版本号完全一样，只要某个内核配置项（尤其是影响函数签名的）不同，CRC 就会不匹配。&lt;/p&gt;
&lt;p&gt;这三道锁层层递进：vermagic 是粗粒度的字符串匹配，module_layout 是数据结构级别的校验，modversions 是符号级别的校验。全部通过后才能成功加载。理解这些机制是后续调试 &lt;code&gt;insmod&lt;/code&gt; 失败的基础。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;遇到的困难&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;阶段一：编译困难&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;问题现象原因分析解决过程&lt;/strong&gt;内核源码不在工作目录&lt;code&gt;tar -xvf linux-4.19-0627.tar.gz&lt;/code&gt; 报文件不存在浏览器下载时使用了默认路径 &lt;code&gt;~/Downloads/&lt;/code&gt;，而我当时的工作目录是 &lt;code&gt;~/loongarch-dev/&lt;/code&gt;在 Downloads 目录找到后 &lt;code&gt;mv&lt;/code&gt; 迁移过来缺少编译依赖&lt;code&gt;make menuconfig&lt;/code&gt; 报 &lt;code&gt;fatal error: openssl/opensslv.h: No such file or directory&lt;/code&gt;虚拟机是相对干净的 Ubuntu 22.04，缺少内核编译所需的大量开发库&lt;code&gt;sudo apt install libssl-dev libelf-dev flex bison bc&lt;/code&gt;没有精确匹配的 defconfig&lt;code&gt;make loongson_2k0300_defconfig&lt;/code&gt; 报 &lt;code&gt;No rule to make target&lt;/code&gt;2K0300 是较新的开发板，该版内核源码中并没有预置同名的默认配置以最接近的 &lt;code&gt;loongson2_defconfig&lt;/code&gt; 为基础，再通过 &lt;code&gt;make menuconfig&lt;/code&gt; 手动设置 &lt;code&gt;CONFIG_LOCALVERSION=&quot;+&quot;&lt;/code&gt; 等选项&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;阶段二：设备树困难 —— “看不到的依赖”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;交叉编译走通了（&lt;code&gt;make modules&lt;/code&gt; 成功生成 &lt;code&gt;.ko&lt;/code&gt;），但设备树这边接连爆雷：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;问题 1：dtbs 编译无任何输出。&lt;/strong&gt; 执行 &lt;code&gt;make dtbs&lt;/code&gt; 后静默结束，&lt;code&gt;arch/loongarch/boot/dts/loongson/&lt;/code&gt; 下空空如也。排查后发现 &lt;code&gt;.config&lt;/code&gt; 中 &lt;code&gt;CONFIG_BUILTIN_DTB_NAME=&quot;&quot;&lt;/code&gt;——内核不知道要为哪个板子编译 DTB。通过 &lt;code&gt;make menuconfig&lt;/code&gt; → “Boot options” → “Built-in DTB name” 设置为 &lt;code&gt;loongson_2k0300_pai_99&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;问题 2：DTC include 路径错误。&lt;/strong&gt; 设置 DTB 名称后再次编译：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;fatal error: dt-bindings/gpio/gpio.h: No such file or directory&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot; fatal error: dt-bindings/gpio/gpio.h: No such file or directory&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;DTS 源文件顶部有 &lt;code&gt;#include &amp;#x3C;dt-bindings/gpio/gpio.h&gt;&lt;/code&gt;，这个 &lt;code&gt;&amp;#x3C;&gt;&lt;/code&gt; 的搜索路径中包含 &lt;code&gt;arch/loongarch/boot/dts/include/&lt;/code&gt;，但这个目录&lt;strong&gt;不存在&lt;/strong&gt;——它应该是一个符号链接，指向内核源码根目录的 &lt;code&gt;include/&lt;/code&gt;。为什么它不存在？可能是在解压或迁移源码时符号链接丢失了（某些压缩格式不保留符号链接）。&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;cd arch/loongarch/boot/dts&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;ln -s ../../../../include include&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot; cd arch/loongarch/boot/dts ln -s ../../../../include include&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;创建符号链接后 include 问题解决。这件事让我学到一个经验：&lt;strong&gt;解压内核源码后，先检查符号链接是否完整&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;问题 3：pinctrl 标签未定义。&lt;/strong&gt; 引用了 &lt;code&gt;.dtsi&lt;/code&gt; 头文件中不存在的 pinctrl 标签。需要仔细检查所 include 的 &lt;code&gt;.dtsi&lt;/code&gt; 文件中实际定义了哪些标签，确保引用名称完全一致。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;阶段三：模块加载困难 —— 内核设下的三道校验锁&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;编译终于全部通过，&lt;code&gt;leds-2k0300-gpio.ko&lt;/code&gt; 成功生成。通过 U 盘拷到开发板上，敲下 &lt;code&gt;insmod&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;insmod leds_2k0300_gpio.ko&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot; insmod leds_2k0300_gpio.ko&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;第一道锁：module_layout 版本号后缀不匹配&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;insmod: ERROR: could not insert module leds_2k0300_gpio.ko: Invalid module format&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot; insmod: ERROR: could not insert module leds_2k0300_gpio.ko: Invalid module format&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;dmesg&lt;/code&gt; 查看内核日志：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;leds_2k0300_gpio: disagrees about version of symbol module_layout&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot; leds_2k0300_gpio: disagrees about version of symbol module_layout&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;我首先用 &lt;code&gt;modinfo&lt;/code&gt; 查看模块信息：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;vermagic:       4.19.190 SMP preempt mod_unload modversions LOONGARCH64&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot; vermagic:       4.19.190 SMP preempt mod_unload modversions LOONGARCH64&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;而开发板 &lt;code&gt;uname -r&lt;/code&gt; 输出：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;4.19.190+&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot; 4.19.190+&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;就差了末尾的 &lt;code&gt;+&lt;/code&gt; 号，这个 &lt;code&gt;+&lt;/code&gt; 来自内核配置项 &lt;code&gt;CONFIG_LOCALVERSION=&quot;+&quot;&lt;/code&gt;（Yocto 构建系统默认加的后缀，表示代码树有未提交的修改）。我们在 Ubuntu 的内核 &lt;code&gt;.config&lt;/code&gt; 中加上 &lt;code&gt;CONFIG_LOCALVERSION=&quot;+&quot;&lt;/code&gt; 重新编译模块，vermagic 对齐。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第二道锁：CONFIG_MODVERSIONS CRC 校验不匹配&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;版本对齐后再次 &lt;code&gt;insmod&lt;/code&gt;，又报新的错误：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;leds_2k0300_gpio: disagrees about version of symbol gpio_request&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;leds_2k0300_gpio: Unknown symbol gpio_request (err -22)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot; leds_2k0300_gpio: disagrees about version of symbol gpio_request leds_2k0300_gpio: Unknown symbol gpio_request (err -22)&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;这表示开发板内核启用了 &lt;code&gt;CONFIG_MODVERSIONS&lt;/code&gt;，每个导出符号都有 CRC 校验和。要让 CRC 完全匹配，需要获得开发板的精确内核配置。理想方案是从 &lt;code&gt;/proc/config.gz&lt;/code&gt; 导出配置后完整重编译内核，但这有两个前置条件：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;CONFIG_IKCONFIG_PROC&lt;/code&gt; 必须已在内核中启用（否则 &lt;code&gt;/proc/config.gz&lt;/code&gt; 不存在）&lt;/li&gt;
&lt;li&gt;完整重编译内核在我们环境中需要数十分钟&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;在我们的场景下，时间不允许完整重编译。经过分析后做出的决策是：&lt;strong&gt;使用&lt;/strong&gt; &lt;code&gt;insmod -f&lt;/code&gt;&lt;strong&gt;（&lt;/strong&gt;&lt;code&gt;--force&lt;/code&gt;&lt;strong&gt;）强制加载，跳过 CRC 校验。&lt;/strong&gt; 这不是盲目试错——我们的驱动只使用了标准 GPIO 子系统 API（&lt;code&gt;gpio_request&lt;/code&gt;、&lt;code&gt;gpio_direction_output&lt;/code&gt;、&lt;code&gt;gpio_set_value&lt;/code&gt;、&lt;code&gt;gpio_free&lt;/code&gt;），这些 API 的签名和实现在标准内核和 RT 内核中是完全一致的，不受 PREEMPT_RT 抢占模型差异的影响。强制加载不会导致内核崩溃或数据损坏。&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;insmod -f leds_2k0300_gpio.ko&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot; insmod -f leds_2k0300_gpio.ko&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;成功。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;关于&lt;/strong&gt; &lt;code&gt;insmod -f&lt;/code&gt; &lt;strong&gt;的深入思考&lt;/strong&gt;：在工程实践中，“强制”操作永远应该是&lt;strong&gt;最后的选择而非第一反应&lt;/strong&gt;。如果一个新人遇到 &lt;code&gt;insmod&lt;/code&gt; 失败就立刻 &lt;code&gt;-f&lt;/code&gt;，那就是在蒙。但如果理解了 vermagic 的作用、module_layout 的编码内容、modversions 的 CRC 机制、以及 RT 补丁对内核数据结构的影响范围，然后做出”标准 GPIO API 不受影响、强制加载是安全的”这个判断，那就是真正的工程能力。&lt;strong&gt;“知其所以然”的变通，和”不知其所以然”的蛮干，表面上看起来做了同一件事，本质完全不同。&lt;/strong&gt; 这是本次实训最重要的方法论收获。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;阶段补充：内核启动 panic —— 设备树问题的连锁反应&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在实验过程中，开发板还出现过一次严重的启动失败——内核 panic。这对理解设备树的作用是一次”反面教材”式的加深：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;[    5.734658] mmc0: new HS200 MMC card at address 0001&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;[    5.750356] mmcblk0: mmc0:0001 8GTF4R 7.28 GiB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;[    9.947322] VFS: Cannot open root device &quot;mmcblk0p1&quot; or unknown-block(0,0): error -6&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;[    9.955287] Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot; [    5.734658] mmc0: new HS200 MMC card at address 0001 [    5.750356] mmcblk0: mmc0:0001 8GTF4R 7.28 GiB ... [    9.947322] VFS: Cannot open root device &amp;#x22;mmcblk0p1&amp;#x22; or unknown-block(0,0): error -6 [    9.955287] Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;分析日志：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在 5.73 秒，MMC 控制器（&lt;code&gt;ls2k_sdio&lt;/code&gt; 驱动）探测到了 eMMC 卡，显示了正确的容量（7.28 GiB）&lt;/li&gt;
&lt;li&gt;但在 9.95 秒，内核报告找不到 &lt;code&gt;mmcblk0p1&lt;/code&gt; 块设备，error -6 = &lt;code&gt;ENXIO&lt;/code&gt;（No such device or address）&lt;/li&gt;
&lt;li&gt;关键线索在 PMON 启动阶段的输出中：&lt;code&gt;dtb chsum err!!!&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这个 &lt;code&gt;dtb chsum err!!!&lt;/code&gt; 意味着 PMON 传递给内核的设备树二进制文件校验和错误——文件可能已经损坏。设备树中 MMC 控制器的描述信息可能不完整或错误，导致内核虽然在底层硬件枚举阶段探测到了卡（这也是 &lt;code&gt;mmc0: new HS200 MMC card&lt;/code&gt; 能打出来的原因——硬件枚举是控制器驱动直接做的，不完全依赖设备树），但在上层的块设备注册阶段（block layer）失败了——因为没有正确的设备树节点，块设备层不知道要创建 &lt;code&gt;/dev/mmcblk0&lt;/code&gt; 这个设备。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;时序排查&lt;/strong&gt;：将内核命令行参数 &lt;code&gt;rootdelay&lt;/code&gt; 从 5 秒增大到 15 秒后依然 panic，排除了”设备还没就绪”的时序假说。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;两套设备名验证&lt;/strong&gt;：回到 PMON 用 &lt;code&gt;devls&lt;/code&gt; 确认 &lt;code&gt;emmc0&lt;/code&gt; 设备存在——硬件没问题，eMMC 卡是好的。问题出在 PMON → 内核的数据传递环节。这个排查链条完美体现了”理解 PMON 设备名和 Linux 设备名的对应关系”的实用价值。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;最终成果&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;# insmod -f leds_2k0300_gpio.ko&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;# dmesg | tail -5&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;[ 2988.090198] Simple LED driver loading&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;[ 2988.097566] loongson pinctrl 16000490.pinctrl: request pin 83 (GPA80-3) for GPA80:83&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;[ 2988.097591] LED on GPIO 83 turned ON&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;[ 2988.097591] Student ID: 2024304788&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot; # insmod -f leds_2k0300_gpio.ko # dmesg | tail -5 [ 2988.090198] Simple LED driver loading [ 2988.097566] loongson pinctrl 16000490.pinctrl: request pin 83 (GPA80-3) for GPA80:83 [ 2988.097591] LED on GPIO 83 turned ON [ 2988.097591] Student ID: 2024304788&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;两行关键日志：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;loongson pinctrl ... request pin 83 ... for GPA80:83&lt;/code&gt; — 引脚控制子系统成功为 GPIO 83 分配了引脚复用功能&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LED on GPIO 83 turned ON&lt;/code&gt; — LED 亮了&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Student ID: 2024304788&lt;/code&gt; — 学号输出成功&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;两个多小时的鏖战，从编译到设备树到模块加载，终于在看到 LED 亮起的那一刻得到了回报。这是整个实训最具挑战也最有成就感的时刻。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;四实训收获--技术工程方法论&quot;&gt;&lt;strong&gt;四、实训收获 —— 技术、工程、方法论&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;41-技术知识体系&quot;&gt;&lt;strong&gt;4.1 技术知识体系&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;整个实训帮助我建立起了一个清晰的嵌入式 Linux 分层认知模型：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;┌──────────────────────────────────────────┐&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  系统构建层 │ OpenWrt / Yocto / Buildroot │ ← 固件镜像的自动化生产&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;├──────────────────────────────────────────┤&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  内核驱动层 │ .ko 模块、设备树、pinctrl   │ ← 与硬件直接对话&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;├──────────────────────────────────────────┤&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  编译部署层 │ 交叉编译、Makefile          │ ← 连接开发和运行的桥梁&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;├──────────────────────────────────────────┤&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  应用编程层 │ C / Shell / Python           │ ← 用户空间的逻辑&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;├──────────────────────────────────────────┤&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  固件引导层 │ PMON                        │ ← 硬件苏醒的第一段代码&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;└──────────────────────────────────────────┘&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot; ┌──────────────────────────────────────────┐ │  系统构建层 │ OpenWrt / Yocto / Buildroot │ ← 固件镜像的自动化生产 ├──────────────────────────────────────────┤ │  内核驱动层 │ .ko 模块、设备树、pinctrl   │ ← 与硬件直接对话 ├──────────────────────────────────────────┤ │  编译部署层 │ 交叉编译、Makefile          │ ← 连接开发和运行的桥梁 ├──────────────────────────────────────────┤ │  应用编程层 │ C / Shell / Python           │ ← 用户空间的逻辑 ├──────────────────────────────────────────┤ │  固件引导层 │ PMON                        │ ← 硬件苏醒的第一段代码 └──────────────────────────────────────────┘&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;具体的技术收获包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GPIO 子系统&lt;/strong&gt;：从 sysfs 用户空间接口到内核 API（&lt;code&gt;gpio_request&lt;/code&gt;/&lt;code&gt;gpio_set_value&lt;/code&gt;/&lt;code&gt;gpio_free&lt;/code&gt;），理解了”文件即硬件”的 Unix 哲学如何在 Linux 中不同层次体现&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;交叉编译&lt;/strong&gt;：深刻理解 Build ≠ Target，掌握了 &lt;code&gt;ARCH&lt;/code&gt;、&lt;code&gt;CROSS_COMPILE&lt;/code&gt;、工具链安装、&lt;code&gt;file&lt;/code&gt; 命令验证架构的完整流程&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;内核模块开发&lt;/strong&gt;：&lt;code&gt;module_init&lt;/code&gt;/&lt;code&gt;module_exit&lt;/code&gt; 框架、Kconfig/Makefile 构建系统集成、printk 日志级别&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;设备树&lt;/strong&gt;：DTS 语法（label reference &lt;code&gt;&amp;#x26;&lt;/code&gt;、pinctrl）、DTC 编译、include 路径依赖、dtb 在启动流程中的传递机制&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;模块加载校验&lt;/strong&gt;：vermagic、module_layout、CONFIG_MODVERSIONS 三道锁的递进关系和破解方法&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;龙芯生态&lt;/strong&gt;：LoongArch64 指令集、PMON 固件、Loongnix 软件源、2K0300 BSP 版本兼容性&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;内核启动调试&lt;/strong&gt;：panic 日志分析、&lt;code&gt;ENXIO&lt;/code&gt; 错误码含义、&lt;code&gt;rootdelay&lt;/code&gt; 参数调优、设备树完整性验证&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;42-调试思维的建立--最大的成长&quot;&gt;&lt;strong&gt;4.2 调试思维的建立 —— 最大的成长&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;回顾实训全程，最有长远价值的不是某一个技术点，而是一套&lt;strong&gt;面对未知错误的系统化分析思路&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;以前遇到报错，我的反应是：看到错误信息 → 感到慌张 → 复制粘贴去搜索引擎 → 找到第一个看起来相关的结果 → 照做。这种方法的缺点是：不理解原因，下次遇到类似的变种错误还是不会。&lt;/p&gt;
&lt;p&gt;经过这五个模块 20+ 个问题的训练，现在遇到报错会按这样的流程思考：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;准确阅读错误信息&lt;/strong&gt;：逐词理解报错文本，而不是扫一眼就下结论。&lt;code&gt;Permission denied&lt;/code&gt; 不一定是权限问题，&lt;code&gt;missing separator&lt;/code&gt; 指的是不可见字符 Tab 缺失。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;区分症状和根因&lt;/strong&gt;：&lt;code&gt;module_layout&lt;/code&gt; 不匹配是症状，根因是内核配置中 &lt;code&gt;CONFIG_LOCALVERSION&lt;/code&gt; 和 &lt;code&gt;CONFIG_MODVERSIONS&lt;/code&gt; 与开发板不一致。只治症状（如随意 &lt;code&gt;insmod -f&lt;/code&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;code&gt;insmod -f&lt;/code&gt; 跳过了 CRC 校验，在确认 GPIO API 不受 RT 补丁影响的前提下是安全的工程权衡。理解了什么被跳过了、为什么跳过是安全的——这是”知其所以然”的工程判断。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;善用工具但不依赖工具&lt;/strong&gt;：&lt;code&gt;dmesg&lt;/code&gt; 查看内核日志、&lt;code&gt;modinfo&lt;/code&gt; 查看模块 vermagic、&lt;code&gt;file&lt;/code&gt; 验证 ELF 架构、PMON &lt;code&gt;devls&lt;/code&gt; 确认硬件——每个工具都有它最适合的诊断场景。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;43-国产平台的实践认知&quot;&gt;&lt;strong&gt;4.3 国产平台的实践认知&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;本次实训的特殊之处在于使用了龙芯 LoongArch 平台而非常见的 ARM（STM32/Raspberry Pi）。通过实训对国产自主平台有了直观认识：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;技术成熟度&lt;/strong&gt;：龙芯的 GNU 工具链已经很完善，GCC 8.3 对 LoongArch64 的支持稳定可靠。PMON 固件功能完整。Linux 内核主线从 5.19 开始逐步合入 LoongArch 支持。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;生态现状&lt;/strong&gt;：基础工具链成熟，但上层生态（包管理器覆盖、OpenWrt 支持、社区文档）与 ARM 相比还有差距。很多软件包需要从 FTP 或源码手动获取，不像 ARM 那样 &lt;code&gt;apt install&lt;/code&gt; 就能搞定。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;发展趋势&lt;/strong&gt;：龙芯生态正在快速完善中（BSP 从 1.0 到 2.0，内核从 5.10 到 6.12，GCC 从 8.3 到 13.3），国产自主处理器的软件生态建设是一个长期但方向明确的工程。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;44-ai-辅助学习的方法论&quot;&gt;&lt;strong&gt;4.4 AI 辅助学习的方法论&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;实训全程在 Claude Code 的辅助下完成。关于如何使用 AI 来辅助编程学习，我有几点体会：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;AI 最适合的角色是”实时导师”&lt;/strong&gt;：当终端报出一个错误时，AI 能够同时在几个维度给出反馈——错误的字面含义、可能的根因（通常给出 2-3 个假设）、每种假设的验证方法、以及背后的原理。这个”一条龙”式的诊断流程，比搜索引擎逐个页面翻阅的效率高出一个数量级。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AI 不能替代自己的判断&lt;/strong&gt;：&lt;code&gt;insmod -f&lt;/code&gt; 的方案是 AI 给出的（带着风险提示），但最终采不采用，取决于自己是否真的理解了 module_layout 和 modversions 的含义。如果无脑执行 AI 的每一条建议，学到的是零。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AI 辅助下，“不怕遇到问题”成为可能&lt;/strong&gt;：以前遇到一个完全不懂的错误（如”module_layout 不匹配”），可能要花很长时间搜索、理解、尝试。现在 AI 能在几分钟内讲清楚来龙去脉，让”踩坑 → 学习”的循环极大加速。这也是为什么我能在一次实训中经历 20+ 个问题却依然能推进——每个问题都成为了一个高效的学习机会。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;五总结&quot;&gt;&lt;strong&gt;五、总结&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;本次实训以龙芯 2K0300 久久派开发板为硬件平台，围绕嵌入式 Linux 开发的核心技术链路，从应用层到底层固件，逐层深入，完成了五个模块的学习与实践：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;模块内容概述层次核心困难关键收获&lt;/strong&gt;GPIO 用户空间 LED 控制Shell/Python + sysfs GPIO 83 闪烁应用层裁剪系统缺工具、vim 困境、Python 标准库不全建立”嵌入式是减法世界”的认知交叉编译与 C 程序部署工具链安装、Makefile、C LED、U 盘传输编译层工具链下载源不稳定、Tab 缩进、USB 兼容性、文件系统不匹配、网络不通理解 Build ≠ Target，掌握工程环境搭建能力PMON 固件基础固件命令体系、启动流程、自动启动配置固件层裸机思维切换、设备命名体系差异理解固件在启动链中的角色内核模块驱动开发.ko 模块、Kconfig/Makefile、设备树、insmod 加载内核层编译依赖缺失、设备树 include 路径/语法、module_layout 版本不匹配、CRC 校验失败、内核 panic掌握驱动开发全流程和模块加载校验机制OpenWrt 系统构建初探构建系统架构、menuconfig 配置、软件包选择系统层依赖安装、源码获取理解从源码到固件的自动化构建流程&lt;/p&gt;
&lt;p&gt;20 余个问题，从命令拼写到内核 ABI 校验，从 USB 控制器驱动到设备树传递机制——每一个问题的排查和解决，都迫使我深入理解背后的原理。&lt;/p&gt;
&lt;p&gt;实训最大的价值不在于”点亮了一个 LED”，也不在于”写出了第一个内核模块”，而在于建立起了一套&lt;strong&gt;面对未知问题时的系统化分析框架&lt;/strong&gt;——遇到报错不慌张，逐层定位根因，充分理解原理后再做工程决策。这种”调试思维”不是一个具体的技术栈，而是一种可以迁移到任何技术栈的底层能力。它会伴随我在计算机领域的每一步成长。&lt;/p&gt;
&lt;p&gt;在国产自主处理器平台上完成这次实训，还多了一层特殊意义。从龙芯的指令集到 PMON 固件，从 Loongnix 的软件源到 LoongOS 的实时内核。在这个国际技术环境日趋复杂的时代，了解并掌握自主平台的技术体系，是我们这一代计算机专业学生的责任，也是机遇。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;📅 &lt;em&gt;实训时间：2026年7月&lt;/em&gt;&lt;/p&gt; &lt;blockquote&gt;This rendering was automatically generated by Frosti Feed and may have formatting issues. For the best experience, please visit: &lt;a href=&quot;https://www.originblog.xyz/blog/post_1783926984/&quot;&gt;https://www.originblog.xyz/blog/post_1783926984/&lt;/a&gt;&lt;/blockquote&gt;</content:encoded><dc:creator>Origin Blog</dc:creator><pubDate>Mon, 13 Jul 2026 00:00:00 GMT</pubDate></item><item><title>Hello-Agent:构建一个能处理分步任务的智能旅行助手暨拓展</title><link>https://www.originblog.xyz/blog/post_1780708366/</link><guid isPermaLink="true">https://www.originblog.xyz/blog/post_1780708366/</guid><description>学习构建智能旅行助手的一些思考和修改，对于交互式人工智能的进一步摸索</description><content:encoded>&lt;p&gt;为了能从 Python 程序中访问网络 API，我们需要一个&lt;code&gt;requests&lt;/code&gt; 库。&lt;code&gt;avily-python&lt;/code&gt;是一个强大的 AI 搜索 API 客户端，用于获取实时的网络搜索结果&lt;/p&gt;
&lt;p&gt;&lt;code&gt;openai&lt;/code&gt;是 OpenAI 官方提供的 Python SDK，用于调用 GPT 等大语言模型服务，通过指令安装：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;link rel=&quot;https://www.originblog.xyz/stylesheet&quot; href=&quot;https://www.originblog.xyz/_astro/ec.vdc5k.css&quot;&gt;&lt;script type=&quot;module&quot; src=&quot;https://www.originblog.xyz/_astro/ec.0vx5m.js&quot;&gt;&lt;/script&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;pip install requests tavily-python openai&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;pip install requests tavily-python openai&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;驱动真实 LLM 的关键在于**提示工程（Prompt Engineering），**将作为&lt;code&gt;system_prompt&lt;/code&gt;传递给 LLM&lt;/p&gt;
&lt;p&gt;当前，许多 LLM 服务提供商（包括 OpenAI、Azure、以及众多开源模型服务框架如 Ollama、vLLM 等）都遵循了与 OpenAI API 相似的接口规范。这种标准化为开发者带来了极大的便利。智能体的自主决策能力来源于 LLM。例如&lt;code&gt;OpenAICompatibleClient&lt;/code&gt;，它可以连接到任何兼容 OpenAI 接口规范的 LLM 服务。&lt;/p&gt;
&lt;p&gt;这个简单的旅行助手案例，集中演示了基于&lt;code&gt;Thought-Action-Observation&lt;/code&gt;范式的智能体所具备的四项基本能力：任务分解、工具调用、上下文理解和结果合成。正是通过这个循环的不断迭代，智能体才得以将一个模糊的用户意图，转化为一系列具体、可执行的步骤，并最终达成目标。&lt;/p&gt;
&lt;p&gt;但在运行过程中发现有以下问题：&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;第 2-3 轮模型返回了空内容，导致解析失败浪费了 2 次循环。这是因为 我的DeepSeek 模型在收到 &lt;code&gt;Observation&lt;/code&gt; 后偶尔会输出空行。&lt;/p&gt;
&lt;p&gt;所以我加一个&lt;strong&gt;重试机制&lt;/strong&gt;——当输出为空时自动重新调用 LLM，让整个流程更稳定&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;if llm_output.startswith(&quot;错误:&quot;):&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;print(f&quot;🚫 LLM 调用彻底失败: {llm_output}\n&quot; + &quot;=&quot;*40)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;break&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;if llm_output.startswith(&amp;#x22;错误:&amp;#x22;):        print(f&amp;#x22;🚫 LLM 调用彻底失败: {llm_output}\n&amp;#x22; + &amp;#x22;=&amp;#x22;*40)        break&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;核心问题：LLM（包括 DeepSeek）本质上是&lt;strong&gt;概率采样系统&lt;/strong&gt;——每次根据概率分布随机选择下一个 token。这意味着&lt;strong&gt;同样的输入可能产生不同输出&lt;/strong&gt;。偶尔采样路径会走进”死胡同”，模型输出空字符串——API 返回 200 但 &lt;code&gt;content&lt;/code&gt; 为 &lt;code&gt;None&lt;/code&gt; 或 &lt;code&gt;&quot;&quot;&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;正常采样路径:  &quot;Thought&quot; → &quot;:&quot; → &quot; 查询&quot; → &quot;天气&quot; → ... ✅&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;异常采样路径:  &amp;#x3C;EOS&gt;  (模型直接选择结束)              ❌ → 空输出&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;正常采样路径:  &amp;#x22;Thought&amp;#x22; → &amp;#x22;:&amp;#x22; → &amp;#x22; 查询&amp;#x22; → &amp;#x22;天气&amp;#x22; → ... ✅异常采样路径:  &lt;EOS&gt;  (模型直接选择结束)              ❌ → 空输出&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;https://bu.dusays.com/2026/06/06/6a236d543a671.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;拓展&quot;&gt;拓展：&lt;/h1&gt;
&lt;p&gt;当前的架构：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;┌──────────────┐     ┌──────────────────┐&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  prompt.py   │ ──▶ │ OpenAICompat...  │ ──▶  DeepSeek API&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  (主循环)     │ ◀── │ (generate方法)    │ ◀──&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;└──────┬───────┘     └──────────────────┘&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;▼&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;┌──────────────┐&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  工具调度器    │ ──▶ │ get_weather()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│ (正则解析)     │     │ get_attraction()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;└──────────────┘&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;┌──────────────┐     ┌──────────────────┐│  prompt.py   │ ──▶ │ OpenAICompat...  │ ──▶  DeepSeek API│  (主循环)     │ ◀── │ (generate方法)    │ ◀──└──────┬───────┘     └──────────────────┘       │       ▼┌──────────────┐│  工具调度器    │ ──▶ │ get_weather()│ (正则解析)     │     │ get_attraction()└──────────────┘&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3 id=&quot;1-字符串解析工具调用--脆弱且不精确&quot;&gt;1. 字符串解析工具调用 — 脆弱且不精确&lt;/h3&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;模型输出字符串&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;▼&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;re.search(r&quot;Action: (.*)&quot;, llm_output)    ← 模型多写一个空格&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;▼&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;re.search(r&quot;(\w+)\(&quot;, action_str)          ← 模型写 get_weather (city=&quot;&quot;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;     &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;▼&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;re.findall(r&apos;(\w+)=&quot;([^&quot;]*)&quot;&apos;, args_str)  ← 参数是整数？嵌套引号？&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;模型输出字符串     │     ▼re.search(r&amp;#x22;Action: (.*)&amp;#x22;, llm_output)    ← 模型多写一个空格     │     ▼re.search(r&amp;#x22;(\w+)\(&amp;#x22;, action_str)          ← 模型写 get_weather (city=&amp;#x22;&amp;#x22;)     │     ▼re.findall(r&amp;#x27;(\w+)=&amp;#x22;([^&amp;#x22;]*)&amp;#x22;&amp;#x27;, args_str)  ← 参数是整数？嵌套引号？&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;问题&lt;/strong&gt;：正则匹配容易出错，更适合用于判断。模型哪怕多一个换行、少一个引号都不行&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解：因为&lt;/strong&gt;DeepSeek 支持 &lt;strong&gt;原生 Function Calling&lt;/strong&gt;，模型直接返回结构化的 JSON，由 SDK 自动解析。&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;h3 id=&quot;2-没有区分系统提示和工具定义&quot;&gt;2. 没有区分”系统提示”和”工具定义”&lt;/h3&gt;
&lt;p&gt;当前做法是把工具描述写死在 &lt;code&gt;AGENT_SYSTEM_PROMPT&lt;/code&gt; 字符串里：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;# 硬编码在字符串里&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;- `get_weather(city: str)`: 查询指定城市的实时天气。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;- `get_attraction(city: str, weather: str)`: 根据城市和天气搜索...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;# 硬编码在字符串里- &amp;#x60;get_weather(city: str)&amp;#x60;: 查询指定城市的实时天气。- &amp;#x60;get_attraction(city: str, weather: str)&amp;#x60;: 根据城市和天气搜索...&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3 id=&quot;3-主循环没有终止条件&quot;&gt;3. 主循环没有终止条件&lt;/h3&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;for i in range(5):  # 写死 5 轮&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;for i in range(5):  # 写死 5 轮&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;如果任务不需要五轮或者需要五轮以上呢？我认为没有动态判断！&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;&quot;&gt;&lt;/h3&gt;
&lt;p&gt;改动后架构层：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;┌─────────────────────────────────────────────────┐&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  第 6 层: 交互入口                                │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  while True → input() → run_agent()             │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;├─────────────────────────────────────────────────┤&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  第 5 层: Agent 主循环                            │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  while turn ≤ 10: chat() → 检查 tool_calls       │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  → 有 → 执行工具 → 追加 tool 消息 → 继续            │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  → 无 → 输出 content → 结束                       │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;├─────────────────────────────────────────────────┤&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  第 4 层: 配置 (API Key / URL / Model)           │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;├─────────────────────────────────────────────────┤&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  第 3 层: DeepSeekClient.chat()                  │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  发送 messages + tools → 返回 Message 对象        │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  内置重试: 网络异常 / 空响应                        │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;├─────────────────────────────────────────────────┤&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  第 2 层: 工具实现 (get_weather / get_attraction) │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  纯函数，与框架完全解耦                             │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;├─────────────────────────────────────────────────┤&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  第 1 层: 工具 Schema (JSON 定义)                 │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  同时用于: API tools 参数 + 自动生成提示词          │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;└─────────────────────────────────────────────────┘&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;┌─────────────────────────────────────────────────┐│  第 6 层: 交互入口                                ││  while True → input() → run_agent()             │├─────────────────────────────────────────────────┤│  第 5 层: Agent 主循环                            ││  while turn ≤ 10: chat() → 检查 tool_calls       ││  → 有 → 执行工具 → 追加 tool 消息 → 继续            ││  → 无 → 输出 content → 结束                       │├─────────────────────────────────────────────────┤│  第 4 层: 配置 (API Key / URL / Model)           │├─────────────────────────────────────────────────┤│  第 3 层: DeepSeekClient.chat()                  ││  发送 messages + tools → 返回 Message 对象        ││  内置重试: 网络异常 / 空响应                        │├─────────────────────────────────────────────────┤│  第 2 层: 工具实现 (get_weather / get_attraction) ││  纯函数，与框架完全解耦                             │├─────────────────────────────────────────────────┤│  第 1 层: 工具 Schema (JSON 定义)                 ││  同时用于: API tools 参数 + 自动生成提示词          │└─────────────────────────────────────────────────┘&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;数据流Before：&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;LLM 输出字符串&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  &quot;Thought: xxx\nAction: get_weather(city=\&quot;\&quot;)&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;▼&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;re.search(r&quot;Action: (.*)&quot;)     ← 匹配失败，一轮浪费&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  &quot;get_weather(city=\&quot;北京\&quot;)&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;▼&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;re.search(r&quot;(\w+)\(&quot;)          ← 工具名提取&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;re.findall(r&apos;(\w+)=&quot;([^&quot;]*)&quot;&apos;) ← 参数提取&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;▼&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;available_tools[&quot;get_weather&quot;](**kwargs)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;LLM 输出字符串    │  &amp;#x22;Thought: xxx\nAction: get_weather(city=\&amp;#x22;\&amp;#x22;)&amp;#x22;    ▼re.search(r&amp;#x22;Action: (.*)&amp;#x22;)     ← 匹配失败，一轮浪费    │  &amp;#x22;get_weather(city=\&amp;#x22;北京\&amp;#x22;)&amp;#x22;    ▼re.search(r&amp;#x22;(\w+)\(&amp;#x22;)          ← 工具名提取re.findall(r&amp;#x27;(\w+)=&amp;#x22;([^&amp;#x22;]*)&amp;#x22;&amp;#x27;) ← 参数提取    │    ▼available_tools[&amp;#x22;get_weather&amp;#x22;](**kwargs)&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;数据流After：&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;LLM 返回 Message 对象&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  .tool_calls = [{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│    function: { name: &quot;get_weather&quot;, arguments: &apos;{&quot;city&quot;:&quot;&quot;}&apos; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  }]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;▼&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;json.loads(tool_call.function.arguments)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;│  {&quot;city&quot;: &quot;&quot;}    ← 已经是 Python dict，零解析&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;▼&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;TOOL_MAP[&quot;get_weather&quot;](**{&quot;city&quot;: &quot;北京&quot;})   ← 直接执行&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;LLM 返回 Message 对象    │  .tool_calls = [{    │    function: { name: &amp;#x22;get_weather&amp;#x22;, arguments: &amp;#x27;{&amp;#x22;city&amp;#x22;:&amp;#x22;&amp;#x22;}&amp;#x27; }    │  }]    ▼json.loads(tool_call.function.arguments)    │  {&amp;#x22;city&amp;#x22;: &amp;#x22;&amp;#x22;}    ← 已经是 Python dict，零解析    ▼TOOL_MAP[&amp;#x22;get_weather&amp;#x22;](**{&amp;#x22;city&amp;#x22;: &amp;#x22;北京&amp;#x22;})   ← 直接执行&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;添加新工具&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;# 1. 在 TOOLS 列表中加 Schema&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;&quot;type&quot;: &quot;function&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;&quot;function&quot;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;&quot;name&quot;: &quot;get_restaurant&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;&quot;description&quot;: &quot;搜索指定城市的美食餐厅&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;&quot;parameters&quot;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;&quot;type&quot;: &quot;object&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;&quot;properties&quot;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;&quot;city&quot;: {&quot;type&quot;: &quot;string&quot;, &quot;description&quot;: &quot;城市名称&quot;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;&quot;cuisine&quot;: {&quot;type&quot;: &quot;string&quot;, &quot;description&quot;: &quot;菜系，如&apos;川菜&apos;、&apos;日料&apos;&quot;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;&quot;required&quot;: [&quot;city&quot;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;# 2. 实现函数 + 注册到 TOOL_MAP&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;def get_restaurant(city: str, cuisine: str | None = None) -&gt; str:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;TOOL_MAP[&quot;get_restaurant&quot;] = get_restaurant&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;# 这样提示词自动更新，主循环自动调度。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;添加新工具# 1. 在 TOOLS 列表中加 Schema{    &amp;#x22;type&amp;#x22;: &amp;#x22;function&amp;#x22;,    &amp;#x22;function&amp;#x22;: {        &amp;#x22;name&amp;#x22;: &amp;#x22;get_restaurant&amp;#x22;,        &amp;#x22;description&amp;#x22;: &amp;#x22;搜索指定城市的美食餐厅&amp;#x22;,        &amp;#x22;parameters&amp;#x22;: {            &amp;#x22;type&amp;#x22;: &amp;#x22;object&amp;#x22;,            &amp;#x22;properties&amp;#x22;: {                &amp;#x22;city&amp;#x22;: {&amp;#x22;type&amp;#x22;: &amp;#x22;string&amp;#x22;, &amp;#x22;description&amp;#x22;: &amp;#x22;城市名称&amp;#x22;},                &amp;#x22;cuisine&amp;#x22;: {&amp;#x22;type&amp;#x22;: &amp;#x22;string&amp;#x22;, &amp;#x22;description&amp;#x22;: &amp;#x22;菜系，如&amp;#x27;川菜&amp;#x27;、&amp;#x27;日料&amp;#x27;&amp;#x22;}            },            &amp;#x22;required&amp;#x22;: [&amp;#x22;city&amp;#x22;]        }    }}# 2. 实现函数 + 注册到 TOOL_MAPdef get_restaurant(city: str, cuisine: str | None = None) -&gt; str:    ...TOOL_MAP[&amp;#x22;get_restaurant&amp;#x22;] = get_restaurant# 这样提示词自动更新，主循环自动调度。&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;关键点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;content: null&lt;/code&gt; — Function Calling 模式下，模型决定调用工具时&lt;strong&gt;不输出文本&lt;/strong&gt;，&lt;code&gt;content&lt;/code&gt; 就是 &lt;code&gt;null&lt;/code&gt;。这也是为什么重试检测要区分”有 tool_calls（正常）“和”没 tool_calls 且没 content（异常）”&lt;/li&gt;
&lt;li&gt;&lt;code&gt;arguments&lt;/code&gt; 是一个 &lt;strong&gt;JSON 字符串&lt;/strong&gt;，不是对象。所以代码里必须 &lt;code&gt;json.loads()&lt;/code&gt; 才能拿到 &lt;code&gt;{&quot;city&quot;: &quot;城市&quot;}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;id: &quot;call_abc123&quot;&lt;/code&gt; — 这个就是 &lt;code&gt;tool_call_id&lt;/code&gt;，后面回传时必须带上。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;h3 id=&quot;_build_tool_description-做了什么&quot;&gt;&lt;code&gt;_build_tool_description()&lt;/code&gt; 做了什么&lt;/h3&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;# 遍历同一个 TOOLS 列表，自动拼出:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;# - `get_weather(city: str)`: 查询指定城市的实时天气。返回天气描述和气温。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;# - `get_attraction(city: str, weather: str)`: 根据城市和天气搜索推荐的旅游景点...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;# 遍历同一个 TOOLS 列表，自动拼出:# - &amp;#x60;get_weather(city: str)&amp;#x60;: 查询指定城市的实时天气。返回天气描述和气温。# - &amp;#x60;get_attraction(city: str, weather: str)&amp;#x60;: 根据城市和天气搜索推荐的旅游景点...&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;为什么存在&lt;/strong&gt;：Function Calling 的 &lt;code&gt;tools&lt;/code&gt; 参数是发给 API 的（模型据此决策），系统提示词是给我们调试看的。而两份信息来自同一份 Schema，改了 TOOLS 就不用担心提示词忘记更新QWQ。&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;continue ：执行完工具后重新进入循环，把带有工具结果的新 messages 再次发给 LLM。这是 ReAct 模式的核心——模型看到工具返回后，可能再调下一个工具，也可能直接给最终答案。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;continue ：执行完工具后重新进入循环，把带有工具结果的新 messages 再次发给 LLM。这是 ReAct 模式的核心——模型看到工具返回后，可能再调下一个工具，也可能直接给最终答案。&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;‍&lt;/p&gt; &lt;blockquote&gt;This rendering was automatically generated by Frosti Feed and may have formatting issues. For the best experience, please visit: &lt;a href=&quot;https://www.originblog.xyz/blog/post_1780708366/&quot;&gt;https://www.originblog.xyz/blog/post_1780708366/&lt;/a&gt;&lt;/blockquote&gt;</content:encoded><dc:creator>Origin Blog</dc:creator><pubDate>Sat, 06 Jun 2026 00:00:00 GMT</pubDate></item><item><title>一个拍纪录片的人，正在被时代淘汰</title><link>https://www.originblog.xyz/blog/chatter_1780412512/</link><guid isPermaLink="true">https://www.originblog.xyz/blog/chatter_1780412512/</guid><description>关于纪录片、时代速度与认真生活的思考</description><content:encoded>&lt;p&gt;这两天看到了一个视频，讲的是一位田野就是乡工作者，也就是我们所说的纪录片导演，对当今世界的思考，受益良多：&lt;/p&gt;
&lt;p&gt;在如今信息洪流的快节奏时代，一切都如同流星一样划过。短视频时代使得人们的耐心变得越来越少，但是这些纪录片记录者，放慢姿态，用时间来弥补，用热爱来守护。越是瞬息万变的节奏，越要定下心去守护内心的宁静，正如作者说的：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这几年越来越意识到：&lt;br&gt;
很多人并不是不痛苦，&lt;br&gt;
只是已经学会了快速地活着。&lt;/p&gt;
&lt;p&gt;但纪录片这门手艺，&lt;br&gt;
偏偏反着来。&lt;/p&gt;
&lt;p&gt;谨以此片献给时代洪流下，&lt;br&gt;
仍认真用力活着的平凡人。&lt;/p&gt;
&lt;p&gt;我们不是时代的注脚，&lt;br&gt;
我们就是时代本身。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1NSRXBwEZp/?spm_id_from=333.1391.0.0&amp;#x26;vd_source=20fae464d78a66a35f63c35e32543721&quot; rel=&quot;https://www.originblog.xyz/nofollow&quot;&gt;一个拍纪录片的人，正在被时代淘汰_哔哩哔哩_bilibili&lt;span&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;‍&lt;img src=&quot;https://bu.dusays.com/2026/06/02/6a1ef03057ebe.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt; &lt;blockquote&gt;This rendering was automatically generated by Frosti Feed and may have formatting issues. For the best experience, please visit: &lt;a href=&quot;https://www.originblog.xyz/blog/chatter_1780412512/&quot;&gt;https://www.originblog.xyz/blog/chatter_1780412512/&lt;/a&gt;&lt;/blockquote&gt;</content:encoded><dc:creator>Origin Blog</dc:creator><pubDate>Tue, 02 Jun 2026 00:00:00 GMT</pubDate></item><item><title>🐱煤球接入Deepseek</title><link>https://www.originblog.xyz/blog/chatter_1780305190/</link><guid isPermaLink="true">https://www.originblog.xyz/blog/chatter_1780305190/</guid><description>赐予网页的小猫数字生命！（）</description><content:encoded>&lt;p&gt;在此之前，煤球的模型是Gemini：&lt;/p&gt;
&lt;h3 id=&quot;1-api-路由核心请求入口&quot;&gt;1. API 路由（核心请求入口）&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;my-blog-manager/app/api/chat/route.ts&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这是小猫 AI 的&lt;strong&gt;后端路由&lt;/strong&gt;，直接对接 &lt;strong&gt;Google Gemini API&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;第 13 行：从环境变量 &lt;code&gt;GEMINI_API_KEY&lt;/code&gt; 或 &lt;code&gt;OPENAI_API_KEY&lt;/code&gt; 读取密钥&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;第 21-22 行：从 &lt;code&gt;siteConfig.geminiConfig&lt;/code&gt; 读取模型 ID，构建请求 URL：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;link rel=&quot;https://www.originblog.xyz/stylesheet&quot; href=&quot;https://www.originblog.xyz/_astro/ec.vdc5k.css&quot;&gt;&lt;script type=&quot;module&quot; src=&quot;https://www.originblog.xyz/_astro/ec.0vx5m.js&quot;&gt;&lt;/script&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;https://generativelanguage.googleapis.com/v1beta/models/{modelId}:generateContent?key={apiKey}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;https://generativelanguage.googleapis.com/v1beta/models/{modelId}:generateContent?key={apiKey}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;第 29-42 行：发送请求体，包含 &lt;code&gt;system_instruction&lt;/code&gt;（猫设 prompt）、用户消息、&lt;code&gt;generationConfig&lt;/code&gt;（token 上限、temperature）&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;2-api-配置模型参数--猫设-prompt&quot;&gt;2. API 配置（模型参数 + 猫设 Prompt）&lt;/h3&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;geminiConfig: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;modelId: &quot;&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;systemPrompt: &quot;&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;maxOutputTokens: ,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;temperature: ,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;geminiConfig: {  modelId: &amp;#x22;&amp;#x22;,  systemPrompt: &amp;#x22;&amp;#x22;,  maxOutputTokens: ,  temperature: ,},&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h3 id=&quot;3-前端调用方&quot;&gt;3. 前端调用方&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;my-blog-manager/components/CyberCat.tsx&lt;/strong&gt; — 页面右下角的赛博小猫组件，&lt;code&gt;handleFeed()&lt;/code&gt;和 &lt;code&gt;handleChatSubmit()&lt;/code&gt;都向 &lt;code&gt;/api/chat&lt;/code&gt; 发 POST 请求&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;my-blog-manager/components/settings/AICatSection.tsx&lt;/strong&gt; — 后台设置页面，可修改 modelId、systemPrompt、temperature、maxOutputTokens，点”暂存至队列”写入 &lt;code&gt;geminiConfig&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;而接入Deepseek有两个需要注意的点：&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;DeepSeek 用的是 OpenAI 兼容接口，跟 Gemini 的请求体结构完全不同：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;端点，鉴别权限都不相同&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;此外：CMS 管理后台（Settings 页面 → AI 小猫配置）点「暂存至队列」并应用，&lt;strong&gt;会丢失&lt;/strong&gt; &lt;code&gt;apiKey&lt;/code&gt; &lt;strong&gt;和&lt;/strong&gt; &lt;code&gt;baseUrl&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;原因是 cms_core/api/config.py:178-185 的写入逻辑——前端只传 4 个字段：&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;{ modelId, systemPrompt, maxOutputTokens, temperature }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot;{ modelId, systemPrompt, maxOutputTokens, temperature }&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;后端用正则直接把整个 &lt;code&gt;geminiConfig&lt;/code&gt; 块替换掉，新增的 &lt;code&gt;apiKey&lt;/code&gt; 和 &lt;code&gt;baseUrl&lt;/code&gt; 不在前端传的字段里&lt;/p&gt;
&lt;p&gt;‍&lt;/p&gt;
&lt;p&gt;解决问题：在 config.py 的 update_site_config 里加一个合并逻辑 —— 更新 dict 之前先保留已有字段，缺少的 key 不被丢弃。不影响现有任何功能。&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;existing = {}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;m = re.search(rf&quot;{key}\s*:\s*\{{([\s\S]*?)\}}&quot;, content)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;if m:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;existing = parse_ts_dict(m.group(1))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;gutter&quot;&gt;&lt;div class=&quot;ln&quot; aria-hidden=&quot;true&quot;&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e1e4e8&quot;&gt;merged = {**existing, **value}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;button title=&quot;Copy to clipboard&quot; data-copied=&quot;Copied!&quot; data-code=&quot; existing = {}                m = re.search(rf&amp;#x22;{key}\s*:\s*\{{([\s\S]*?)\}}&amp;#x22;, content)                if m:                    existing = parse_ts_dict(m.group(1))                merged = {**existing, **value}&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;/button&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;不过！！！把 key 明文写在代码里相当于把房门钥匙贴在门上。🐖&lt;/p&gt;
&lt;p&gt;所以我在&lt;strong&gt;在 Vercel Dashboard → Settings → Environment Variables 加入环境变量，这样就不用把API放在仓库里面&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://bu.dusays.com/2026/06/01/6a1d4d148444c.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/p&gt;
&lt;p&gt;搞定！&lt;/p&gt; &lt;blockquote&gt;This rendering was automatically generated by Frosti Feed and may have formatting issues. For the best experience, please visit: &lt;a href=&quot;https://www.originblog.xyz/blog/chatter_1780305190/&quot;&gt;https://www.originblog.xyz/blog/chatter_1780305190/&lt;/a&gt;&lt;/blockquote&gt;</content:encoded><dc:creator>Origin Blog</dc:creator><pubDate>Mon, 01 Jun 2026 00:00:00 GMT</pubDate></item></channel></rss>