[{"data":1,"prerenderedAt":698},["ShallowReactive",2],{"content:\u002F2026\u002Frust-ai-agent-from-loop-to-engineering":3,"surround:\u002F2026\u002Frust-ai-agent-from-loop-to-engineering":692},{"id":4,"title":5,"body":6,"categories":648,"date":650,"description":651,"draft":652,"extension":653,"image":654,"meta":655,"navigation":657,"path":658,"permalink":654,"published":654,"readingTime":659,"recommend":654,"references":664,"seo":679,"sitemap":680,"stem":681,"tags":682,"type":690,"updated":650,"__hash__":691},"content\u002Fposts\u002F2026\u002Frust-ai-agent-from-loop-to-engineering.md","用 Rust 开发 AI Agent：先别急着堆工具，把 Loop 跑起来",{"type":7,"value":8,"toc":629},"minimark",[9,13,16,27,35,47,58,63,66,69,76,79,104,107,111,114,126,129,156,159,166,170,173,184,187,269,272,278,282,285,288,304,315,319,325,328,331,334,406,413,417,420,423,428,438,448,451,455,462,473,477,480,484,491,497,501,523,526,534,537,544,547,567,570,574,613,617,620,623,626],[10,11,12],"p",{},"提到 AI Agent，很多人的第一反应是：选一个模型，塞十几个工具，再写一段「你是一个全能助手」的提示词。然后按下运行键，期待一个数字员工从终端里站起来。",[10,14,15],{},"通常站起来的是报错。",[10,17,18,19,26],{},"B 站 UP 主「软件工艺师」的",[20,21,25],"a",{"href":22,"rel":23},"https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1qHEQ6RERo",[24],"nofollow","《使用 Rust 开发 AI Agent - 简介》","没有急着写代码。它先花近 24 分钟回答更基础的问题：什么才算 Agent？它与普通 LLM 调用、固定工作流有何区别？为什么上下文不是越多越好？又该怎样判断系统真的在进步？",[10,28,29,30,34],{},"这其实是很合适的开场。因为 Agent 最难的从来不是发出一次 HTTP 请求，而是让模型在一个",[31,32,33],"strong",{},"受约束、可观察、能停止的循环","里持续做决定。",[36,37,40],"alert",{"title":38,"type":39},"先给结论","info",[10,41,42,43,46],{},"一个可用的 AI Agent，不等于「LLM + 很多 API」。更准确的表达是：",[31,44,45],{},"模型在目标和边界内，根据当前状态选择下一步行动；程序执行行动、返回观察，再由模型继续判断，直到完成或触发停止条件。"," Rust 的价值，正是在这个循环开始变长、变并发、变得不能随便出错以后出现。",[48,49,50],"blockquote",{},[10,51,52,53,57],{},"说明：本文主要总结这期「简介」视频，并结合 UP 主公开的 ",[54,55,56],"code",{"code":56},"rust-agent"," 配套仓库做工程化延伸。视频本身以概念为主，不是一节完整的 Rust 编码课。",[59,60,62],"h2",{"id":61},"从聊天模型到-agent差的不是一个名字","从聊天模型到 Agent，差的不是一个名字",[10,64,65],{},"视频先从 LLM 的泛化能力讲起。",[10,67,68],{},"传统程序需要开发者把规则写出来：输入是什么、走哪个分支、返回什么结果。LLM 则能从自然语言指令和上下文中识别任务。视频用 Zero-shot 与 Few-shot 举例：你可以直接要求模型翻译一个词，也可以先放几个输入输出样例，让它从样例中学会任务格式。",[10,70,71,72,75],{},"这意味着我们不必把每一种情况都改写成 ",[54,73,74],{"code":74},"if\u002Felse","。但「能理解任务」还不是 Agent。一次模型调用更像请顾问回答一个问题；Agent 则是把目标交给一个执行者，让它在运行过程中不断观察、判断和行动。",[10,77,78],{},"视频展示的生态也可以看成一条向上的应用栈：",[80,81,82],"card-list",{},[83,84,85,92,98],"ul",{},[86,87,88,91],"li",{},[31,89,90],{},"模型与通用助手","：ChatGPT、Claude、Gemini 等提供理解、生成与推理能力。",[86,93,94,97],{},[31,95,96],{},"编排与能力层","：负责提示词、工具、状态、检索、路由和执行过程。",[86,99,100,103],{},[31,101,102],{},"垂直 Agent 应用","：Claude Code、Cursor 等把循环落到编程、研究或其他具体任务中。",[10,105,106],{},"真正值得关心的，不是产品都叫了什么，而是谁在决定「下一步」。",[59,108,110],{"id":109},"agent-的心脏是一只-loop","Agent 的心脏是一只 Loop",[10,112,113],{},"视频中最关键的一页是 Agent 循环。把装饰拿掉，它大致是这样：",[115,116,123],"pre",{"className":117,"code":119,"filename":120,"language":121,"meta":122},[118],"language-text","用户目标\n   ↓\nLLM 读取指令、历史与当前状态\n   ↓\n决定：直接回答，还是调用工具？\n   ├─ 直接回答 ───────────────→ 完成\n   └─ 调用工具 → 程序执行 → 返回观察\n                         ↑          ↓\n                         └─ 更新上下文\n","agent-loop.txt","text","icon=tabler:repeat",[54,124,119],{"__ignoreMap":125},"",[10,127,128],{},"一次循环通常包含四种东西：",[130,131,132,138,144,150],"ol",{},[86,133,134,137],{},[31,135,136],{},"目标与约束","：用户到底要什么，哪些事情不能做。",[86,139,140,143],{},[31,141,142],{},"模型决策","：基于当前上下文选择回答、调用工具或继续规划。",[86,145,146,149],{},[31,147,148],{},"外部行动","：搜索、读取文件、执行程序、访问数据库或调用业务 API。",[86,151,152,155],{},[31,153,154],{},"状态与观察","：把工具结果、错误和中间进度带回下一轮。",[10,157,158],{},"少了模型，它只是普通工作流；少了工具，它只是多轮聊天；少了状态，它会不断失忆；少了停止条件，它可能像扫地机器人卡在桌腿边，一圈一圈认真撞墙。",[36,160,163],{"title":161,"type":162},"循环必须有护栏","warning",[10,164,165],{},"至少给 Agent 设置最大步数、超时、工具白名单、参数校验、重试上限和人工确认点。模型负责提出行动，程序负责判断这个行动能不能真的发生。",[59,167,169],{"id":168},"工作流和-agent-不是一刀切","工作流和 Agent 不是一刀切",[10,171,172],{},"视频用一张从「代码」到「多步骤自主执行」的连续谱解释工作流与 Agent。判断系统有多“Agentic”，可以观察三种控制权：",[83,174,175,178,181],{},[86,176,177],{},"当前步骤的输出由谁决定？",[86,179,180],{},"下一步由谁选择？",[86,182,183],{},"可用步骤或能力由谁限定？",[10,185,186],{},"越靠近固定代码，开发者掌握的控制权越多；经过单次 LLM 调用、链式调用、路由、工具调用和多步执行后，模型获得的运行时决策权逐渐增大。",[188,189,190,209],"table",{},[191,192,193],"thead",{},[194,195,196,200,203,206],"tr",{},[197,198,199],"th",{},"形态",[197,201,202],{},"下一步怎么来",[197,204,205],{},"优点",[197,207,208],{},"主要风险",[210,211,212,227,241,255],"tbody",{},[194,213,214,218,221,224],{},[215,216,217],"td",{},"固定代码",[215,219,220],{},"开发者写死",[215,222,223],{},"稳定、便宜、可预测",[215,225,226],{},"难处理开放问题",[194,228,229,232,235,238],{},[215,230,231],{},"LLM 工作流",[215,233,234],{},"开发者编排，模型完成局部任务",[215,236,237],{},"易测试、边界清楚",[215,239,240],{},"分支扩张后维护复杂",[194,242,243,246,249,252],{},[215,244,245],{},"路由 \u002F 工具调用",[215,247,248],{},"模型在候选项中选择",[215,250,251],{},"更灵活",[215,253,254],{},"选错工具或参数",[194,256,257,260,263,266],{},[215,258,259],{},"多步 Agent",[215,261,262],{},"模型根据观察持续决定",[215,264,265],{},"能处理开放、复杂任务",[215,267,268],{},"成本、延迟和失控风险更高",[10,270,271],{},"所以，不要为了“看起来先进”把确定流程改成 Agent。退款规则、字段校验、审批权限这类可以写清楚的逻辑，代码通常比模型靠谱。Agent 更适合那些步骤难以提前穷举、需要根据新信息调整路径、但结果又能被工具或人验证的任务。",[10,273,274,275],{},"我的判断标准很简单：",[31,276,277],{},"如果流程图能提前画完整，就先做工作流；如果只能定义目标、工具和边界，而路径必须运行时发现，再考虑 Agent。",[59,279,281],{"id":280},"别只看演示用-gaia-检查它会不会干活","别只看演示：用 GAIA 检查它会不会干活",[10,283,284],{},"视频随后介绍了 GAIA。这个基准由 Meta AI、Hugging Face 等研究者提出，用一组对人类相对自然、却要求 AI 同时具备推理、工具使用和信息处理能力的问题，测试通用 AI 助手。",[10,286,287],{},"它给 Agent 开发带来的提醒比榜单本身更重要：",[80,289,290],{},[83,291,292,295,298,301],{},[86,293,294],{},"演示成功不等于系统可靠。挑一道恰好会做的题，很容易拍出漂亮视频。",[86,296,297],{},"最终答案要可判定。否则模型写得越长，越难知道它究竟有没有完成任务。",[86,299,300],{},"失败也要结构化记录：是知识不足、工具缺失、调用失败，还是推理走偏？",[86,302,303],{},"模型、提示词或工具一变，就应该重新跑同一组任务，而不是凭感觉宣布“更聪明了”。",[10,305,306,307,310,311,314],{},"配套仓库已经体现了这条思路：它会读取 GAIA Level 1 数据，把模型输出约束成强类型结构，记录 ",[54,308,309],{"code":309},"is_solvable","、",[54,312,313],{"code":313},"final_answer"," 与错误，并按模型统计正确率。Agent 不是写完就结束，它需要一个持续回归的考场。",[59,316,318],{"id":317},"上下文工程不是把整个仓库塞进-prompt","上下文工程：不是把整个仓库塞进 Prompt",[10,320,321,322],{},"视频后半段转向上下文工程。它给出的核心提醒是：",[31,323,324],{},"上下文窗口更长，不代表上下文应该更满。",[10,326,327],{},"当历史对话、网页全文、工具输出、代码、日志和系统说明一起涌进模型，重要信息会被噪声包围。模型也许“看见了”，却不一定能在正确位置把它用出来。视频用 Context Rot 的曲线说明：随着上下文增长，多种模型的任务表现都可能下降，只是下降速度不同。",[10,329,330],{},"这像给一名工程师发来 200 个附件，然后补一句：“答案就在里面。”附件确实都送到了，生产力不一定也到了。",[10,332,333],{},"视频最后归纳了五种上下文工程策略：",[188,335,336,349],{},[191,337,338],{},[194,339,340,343,346],{},[197,341,342],{},"策略",[197,344,345],{},"要解决的问题",[197,347,348],{},"在 Rust Agent 中可以怎么做",[210,350,351,362,373,384,395],{},[194,352,353,356,359],{},[215,354,355],{},"写入",[215,357,358],{},"重要信息不能只留在短期对话里",[215,360,361],{},"把计划、观察和结论写入结构化状态或持久存储",[194,363,364,367,370],{},[215,365,366],{},"选择",[215,368,369],{},"每轮不需要读取全部资料",[215,371,372],{},"按任务检索相关记录，只注入 Top-K 片段",[194,374,375,378,381],{},[215,376,377],{},"压缩",[215,379,380],{},"历史越来越长",[215,382,383],{},"对旧轮次做摘要，保留结论、证据和未完成事项",[194,385,386,389,392],{},[215,387,388],{},"隔离",[215,390,391],{},"不同子任务互相污染",[215,393,394],{},"为子 Agent、工具调用或阶段使用独立上下文",[194,396,397,400,403],{},[215,398,399],{},"缓存",[215,401,402],{},"稳定前缀被反复计算",[215,404,405],{},"复用 Prompt \u002F KV Cache，减少延迟与费用",[36,407,410],{"title":408,"type":409},"上下文工程到底在工程什么？","question",[10,411,412],{},"不是“写一段更玄学的 Prompt”，而是在正确时间，以正确格式，把完成当前决策所需的最少充分信息交给模型。",[59,414,416],{"id":415},"为什么偏偏用-rust","为什么偏偏用 Rust",[10,418,419],{},"Python 和 TypeScript 的 Agent 生态更成熟，教程、框架和集成都更多。如果目标是两小时做出原型，我大概率仍会先选它们。Rust 并不会让模型推理得更聪明，也不会自动治好幻觉。",[10,421,422],{},"但当 Agent 从 Demo 进入长期运行的程序，Rust 的优势开始变得具体：",[424,425,427],"h3",{"id":426},"_1-把模型输出变成可检查的数据","1. 把模型输出变成可检查的数据",[10,429,430,431,310,434,437],{},"模型返回的 JSON 本质上仍是不可信输入。Rust 可以用 ",[54,432,433],{"code":433},"serde",[54,435,436],{"code":436},"schemars"," 和枚举，把“回答”“工具调用”“失败”收敛成明确类型。字段缺失、类型错误或未知动作，应当在边界处失败，而不是一路以松散字典传到执行器。",[115,439,446],{"className":440,"code":442,"filename":443,"language":444,"meta":445},[441],"language-rust","#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]\n#[serde(tag = \"kind\", rename_all = \"snake_case\")]\nenum Decision {\n    Final {\n        answer: String,\n    },\n    ToolCall {\n        name: String,\n        arguments: serde_json::Value,\n    },\n}\n","decision.rs","rust","icon=tabler:brand-rust wrap",[54,447,442],{"__ignoreMap":125},[10,449,450],{},"类型安全不能保证模型做对决定，但可以保证程序知道它究竟提交了什么决定。",[424,452,454],{"id":453},"_2-异步与并发更适合被认真管理","2. 异步与并发更适合被认真管理",[10,456,457,458,461],{},"Agent 常常同时碰到网络请求、模型流式输出、工具执行和多个子任务。",[54,459,460],{"code":460},"tokio"," 的异步任务、超时、信号量和取消机制很适合做这类编排。",[10,463,464,465,468,469,472],{},"配套仓库没有无限并发地把请求砸向供应商，而是为不同 Provider 建立 ",[54,466,467],{"code":467},"Semaphore","，限制并发数；流式调用失败时，再用 ",[54,470,471],{"code":471},"backon"," 做有上限的指数退避。这些不炫，但比“再请求一次试试”可靠得多。",[424,474,476],{"id":475},"_3-单二进制低运行时负担","3. 单二进制、低运行时负担",[10,478,479],{},"对于 CLI、桌面端、本地工具和边缘服务，Rust 可以交付一个启动快、依赖少的二进制。Agent 如果需要在用户机器上读文件、跑命令或集成 Tauri，部署体验会比携带一整套动态语言环境更干净。",[424,481,483],{"id":482},"_4-错误不会轻易被揉成一团字符串","4. 错误不会轻易被揉成一团字符串",[10,485,486,487,490],{},"模型服务超时、限流、JSON 解析失败、工具退出码非零，这些错误的处理策略并不相同。Rust 的 ",[54,488,489],{"code":489},"Result"," 与错误类型迫使我们在调用链上面对失败，而不是让异常从循环深处突然冒出来。",[10,492,493,494],{},"当然也有代价：编译时间更长，AI 框架和现成集成少于 Python，很多新 SDK 会晚一拍。我的建议不是“所有 Agent 都该用 Rust”，而是：",[31,495,496],{},"先确认你需要的是一个长期运行的软件系统，而不只是一次实验。",[59,498,500],{"id":499},"一套适合-rust-的最小项目结构","一套适合 Rust 的最小项目结构",[10,502,503,504,310,507,310,509,310,511,310,513,310,516,518,519,522],{},"配套仓库选择从底层调用开始，而不是一上来套一个大而全的 Agent 框架。当前公开代码使用 ",[54,505,506],{"code":506},"async-openai",[54,508,460],{"code":460},[54,510,433],{"code":433},[54,512,436],{"code":436},[54,514,515],{"code":515},"tracing",[54,517,471],{"code":471}," 与 ",[54,520,521],{"code":521},"reqwest","，并按章节拆分 LLM 调用、结构化输出、流式响应和 GAIA 评测。",[10,524,525],{},"沿着视频的概念，一套最小结构可以这样长出来：",[115,527,532],{"className":528,"code":529,"filename":530,"language":121,"meta":531},[118],"rust-agent\u002F\n├─ src\u002F\n│  ├─ main.rs          # 入口、配置、优雅退出\n│  ├─ agent.rs         # Loop、最大步数、停止条件\n│  ├─ llm.rs           # 模型请求、流式输出、结构化响应\n│  ├─ context.rs       # 选择、压缩、隔离与状态装配\n│  ├─ tools\u002F\n│  │  ├─ mod.rs        # 工具注册表与权限\n│  │  └─ search.rs     # 具体工具\n│  └─ eval.rs          # 固定任务集与回归评测\n├─ tests\u002F\n└─ Cargo.toml\n","project-layout.txt","icon=tabler:folders",[54,533,529],{"__ignoreMap":125},[10,535,536],{},"最小循环不需要先设计成“自主公司”。把供应商细节删掉后，它更像下面这段 Rust 风格骨架：",[115,538,542],{"className":539,"code":540,"filename":541,"language":444,"meta":445},[441],"async fn run_agent(task: &str, max_steps: usize) -> anyhow::Result\u003CString> {\n    let mut state = AgentState::new(task);\n\n    for step in 0..max_steps {\n        let context = state.context_for_next_step()?;\n        let decision = llm_decide(context).await?;\n\n        match decision {\n            Decision::Final { answer } => return Ok(answer),\n            Decision::ToolCall { name, arguments } => {\n                let observation = tools()\n                    .execute_checked(&name, arguments)\n                    .await?;\n                state.record(step, name, observation);\n            }\n        }\n    }\n\n    anyhow::bail!(\"agent exceeded max_steps without a final answer\")\n}\n","agent_loop.rs",[54,543,540],{"__ignoreMap":125},[10,545,546],{},"这段骨架刻意做了几件事：",[83,548,549,555,558,561,564],{},[86,550,551,554],{},[54,552,553],{"code":553},"Decision"," 必须能被反序列化和校验；",[86,556,557],{},"工具只能从注册表中调用；",[86,559,560],{},"每次观察都进入状态，而不是偷偷打印后丢掉；",[86,562,563],{},"循环有明确上限；",[86,565,566],{},"没有最终答案就返回错误，不伪装成成功。",[10,568,569],{},"接下来再加流式输出、重试、记忆、RAG、多 Agent 或 MCP，系统仍然围绕同一只 Loop 生长。顺序反过来，很容易得到一棵挂满功能、却没有树干的圣诞树。",[59,571,573],{"id":572},"从-demo-走向可用系统的顺序","从 Demo 走向可用系统的顺序",[575,576,577,580,583,586,589,592,595,598,601,604,607,610],"timeline",{},[10,578,579],{},"{1. 先跑通一次强类型 LLM 调用}",[10,581,582],{},"确认鉴权、超时、错误和结构化输出都能稳定处理。不要第一天就接十家模型。",[10,584,585],{},"{2. 只接一个只读工具}",[10,587,588],{},"例如搜索或读取公开资料。先验证工具选择、参数校验和观察回传。",[10,590,591],{},"{3. 加入有界循环}",[10,593,594],{},"设置最大步数、总超时与停止条件，并完整记录每轮决策。",[10,596,597],{},"{4. 建立十几道固定评测题}",[10,599,600],{},"不必等到完整 GAIA。先覆盖你的真实任务、失败路径和拒答场景。",[10,602,603],{},"{5. 做上下文预算}",[10,605,606],{},"为系统指令、当前任务、历史摘要、检索片段和工具输出分别设上限。",[10,608,609],{},"{6. 最后才增加写操作和自主性}",[10,611,612],{},"涉及文件修改、外部发送、支付或生产环境时，加入权限与人工确认。",[59,614,616],{"id":615},"最后rust-负责让边界变硬","最后：Rust 负责让边界变硬",[10,618,619],{},"这期视频最有价值的地方，不是证明 Rust 也能请求大模型。任何能发 HTTP 的语言都能做到。",[10,621,622],{},"它先把 Agent 拆回几个朴素问题：模型为什么能理解任务？谁决定下一步？工具结果如何回到循环？什么时候该用 Agent？怎么评测？上下文太长怎么办？这些问题想不清楚，换语言只是换一种方式制造不确定性。",[10,624,625],{},"Rust 也不会消灭不确定性。它做的是把不确定性关在边界内：模型可以自由提出下一步，但动作必须通过类型、权限和校验；网络可以失败，但重试有上限；任务可以很长，但状态可追踪；上下文可以增长，但每一轮都有预算；Agent 可以探索，但一定有刹车。",[10,627,628],{},"这才是我理解的「用 Rust 开发 AI Agent」：不是让螃蟹替模型思考，而是让螃蟹把护栏焊牢。模型负责往前走，程序负责别让它走丢。",{"title":125,"searchDepth":630,"depth":630,"links":631},4,[632,634,635,636,637,638,645,646,647],{"id":61,"depth":633,"text":62},2,{"id":109,"depth":633,"text":110},{"id":168,"depth":633,"text":169},{"id":280,"depth":633,"text":281},{"id":317,"depth":633,"text":318},{"id":415,"depth":633,"text":416,"children":639},[640,642,643,644],{"id":426,"depth":641,"text":427},3,{"id":453,"depth":641,"text":454},{"id":475,"depth":641,"text":476},{"id":482,"depth":641,"text":483},{"id":499,"depth":633,"text":500},{"id":572,"depth":633,"text":573},{"id":615,"depth":633,"text":616},[649],"开发","2026-07-13 12:17:39","总结「使用 Rust 开发 AI Agent - 简介」：从 LLM 的泛化能力、Agent Loop、工作流与 Agent 的边界，到 GAIA、上下文工程，以及一套适合 Rust 的落地结构。",false,"md",null,{"slots":656},{},true,"\u002F2026\u002Frust-ai-agent-from-loop-to-engineering",{"text":660,"minutes":661,"time":662,"words":663},"19 min read",18.3,1098000,3660,[665,667,670,673,676],{"title":666,"link":22},"使用 Rust 开发 AI Agent - 简介",{"title":668,"link":669},"rust-agent 配套代码仓库","https:\u002F\u002Fgithub.com\u002Fsolenovex\u002Frust-agent",{"title":671,"link":672},"GAIA: a benchmark for General AI Assistants","https:\u002F\u002Farxiv.org\u002Fabs\u002F2311.12983",{"title":674,"link":675},"async-openai documentation","https:\u002F\u002Fdocs.rs\u002Fasync-openai\u002Flatest\u002Fasync_openai\u002F",{"title":677,"link":678},"Building effective agents","https:\u002F\u002Fwww.anthropic.com\u002Fengineering\u002Fbuilding-effective-agents",{"title":5,"description":651},{"loc":658},"posts\u002F2026\u002Frust-ai-agent-from-loop-to-engineering",[683,684,685,686,687,688,689],"Rust","AI","Agent","LLM","Context Engineering","GAIA","工程实践","tech","pcNn64UApCAIdgjZFZb7dcEkdQZtjHXoXx8_5N4axd8",[693,654],{"title":694,"path":695,"stem":696,"date":697,"type":690,"children":-1},"Vercel AI SDK 入门：从 0 搭一个会流式回复的聊天页","\u002F2026\u002Fvercel-ai-sdk-getting-started","posts\u002F2026\u002Fvercel-ai-sdk-getting-started","2026-07-08 21:56:13",1784687121819]