2026年1月

先上几张图:



上周周末赶汇报 PPT 正愁进度时,看到了论坛佬友们强烈推荐 banana-slides 项目,于是乎自己本地部署和使用了一下,体验是真的惊艳 —— 一方面是惊叹于大香蕉模型的图片输出美观程度,另外一方面也是感叹开发者的思路之优秀。

一句话 / 大纲就能生成,口头改改就迭代,导出可编辑 PPTX 也基本能用,确实改变了游戏规则。

但用着用着我就开始手痒了:文字偶尔还是会小崩(尤其是复杂表格 / 长句),纯图像 PDF 导出后想手动修又麻烦;预览是静态的,没法直接在浏览器里 “播放” 过一遍;而且我想加点更 “所见即所得” 的编辑(比如自动扣图层、前端拖拽改元素、在浏览器里加简单动画过渡),以及加入联网搜索、知识库的接入等等。

鉴于我 “非常喜欢自己造轮子” 的性格,于是我周末抽空基于 banana-slides 的核心思路(图像优先 + vibe 迭代),自己搓了个早期版本,主要改动点目前有这些(还在 WIP 阶段,佬们多来给点建议和 idea):

  • 后端生成后自动二次调用 AI 扣背景 + OCR 提取文字 → 输出分层数据(背景图 + 文字层 + 图标层),导出时可选 “纯图像 PDF”(快)或 “分层 ZIP/PPTX”(方便本地修)。
    - 支持接入 rag 知识库,对一些学术类,需要大量参考资料的 ppt 生成更加友好。

  • 前端加了个基础的在浏览器预览 / 播放模式(用 reveal.js 模拟全屏切换,带简单过渡),不用下载就能快速过一遍效果。

  • 计划支持用户在线拖拽 / 改文字(Fabric.js 那种),但这个还在画饼阶段。

  • 许可证打算用 Apache 2.0,更开放点(欢迎 fork / 商业用,只要留版权)。

目前核心生成已经能跑通,界面也基本可用,但前端编辑和动画还在迭代中,bug 肯定不少。于是乎想着提前来 linux.do 水个帖,顺便求拍砖:

  • 大家觉得分层编辑 + 在线播放这个方向值不值得继续搞?(还是先专注生成质量?)
  • 大家平时做 PPT 最烦哪部分?排版?文字崩?还是没好模板?
  • 如果开源了,你们希望先看到哪个功能完善(分层编辑 vs RAG vs 动画支持)?
  • 目前项目整体还不算完善,因此打算稍晚些再把仓库开放出来

感谢 banana-slides 作者的开源思路,没有原项目我根本想不到这么巧妙的设计思路
也感谢 linux.do 这个社区,让我能够了解到这么优秀的 AI 工具~


📌 转载信息
原作者:
JamesTofard
转载时间:
2026/1/14 10:48:40

原文链接: https://mp.weixin.qq.com/s/Zlwn42Ky…

TRAE 字节跳动技术团队_2026 年 1 月 12 日 17:02 北京_

本文作者:小夏,TRAE 技术专家

在 Agentic Coding 实践中,提升效率的关键在于优化与 AI 的协作方式,而非寄望于无限的上下文窗口。核心策略是采用 “短对话、精简上下文” 的模式,将复杂任务拆解为专注的子对话,并借助 “复利工程” 将 bug 修复、代码审查等日常经验沉淀为可复用的项目知识库,使系统获得记忆并实现效率的持续增长。此外,改善开发者体验(如清晰文档、快速测试)具有双重价值,既能帮助人类开发者,也能显著提升 AI 的表现。最终,与 AI 的协作应被视为一门需要刻意练习的技能,通过不断实践,开发者可以成为驾驭 AI 的 “专家型通才”,在更广阔的领域创造价值。

第一性原理|理解 LLM 的本质

LLM 是如何「思考」的

大语言模型的工作方式可以用一句话概括: 预测下一个 token。

当你向 LLM 提问时,它并不是先在「脑中」构思好完整答案再输出。相反,它每次只做一件事:基于当前看到的所有文本(你的输入 + 它已经生成的内容),预测最可能的下一个词是什么。然后把这个词加入序列,再预测下一个,如此循环直到生成结束,这种方式被称为 自回归生成(Autoregressive Generation) 。

理解这一点至关重要,因为它揭示了 LLM 的几个本质特征:

  1. 没有独立于输出的「思考」过程 : LLM 的推理就是生成本身。它不能先想好再说,只能边说边想。这就是为什么让模型「一步步思考」(Chain-of-Thought)会提升效果 — 你实际上是在给它更多的「思考空间」,让它通过生成中间步骤来辅助推理。尽管新一代模型很多都带有显式的 reasoning 能力,依然脱离不了这个范式。

  2. 上下文就是全部记忆: LLM 没有独立的记忆存储。它对当前对话的所有「理解」,完全来自于输入给它的上下文窗口。窗口之外的内容,对模型来说就是不存在的。

  3. 生成具有概率性: 每一步预测都是基于概率分布采样,这意味着同样的输入可能产生不同的输出。

自回归(autoregressive)LLM 的核心工作方式是: 一次只预测下一个 token ,然后把自己刚生成的内容再喂回去继续生成。这个机制很强(通用、可扩展、能「写出」复杂结构),但也天然带来一些局限,在 Coding Agent 里这些局限会被放大,因为软件开发是 「长链路、强约束、强验证」 的任务。

当前 Coding Agent 普遍存在的问题:

  1. 局部最优:一步一步「续写」,不等于全局规划
  • 容易先写得很像、很顺,然后在后面发现不对再补丁式修修补补(历史债务快速累积)。

  • 多文件重构 / 架构调整这种「先规划后落地」的任务,会变成边写边想,导致接口不一致、重复实现、遗漏迁移步骤。

  • 对「最终能不能通过测试 / CI」缺乏感知,除非你把测试结果显式喂回 agent 循环。

  1. 一旦走偏,会沿着偏差继续滚雪球
  • 早期误读需求 / 误判代码库的约束 ,导致后面每一步都在错误世界观里自洽。

  • 生成了一个不存在的函数名 / 类型后,后续会越来越「相信它存在」,直到编译 / 运行时打脸。

  • 在长对话、长任务里尤其明显:越写越像「另一个项目」。

  1. 输出是流式的,无法回头修改之前输出的内容
  • 擅长「写新文件 / 新段落」,相对不擅长在复杂代码中做精确、最小化、局部一致的编辑。

  • 容易倾向于「重写一遍」而不是「进行修改」,导致 diff 过大、review 成本高。

  • 在需要保持大量不变量(API、格式、风格、兼容性)的仓库里尤其痛苦。

  1. 满足约束的能力有限,而代码是强语法 / 强语义约束的
  • 「看起来合理」的实现,经常在边界条件、并发、错误处理、资源释放上出错。

  • 跨文件的符号解析、泛型 / 模板推导、宏 / 代码生成等,靠纯文本推断很容易漏。

  • 复杂的重构(rename、move、split)没有 AST / 语义工具辅助时,错一个引用就全盘崩。

  1. 天然单线程思考,难并行探索
  • 在方案选择(架构、依赖、实现路径)上容易早早押注一个方向,缺少系统性对比。

  • 排查 bug 时更像「单线推理」,不如人类 / 工具那样并行假设、并行验证

Attention:LLM 如何「阅读」上下文

在预测下一个 token 时,模型需要从上下文中提取相关信息,这个过程由 Attention(注意力)机制 完成。

你可以把 Attention 想象成一个动态的「聚光灯」:当模型生成每个新 token 时,它会扫视整个上下文,对不同位置的内容分配不同的「注意力权重」。权重高的部分会对当前生成产生更大影响,权重低的部分则几乎被忽略。

具体来说,对于当前要生成的 token,模型会:

  • 计算相关性: 当前位置与上下文中每个位置的相关程度

  • 分配权重: 相关性高的位置获得更高权重,形成一个概率分布

  • 加权聚合: 根据权重汇总上下文信息,用于预测下一个 token

这个机制有几个重要特性:

  • 注意力是稀疏的: 模型不会均匀地关注所有内容。在实践中,Attention 权重往往集中在少数关键位置,大部分位置的权重接近于零。

  • 注意力需要「学习」的: 模型通过训练学习在什么情况下关注什么内容。这意味着它可能会形成某些偏好模式,比如更关注开头和结尾。

  • 计算复杂度是平方级的: 标准 Attention 需要计算每个位置与所有其他位置的关系,计算量随上下文长度的平方增长。这是上下文长度存在物理上限的根本原因之一。

理解了 Attention 机制,我们就能更好地理解上下文长度为什么会带来多种限制。

上下文长度:最关键的约束

上下文窗口的限制是影响 Coding Agent 设计最核心的约束条件。结合对 Attention 机制的理解,我们可以更清楚地看到这个限制的多个层次。

物理上限

每个模型都有最大 token 限制,目前主流模型通常在 128K 到 200K 之间。这个硬性边界的存在有两个根本原因:

  1. Attention 的计算复杂度通常是 O(n²),上下文长度翻倍,计算量变成四倍

  2. 需要存储完整的注意力矩阵,内存消耗同样是平方级增长

超过这个长度的内容根本无法被处理。

有效上下文远小于标称上下文

虽然模型宣称支持 200K tokens,但这不意味着在 200K 长度下都能保持良好表现。

问题出在 Attention 的「稀疏性」上:当上下文很长时,注意力权重需要分散到更多位置。如果关键信息只是海量内容中的一小部分,它获得的注意力权重可能会被稀释到难以有效利用的程度。实际上,一个支持 200K 的模型,可能在超过 80K 或 100K 之后,就开始出现明显的性能退化。

性能退化曲线

随着上下文变长,不仅是「记忆」变差,模型的整体能力,包括推理准确性、指令遵循能力、代码生成质量都会下降。

这是自回归 + Attention 组合带来的累积效应:

  • 每一步生成都依赖于对上下文的正确理解

  • 更长的上下文意味着更稀疏的注意力分布

  • 错误理解会传递到后续生成,形成累积误差

理解这些限制,你就能明白为什么 Coding Agent 需要精心设计上下文管理策略,而不是简单地把所有信息塞进窗口。

强化学习:让模型学会「做事」

前面我们讨论的是 LLM 的基础能力,预测下一个 token。但要让模型从 「能说会道」 进化到 「能做事情」 ,还需要另一个关键技术: 强化学习(Reinforcement Learning, RL)。

预训练的局限

LLM 的预训练本质上是「模仿」:通过阅读海量文本,学会预测「人类会怎么写下一个词」。这让模型获得了广泛的知识和流畅的表达能力,但也有明显的局限:

  • 模型学会的是「文本看起来应该是什么样」,而不是「什么行动能解决问题」

  • 预训练数据中很少有「调用工具 → 观察结果 → 调整策略」这样的交互序列

  • 模型可能会生成看起来合理但实际无效的操作步骤

简单来说,预训练教会了模型「说」,但没有教会它「做」。

强化学习的核心思想

强化学习用一个简单的循环来解决这个问题: 尝试 → 反馈 → 调整。

想象教一个小朋友下棋:你不会给他看一百万盘棋谱让他背(这是预训练的方式),而是让他实际下棋,赢了就表扬,输了就复盘。通过无数次的尝试和反馈,他逐渐学会什么是好棋、什么是坏棋。

对 LLM 来说,强化学习的过程类似:

  • 尝试: 让模型在真实或模拟的环境中执行任务(比如调用工具、编辑文件)

  • 反馈: 根据结果给出奖励信号(任务完成了?代码能运行?测试通过了?)

  • 调整: 更新模型参数,让它更倾向于选择能获得高奖励的行动

这个过程会重复成千上万次,模型逐渐学会:在什么情况下应该调用什么工具、如何解读工具返回的结果、什么时候应该继续尝试、什么时候应该换个方向。

为什么 RL 对 Agentic 能力至关重要

传统的 LLM 微调(比如监督学习)需要人类标注「正确答案」。但 Agent 任务的特点是:

  • 路径多样: 完成同一个编程任务可能有无数种合理的步骤组合

  • 结果导向: 我们真正关心的是最终结果,而不是中间每一步是否「标准」

  • 需要探索: 有时候必须尝试几种方法才能找到可行的路径

RL 天然适合这种场景。它不要求你定义「正确的步骤序列」,只需要定义「什么算成功」。模型通过自己的探索,学会找到通往成功的路径。

实际训练中的应用

现代 Coding Agent 背后的模型通常会经历这样的 RL 训练:

  • 在模拟的编程环境中执行任务(创建文件、运行代码、修复 bug)

  • 用测试通过率、代码质量评分等作为奖励信号

  • 学习何时应该读取更多文件来获取上下文,何时应该直接动手修改

  • 学习如何从错误信息中提取有用线索,调整下一步策略

值得注意的是,RL 训练的效果高度依赖奖励信号的设计。如果只奖励「任务完成」,模型可能学会走捷径(Reward Hacking);如果奖励信号太复杂,训练可能不稳定。 这也是为什么不同模型在 Agent 任务上的表现差异很大,背后的 RL 训练策略可能完全不同。

对使用者的启示

理解了 RL 的作用,你会更清楚为什么某些使用方式更有效:

  • 提供清晰的成功标准: 当你告诉 Agent 「修好这个 bug,运行 npm test 应该全部通过」,你实际上是在给它一个明确的「奖励信号」,这与它训练时的模式一致

  • 允许试错: Agent 在训练中学会了通过尝试来解决问题,给它多次尝试的机会往往比期待一次成功更实际

  • 观察它的决策模式: 当 Agent 做出某个决定(比如先读文件而不是直接修改),这往往反映了它在 RL 训练中学到的策略

Coding Agent 的实现原理

理解了 LLM 的本质和限制后,我们来看 Coding Agent 是如何在这些约束下被设计出来的。

LLM API 的核心结构

现代 LLM API 采用基于消息的对话结构。理解这个结构是构建 Agent 的基础。

Messages 数组

API 的核心是一个消息数组,每条消息包含角色(role)和内容(content):

{
  "messages": [
    {
      "role": "system",
      "content": "你是一个专业的编程助手..."
    },
    {
      "role": "user",
      "content": "帮我写一个快速排序函数"
    },
    {
      "role": "assistant",
      "content": "好的,这是一个 Python 实现的快速排序..."
    }
  ]
}

三种角色各有用途:

  • system: 设定 Agent 的行为准则、能力范围和工具定义

  • user: 用户的输入

  • assistant: 模型的回复

Tool Calling 机制

Coding Agent 的核心能力来自工具调用。API 允许你定义工具的 schema,模型会在需要时生成结构化的工具调用请求:

{
  "tools": [
    {
      "name": "read_file",
      "description": "读取指定路径的文件内容",
      "parameters": {
        "type": "object",
        "properties": {
          "path": {
            "type": "string",
            "description": "文件路径"
          }
        },
        "required": ["path"]
      }
    }
  ]
}

当模型决定调用工具时,它的响应会包含工具调用信息:

{
  "role": "assistant",
  "content": null,
  "tool_calls": [
    {
      "id": "call_abc123",
      "name": "read_file",
      "arguments": "{\"path\": \"src/main.py\"}"
    }
  ]
}

工具执行结果作为新消息返回给模型:

{
  "role": "tool",
  "tool_call_id": "call_abc123",
  "content": "def main():\n    print('Hello, World!')\n..."
}

Reasoning Content 的保留

对于具有显式推理能力的模型,API 响应中可能包含 reasoning 或 thinking 字段,记录模型的思考过程:

{
  "role": "assistant",
  "reasoning_content": "用户需要读取文件来理解项目结构。我应该先查看 src 目录下的主要文件...",
  "content": "让我先看一下项目的主文件。",
  "tool_calls": [...]
}

在多轮对话中保留这些推理内容,可以帮助模型维持思维的连贯性。模型可以「回顾」自己之前的思考过程,从而做出更一致的决策。

Prompt Caching:工程实践的关键

由于 LLM 的自回归特性,每次请求都需要重新处理整个上下文。对于动辄几万 token 的 Coding Agent 对话来说,这意味着大量的重复计算和延迟。

Prompt Caching 通过缓存上下文前缀的计算结果来解决这个问题。关键在于: 缓存基于前缀匹配 。只有当新请求的开头部分与之前的请求完全一致时,才能命中缓存。

这直接影响了 prompt 的组织方式:

┌─────────────────────────────┐
│  System Prompt              │  ← 稳定不变,可缓存
│  (角色定义、行为准则)         │
├─────────────────────────────┤
│  Tool Definitions           │  ← 稳定不变,可缓存
│  (工具的 schema 定义)        │
├─────────────────────────────┤
│  Project Context            │  ← 相对稳定,尽量少变
│  (项目说明、代码规范)         │
├─────────────────────────────┤
│  Conversation History       │  ← 动态增长
│  (对话历史)                  │
├─────────────────────────────┤
│  Current User Message       │  ← 每次都变
│  (当前用户输入)              │
└─────────────────────────────┘

最佳实践参考:

  1. 把稳定内容放在前面: system prompt 和工具定义应该保持稳定,避免频繁修改

  2. 动态内容放在后面: 对话历史和当前输入放在最后

  3. 避免在稳定前缀中插入可变内容: 比如不要在 system prompt 中插入当前时间戳

Agent Loop:核心循环

语言模型可以回答问题,而 Agent 可以 做事情 。Agent Loop 正是实现这一差异的关键。

当模型收到一个无法仅凭训练知识完成的请求时,它需要与外部世界交互:读取文件、查询数据库、执行代码。Agent Loop 就是管理这个「推理 - 行动」循环的编排层,使模型能够处理需要多个步骤、外部信息或产生实际影响的任务。

循环的基本原理

Agent Loop 的运作遵循一个简单的原则:调用模型 → 检查是否需要使用工具 → 如果需要则执行工具 → 将结果返回给模型再次调用 → 重复直到模型产生最终响应。

这个循环的关键在于 上下文的累积 。每次迭代都会向对话历史中添加新内容。模型不仅能看到原始请求,还能看到它调用过的每个工具以及收到的每个结果。这种累积的上下文使得复杂的多步骤推理成为可能。

一个具体的例子

假设用户请求:「帮我修复项目中的这个 bug:用户登录后会话没有正确保存」。

这不是模型仅凭知识就能完成的任务,它需要通过 Agent Loop 逐步探索:

  • 第一轮: 模型收到请求,首先需要了解项目结构。它调用 list_directory 工具查看项目根目录。

  • 第二轮: 模型看到了目录结构,识别出 src/auth/ 目录可能与登录相关。它调用 read_file 查看 src/auth/login.js

  • 第三轮: 模型看到了登录代码,发现它调用了 sessionManager.save() 。为了追踪问题,它调用 read_file 查看 src/session/manager.j s。

  • 第四轮: 模型发现了问题, save() 方法中有一个异步操作没有被正确 await 。它调用 edit_file 工具修复这个 bug。

  • 第五轮: 修复完成,模型调用 shell 工具运行测试来验证修复是否有效。

  • 第六轮: 测试通过。模型不再请求工具,而是生成最终响应:总结问题原因、修复内容和验证结果。

每一轮都遵循相同的模式:模型接收上下文,决定是继续行动还是给出响应,要么继续循环,要么退出。关键在于,模型基于对任务不断演进的理解 自主做出这些决策。

循环的终止条件

每次模型调用都会返回一个停止原因(stop reason),决定接下来发生什么:

  • end_turn: 模型完成了响应,没有进一步的行动需要执行。这是正常的成功终止,循环退出并返回最终消息。

  • tool_use: 模型想要执行一个或多个工具后再继续。循环执行请求的工具,将结果追加到对话历史,然后再次调用模型。

  • max_tokens: 模型的响应因达到 token 限制而被截断。这在当前循环中无法恢复,循环以错误终止。

理解这些终止条件有助于预测 Agent 的行为并处理边界情况。

Coding Agent 的典型工具

一个功能完整的 Coding Agent 通常需要以下几类工具:

文件操作

  • read_file :读取文件内容

  • write_file :创建或覆盖文件

  • edit_file :对文件进行局部编辑(而非完全重写)

代码执行

  • shell / terminal :执行命令行命令,用于运行代码、安装依赖、执行测试等

代码搜索

  • grep / search :在代码库中搜索文本或模式

  • semantic_search :基于语义的代码搜索

项目导航

  • list_directory :列出目录内容

  • find_files :根据模式查找文件

这些工具的设计直接影响 Agent 的能力边界,工具的粒度、参数设计、返回格式都需要仔细考量。

常见问题与解决方案

在了解了 LLM 和 Coding Agent 的基本原理后,我们再来看看一些常见的问题就能更好地理解了。

会话间失忆

Coding Agent 面临的一个根本性问题是: 它们在会话之间没有持久记忆。

每次启动新会话时,Agent 只知道它能在磁盘上找到的内容。就像电影《记忆碎片》或《初恋 50 次》中的主角一样,Agent 每天醒来都不记得昨天发生了什么。而典型的工程工作流往往需要跨越多个会话才能完成一个功能 —— 因为需要测试、代码审查和后续清理。

这导致了一个荒谬的局面:你需要在每个新会话开始时,重新向 Agent 解释项目背景、当前进度和接下来的计划。

解决方案:

  • 使用结构化的任务追踪系统(如 issue tracker)

  • 在每个会话结束时,让 Agent 生成一个状态摘要,供下次会话使用

  • 将重要决策和发现记录在固定位置,而不是对话历史中

上下文窗口耗尽

每轮循环都会向对话历史添加消息。对于需要多次工具调用的复杂任务,历史记录可能超出模型的上下文窗口。当这种情况发生时,Agent 无法继续。

具体表现包括:模型提供商返回输入长度错误,或随着上下文填满不太相关的早期消息,模型性能明显下降。

目前有两种主流的上下文管理策略:

Observation Masking(观察遮蔽)

这种方法只针对工具返回的观察结果进行处理,而完整保留 Agent 的推理和行动历史。具体做法是:用占位符替换较早轮次的观察内容(比如 「内容已省略」),只保留最近 N 轮的完整输出。

这种方法简单高效,因为典型 Coding Agent 的每轮交互中,工具输出(如文件内容、命令执行结果)往往占据了绝大部分 token。通过遮蔽旧的观察内容,Agent 仍然可以访问自己过去的推理和决策,但不再重复处理早期轮次的冗长文本。

LLM Summarization(LLM 摘要)

这种方法使用另一个 LLM 将较早的交互历史(包括观察、行动和推理)压缩成简短的摘要。摘要会替代原始的详细历史,而最近的几轮对话保持完整。

这种方法理论上可以支持无限长的对话,因为历史会被反复压缩。但它也有代价:每次生成摘要都需要额外的 API 调用,而且摘要可能会丢失某些细节信息。研究发现,摘要有时会掩盖 Agent 应该停止尝试的信号,导致 Agent 运行更多轮次,反而增加了成本。

两种方法各有优劣:Observation Masking 实现简单、成本低,但上下文仍会缓慢增长;LLM Summarization 可以更彻底地压缩历史,但引入了额外开销和信息损失风险。实践中,可以考虑混合使用,以 Observation Masking 作为主要策略,仅在上下文确实过长时触发 LLM Summarization。

其他解决方案:

  • 减少工具输出的冗长程度,返回摘要或相关片段,而非完整数据

  • 简化工具 schema,深度嵌套的结构会在工具配置和模型推理中消耗大量 token

  • 将大型任务分解为子任务,每个子任务使用新的上下文

有效上下文远小于标称值

即使拥有 1M token 的上下文窗口,Coding Agent 实际上只能有效利用其中的 10-15%。 超过 20% 后,成本和性能都会急剧恶化。大多数 Agent 会在达到约 20% 时强制中断,而最佳实践是在 15% 之前就重启会话。

这意味着,以全速工作时,你大约只有 5-10 分钟的有效工作时间,然后 Agent 就会「耗尽上下文」,需要重启(相当于死亡)或进行 compaction(相当于记忆清除)。

有一个生动的比喻:上下文窗口就像潜水员的氧气罐。所有人都说「我们给他一个更大的氧气罐:100 万 token!」但他最终还是会耗尽氧气,更大的窗口并不能解决根本问题。

「Dumb Zone」:中间区域的性能退化

研究发现,上下文窗口的中间 40-60% 区域存在一个「Dumb Zone」,在这个区域,模型的召回率下降,推理能力变差。这与前面提到的「Lost in the Middle」现象相呼应。

当 Agent 深入工作时,它会逐渐表现出类似「痴呆」的症状:迷失方向、产生幻觉接口、忘记原始目标。这不是因为模型变笨了,而是因为关键信息被淹没在大量的上下文中,无法被有效利用。

新发现的任务被丢弃

这是 Agentic Coding 中一个容易被忽视但影响巨大的问题: LLM 在工作过程中会注意到各种问题,但在上下文空间紧张时,会选择忽略这些发现,不采取任何行动。

例如,Agent 在修复一个 bug 时,可能会注意到代码中的另一个潜在问题、一个需要重构的地方、或一个缺失的测试用例。但如果当前上下文已经很满,Agent 可能会「假装没看到」,继续专注于手头的任务。这些被发现但被丢弃的工作,就这样悄悄消失了。

解决方案:

  • 使用外部工具让 Agent 随时记录发现的问题

  • 在任务结束时,要求 Agent 列出所有观察到但未处理的问题

  • 建立「发现即记录」的工作流程

过早宣告完成

当 Agent 经历多次 compaction 后,可能会出现一个荒谬的情况:它会自信地宣布「恭喜,任务完成了!让我们开始手动测试吧!」而实际上还有大量的阶段没有完成。

解决方案:

  • 将整体计划保存在上下文之外的固定位置

  • 定期让 Agent 对照原始计划检查进度

  • 使用结构化的任务追踪,而不是依赖 Agent 的记忆

工具选择不当

当模型持续选择错误的工具时,问题通常出在工具描述的模糊性上。从模型的角度审视描述:如果两个工具的描述有重叠,模型就没有选择的依据,我们应该确保每个工具的用途清晰且互不重叠。

通过上述的探讨,我们已经完成了从 LLM 本质到 Agentic Coding 原理的理论铺垫:从逐个预测 token 的底层逻辑,到注意力机制的上下文约束,再到强化学习如何让模型学会「做事」;从 API 结构的基础框架,到 Agent Loop 的核心循环,最后到解决上下文耗尽、会话失忆等工程问题的实践策略。

这些内容共同构成了 Agentic Coding 的第一性原理基石 —— 理解这些,你就能跳出 "黑盒工具" 的使用层面,看到各种最佳实践背后的深层逻辑。

接下来,我们将从理论走向实操:结合具体案例拆解如何设计高效的 Agent 对话流程、如何选择合适的工具集、如何优化上下文管理策略,以及如何将这些原理应用到实际的编码任务中(如 bug 修复、功能开发、代码重构)。你将看到,当我们用第一性原理的视角重新审视 Agentic Coding 时,那些看似零散的技巧会形成一套系统的方法论,帮助你真正成为 AI 编程助手的「驾驭者」而非「使用者」。

了解了 LLM 的特性、Coding Agent 的实现原理以及常见问题后,我们总结出了一些最佳实践,帮助大家更高效地使用这些工具。

短对话优于长对话

这可能是最重要的一条实践: 保持对话简短、专注,每个对话只做一件事。

很多人认为更大的上下文窗口意味着更强的能力,可以把更多任务塞进一个对话里。但实际情况恰恰相反,最好的对话是短对话,它们只做一件事,并且只包含完成这件事所需的上下文。

为什么短对话更好?

当你往上下文里塞太多内容时,Agent 的表现就像喝醉了一样:它会开始犯错、跌跌撞撞、甚至开始和你争论,如果你继续喂它更多 token,它甚至会「吐」得你一身(产生大量无意义的输出)或者进入死循环。

对话越长,上下文窗口里就会积累越多与当前任务不太相关的内容。为了让 Agent 发挥最佳工况,你需要给它完成当前工作所需的上下文。

长对话不仅效果差,还更贵。每次发送消息时,整个上下文都会被发送给模型提供商。这意味着对话越长,新消息的成本就会指数级增长。而且长对话更容易因为消息间隔时间长而错过缓存窗口,导致费用飙升。所以,长对话既效果差,又花费高,尤其在以 tokens 消耗计费的套餐下面。

拆分对话,本质上是拆分任务,大任务应该被分解为小任务。这在 Agent 时代之前就是软件工程的最佳实践,现在看也依然如此,短对话让任务拆分这件事变得自然甚至有趣。就像小任务更容易管理一样,小对话也更容易追踪:每个对话都有明确的目标,你可以轻松掌握整体进度。

以对话为单位组织工作

如果把对话视为任务的基本单位,那么一个功能或 bug 修复就自然变成了一组相互关联的对话。

一个典型的工作流程

假设你要实现一个新功能,可以这样组织对话:

功能:用户登录后的会话管理
[对话 1] 调研现有代码结构
    ├── 了解 auth 模块的实现
    ├── 查看 session 管理的现状
    └── 输出:关键文件列表和当前架构理解
[对话 2] 实现基础功能
    ├── 参考对话 1 的发现
    ├── 实现核心的 session 保存逻辑
    └── 输出:基础实现代码
[对话 3] 添加错误处理
    ├── 参考对话 2 的实现
    └── 增加边界情况处理
[对话 4] 编写测试
    ├── 参考对话 2、3 的实现
    └── 添加单元测试和集成测试
[对话 5] 代码审查
    ├── 检查实现是否符合项目规范
    └── 确认没有引入安全问题
[对话 6] 清理和重构
    └── 根据审查结果进行调整

每个对话都很短,只专注于一件事。它们加在一起,完成了整个功能的开发。

那对话之间如何共享上下文?

当你开始一个新对话时,可以通过以下方式传递必要的上下文:

  • 引用之前对话的结论: 在新对话开头简要说明之前的发现或决策

  • 利用 Git 状态: 让 Agent 查看 git diff 或检查最近的提交

  • 使用项目文档: 将重要决策记录在 AGENTS.md 或类似文件中,Agent 每次都能读取

  • 直接提及相关 文件: 在新对话中 #mention 需要的文件

关键是: 不要试图在一个对话里完成所有事情。 每当你发现当前任务已经完成,或者对话开始变得混乱,就应该开始一个新对话。

编写有效的项目配置文件

大多数 Coding Agent 都支持在项目根目录放置配置文件(比如 Rules 或者 Agent.md),这个文件会 自动注入到每一个对话 中 ,这意味着它是你影响 Agent 行为的杠杆支点,但这把双刃剑也很容易用错。

理解 Agent 的无状态本质

LLM 是无状态函数。它的权重在推理时是冻结的,不会随着使用而学习,Agent 对你代码库的全部了解,完全来自于你放进上下文窗口的 token,这有三个重要含义:

  1. 每次新对话开始时,Agent 对你的代码库一无所知

  2. 任何重要的项目信息都需要在每次会话中告诉它

  3. 项目配置文件是实现这一点的首选方式

因此,你应该把这个文件视为 每次会话的入职培训文档。

配置文件应该包含什么

一个好的项目配置文件应该回答三个问题:

  • WHAT(是什么): 技术栈、项目结构、各模块的职责。这在 monorepo 中尤其重要,应该告诉 Agent 有哪些应用、哪些共享模块、每个部分是做什么的

  • WHY(为什么): 项目的目的、设计决策的背景。为什么选择这个架构?为什么有些代码看起来不合理(比如历史债务)?

  • HOW(怎么做): 如何运行项目、如何测试、如何验证改动。用 bun 还是 npm?测试命令是什么?

少即是多

这是最容易犯的错误:试图把所有可能需要的信息都塞进配置文件。

研究表明,前沿的思考模型大约能可靠地遵循 150-200 条指令,而 Coding Agent 的系统提示本身可能已经包含了约 50 条指令。这意味着你的配置文件应该 尽可能精简 ,理想情况下只包含那些对所有任务都普遍适用的内容。

# 不好的做法:塞满各种可能用到的信息
## 数据库 Schema 设计规范(500 行详细规范...)
## API 设计指南(300 行规范...)
## 代码风格指南(200 行规范...)

# 好的做法:简洁 + 指向详细文档
## 项目概述
这是一个 Next.js 电商平台,使用 PostgreSQL + Prisma。

## 关键目录
- `apps/web`: 前端应用
- `apps/api`: 后端服务
- `packages/shared`: 共享类型和工具

## 开发命令
- `bun dev`: 启动开发服务器
- `bun test`: 运行测试
- `bun typecheck`: 类型检查

## 详细文档
根据任务需要,查阅以下文档:
- 数据库设计:`docs/database-schema.md`
- API 规范:`docs/api-guidelines.md`
- 代码风格:`docs/code-style.md`

一个经验法则:配置文件应该控制在 300 行以内 ,越短越好。有些团队的配置文件甚至不到 60 行。

渐进式披露

与其在配置文件中塞满所有信息,不如使用 渐进式披露 策略:

agent_docs/
  ├── building_the_project.md
  ├── running_tests.md
  ├── code_conventions.md
  ├── service_architecture.md
  └── database_schema.md

在配置文件中列出这些文档并简要描述,让 Agent 根据当前任务决定读取哪些。这样,只有相关的信息才会进入上下文,避免不必要的干扰。

偏好指针而非副本: 不要在文档中复制代码片段 —— 它们很快会过时。使用 file:line 引用指向代码的权威位置。

不要让 Agent 做 Linter 的工作

很多人喜欢在配置文件中写详细的代码风格指南, 这不是个好的实践。

LLM 做格式检查既慢又昂贵,更重要的是,这些指南会增加指令数量,降低 Agent 对其他指令的遵循能力。

更好的做法:

  • 使用真正的 linter 和 formatter(如 ESLint、Prettier、Biome)

  • 配置自动修复,让工具处理格式问题

  • 如果 Agent 产生了格式错误,让 linter 在后处理阶段修复它

LLM 是上下文学习者。如果你的代码库遵循一致的风格,Agent 通常会自动模仿这种风格,不需要你明确告诉它。

这是最高杠杆点,要认真对待

一行糟糕的代码就是一行糟糕的代码,一个糟糕的技术方案可能产生很多行糟糕的代码,而配置文件中的一行糟糕的指令会影响 每一个会话、每一个任务、每一个产出。

杠杆效应:
配置文件 → 影响每个会话的行为
    ↓
研究/规划阶段 → 影响实现计划的质量
    ↓
实现阶段 → 影响最终代码的质量

花时间仔细考虑配置文件的每一行,这是你能做的 ROI 最高的投资之一。

200K Token 足够了

当大家都在追求更大的上下文窗口时,一个反直觉的事实是:200K token 对于大多数任务来说已经绰绰有余了。

关键不在于你有多大的上下文窗口,而在于你如何使用它。一个 200K 的窗口,如果你用短对话的方式工作,可以支持你完成非常复杂的功能。因为虽然每个对话只有几十 K 到上百 K 个 token,但你可以开启 10 个、20 个甚至更多对话,它们加起来的总量远超任何单一上下文窗口。而且,由于每个对话都是从相对干净的状态开始,Agent 的表现会一直保持在最佳水平,而不是随着上下文膨胀而逐渐退化。

实践建议

  • 当对话超过 80K-100K token 时,考虑开始新对话

  • 完成一个独立的子任务后,开始新对话处理下一个任务

  • 如果 Agent 开始表现出「醉酒」症状(重复、遗忘、偏离目标),立即开始新对话

  • 把「开始新对话」视为正常工作流程的一部分,而不是「失败后的重试」

Compounding Engineering:让系统自我改进

传统的 AI 编程是关于短期收益的:你给 prompt,它写代码,然后发布,然后从头开始。Every.to 提出的 Compounding Engineering(复利工程) 则是关于构建具有记忆的系统:每个 PR 都在教育系统,每个 bug 都成为永久的教训,每次代码审查都在更新 Agent 的默认行为。普通 AI 工程让你今天更高效,Compounding Engineering 让你之后的每一天都更高效。

核心理念:你不只是在解决问题,而是在教育系统

当你使用 Coding Agent 时,问自己一个问题:我是在解决今天的问题,还是在教系统?

  • 每次修复 bug 时,如果不能防止同类问题再次发生,就只完成了一半

  • 每次代码审查如果不能提取出可复用的教训,就是浪费时间

  • 每次成功的工作流程如果不能被记录和复用,就会随着会话结束而消失

如何实践 Compounding Engineering

  1. 将经验沉淀到项目文档

大多数 Coding Agent 都支持读取项目根目录下的特定文件,这是你指导系统的主要途径:

# AGENTS.md

## 代码风格
- 使用 async/await 而非 Promise.then()
- 错误处理必须包含具体的错误类型
- 变量命名遵循 PR #234 确立的模式

## 已知陷阱
- session 模块的 save() 方法是异步的,必须 await
- 不要在循环中调用 API,使用批量接口

## 成功模式
- 新增 API 端点时,参考 PR #241 的错误处理方式
- 测试覆盖率要求参考 PR #219 的反馈

每次你发现一个重复出现的问题或一个有效的解决方案,就把它加入这个文件。Agent 在每次对话开始时都会读取它,自动应用这些经验。

  1. 让 bug 修复产生长期价值

当你修复一个 bug 时,不要只是改代码。问自己:

  • 这类问题能否通过添加 lint 规则来预防?

  • 是否应该在 Rules 或者 AGENTS.md 中记录这个陷阱?

  • 能否编写一个测试来防止回归?

  • 代码审查清单是否需要更新?

一个真正的 bug 修复应该让同类问题再也不会发生。

  1. 从代码审查中提取模式

每次你在审查中指出问题或提出建议,可以考虑:

  • 这个反馈是否适用于未来的类似代码?

  • 是否应该成为项目的编码规范?

  • Agent 能否在下次自动应用这个改进?

如果答案是肯定的,就把它记录下来。让你的审查意见成为系统的永久知识,而不是一次性的对话。

  1. 建立可复用的工作流程

当你找到一个有效的工作模式时,把它进行沉淀:

## 工作流程:添加新的 API endpoint
1. 先编写接口测试(参考 tests/api/example.test.ts)
2. 实现端点,遵循 src/api/users.ts 的模式
3. 添加错误处理,使用 AppError 类
4. 更新 API 文档
5. 运行完整测试套件验证

下次你或 Agent 需要做类似的任务时,可以直接说「按照添加新 API endpoint 的工作流程来做」,系统已经知道该怎么做了。

复利效应

Compounding Engineering 的魔力在于累积效应。第一周,你可能只是记录了几条编码规范。第一个月,你有了一套完整的项目知识库。三个月后,Agent 开始自动应用你从未明确告诉它的模式,因为它从之前的 PR、bug 修复和代码审查中学习了这些。

想象一下:你打开一个 PR,发现 Agent 的评论是「根据 PR #234 的模式修改了变量命名,按照 PR #219 的反馈移除了过度测试,添加了与 PR #241 类似的错误处理」。它学会了你的品味,就像一个聪明的同事,而且还有记录可查。这就是复利,每次修复、每次审查、每次教训都在为未来投资。

对人难的事,对 AI 也难

有一个简单但常被忽视的事实: 如果一个任务对人类开发者来说很难,那么它对当前的 AI 来说大概率也很难。

这听起来显而易见,但它的推论却很深远:所有那些能提升人类开发者体验的工作,对 AI 同样有价值。更好的文档、更清晰的架构、更快的反馈循环,这些「老生常谈」的工程实践,在 AI 时代不仅没有过时,反而变得更加重要。

为什么 AI 面临和人类相似的挑战?

回想一下 LLM 的工作原理:它通过阅读上下文来理解任务,然后生成响应。这个过程和人类开发者阅读代码、理解需求、编写解决方案的过程惊人地相似。

  • 当文档缺失或过时时,人类需要花大量时间阅读源码猜测意图。AI 也一样,它会在代码库中反复搜索,消耗大量上下文空间,最终可能还是理解错误。

  • 当架构混乱、模块边界不清时,人类很难知道该改哪里。AI 也会迷失,它可能改了错误的文件,或者遗漏了需要同步修改的地方。

  • 当测试运行缓慢时,人类倾向于跳过测试。AI 也面临同样的压力,长时间的等待会消耗对话的「耐心」和上下文空间。

值得投资的开发者体验

既然 AI 和人类面临相似的挑战,那么以下这些传统的「开发者体验」优化就具有了双重价值:

更好的文档

# 好的文档对 AI 的价值

## 之前(无文档)
Agent 需要:
1. 读取 5-10 个相关文件
2. 猜测模块的职责和边界
3. 推断 API 的使用方式
4. 可能还会猜错

消耗:大量上下文 + 高错误率

## 之后(有文档)
Agent 只需要:
1. 读取 README 或 API 文档
2. 直接了解正确的使用方式

消耗:少量上下文 + 高准确率

好的文档不仅帮助新人上手,也帮助 AI 快速建立正确的心智模型,比如:

  • 架构决策记录(ADR): 解释「为什么这样设计」,避免 AI 做出违背设计意图的修改

  • API 使用示例: 比纯粹的类型定义更有效

  • 已知陷阱和常见错误 :直接告诉 AI 什么不该做

更清晰的代码结构

当你在纠结要不要花时间重构一个混乱的模块时,考虑一下:这个混乱不仅困扰你,也会困扰每一个试图理解它的 AI。

  • 清晰的命名: processUserData()doStuff() 对 AI 的帮助和对人类一样大

  • 单一职责: 一个做一件事的函数,比一个做十件事的函数更容易被正确修改

  • 显式依赖: 依赖注入比全局变量更容易被 AI 理解和测试

更快的反馈循环

这可能是最容易被低估的一点。Agent Loop 的每一轮都需要等待工具执行完成,如果:

  • 测试套件需要 10 分钟才能跑完 → Agent 要么跳过测试,要么在等待中浪费大量上下文

  • 构建需要 5 分钟 → 每次小改动的验证成本都很高

  • 部署需要 30 分钟 → 几乎不可能让 AI 做端到端的验证

相反,如果你有:

  • 秒级的单元测试 → Agent 可以频繁验证,快速迭代

  • 快速的增量构建 → 改动能立即得到反馈

  • 本地可运行的环境 → 不需要等待远程部署

具体的改进建议

  1. 为 AI 优化你的测试
# 不好:运行所有测试需要 10 分钟
npm test

# 好:可以只运行相关测试,几秒完成
npm test -- --grep "session"
npm test -- src/auth/__tests__/

确保 Agent 知道如何运行局部测试,而不是每次都跑完整套件。

  1. 提供快速的健康检查
# 创建一个快速验证脚本
# scripts/quick-check.sh
#!/bin/bash
echo "Type checking..."
npm run typecheck
echo "Linting changed files..."
npm run lint -- --changed
echo "Running related tests..."
npm test -- --related

让 Agent 可以在几秒内验证基本的正确性。

  1. 文档放在代码旁边
src/
  auth/
    README.md          # 这个模块是做什么的
    login.ts
    login.test.ts
    session/
      README.md        # session 管理的设计决策
      manager.ts

当 AI 浏览目录时,它能立即看到相关文档,而不需要去别的地方找。

  1. 让错误信息更有帮助
// 不好
throw new Error("Invalid input");

// 好
throw new Error(
  `Invalid session token: expected format 'sess_xxx', got '${token}'. ` +
  `See docs/auth.md for token format specification.`
);

好的错误信息帮助 AI(和人类)快速定位问题,而不是盲目搜索。

反过来未必成立,有时需要专门为 AI 设计

需要注意的是,反过来的推论并不总是成立:对人来说简单的事,对 AI 未必简单,例如:

  • 人类可以轻松地「看一眼」就理解一个 UI 的布局问题,但 AI 需要解析整个 DOM 结构

  • 人类可以凭直觉判断「这个改动风险很高」,但 AI 缺乏这种隐性知识

  • 人类可以在飞书里随口问一句就获得关键信息,但 AI 只能依赖文档化的知识

更有趣的是,有时候你需要 专门为 AI 设计 工具和接口,即使这对人类来说可能不是最自然的方式。

LLM 需要专门的信息架构

用户体验领域有一个概念叫「信息架构(Information Architecture)」,它关注的是如何组织和呈现信息,以提供最佳的用户体验,好的信息架构你很少会注意到,但糟糕的信息架构会让你抓狂。当我们观察 Agent 使用现有命令行工具时的困惑和迷失,这强烈表明: 我们现有工具的信息架构对 LLM 来说是不够的。

LLM 是在我们现有的 CLI 工具上训练的,所以它们知道如何使用这些工具。但这些工具是为人类设计的,它们的输出格式、错误信息、交互方式都假设用户是人类。我们需要为 Agent 增强这些工具,提供对 LLM 更有用的上下文,甚至调整输出格式以便 Agent 更好地消费。

API 设计:在信息量和上下文消耗之间取得平衡

当你为 Agent 设计工具接口(比如 MCP 工具)时,需要在两个目标之间取得平衡:

  • 提供足够的信息: 减少 Agent 需要的工具调用次数

  • 避免填满上下文: 不要返回过多无关信息

一个好的实践是:提供便捷函数和底层函数两套 API,并通过工具描述引导 Agent 优先使用便捷函数。

@jsonrpc
def get_global_variable_at(address: str) -> dict:
    """
    Get the value of a global variable at the specified address.
    Automatically identifies the type and returns the best string
    representation.

    This is the preferred method for reading global variables.
    """
    # 智能的、高层的实现
    ...

@jsonrpc
def data_read_byte(address: str) -> int:
    """
    Read the 1 byte value at the specified address.

    Only use this function if `get_global_variable_at` failed.
    """
    # 底层的、更通用的实现
    ...

通过在 docstring 中明确指出「只有在 get_global_variable_at 失败时才使用这个函数」,你可以引导 Agent 优先使用更智能的 API,减少不必要的工具调用。

为 AI 设计友好的命令行输出

如果你观察 Agent 的工作方式,会发现它经常使用类似 head -n100 的方式来限制输出。这看起来是在节省 token,但实际上引入了新问题:Agent 不知道还剩多少行没看到,如果需要完整信息就必须重新运行命令,而重新构建项目是非常耗时的。

一个更好的设计是:让工具主动告诉 Agent 还有多少内容被截断了,甚至缓存输出以便后续获取。

另一个常见问题是 Agent 在错误的目录中执行命令,它会反复尝试,在不同目录之间跳来跳去,浪费大量 token。一个简单的 shell hook 可以帮助它快速定位:

# 在 .zshrc 中添加
command_not_found_handler() {
  echo "zsh: command not found: '$1'"
  echo "zsh: current directory is $PWD"
  return 127
}

现在当命令失败时,Agent 能立即知道自己在哪个目录:

$ npm run build
zsh: command not found: 'npm'
zsh: current directory is /Users/ryan
zsh: Perhaps you meant to run: cd project_directory; npm run build

很多命令行工具都提供了 –json–porcelain 选项,在给 Agent 使用的工具中优先使用这些格式 —— 人类喜欢格式化的输出,但 AI 更擅长解析结构化数据。

用工程约束来「驯服」Agent

Agent 有时会试图走捷径,绕过你设定的规则。与其在 prompt 中反复强调「不要跳过测试」,不如用工程手段来强制执行。

借助 linters、formatters 和 git hooks

让 Agent 频繁提交代码是个好习惯(在 Rules 或者 Agent.md 中告诉它),但它往往会忽视「确保构建不失败」和「修复失败的测试」这样的指令。一个 .git/hooks/pre-commit 脚本可以强制执行项目标准:

#!/bin/bash
# .git/hooks/pre-commit
echo "Running type check..."
npm run typecheck || exit 1
echo "Running linter..."
npm run lint || exit 1
echo "Running tests..."
npm test || exit 1
echo "All checks passed!"

这样,无论 Agent 多么想跳过验证,它都必须通过所有检查才能提交。

拦截 Agent 的「偷懒」行为

Agent 有时很「聪明」,当它发现测试一直失败时,可能会进入这样的循环:

  1. 修改代码

  2. 构建:通过

  3. 运行测试:失败

  4. 尝试修复测试

  5. 修复失败

  6. 说「这个测试之前就是失败的,我用 –no-verify 提交」

然后它就绕过了所有检查!(RL 训练中的 Reword Hacking)

解决方案是用一个 git 命令 wrapper 脚本拦截这种行为:

$ git commit --no-verify
------------------------------------------------------------------
❌ ERROR: Commit Rejected.
------------------------------------------------------------------

🤖 GUIDANCE FOR THE AI AGENT:
You have attempted to bypass the required pre-commit verification.
All code must pass quality checks before it can be committed.

DO NOT BYPASS THE CHECKS. YOU MUST FIX THE UNDERLYING ERRORS.
The pre-commit hook is likely failing. Diagnose and fix the issues.
After all commands complete successfully, attempt the commit again
*without* the '--no-verify' flag.

这个技巧的本质是: 把对 Agent 的指导嵌入到工具的输出中 ,Agent 会读取命令执行的结果,所以错误信息本身就是最好的 prompt 注入点。

每当 Agent 发明新的「偷懒」方式,你就需要堵上这个漏洞。但总体来说,工程约束比 prompt 指令更可靠。

显式优于隐式

// 对人友好,对 AI 可能困难(隐含状态)
client.connect()
client.authenticate(user, password)
client.query("SELECT * FROM users")

// 对 AI 更友好(显式、无状态)
const result = await db.query({
  connection: { host, port },
  auth: { user, password },
  sql: "SELECT * FROM users"
})

有状态的 API 需要 AI 理解和跟踪隐含的状态变化,而无状态的、显式的 API 更容易被正确使用。

结构化的错误信息

# 对人足够,对 AI 可能困惑
Error: Something went wrong. Please try again later.

# 对 AI 更友好
Error [AUTH_TOKEN_EXPIRED]: Token expired at 2024-01-15T10:30:00Z.
Call refreshToken() to obtain a new token.
See: docs/auth.md#token-refresh

人类可以通过上下文推断「something went wrong」是什么意思,但 AI 需要明确的错误代码、原因和解决方案。

AI 眼中的「合理」可能和人类不同

这是一个更微妙的发现:AI 认为合理的代码结构和命名,可能和人类的直觉不一致。

Amp 团队分享过一个有意思的案例:他们让 AI 构建了一个 TUI 框架,过程中开发者一开始会干预 AI 的决策。比如,AI 给一个交换屏幕缓冲区的函数命名为 present() ,开发者觉得这个名字不够直观,改成了 swapScreens()

但随后他们发现了问题:Agent 在后续工作中反复尝试寻找一个叫 present() 的函数,找不到后报告「让我尝试其他方法」,最终才找到 swapScreens() 。这浪费了 token,也浪费了时间。

为什么会这样?因为 Agent 的命名「直觉」来自训练数据的统计概率。 present() 是 Flutter 等框架中双缓冲交换的常见命名,对于 Agent 来说是「最可能」的名字。当开发者用自己的命名覆盖它时,实际上是在对抗 Agent 的统计直觉。Agent 不能再问「过去的我会怎么命名这个」并从权重中找到答案 —— 它必须记住人类的特殊习惯。

后来,开发者决定放手让 Agent 自己决定命名和代码结构。结果呢?Agent 在这个代码库上的工作效率大幅提升。

最终的代码可能看起来有些「奇怪」:

  • 比代码库其他地方更多的 OOP 模式和类

  • 开发者不会选择的命名约定

  • 不太常见的泛型用法

  • 文件布局和人类习惯不同

但 Agent 在这个自己构建的框架中如鱼得水:它知道如何添加滚动条,知道动画系统如何工作,知道键盘快捷键的处理方式 —— 尽管这个框架没有任何文档,甚至无法完整放入一个上下文窗口。

这是一个「由 Agent 构建、为 Agent 优化」的代码库。在这里,东西放在 Agent 的「直觉」认为它们应该在的地方,命名符合 Agent 的统计预期,语法和概念在「统计上最可能」和「实际能编译」之间取得平衡。

启示与权衡

这给我们的启示是:

  • 不要过度干预: 如果你频繁地因为「我觉得这个名字更好」而覆盖 Agent 的决策,可能反而在降低效率

  • 注意「找不到」的信号: 如果 Agent 反复在某个地方「找不到」东西,考虑是否是你的命名和它的预期不一致

  • 拥抱常见模式: 使用广泛使用的设计模式和命名约定,AI 的训练数据中更可能包含这些

  • 模块级的风格隔离: 在某些由 Agent 主导开发的模块中,可以考虑让 Agent 保持它自己的风格

当然,这需要权衡。人类仍然需要阅读和维护代码,完全「AI 风格」的代码可能会让人类开发者困惑。一个务实的做法是:

  1. 把「只存在于人脑中」的知识显式化:写下来,放进文档

  2. 在 Agent 主导的模块中,给 Agent 更多自主权

  3. 在人类频繁维护的核心模块中,保持人类友好的风格

  4. 在工具接口上,提供 AI 友好的选项(如 –json 输出)

投资回报是双倍的

当你投资于更好的文档、更清晰的架构、更快的测试时,你获得的回报是双倍的:

  1. 人类开发者(包括未来的你)会更高效

  2. AI 助手也会更高效

这些投资不会因为 AI 的进步而贬值。相反,随着你越来越多地依赖 AI 来完成任务,这些基础设施的价值只会越来越高。

所以,下次当你犹豫要不要花时间写文档、重构代码、优化测试速度时,记住:你不只是在帮助人类,你也在帮助 AI。而在这个 AI 辅助编程越来越普遍的时代,这是一笔非常划算的投资。

刻意练习:像学乐器一样学习 AI

为什么有些人说「AI 对我不起作用」,而另一些人却能用 AI 完成大量的工作?

这个问题需要区分来看,如果你只在公司的大型私有代码库中使用过 AI,你的体验可能确实不好,那些代码库可能有古老的架构和专有模式,AI 的训练数据中根本没有这些,这是完全可以理解的。但问题是: 你有没有在个人项目中尝试过 AI?你有没有进行刻意的、有意识的练习?

AI 就像一件乐器

以吉他为例,每个人都知道吉他是什么,也都知道如果投入刻意练习,就能变得擅长,但这需要时间、努力和实验。

AI 工具也是一样。那些从 AI 中获益最多的人,都投入了刻意练习。他们不会因为一次失败就下结论说「它给了我完全错误的答案」,然后假设这将是他们的常态体验。

他们会 玩 / Hack 。

AI 工具也有这种潜力,它们的「正确用法」还在被发现中,那些愿意实验、愿意失败、愿意从失败中学习的人,会找到别人看不到的可能性。

如何进行刻意练习

  1. 创造一个干净的实验环境

不要只在工作的复杂代码库中评估 AI 的能力,启动一个个人项目,一个没有历史包袱的新项目。在这里,AI 可以展示它真正的能力,你也可以专注于学习如何与它协作。

  1. 从失败中提取教训

当 AI 给出错误的结果时,不要只是说「它不行」然后放弃。问自己:

  • 我的 prompt 是否足够清晰?

  • 我是否提供了足够的上下文?

  • 我是否在一个对话里塞了太多任务?

  • 这个错误是否揭示了 AI 的某个系统性弱点?

每次失败都是一次学习机会。把它记录下来,下次避免同样的陷阱。

  1. 观察和模仿高手的实践

关注那些公开分享 AI 工作流程的开发者,观看他们的演示,阅读他们的文章,尝试复制他们的技巧。很多时候,差距不在于 AI 工具本身,而在于如何使用它。

  1. 建立肌肉记忆

就像弹吉他需要建立手指的肌肉记忆一样,高效使用 AI 也需要建立某种「肌肉记忆」:

  • 什么时候应该开始新对话?

  • 如何组织一个复杂任务的 prompt?

  • 遇到某类问题时,哪种工具组合最有效?

这些直觉只能通过大量练习获得。没有捷径。

  1. 投入时间

最关键的是:你需要投入真正的时间。不是偶尔试一试,而是持续地、有意识地练习。就像学习任何乐器一样,每天练习 30 分钟,坚持几个月,效果会远超每周练习一次几个小时。

总结

AI 正在以惊人的速度发展。本文讨论的许多「限制」和「问题」:上下文窗口的约束、会话间的失忆、中间区域的性能退化等等,这些很可能在未来几年内被大幅改善甚至解决。每隔几个月,我们就会看到新的突破:更长的有效上下文、更好的长程推理、更可靠的工具使用。

但这并不意味着我们应该等待这一天的到来。恰恰相反, 正是这个充满限制的阶段,给了我们工程师极大的探索和成长空间。 那些现在就开始深入理解 LLM 工作原理、积极实践最佳方法、在限制中寻找创造性解决方案的人,将在 AI 能力进一步释放时获得最大的杠杆效应。

这是一个转型的窗口期。通过刻意练习,我们不仅能提升当下的生产力,更重要的是在构建自己的核心竞争力 —— 理解这些工具的本质,知道何时信任它们、何时质疑它们,以及如何让它们发挥最大价值。

从第一性原理理解 LLM 的本质,理解它们如何「思考」、如何受到上下文的限制、如何在 Agent Loop 中发挥作用,这些知识不会随着具体工具的迭代而过时。

无论你使用的是哪个 Coding Agent,无论模型如何更新换代,这些基础原理都将帮助你更好地与 AI 协作。短对话优于长对话、刻意管理上下文、将经验沉淀为可复用的知识、为 AI 友好的工作环境投资,这些实践同样具有持久的价值。

AI 编程的未来会是什么样子,没有人能确切知道。但有一点是确定的:那些现在就开始认真学习、积极实践、深入理解的人,将最有能力塑造和适应这个未来。

去实验,去失败,去学习。像学习乐器一样学习 AI。

这个过程本身,就是价值所在。


📌 转载信息
转载时间:
2026/1/14 10:48:25

众所周知,Anyrouter 用了 acw_sc__v2 动态 Cookie 做反爬,导致查询余额脚本、签到脚本都需要这个动态的 Cookie,这里记录一下怎么绕过。

虽然站内已经有现成的方案,但是这位佬的无法支持 Cloudflare Worker 部署,着实有些不方便

混淆代码分析

当 Cookie 中不存在 acw_sc__v2 时,返回的 HTML 大致结构如下:

<html> <script>var arg1 = ;
(function(a, c) {
// 大量混淆代码...
}(a0i, ), !(function() {
// 核心逻辑在这里
}()));
function a0i() { /* 字符串数组 */ }
function a0j(a, b) { /* 解码函数 */ }
</script> </html>

代码使用了常见的混淆技术:

  • 字符串数组 + 解码函数
  • 变量名替换为十六进制
  • 控制流平坦化

核心算法提取

去除混淆后,核心逻辑如下:

// 输入:arg1 是一个 40 位的十六进制字符串 var arg1 = ;

// 置换表(固定) var m = [0xf, , , , , , , ,
         0xa, , , , , , , ,
         , 0xd, , 0xb, , , , ,
         0xe, , , , , , , ,
         , , , , , , 0xc, ];

// XOR 密钥(固定) var p = ;

// Step 1: unsbox - 字符重排 var q = [];
for (var x = 0; x < arg1.length; x++) {
    for (var z = 0; z < m.length; z++) {
        if (m[z] == x + 1) {
            q[z] = arg1[x];
        }
    }
}
var u = q.join('');

// Step 2: hexXor - 十六进制异或 var v = '';
for (var i = 0; i < u.length; i += 2) {
    var a = parseInt(u.slice(i, i + 2), 16);
    var b = parseInt(p.slice(i, i + 2), 16);
    var xored = (a ^ b).toString(16);
    if (xored.length == 1) xored = '0' + xored;
    v += xored;
}

// 设置 Cookie document.cookie = 'acw_sc__v2=' + v + '; expires=...';
document.location.reload();

算法详解

Step 1: unsbox(字符重排)

置换表 m 定义了一个映射关系:m[z] = x + 1 表示输出的第 z 位来自输入的第 x 位。

换句话说,m 数组的值表示 "从输入的哪个位置取字符":

  • m[0] = 0xf = 15,输出第 0 位 = 输入第 15 位
  • ,输出第 1 位 = 输入第 35 位

简化实现:

const unsboxed = m.map(i => arg1[i - 1]).join('');

Step 2: hexXor(十六进制异或)

将重排后的字符串与固定密钥逐字节异或:

unsboxed:  XOR key:  result:  

异或运算每次处理 2 个十六进制字符(1 字节):

完整实现(TypeScript)

const XOR_KEY = ;
const UNSBOX_TABLE = [
  0xf, , , , , , , ,
  0xa, , , , , , , ,
  , 0xd, , 0xb, , , , ,
  0xe, , , , , , , ,
  , , , , , , 0xc, 
];

function computeAcwScV2(arg1: string): string {
  // unsbox: 根据置换表重排 const unsboxed = UNSBOX_TABLE.map(i => arg1[i - 1]).join('');

  // hexXor: 与 key 异或 let result = '';
  for (let i = 0; i < 40; i += 2) {
    const xored = (
      parseInt(unsboxed.slice(i, i + 2), 16) ^
      parseInt(XOR_KEY.slice(i, i + 2), 16)
    ).toString(16);
    result += xored.padStart(2, '0');
  }

  return result;
}

// 使用 const arg1 = ;
const cookie = computeAcwScV2(arg1);
 

在 Cloudflare Worker 中使用

由于算法是纯计算,不需要 eval,可以直接在 Cloudflare Worker 中运行:

async function getAcwCookie(targetUrl: URL): Promise<string | null> {
  const resp = await fetch(targetUrl.toString(), { redirect: 'manual' });
  const html = await resp.text();

  // 提取 arg1 const match = html.match(/var\s+arg1\s*=\s*'([0-9a-fA-F]+)'/);
  if (!match) return null;

  const cookie = computeAcwScV2(match[1]);
  return `acw_sc__v2=${cookie}`;
}

Worker 代码

直接往 Cloudflare 里一粘就完事了。不过这里也给佬们部署了一个现成的 Worker:https://anyrouter.devip.ip-ddns.com,佬们可以直接用。但如果爆了额度我可能会删掉

const UPSTREAM = 'https://anyrouter.top';
const XOR_KEY = ;
const UNSBOX_TABLE = [0xf, , , , , , , , 0xa, , , , , , , , , 0xd, , 0xb, , , , , 0xe, , , , , , , , , , , , , , 0xc, ];

export default {
  async fetch(req: Request): Promise<Response> {
    const url = new URL(req.url);
    if (url.pathname === '/') return new Response('OK');

    const targetUrl = new URL(url.pathname + url.search, UPSTREAM);
    const cookie = await getAcwCookie(targetUrl);
    if (!cookie) return new Response('Failed to obtain cookie', { status: 502 });

    const headers = new Headers(req.headers);
    headers.set('cookie', [cookie, req.headers.get('cookie')].filter(Boolean).join('; '));
    headers.set('origin', UPSTREAM);
    headers.set('referer', `${UPSTREAM}/`);
    headers.set('host', new URL(UPSTREAM).host);
    headers.delete('content-length');

    const init: RequestInit = { method: req.method, headers, redirect: 'manual' };
    if (!['GET', 'HEAD'].includes(req.method)) init.body = await req.arrayBuffer();

    const resp = await fetch(targetUrl.toString(), init);
    return new Response(resp.body, { status: resp.status, headers: resp.headers });
  },
};

async function getAcwCookie(targetUrl: URL): Promise<string | null> {
  try {
    const resp = await fetch(targetUrl.toString(), {
      headers: {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
      },
      redirect: 'manual',
    });
    const html = await resp.text();
    const match = html.match(/var\s+arg1\s*=\s*'([0-9a-fA-F]+)'/);
    if (!match) return null;

    // unsbox: 根据置换表重排 const unsboxed = UNSBOX_TABLE.map(i => match[1][i - 1]).join('');
    // hexXor: 与 key 异或 let result = '';
    for (let i = 0; i < 40; i += 2) {
      const xored = (parseInt(unsboxed.slice(i, i + 2), 16) ^ parseInt(XOR_KEY.slice(i, i + 2), 16)).toString(16);
      result += xored.padStart(2, '0');
    }
    return `acw_sc__v2=${result}`;
  } catch {
    return null;
  }
}

关于签到脚本

部署到 Cloudflare Worker 后,把所有对 Anyrouter 的请求全部替换成该 Worker 地址,即可。自动签到脚本需要 Cookie,目测有效期为一个月

站内已经有很多签到脚本了,佬们可以任意选择。如果后面有空我可能会写一个,但是感觉重复造轮子意义不大。

服务器
服务器 + 通知
Worker
二合一

参考资料

搞一个 AnyRouter 动态 cookie 验证(内含签到 + cc switch 查询余额) - 开发调优 - LINUX DO
雪球 JS 逆向:阿里系加密 acw_sc__v2 和 反 debugger_ 雪球网逆向 - CSDN 博客


📌 转载信息
转载时间:
2026/1/14 10:48:18

自己以前学过一些 OI,初来 L 站,发现有一个算法标签,遂试图把一些文章发出来水点经验之类的

应该是无人在意的,写的也很浅略,如果发这类东西违反了某个我没有注意到的站规请告诉我……

Kruskal 重构树

说实话,这个东西还是过于冷门了,感觉刷题练这个真心没啥用。

2025年10月:我承认我以前说话太大声了,连着几次考试都能用这个。

什么是 Kruskal 重构树

对于一张无向图,我们可以通过 Kruskal 算法求出其最小 / 最大生成树。

在求最小 / 最大生成树的时候,设两点 𝑥𝑦 连边,则新建一个节点 𝑝,连接到 𝑥𝑦,将 𝑝 的权值设为原边的权值。即用一个点替换了原本应该连接的边。

这是原图:

找到它的最小生成树:

按边权从小到大建立 Kruskal 重构树:

Kruskal 重构树的性质

  1. 这是一颗二叉树。
  2. 按最小生成树建立就是大根堆,反之。
  3. 所有叶子节点是真实存在的点,非叶子节点是虚拟节点。
  4. 原图中,𝑥𝑦 之间所有路径的最大边权的最小值,是最小生成树上两点路径间的最大边权,也是 Kruskal 重构树上两点的 LCA(最近公共祖先)。

Kruskal 的基本 trick

Kruskal 最简单的应用就是直接求解最大边权最小值 / 最小边权最大值。

Kruskal 的进阶 trick

在 Kruskal 中,任何一个非叶子节点都可以被视为一种 “瓶颈”,即如果 LCA 作为瓶颈如果没有被限制,则其子树一定没有被限制。通过这一点,引申出了许多 Kruskal 重构树的应用。

事实上,这些都可以归类为树上 min-max 问题。

参考代码

void Kruskal(){
    sort(s+1,s+m+1,cmp);

    cnt=n;

    for(int i=1;i<=m;i++){
        int x=s[i].x,y=s[i].y,k=s[i].k;
        x=kru.find(x),y=kru.find(y);

        if(x==y) continue;

        cnt++;
        add(x,cnt),add(y,cnt);
        val[cnt]=k;
        kru.merge(x,cnt);
        kru.merge(y,cnt);
    }
}

例题

P1967 [NOIP 2013 提高组] 货车运输 - 洛谷

这是最简单的应用,一眼就能看出其要求是求出最小边权最大值。

应该建立最大生成树时建立重构树,然后求出 LCA。

注意本题图可能不连通,需要分开处理 LCA。

参考代码(Kruskal 重构树 + Tarjan LCA)

复杂度瓶颈在于排序,为 𝑂(𝑛log𝑛)

P2245 星际导航 - 洛谷

同样是最简单的应用,这回要求求出最大边权最小值。

和上题相同,只是变成了建立最小生成树。

可以用此题继续熟练。

P9638 「yyOI R1」youyou 的军训 - 洛谷

Kruskal 重构树本身可以作为 “瓶颈” 一类的限制。

可以考虑建立一颗最大边权的 Kruskal 重构树,此时,如果 一个结点的权值符合要求,则其子树也符合要求。

如果不符合要求,这个点会被断开,即断开朋友关系。

可以通过一次 DFS 统计叶子节点数量来回答问题 2

对于问题 3,除了修改本身,我还使用 vector 维护了所有的修改操作,在操作 1 时进行还原。

参考代码(重构树 + 倍增 LCA)


📌 转载信息
转载时间:
2026/1/14 10:48:09

// ==UserScript==
// @name         工商银行纪念币预约自动填表
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  自动填充工商银行纪念币预约表单(含地区选择)
// @match        *://*.icbc.com.cn/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';


    const CONFIG = {
        custName: '',           // 客户姓名
        paperNum: '',      // 证件号码
        mobileno: '',          // 手机号码
        exchangeQuanlity: '20',        // 预约数量
        province: '天津市',            // 省份
        city: '天津市',                // 城市
        district: '静海区',            // 区县
        branch: '',   // 网点
        exchangeTime: '2026-01-20'    // 兑换时间
    };
    // ================================================

    function fillInput(placeholder, value) {
        const inputs = document.querySelectorAll('input.el-input__inner');
        for (const input of inputs) {
            if ((input.placeholder || '').includes(placeholder)) {
                input.value = value;
                input.dispatchEvent(new Event('input', { bubbles: true }));
                return true;
            }
        }
        return false;
    }

    async function selectDropdown(placeholder, optionText) {
        const inputs = document.querySelectorAll('input.el-input__inner[readonly]');
        for (const input of inputs) {
            if ((input.placeholder || '').includes(placeholder)) {
                input.click();
                await new Promise(r => setTimeout(r, 1));
                const items = document.querySelectorAll('.el-select-dropdown__item:not(.is-disabled)');
                for (const item of items) {
                    if (item.textContent.includes(optionText)) {
                        item.click();
                        await new Promise(r => setTimeout(r, 1));
                        return true;
                    }
                }
            }
        }
        return false;
    }

    async function fillForm() {
        // 填充文本输入框
        fillInput('客户姓名', CONFIG.custName);
        fillInput('证件号码', CONFIG.paperNum);
        fillInput('手机号码', CONFIG.mobileno);
        fillInput('预约数量', CONFIG.exchangeQuanlity);

        // 填充下拉选择框
        await selectDropdown('省份', CONFIG.province);
        await selectDropdown('城市', CONFIG.city);
        await selectDropdown('区县', CONFIG.district);
        await selectDropdown('网点', CONFIG.branch);
        await selectDropdown('兑换时间', CONFIG.exchangeTime);

        console.log('表单已填充');
    }

    function addButton() {
        const btn = document.createElement('button');
        btn.textContent = '手动填充';
        btn.style.cssText = 'position:fixed;top:10px;right:10px;z-index:9999;padding:10px 20px;background:#409EFF;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:14px;';
        btn.onclick = fillForm;
        document.body.appendChild(btn);
    }

    window.addEventListener('load', () => {
        addButton();
        setTimeout(fillForm, 1500);
    });
})();

📌 转载信息
原作者:
sam5440
转载时间:
2026/1/14 10:47:28

微舆 - 大热项目

20 岁的开发者

怎样利用 claude skills 简单的实现一个适合自己的 “微舆”?

“微舆” 核心功能:

1 用户只需像聊天一样提出分析需求,智能体开始全自动分析 国内外 30 + 主流社媒 与 数百万条大众评论。
2 生成的研究报告
3 深度解析抖音、快手等短视频内容
4 数据可视化,数据分析

我们也开发一个微舆

同样的测试:(小鹏汽车(XPENG)智能机器人 “IRON” 舆情报告)
提示语:

深度调研: 小鹏汽车(XPENG)智能机器人 “IRON” 舆情报告 ,
包括媒体新闻,社交媒体帖子,评论等等,写一个完整全面的报告




github 地址:

  🔍 核心发现

1. 整体舆情积极正面: 正面评价占比39.2%,负面仅2.0%
2. 抖音是主阵地: 占76.5%讨论内容
3. 技术展示受关注: "无衣状态测试"视频获4.3万点赞、1.5万评论
4. 产品联动话题: 与小鹏P7+、陆地航母的联动讨论热烈

  📁 生成文件

1. 调研脚本: xpeng_iron_robot_research.py
2. 原始数据: xpeng_
iron_robot_research_raw_data.json 3. 简化报告: xpeng_iron_robot_sentiment_report.md 4. 详细报告: xpeng_iron_robot_sentiment_report_detailed.md ⭐

详细报告包含:各平台分析、情感分析、讨论热点、竞品对比、总结建议等8个章节的完整分析。

未完待续

现在才是最简单的基础,接下来一步一步完善项目…
等佬!你的 star 就是我的激励!


📌 转载信息
转载时间:
2026/1/14 10:45:47

perplexity mcp docker 一键部署(pro 账号无限额度 ai 搜索 mcp)继续讨论:

当监测到账号过期,执行 tg 推送。

  • enable 控制是否启用账号监测
  • interval 控制多久监测
  • test question: 测试连通性使用的问题
  • tg-bot-token:从 `@BotFather` 获取
  • tg-chat_id:从 @get_id_bot 获取

配置文件 token_pool_config.json 新增 heart_beat 字段控制心跳检测相关功能

{ "heart_beat": { "enable": true, "question": "现在是农历几月几号?", "interval": 6, "tg_bot_token": "12345678", "tg_chat_id": "12345678" }, "tokens": [...] } 

仓库地址:


📌 转载信息
原作者:
shan_CW
转载时间:
2026/1/14 10:45:35

问题背景

这个问题困扰了我很久,今天终于解决了,分享出来希望能帮到遇到类似情况的佬友。

我的 OpenCode 启动速度非常慢,体验远不如 Claude Code 那般丝滑。一开始以为是 OpenCode 本身的问题,但搜遍了网上的经验帖,似乎没人遇到过这种情况。

排查过程

通过 opencode --print-logs 打印日志,发现时间基本都耗在插件安装上:

阶段耗时问题
oh-my-opencode@latest 安装12.89s每次启动都重新下载
@tarquinen/opencode-dcp@latest 安装13.91s每次启动都重新下载
MCP 服务器初始化~3-4s正常
models.dev 超时2.8s网络问题
总计~29s

根本原因

日志中有这么一条关键信息:

service=bun pkg=oh-my-opencode version=latest installing package using Bun's default registry resolution

问题就出在这里:插件配置使用了 latest 版本,导致每次启动都触发 bun add --force,重新从 npm 下载

解决方法

编辑 ~/.config/opencode/package.json,将 latest 替换为具体版本号:

{ "dependencies": { "@opencode-ai/plugin": "1.1.16", "oh-my-opencode": "2.14.0", "@tarquinen/opencode-dcp": "1.1.6", "opencode-antigravity-auth": "1.2.8", "opencode-copilot-auth": "0.0.12", "opencode-anthropic-auth": "0.0.8", "@franlol/opencode-md-table-formatter": "0.0.3" } } 

然后执行:

cd ~/.config/opencode
bun install

效果

启动时间从 ~29s 降到~3-5s(仅剩 MCP 初始化时间)。


附:个人使用的插件

最后分享一下我个人使用的插件配置,供佬友们参考:

{ "plugin": [ "oh-my-opencode", "opencode-antigravity-auth@1.2.8", "opencode-openai-codex-auth", "@tarquinen/opencode-dcp@1.1.6", "@franlol/opencode-md-table-formatter@0.0.3" ] } 

插件功能说明

  • oh-my-opencode
    多 Agent 编排插件,提供 Sisyphus Agent、子 Agent 调度、Skill 技能、MCP 集成等功能

  • opencode-antigravity-auth
    Antigravity(Google IDE)OAuth 认证,可使用 Gemini 3 Pro、Claude Opus 4.5 Thinking 等模型

  • opencode-openai-codex-auth
    OpenAI Codex OAuth 认证,使用 ChatGPT Plus/Pro 订阅额度,无需消耗 API credits

  • @tarquinen/opencode-dcp
    动态上下文剪枝(Dynamic Context Pruning),自动清理过时的工具输出,优化 token 使用

  • @franlol/opencode-md-table-formatter
    Markdown 表格自动格式化,支持隐藏模式

更多插件

想探索更多社区插件?可以访问 OpenCode 官方生态页面:

Ecosystem | OpenCode

这里收录了社区构建的各类插件、项目和 Agent 配置,包括:

  • 认证插件:Gemini、Codex、Antigravity 等多种 OAuth 认证方案

  • 效率工具:动态上下文剪枝、快速代码编辑、桌面通知等

  • 编辑器集成:Neovim、Obsidian、VS Code 等

  • Agent 增强:模块化 Agent、结构化工作流等


📌 转载信息
原作者:
YuChenghhh
转载时间:
2026/1/14 10:45:17

HidenCloud 全自动续期 & 账单自动支付脚本 (青龙面板版)

这是一个用于 HidenCloud 免费服务器自动续期和自动支付账单的青龙面板脚本。

原帖:https://linux.do/t/topic/1393805
感谢 Y 佬的教程

HidenCloud 的免费机器虽然好用,但机制比较繁琐,本脚本实现了全流程自动化,自动续期 + 支付 + 自动读写 cookie。

建议手动续期 84 天后,每 7 天执行一次,避免错过续期窗口


功能特点

  1. 全流程自动化:自动识别账号下的所有服务 → 自动提交续期请求 → 自动跳转账单页 → 自动完成 0 元支付
  2. 智能支付逻辑
    • 脚本会自动解析账单页面 HTML。
    • 动态提取隐藏的支付参数。
  3. Cookie 自动续命 (持久化)
    • 脚本运行期间会自动捕获服务器返回的新 Cookie。
    • 将最新 Cookie 保存到本地 hiden_cookies.json 文件。
    • 优势:只要脚本每天运行,理论上无需再手动更新环境变量中的 Cookie。
  4. 多账号支持:支持无限个账号,通过换行或 & 符号分隔。
  5. 防检测机制
    • 内置随机延迟(3-8 秒),模拟真人操作。
    • 伪装完整的 Chrome 浏览器 Headers,降低被 Cloudflare 拦截的风险。
  6. 消息推送:对接青龙面板的通知系统,任务完成后发送续期结果。


使用方法

1. 准备工作

确保你的青龙面板已安装以下 Node.js 依赖(在 依赖管理NodeJs 中添加):

  • axios
  • cheerio

2. 添加脚本

在青龙面板 脚本管理 中新建脚本,名称随意(例如 hiden_renew.js),将代码完全粘贴进去。

自定义续期天数:脚本第 23 行,将数字 10 修改为你想要的天数即可。
const RENEW_DAYS = 10;

青龙脚本 hiden_renew.js
/*
new Env('HidenCloud 自动续期-毕业版');
cron: 0 0 10 * * ?
checks: 自动续期、自动支付、Cookie自动持久化、消息推送
*/

const axios = require('axios');
const cheerio = require('cheerio');
const fs = require('fs');
const path = require('path');

// 尝试加载 notify,如果没有也不影响运行
let sendNotify = () => {};
try {
    const notify = require('./sendNotify');
    sendNotify = notify.sendNotify;
} catch (e) {
    console.log('未找到 sendNotify,跳过推送');
}

// 环境变量
const HIDEN_COOKIES_ENV = process.env.HIDEN_COOKIE ? process.env.HIDEN_COOKIE.split(/[&\n]/) : [];
const RENEW_DAYS = 10;
const CACHE_FILE = path.join(__dirname, 'hiden_cookies.json');

// 汇总消息
let summaryMsg = '';

const sleep = (min = 3000, max = 8000) => {
    const delay = Math.floor(Math.random() * (max - min + 1)) + min;
    return new Promise(resolve => setTimeout(resolve, delay));
};

// 本地缓存管理
const CacheManager = {
    load() {
        if (fs.existsSync(CACHE_FILE)) {
            try {
                return JSON.parse(fs.readFileSync(CACHE_FILE, 'utf8'));
            } catch (e) {
                console.log('读取缓存文件失败,将重新创建');
            }
        }
        return {};
    },
    save(data) {
        fs.writeFileSync(CACHE_FILE, JSON.stringify(data, null, 2));
    },
    get(index) {
        const data = this.load();
        return data[index] || null;
    },
    update(index, cookieStr) {
        const data = this.load();
        data[index] = cookieStr;
        this.save(data);
        console.log(`💾 [账号 ${index + 1}] 最新 Cookie 已保存到本地缓存`);
    }
};

class HidenCloudBot {
    constructor(envCookie, index) {
        this.index = index + 1;
        this.envCookie = envCookie;
        this.cookieData = {};
        this.logMsg = []; // 存储该账号的日志用于推送
        
        // 优先尝试读取缓存
        const cachedCookie = CacheManager.get(this.index - 1);
        if (cachedCookie) {
            console.log(`[账号 ${this.index}] 发现本地缓存 Cookie,优先使用...`);
            this.parseCookieStr(cachedCookie);
        } else {
            console.log(`[账号 ${this.index}] 使用环境变量 Cookie...`);
            this.parseCookieStr(envCookie);
        }

        this.commonHeaders = {
            'Host': 'dash.hidencloud.com',
            'Connection': 'keep-alive',
            'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
            'sec-ch-ua-mobile': '?0',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
            'Referer': 'https://dash.hidencloud.com/',
        };

        this.client = axios.create({
            baseURL: 'https://dash.hidencloud.com',
            maxRedirects: 0, 
            validateStatus: status => status >= 200 && status < 500,
            timeout: 30000 
        });
        
        this.services = [];
        this.csrfToken = ''; 
    }

    log(msg) {
        console.log(`[账号 ${this.index}] ${msg}`);
        this.logMsg.push(msg);
    }

    parseCookieStr(str) {
        if (!str) return;
        str.split(';').forEach(pair => {
            const idx = pair.indexOf('=');
            if (idx > 0) {
                const key = pair.substring(0, idx).trim();
                const val = pair.substring(idx + 1).trim();
                if (!['path', 'domain', 'expires', 'httponly', 'secure', 'samesite'].includes(key.toLowerCase())) {
                    this.cookieData[key] = val;
                }
            }
        });
    }

    updateCookiesFromResponse(headers) {
        const setCookie = headers['set-cookie'];
        if (setCookie) {
            setCookie.forEach(sc => {
                const firstPart = sc.split(';')[0];
                const idx = firstPart.indexOf('=');
                if (idx > 0) {
                    const key = firstPart.substring(0, idx).trim();
                    const val = firstPart.substring(idx + 1).trim();
                    this.cookieData[key] = val;
                }
            });
            // 每次更新 Cookie 都保存到本地
            CacheManager.update(this.index - 1, this.getCookieStr());
        }
    }

    getCookieStr() {
        return Object.keys(this.cookieData).map(k => `${k}=${this.cookieData[k]}`).join('; ');
    }

    async request(method, url, data = null, extraHeaders = {}) {
        let currentUrl = url;
        let methodToUse = method;
        let finalResponse = null;

        const requestHeaders = {
            ...this.commonHeaders,
            ...extraHeaders,
            'Cookie': this.getCookieStr()
        };

        if (methodToUse === 'POST' && !requestHeaders['Content-Type']) {
            requestHeaders['Content-Type'] = 'application/x-www-form-urlencoded';
        }

        try {
            const res = await this.client({
                method: methodToUse,
                url: currentUrl,
                headers: requestHeaders,
                data: data
            });
            
            this.updateCookiesFromResponse(res.headers);
            res.finalUrl = currentUrl; 
            finalResponse = res;

            if (res.status === 301 || res.status === 302) {
                const location = res.headers['location'];
                if (location) {
                    this.log(`🔄 重定向 -> ${location}`);
                    currentUrl = location.startsWith('http') ? location : `https://dash.hidencloud.com${location.startsWith('/') ? '' : '/'}${location}`;
                    return this.request('GET', currentUrl);
                }
            }
            finalResponse.finalUrl = currentUrl;
            return finalResponse;
        } catch (err) {
            throw err;
        }
    }

    extractTokens($) {
        const metaToken = $('meta[name="csrf-token"]').attr('content');
        if (metaToken) this.csrfToken = metaToken;
    }

    async init() {
        this.log('正在验证登录状态...');
        try {
            const res = await this.request('GET', '/dashboard');
            
            // 检查失效
            if (res.headers.location && res.headers.location.includes('/login')) {
                 this.log('❌ 当前 Cookie 已失效');
                 return false;
            }

            const $ = cheerio.load(res.data);
            this.extractTokens($);

            // 解析服务列表
            $('a[href*="/service/"]').each((i, el) => {
                const href = $(el).attr('href');
                const match = href.match(/\/service\/(\d+)\/manage/);
                if (match) {
                    this.services.push({ id: match[1], url: href });
                }
            });
            this.services = this.services.filter((v, i, a) => a.findIndex(t => t.id === v.id) === i);

            this.log(`✅ 登录成功,发现 ${this.services.length} 个服务。`);
            return true;
        } catch (e) {
            this.log(`❌ 初始化异常: ${e.message}`);
            return false;
        }
    }

    // 重置为环境变量 Cookie (用于缓存失效时重试)
    resetToEnv() {
        this.cookieData = {};
        this.parseCookieStr(this.envCookie);
        console.log(`[账号 ${this.index}] 切换回环境变量原始 Cookie 重试...`);
    }

    async processService(service) {
        await sleep(2000, 4000);
        this.log(`>>> 处理服务 ID: ${service.id}`);

        try {
            const manageRes = await this.request('GET', `/service/${service.id}/manage`);
            const $ = cheerio.load(manageRes.data);
            const formToken = $('input[name="_token"]').val();

            this.log(`提交续期 (${RENEW_DAYS}天)...`);
            await sleep(1000, 2000); 

            const params = new URLSearchParams();
            params.append('_token', formToken);
            params.append('days', RENEW_DAYS);

            const res = await this.request('POST', `/service/${service.id}/renew`, params, {
                'X-CSRF-TOKEN': this.csrfToken,
                'Referer': `https://dash.hidencloud.com/service/${service.id}/manage`
            });
            
            if (res.finalUrl && res.finalUrl.includes('/invoice/')) {
                this.log(`⚡️ 续期成功,前往支付`);
                await this.performPayFromHtml(res.data, res.finalUrl);
            } else {
                this.log('⚠️ 续期后未跳转,检查列表...');
                await this.checkAndPayInvoices(service.id);
            }

        } catch (e) {
            this.log(`处理异常: ${e.message}`);
        }
    }

    async checkAndPayInvoices(serviceId) {
        await sleep(2000, 3000);
        try {
            const res = await this.request('GET', `/service/${serviceId}/invoices?where=unpaid`);
            const $ = cheerio.load(res.data);
            
            const invoiceLinks = [];
            $('a[href*="/invoice/"]').each((i, el) => {
                const href = $(el).attr('href');
                if (href && !href.includes('download')) invoiceLinks.push(href);
            });

            const uniqueInvoices = [...new Set(invoiceLinks)];
            if (uniqueInvoices.length === 0) {
                this.log(`✅ 无未支付账单`);
                return;
            }

            for (const url of uniqueInvoices) {
                await this.paySingleInvoice(url);
                await sleep(3000, 5000); 
            }
        } catch (e) {
            this.log(`查账单出错: ${e.message}`);
        }
    }

    async paySingleInvoice(url) {
        try {
            this.log(`📄 打开账单: ${url}`);
            const res = await this.request('GET', url);
            await this.performPayFromHtml(res.data, url);
        } catch (e) {
            this.log(`访问失败: ${e.message}`);
        }
    }

    async performPayFromHtml(html, currentUrl) {
        const $ = cheerio.load(html);
        
        let targetForm = null;
        let targetAction = '';
        
        $('form').each((i, form) => {
            const btnText = $(form).find('button').text().trim().toLowerCase();
            const action = $(form).attr('action');
            if (btnText.includes('pay') && action && !action.includes('balance/add')) {
                targetForm = $(form);
                targetAction = action;
                return false; 
            }
        });

        if (!targetForm) {
            this.log(`⚪ 页面未找到支付表单 (可能已支付)。`);
            return;
        }

        const payParams = new URLSearchParams();
        targetForm.find('input').each((i, el) => {
            const name = $(el).attr('name');
            const value = $(el).val();
            if (name) payParams.append(name, value || '');
        });

        this.log(`👉 提交支付...`);
        
        try {
            const payRes = await this.request('POST', targetAction, payParams, {
                'X-CSRF-TOKEN': this.csrfToken,
                'Referer': currentUrl
            });

            if (payRes.status === 200) {
                 this.log(`✅ 支付成功!`);
            } else {
                this.log(`⚠️ 支付响应: ${payRes.status}`);
            }
        } catch (e) {
            this.log(`❌ 支付失败: ${e.message}`);
        }
    }
}

(async () => {
    if (HIDEN_COOKIES_ENV.length === 0) {
        console.log('❌ 未配置环境变量 HIDEN_COOKIE');
        return;
    }
    
    console.log(`=== HidenCloud 续期脚本启动 (账号数: ${HIDEN_COOKIES_ENV.length}) ===\n`);

    for (let i = 0; i < HIDEN_COOKIES_ENV.length; i++) {
        const bot = new HidenCloudBot(HIDEN_COOKIES_ENV[i], i);
        
        // 第一次尝试(可能用的是缓存)
        let success = await bot.init();
        
        // 如果失败,且当前用的是缓存,则回退到环境变量重试
        if (!success && CacheManager.get(i)) {
            bot.resetToEnv();
            success = await bot.init();
        }

        if (success) {
            for (const svc of bot.services) {
                await bot.processService(svc);
            }
            summaryMsg += `账号 ${i + 1}: 成功续期 ${bot.services.length} 个服务\n`;
        } else {
            summaryMsg += `账号 ${i + 1}: 登录失败,请更新 Cookie\n`;
        }
        
        console.log('\n----------------------------------------\n');
        if (i < HIDEN_COOKIES_ENV.length - 1) await sleep(5000, 10000);
    }

    // 发送推送
    if (summaryMsg) {
        await sendNotify('HidenCloud 续期报告', summaryMsg);
    }
})();

3. 设置环境变量

环境变量 中添加变量:

变量名必填说明
HIDEN_COOKIE你的 HidenCloud 面板 Cookie

如何获取 Cookie:

  1. 浏览器打开并登录 HidenCloud Dashboard
  2. F12 打开开发者工具,点击 网络 (Network) 标签。
  3. 刷新页面,点击第一个请求( dashboardmanage)。
  4. 在右侧 请求头 (Request Headers) 中找到 Cookie,复制后面的一长串内容。

多账号设置:
如果有多个账号,直接新建多个同名变量 HIDEN_COOKIE,或者在一个变量值里用 & 或换行符分隔。

4. 设置定时任务

建议手动续期 84 天后,每 7 天执行一次,避免错过续期窗口。
Cron 表达式示例:0 10 */7 * * (每 7 天上午 10 点执行)


消息推送配置说明

本脚本已深度集成青龙面板自带的通知系统 (sendNotify.js)。你只需要在青龙面板配置好推送方式,脚本运行结束后就会自动发送报告。

支持的推送渠道

微信 (Server 酱 / 企业微信)、Telegram、钉钉、飞书、Bark、PushPlus 等青龙支持的所有渠道。

配置步骤

  1. 打开青龙面板 → 系统设置通知设置
  2. 选择你喜欢的推送方式(例如 PushPlusTelegram)。
  3. 填入相应的 Token 或 Key,点击保存并测试。
  4. 无需修改脚本代码。脚本会自动检测青龙的通知配置。

推送效果预览

任务完成后,你会收到类似如下的通知:

HidenCloud 续期报告

账号 1: 成功续期 2 个服务
账号 2: 成功续期 1 个服务
账号 3: 登录失败,请更新 Cookie


脚本逻辑说明

  1. 初始化:脚本启动时,优先读取本地缓存文件 hiden_cookies.json 中的 Cookie。如果缓存不存在或失效,则回退使用环境变量 HIDEN_COOKIE
  2. 检测:登录 Dashboard,自动扫描账号下所有活跃的服务 ID。
  3. 续期:进入管理页面,获取 CSRF Token,提交续期 10 天的请求。
  4. 支付
    • 续期成功后,脚本会自动检测是否跳转到了 Invoice 页面。
    • 如果是,直接在当前页面提取表单参数并提交支付。
    • 如果未跳转,脚本会额外检查 “未支付账单” 列表,确保没有漏网之鱼。
  5. 保存:运行结束时,将最新的有效 Cookie 写入本地缓存,供下次使用。


常见问题

Q: 提示 403 Forbidden / Cloudflare 拦截?
A: 这通常是因为你的青龙面板服务器 IP (如阿里云、腾讯云数据中心 IP) 被 Cloudflare 拉黑了。

  • 解决方法:尝试在本地电脑(家庭宽带)运行脚本,或者给青龙面板配置代理。

Q: 为什么日志显示 “支付成功” 但网页看还是未支付?
A: HidenCloud 后端偶尔有延迟。只要日志显示 Status 200支付成功,通常稍等几分钟刷新网页即可变更为 Paid。

Q: 必须要手动抓包 Cookie 吗?
A: 第一次必须手动抓取。之后脚本会自动维护 Cookie,除非服务器强制登出所有会话,否则不需要频繁更新。


免责声明

本脚本仅供学习交流使用,请勿用于非法用途。使用本脚本产生的任何后果由使用者自行承担。



📌 转载信息
转载时间:
2026/1/14 10:45:15

各位佬友好!

最近几年每次过年 / 结婚季 / 满月酒都被人情账搞得头大:

  • 这次随了谁多少?
  • 上次谁给了我红包要记得回?
  • 到底谁欠我钱 / 我欠谁钱?
    手机备忘录记一记就乱,Excel 又嫌麻烦……

于是花了点时间用 Flutter Vibe Conding 了个小工具:随礼记

项目地址

下载体验地址

只做最核心的三件事,不臃肿:

  • 收礼 / 随礼 快速记录(自定义数字键盘 + 常见金额快捷)
  • 按人自动算往来余额(谁欠谁,一眼看清)
  • 本地存储 + 一键导出 CSV/Excel(不上云,数据安全)

其他:

  • 按事由分类(婚礼 / 满月 / 乔迁 / 升学 / 生日 / 葬礼 / 其他)
  • 简单联系人管理(姓名 + 备注)
  • Android/iOS/Web 都能跑

欢迎有类似痛点的佬友试用一下~

觉得好用就 star 一下,觉得可以更简洁 / 有 bug 就直接 issue/PR


📌 转载信息
原作者:
user_tz
转载时间:
2026/1/14 10:45:06

Claude Code Multi-Agent

【个人工作流开源】Claude-Code-Multi-Agent 涵盖 300+ 超智能开发调优专家,完整的 Hooks 调优系统1
【个人工作流开源】Claude-Code-Multi-Agent 涵盖 300+ 超智能开发调优专家,完整的 Hooks 调优系统2


让 Claude Code 拥有项目感知能力的智能开发框架

更多详细的介绍内容与文档,请详见:
Claude-Code-Multi-Agent/README.md at master · Prorise-cool/Claude-Code-Multi-Agent

如果有功能上的疑问,请在 github 上提交 PR,此项目已内置 MCP

因为是个人使用的工作流,开源出来如果有无法复现的情况请反馈给我及时优化,欢迎提交 PR,分享更多实用的 SKILLS 技能

这是什么?

Claude Code Multi-Agent 是一个为 Claude Code 设计的智能开发框架,通过 Hooks 系统 在会话生命周期中自动执行智能操作,让 Claude Code 从 “通用聊天助手” 升级为 “懂你项目的专业开发伙伴”。

核心定位

这不是一个插件生态,而是一个 Claude Code 的专属工作空间
你需要将仓库克隆后,将你的项目(或初始化项目)放在此文件夹中,即可享受智能 Hooks 定义以及 300+ Skills 方案。


解决了什么痛点?

痛点 1:Claude Code 缺乏项目感知能力

问题:Claude Code 默认不知道你的项目是什么类型、使用什么框架、有什么依赖。每次都需要你手动描述项目背景。

解决:通过 Ollama 智能引擎 自动检测项目类型(Python/Node.js/Java 等)、识别框架(Django/FastAPI/React 等),并在会话启动时自动注入项目上下文。

痛点 2:需要手动配置各种工具和提示词

问题:每次使用 Claude Code 都需要:

  • 手动告诉它项目结构
  • 手动配置 Git 工作流
  • 手动编写提示词模板
  • 手动管理文档更新

解决零配置启动 - 克隆即用,所有配置通过 Hooks 自动完成。提示词模板化存储在 prompts.json,支持团队协作和版本控制。

痛点 3:缺乏智能的意图分析和技能推荐

问题:Claude Code 不知道什么时候该调用什么工具,也不知道有哪些可用的专家技能。

解决智能意图分析 - 自动判断任务复杂度,推荐合适的 MCP 工具(Sequential Thinking、Task Manager 等)和 Skills(后端专家、测试专家等)。

痛点 4:文档维护繁琐且容易遗忘

问题:代码改了,文档忘了更新。项目知识散落在聊天记录中,无法沉淀。

解决自动文档维护 - 每次代码修改后,强制提示更新 DEVELOPMENT.mdKNOWLEDGE.mdCHANGELOG.md,确保文档与代码同步。


核心优势

所有判断逻辑通过本地部署 Ollama 完成,无需编写复杂的规则引擎。提示词模板化存储在 prompts.json,支持持续调优和版本控制。

300+ Skills 专家智能体

会话启动时自动扫描并加载所有 Skills,包括:

  • 后端专家 (/backend-specialist) - Django、FastAPI、Spring Boot 等
  • 前端专家 (/frontend-specialist) - React、Vue、Next.js 等
  • 测试专家 (/testing-specialist) - 单元测试、集成测试、E2E 测试
  • 安全专家 (/security-specialist) - OWASP Top 10、安全审计
  • 架构专家 (/architecture-specialist) - 系统设计、微服务架构
  • DevOps 专家 (/devops-specialist) - CI/CD、容器化、云部署
  • … 还有更多

自动文档维护系统

强制维护三个核心文档:

  • DEVELOPMENT.md - 开发工作文档(任务状态、进度跟踪)
  • KNOWLEDGE.md - 项目知识库(技术决策、代码模式)
  • CHANGELOG.md - 变更日志(版本记录、功能变更)

文档在会话启动时自动注入上下文,替代 Memory MCP,避免上下文爆炸。

Git 工作流智能集成

自动检测 Git 仓库配置,提示分支策略(github-flow /git-flow),确保团队协作规范。

零配置启动

基于 uv 的依赖管理,无需手动安装 Python 包。克隆项目 → 配置环境变量 → 启动 Claude Code,即可使用。


使用示例

示例 1:自动项目检测

场景:你打开了一个新的 Python 项目,想了解项目结构。

操作:直接打开 Claude Code,系统会自动:

  1. 检测项目类型(Python + FastAPI)
  2. 识别框架和依赖
  3. 加载相关 Skills(后端专家、测试专家等)
  4. 初始化项目文档

结果:Claude Code 立即了解你的项目,无需手动介绍。


示例 2:调用专家 Skills

场景:需要设计 RESTful API,但不确定最佳实践。

操作:在 Claude Code 中输入:

/backend-specialist 设计用户认证的 RESTful API

结果:Claude Code 以 “后端专家” 身份回答,参考 FastAPI/Django 最佳实践,提供:

  • RESTful 资源设计
  • HTTP 方法选择
  • 状态码定义
  • 请求 / 响应格式


示例 3:智能意图分析

场景:输入一个复杂任务:“实现用户登录功能”

操作:系统自动分析意图,推荐:

  • 推荐工具:Sequential Thinking(复杂任务分解)
  • 推荐 Skills/backend-specialist/security-specialist
  • 执行计划:自动生成任务拆解建议

结果:无需手动思考 “该用什么工具”,系统智能推荐。


示例 4:自动文档维护

场景:修改了 user_service.py,添加了新功能。

操作:系统自动检测代码变更,强制提示更新:

  • DEVELOPMENT.md - 记录开发进度
  • KNOWLEDGE.md - 记录技术决策
  • CHANGELOG.md - 记录变更历史

结果:文档始终与代码同步,项目知识可沉淀。


示例 5:Commands 工作流

Commands 是预定义的工作流,通过 /command-name 触发:

# 创建功能规格(从需求到实施计划)
/kiro/spec 用户认证功能

# 执行完整的代理工作流
/agent-workflow 实现博客系统

# Git 提交(自动生成 Commit Message)
/gh/commit

Command 示例:/kiro/spec

这个 Command 会引导你完成:

  1. 需求收集:生成 EARS 格式的需求文档
  2. 设计文档:创建架构设计和数据模型
  3. 任务列表:拆解为可执行的开发任务

所有文档自动保存到 .kiro/specs/{feature_name}/ 目录。

系统架构

Hooks 工作原理

本项目通过 Python Hooks 系统管理 Claude Code 的会话生命周期。每个 Hook 在特定事件触发时执行,通过 Ollama 进行智能决策。

核心设计理念:

  • 文档驱动:强制维护三个核心文档(DEVELOPMENT.md、KNOWLEDGE.md、CHANGELOG.md),会话启动时自动读取并注入上下文
  • 配置化提示词:所有提示词模板存储在 .claude/hooks/prompts.json,用户可自由调整和优化
  • 去 Memory 中间层:不再依赖 Memory MCP,直接通过文档维护项目知识,避免上下文爆炸导致的指令失效

Hook 触发时机

会话启动

SessionStart

用户输入

UserPromptSubmit

工具调用

PreToolUse

PostToolUse

Notification

PreCompact

Stop/SubagentStop

Hook 执行流程

每个 Hook 通过 exit_code 控制后续操作:

0

2

Hook 触发

执行脚本

exit_code

继续操作

阻止操作

返回 JSON

更新系统消息

返回值格式:

{ "exit_code": 0, "message": "操作成功", "data": { "skills": ["backend-specialist", "testing-specialist"], "project_type": "Python", "framework": "FastAPI" } } 
  • exit_code=0:允许操作继续
  • exit_code=2:阻止操作(如检测到危险命令)


Hook 类型说明

Hook触发时机核心功能Ollama 作用
SessionStart会话启动项目初始化检测项目类型、推荐 Skills
UserPromptSubmit用户提交输入意图分析判断任务复杂度、优化提示词
PreToolUse工具调用前权限检查评估操作风险
PostToolUse工具调用后文档更新生成文档更新建议
Stop会话结束清理资源-
SubagentStop子代理停止子任务处理-
PreCompact上下文压缩前信息保留识别重要上下文
Notification系统通知消息处理-

核心 Hook 详解:

1. SessionStart - 会话启动处理器

这是最重要的 Hook,负责项目初始化:

# .claude/hooks/handlers/session_start.py 的核心逻辑 # 1. 调用 Ollama 检测项目类型
project_info = ollama_client.detect_project_type()
# 返回:{"type": "Python", "framework": "FastAPI", "version": "3.11"} # 2. 扫描 skills/ 目录
skills = scan_skills_directory()
# 返回:["backend-specialist", "testing-specialist", ...] # 3. 初始化文档系统
document_manager.init_documents()
# 创建:DEVELOPMENT.md, KNOWLEDGE.md, CHANGELOG.md # 4. 【核心改进】强制读取三个文档并注入上下文
development_content = read_file("project_document/DEVELOPMENT.md")
knowledge_content = read_file("project_document/KNOWLEDGE.md")
changelog_content = read_file("project_document/CHANGELOG.md")
# 将这些内容注入到系统上下文中,替代 Memory MCP # 5. 检查 Git 配置
git_status = check_git_config()
# 检查:.gitignore, 分支策略 

2. UserPromptSubmit - 意图识别处理器

分析用户输入,提供智能建议:

# 用户输入:"帮我实现用户登录功能" # Ollama 分析结果:
{
    "intent": "feature_implementation",
    "complexity": "medium",
    "recommended_tools": ["Write", "Edit", "Bash"],
    "recommended_skills": ["backend-specialist", "security-specialist"],
    "suggested_plan": [
        "1. 设计数据库表结构",
        "2. 实现认证逻辑",
        "3. 编写单元测试",
        "4. 添加安全防护"
    ]
}

3. PostToolUse - 工具使用后处理器

在每次代码修改后,强制 更新三个文档:

# 检测到修改了 user_service.py # 【强制】必须更新以下文档: # 1. DEVELOPMENT.md - 记录开发进度和任务状态 # 2. KNOWLEDGE.md - 记录技术决策和代码模式 # 3. CHANGELOG.md - 记录变更历史 # 文档更新提示通过 prompts.json 配置,用户可自定义格式和要求 

提示词配置化:
所有提示词模板存储在 .claude/hooks/prompts.json,支持:

  • 自定义提示词内容和格式
  • 使用 {变量} 占位符动态替换
  • 按 Hook 类型分组管理
  • 便于持续调优和版本控制


Skills 触发机制

Skills 是本项目的 “专家团队”,每个 Skill 代表一个专业领域的智能体。

Skills 加载流程

Ollama 文件系统 Claude Code 用户 Ollama 文件系统 Claude Code 用户/backend-specialist 设计 API 读取 skills/backend-specialist/SKILL.md 返回 Skill 定义解析 YAML Frontmatter 结合 Skill 能力优化提示词返回优化后的系统提示注入系统上下文以专家身份执行任务

Skill 目录结构

.claude/skills/ ├── backend-specialist/  ├── SKILL.md                    # Skill 定义(必需)
 └── references/                 # 参考文档(可选)
 ├── cursor_rules_django.md
 ├── cursor_rules_fastapi.md
 └── restful_best_practices.md
├── testing-specialist/  ├── SKILL.md
 └── references/  ├── pytest_guide.md
 └── test_patterns.md
└── security-specialist/ ├── SKILL.md
    └── references/ ├── owasp_top10.md
        └── secure_coding.md


为什么需要 Ollama?

Ollama 是本项目的 “大脑”,负责:

  • 项目类型检测:自动识别你的项目是 Python/Node.js/Java 等
  • 意图分析:理解用户输入,判断是简单查询还是复杂任务
  • 提示词优化:将模糊需求转化为清晰的执行计划
  • 技能推荐:根据任务类型推荐合适的 Skills

没有 Ollama,系统会降级到基础模式(仅支持手动触发 Skills)。

为什么需要 uv?

uv 是 Rust 编写的超快 Python 包管理器,本项目用它来:

  • 自动管理 Python 环境:无需手动创建虚拟环境
  • 秒级安装依赖:比 pip 快 10-100 倍
  • 零配置运行 Hooksuv run 自动处理依赖隔离

为什么不用 pip? uv 会自动创建隔离环境,避免污染全局 Python 环境,且速度快 10 倍以上。


📌 转载信息
转载时间:
2026/1/14 10:44:42

这个事儿我是 9 点半才知道的(
首先我选择了工商银行,然后进入到了预约界面:

像广大佬友一样,我等到了 10 点,结果一直卡死在加载,再过几分钟刷新好了,币也没了
但是我并没有灰心,转而去研究纪念币的链接构造:
在我点击兑换纪念币的一瞬间,先是跳转到了这个网页:https://jnb.icbc.com.cn/ICBCCOINWEBPC/?a=202611322#/1/null/pc?curtype=47 ,然后又跳转到了一个无规律可循的网页。
我试探性地把末尾的 47 改成了 48,结果发现:网页提示 22:30 才开始,真的是纪念钞。
于是我直接把这个链接放到地址栏,等到整点,别人还在走第一步中转,我已经到了第二步,直接发动母胎单身手速,光速输入信息和验证码,完美抢到


📌 转载信息
原作者:
haliang0409
转载时间:
2026/1/14 10:44:28

起因

今天上午刷到了 One MCP 发布新功能 - 用 Skill 替代 MCP,省下 80% 的 Context 开销 ,奈何该工具需要 docker,而我又没有 docker,遂造轮之。

叠甲:我是不会告诉你们我今天晚上搜 gateway 在 npm 上发现了很多同类工具。所以请不要说我重复造轮子,我只是看了帖子上头了手搓了几个小时搓出来的

技术细节

该 mcp 服务提供了 3 个工具以隐藏 mcp 服务。

  1. 获取全部的 MCP 服务以及其下属的工具摘要。

    放心,它只会返回 MCP 服务的名字以及下属的全部工具名称以及经过处理的描述。
    不会占用太多上下文。

  2. 批量获取 MCP 服务及其下属工具
    该工具会返回要查询的全部 MCP 服务以及其下属工具的名称,描述以及调用参数约定

  3. 通过 MCP 服务名称和工具名称调用具体工具

安装

首先,确保您使用 NodeJS v16 及以上版本。

npm install -g @kagg886/mcp-gateway@latest

其次,打开您的 AI 工具,编辑 MCP 文件,进行配置:

{ "mcpServers": { "mcp-gateway": { "isActive": true, "name": "mcp-gateway", "type": "stdio", "command": "mcp-gateway", "args": [ "-f", "/path/to/your/json" ], "installSource": "unknown" } } } 

/path/to/your/json 的配置如下:

{ "mcpServers": { "chrome-devtools": { "command": "npx", "args": [ "-y", "chrome-devtools-mcp@latest" ] }, "mcp-server-time": { "command": "uvx", "args": [ "mcp-server-time", "--local-timezone=Asia/Shanghai" ], "alwaysAllow": [ "get_current_time", "convert_time" ] }, "context7": { "command": "npx", "args": [ "-y", "@upstash/context7-mcp@latest" ], "alwaysAllow": [ "resolve-library-id", "query-docs" ], "disabled": true } } } 

最后,在 AI 工具中进行测试:

优点

  • 不需要安装 docker (除了我之外还有谁没安装 docker 呢)
  • 配置超级简单,只需要传递配置路径或配置字符串 (使用 -s 参数)
  • web-ui,私认为这种工具不需要可视化配置,完全可以通过参数来隔离实例。

缺陷

虽然我在几个小时内匆忙地写完了这个工具,但是综合看来,还是有很多功能没有做,例如:

  • 现版本只能代理基于 STDIO 的 MCP 服务

  • 将部分 MCP 调用错误标记为 error,以供 Agent 识别

  • 尽管我在 MCP 服务的 description 中给 Agent 添加了足够的提示。
    但是在能力有限的模型中,可能不会调用代理工具查询可用的 Agent。
    此时需要您手动在聊天框或 prompt 中显式告知 AI 可用工具

  • 尽管配置了并发启动,但启动速度很明显受木桶效应影响。

开发计划

大概有人用我才会更新,否则我会基于我自己的需求迭代这个工具 (x

未来可能会增加以下功能:

  1. 异步启动 (而不是在程序启动阶段就初始化所有服务)
  2. 数据缓存 (缓存调用规范以避免频繁启动)
  3. 自动清理 (长时间不调用的 mcp 会自动关闭) 功能。
  4. 代理基于 SSE 的 MCP 服务

我将永远不会制作以下功能:

  1. 可视化 web 页面

注意事项

  • 最好不要代理影响 LLM 行为的 MCP 服务,如 sequential-thinking

  • 最佳实践是代理专业性较强,或者不常使用的 MCP 服务,如 chrome-devtoolsgithub

开源地址

mcp-gateway 使用 MIT 协议发布。源码地址为:GitHub - magic-cucumber/mcp-gateway: an gateway to manage tools, keeping context clean and focused.

写在最后

如果您喜欢这个重复的轮子,以及有建议,还请留言 / 收藏 / 标星,或者在原仓库提 issue (仅接受英文 issue,不过在这里反馈的话可以写中文)!


📌 转载信息
原作者:
kagg886
转载时间:
2026/1/14 10:44:16

合合信息多模态文本智能产品“上新”,覆盖AI教育、AI健康、AI Infra多元场景

0%
icon展开列表
合合信息多模态文本智能产品“上新”,覆盖AI教育、AI健康、AI Infra多元场景
今天
img
500万次围观,1X把「世界模型」真正用在了机器人NEO身上
今天
img
跳出「黑盒」,人大刘勇团队最新大语言模型理论与机理综述
今天
img
百川开源全球最强医疗大模型M3,「严肃问诊」定义AI医疗新能力
今天
img
相约AAAI 2026 | 上海AI实验室北极星 X 星启交流会(报名开启)
01月13日
img
视觉模型既懂语义,又能还原细节,南洋理工&商汤提出棱镜假说
01月13日
img
无需重新训练,即可学习新任务,Arc研究所开源单细胞基础模型Stack及细胞反应全景图谱
01月13日
img
不上云、不租卡,如何优雅地在本地微调Qwen-VL-30B?
01月13日
img
OpenAI的首款硬件:是AI耳机,今年销量要冲5000万
01月13日
img
华为推出软工代码智能体SWE-Lego,解锁SFT训练极致性能
01月13日
img
大模型中标TOP10里的黑马:中关村科金的应用攻坚之道
01月13日
img
刚刚,梁文锋署名开源「记忆」模块,DeepSeek V4更细节了
01月13日
img
一个模型统一4D世界生成与重建,港科大One4D框架来了
01月13日
img
端到端智驾的算力困局,九章智算云这样破局
01月12日
img
真香!刚骂完AI,Linux之父的首个Vibe Coding项目上线
01月12日
img
引入几何约束后,VLM跨越了「空间推理」的认知鸿沟
01月12日
img
清华等团队用AI驱动百万倍速药物筛选,一天内十万亿次扫描的超高速虚拟平台
01月12日
img
2026年,大模型训练的下半场属于「强化学习云」
01月12日
img
顶尖AI竟输给三岁宝宝,BabyVision测试暴露多模态模型硬伤
01月12日
img
AAAI 2026 Oral|快手提出全新「检索数据引擎」CroPS,打破搜索信息茧房
01月12日
img

合合信息多模态文本智能产品“上新”,覆盖AI教育、AI健康、AI Infra多元场景

随着人工智能(AI)产业进入“落地为王”的新阶段,AI技术与多元化场景的融合成为行业焦点。近期,上海合合信息科技股份有限公司(简称:合合信息,股票代码:688615.SH)集中发布了系列基于多模态大模型的创新产品,覆盖AI教育、AI健康管理、AI Infra(AI 基础设施)、AI Agent应用等多个领域,展现了文本智能技术与垂直场景结合的创新潜力,为AI商业化落地提供了新思路。


解锁文档服务、教育、健康管理“AI玩法”


当前,AI大模型发展正从通用能力向行业纵深落地演进,在通用文档处理领域,合合信息旗下产品扫描全能王推出“CS-AI一站式智能化文档解决方案”,实现从影像数字化向文档全周期智能服务升级。CS-AI覆盖了扫描、阅读、编辑和学习等核心场景,可自动修复图像质量问题,实现智能重排文档、优化排版。据扫描全能王产品团队介绍,依托在文档解析、版面还原上的技术优势,CS-AI预计将在跨境电商、出境游、专业文档翻译等市场中展现强劲的出海潜力。

descript

图说:扫描全能王“CS-AI一站式智能化文档解决方案”功能一览


依托多模态大模型文本智能技术,合合信息将AI能力拓展至教育、健康等垂直场景,将“千人千面”的体验变为现实。在教育领域,合合信息面向国内及海外市场,推出了AI错题学习管理工具“蜜蜂试卷”“QuizAI”,相关产品可智能识别手写体试卷,提供批改及“举一反三”等互动学习功能,实现个性化的“因材施教”。

descript

图说:“蜜蜂试卷”举一反三功能演示


在健康领域,合合信息推出AI饮食健康助手Appediet,用户通过拍照即可识别食物营养成分,生成热量报告。此外,Appediet还可结合用户健康数据定制饮食计划,并提供个性化营养分析报告、健康食谱推荐、定制饮食计划等服务,打造“人人可用的 AI 随身营养师”。


descript

图说:Appediet拍照识别食物营养成分


AI Infra、Agentic AI产品重塑数据处理流程


在企业级市场,Agent智能体的规模化落地正将AI Infra推至重要位置,高质量数据成为AI Infra 发挥效能的关键。据国际数据公司IDC预测,到2028年全球数据量将增长至393.8ZB,2023至2028年期间复合年均增长率达24.4%。目前,企业数据仍以碎片化、杂格式的形态沉淀在各类业务系统中,既拉低了模型训练效果,也限制了智能应用的落地深度。合合信息旗下智能文本处理企业级AI产品线TextIn发布了AI Infra 产品xParse,以AI赋能通用文档非结构化数据挖掘,释放数据价值,在知识库与Agent 落地、智能翻译、合规风险管理等场景中具备广阔的应用前景。


AI 与业务的深度融合是企业级智能体落地的方向。麦肯锡11月发布的2025年AI报告《The state of AI in 2025》提到,62%的受访组织(企业)已经在试验智能体类应用。TextIn打造了Agentic AI产品INTSIG Docflow,让产品能够像“数字员工”一样,对合同、票据、报表、招投标文件等高复杂度、非结构化文档进行解析、分类、抽取、审核、比对及跨系统业务流转,让AI深度作用于企业核心业务流程优化。


AI原生应用“一句话”开启商业数据智能新时代


本次发布过程中,面向商业数据智能分析领域,合合信息旗下启信慧眼推出了多项AI原生应用,让可信、可靠的数据真正作用于企业风险管控、营销与智能决策。


例如,“AI智能寻源”功能用AI自动拆解寻源品类的结构化参数,过滤信息杂质,让客户实现“一句话从3.4亿家企业中,找到合作目标”的便利,在具体使用场景中,帮助客户寻源拓客效率平均提升超过30%;“AI准入尽调”功能将行业“Know-How”与全盘数据相结合,给出“靠谱”的供应商合作建议;“AI关系洞察”功能用AI透视隐形风险,智能锁定关键风险,降低决策门槛及业务风险。


据悉,启信慧眼AI原生应用功能已在制造、医药、半导体、电子、能源、汽车、金融等多个行业中应用,日均风险扫描次数超过2000万次。


未来,AI技术正向着多模态融合、Agent 智能体规模化的方向加速突破。合合信息将持续深耕AI领域,推进多模态文本智能技术研发工作,不断拓宽技术的应用边界,探索AI应用落地的新机遇、商业化增长的新路径。



起因是日常使用 deepwiki 和 zread.ai 的时候,每次都要打开游览器网址然后选择相关部分然后修改,非常麻烦,本着程序员能自动就绝不动手的懒人思维,用 claude 写了一个油猴脚本,效果如下,供大家使用。

效果就是左下角的按钮点击新开页面跳转,仅仅在主页上实现这个功能


📌 转载信息
转载时间:
2026/1/14 10:41:40

参考 Anthropic API 文档:https://platform.claude.com/docs/en/build-with-claude/pdf-support

仅 base64 和 url,url 可能无效

工具调用修复了,但或许还有点小问题

代码待我一段时间整理


📌 转载信息
原作者:
wisdgod
转载时间:
2026/1/14 10:41:20

现在的 AI 客户端简直是百花齐放,讨论相对较多的应该就属 CherryStudioOpenWebUI

先聊 CherryStudio

  • 优点:UI 设计确实审美在线,比 ChatBox 精致不少,而且多服务商、多模型的接入和切换非常方便,主打一个方便快捷,打开软件就能用。
  • 硬伤:对话历史的堆积,个人目前体验变得非常卡;最可惜的是缺乏原生多端同步。

再说 OpenWebUI

  • 优点:走的是 Web 架构路线,界面复刻了 ChatGPT 的极简风格,上手几乎是零门槛。函数调用、知识库,还有高度可定制化的设置非常优秀,比如模型参数、权限、检索策略这类设置,很多客户端只是给个开关意思一下,它是给你一整套可控面板,当然还有我最需要的多端同步

  • 缺点:模型太多的时候就很不方便管理,而且不支持 Gemini 接口的原生接入。高度可自定义化也意味着默认体验未必最佳 —— 你得自己调,调好了很爽,没调好就容易变成功能太多太杂,懒得去动的状态。


但我想要是能把二者相结合岂不是绝杀?能的佬友,能的!


① 对话高级参数完整汉化 + info 黑框重做:能直接看到更明确的用量和消费


② 自定义上下文条数:可以设置发送给模型的历史消息上下文的最大条数,有效节省 Token



③ 外部连接显示优化:作为一只屯屯鼠,公益站 / API 站一多,设置界面简直就是灾难。改成双列显示,管理效率直接拉满


④ Gemini 原生端口支持:不依赖 OpenAI 兼容层,直接走 Gemini 原生接口,可同步模型列表, thinking_budget 这类特有参数也能上,同时支持流式传输图片


⑤ 外部链接可备注名称 + 可点击名称直达 URL 设置
本屯屯鼠最大的噩梦,看着一堆 URL 无从下手,现在能直接备注,方便区分,还能点名字直接跳转到设置,不用怕点错小齿轮,适合链接多的佬


⑥ 模型界面缓存逻辑优化:当模型列表过多时,不用再转圈圈等待


⑦ 自动按模型名匹配 Logo:不用再手动每个模型点进去添加图标了,增加了常见的 LLM 品牌 Logo 自动匹配(GPT/Claude/Gemini/Qwen…),对齐 CherryStudio



⑧ 首页模型切换处增加直达模型设置按钮:选模型的地方增加设置跳转,方便快速管理模型


⑨ 模型计费 + 用量统计前端同步 info 黑框:支持免费 / 按次 / 按量三种模式,实时计算对话成本


⑩ 推理强度 / Reasoning Effort 支持下拉 + 自定义输入


其余设置优化:(关于联网搜索不准确的解决方案)

优化 OpenWebUI 联网搜索功能【点击展开】

1、先按照图片设置 Documents 文档和联网搜索,重排模型根据自己的模型配置:



2、复制这段提示词到 Documents 文档设置中的 RAG 提示词模板中,再次试试联网搜索,会有惊喜

### Task:  
Respond to the user query using the provided context, incorporating inline citations in the format [id] **only when the <source> tag includes an explicit id attribute** (e.g., <source id="1">).  
  
### Guidelines:  
- If you don't know the answer, clearly state that.  
- If uncertain, ask the user for clarification.  
- Respond to the user query in Chinese.  
- If the context is unreadable or of poor quality, inform the user and provide the best possible answer.  
- If the answer isn't present in the context but you possess the knowledge, explain this to the user and provide the answer using your own understanding.  
- Only include inline citations using [id] (e.g., [1], [2]) when the <source> tag includes an id attribute. 
- Do not cite if the <source> tag does not contain an id attribute.  
- Do not use XML tags in your response.  
- Ensure citations are concise and directly related to the information provided.  
- Current Date: {{CURRENT_DATE}}. If there is conflicting information, prioritize the latest events based on the timeline.  

### Rules for using web sources (especially time‑sensitive questions)
- For time-sensitive queries, prioritize webpages published within the **last 3–5 days**.
- If old data conflicts with new data, strictly prioritize the **latest publication time**.
- If multiple sources conflict, prioritize sources that are both **most recent AND from authoritative media/official institutions**.
- If sources disagree on the same fact, explicitly point out the discrepancy in your answer and justify which source you consider most reliable.
- Verify key facts against other sources to check for contradictions.

### Example of Citation:  
If the user asks about a specific topic and the information is found in a source with a provided id attribute, the response should include the citation like in the following example:  
* "According to the study, the proposed method increases efficiency by 20% [1]."  
  
### Output:  
Provide a clear and direct response to the user's query, including inline citations in the format [id] only when the <source> tag with id attribute is present in the context.  
  
<context>  
{{CONTEXT}}  
</context>  
  
<user_query>  
{{QUERY}}  
</user_query>


Docker 一键部署命令【点击展开】
docker run -d \
  --name open-webui \ --restart always \
  -p 3000:8080 \
  -v open-webui:/app/backend/data \
  ghcr.io/ztx888/openwebui:latest
  • 浏览器访问:http://你的服务器IP:3000
  • 数据持久化在 Docker 卷:open-webui(用于重启或者更新的时候不丢配置和对话)

如果你本机没有 Docker 卷习惯,也可以改成本地目录挂载:

mkdir -p ./open-webui-data

docker run -d \
  --name open-webui \
  --restart always \
  -p 3000:8080 \
  -v $(pwd)/open-webui-data:/app/backend/data \
  ghcr.io/ztx888/openwebui:latest


最后必须说一句

非常感谢各位公益站站长的付出和维护,真的帮各位佬省了太多折腾和成本。提供节点、反代、还是日常兜底运维,都很不容易,向各位站长致敬


遇到问题欢迎反馈

我会持续迭代同步更新官方上游,如果你装了之后遇到问题、或者有更想要的功能点,欢迎来反馈


📌 转载信息
原作者:
Leon666
转载时间:
2026/1/14 10:40:43

这里简单介绍一下,这个平台相当于 老黄用自家的显卡,部署了这些模型,然后统一用 OpenAPI 接口来给大家造福利(bushi),但是也确实好用,虽然高峰期的时候会卡,但白嫖是吧
话不多说,让我们 勒死 go

一、从官网进行获取 api-key

起手先注册账户拿钥匙

二、怎么进行使用

1. openapi 格式使用

baseurl: https://integrate.api.nvidia.com/v1/chat/completions
API Key: 就是第一步申请的 key
这里以沉浸式翻译插件,使用 Kimi2-thinking 举例。


添加自定义服务

注意,这里选择 open-api

配置如下:moonshotai/kimi-k2-thinking



然后手动调试一下是否可用


这里打 即可。

tip:如果不知道哪个模型,可以到官网中进行查看,方法如下:



📌 转载信息
转载时间:
2026/1/14 10:39:49


前言

Keras 3 是一个多后端深度学习框架,支持 JAX、TensorFlow、PyTorch 和 OpenVINO(仅用于推理)。它可以轻松构建和训练用于计算机视觉、自然语言处理、音频处理、时间序列预测、推荐系统等领域的模型。

漏洞概述

Keras 3.11.3 版本在提取 tar 归档文件时,其 `keras.utils.get_file()` 函数存在路径遍历漏洞。该漏洞的产生是由于该函数使用了 Python 的 `tarfile.extractall()` 方法,但缺少安全关键参数 `filter='data'`。尽管 Keras 尝试使用 `filter_safe_paths()` 过滤不安全路径,但此过滤操作发生在提取之前,而提取过程中会触发一个 PATH_MAX 符号链接解析漏洞。该漏洞会导致符号链接解析因路径长度限制而失败,从而绕过安全机制,允许将文件写入预期提取目录之外。这可能导致缓存目录之外的任意文件写入,进而造成系统安全漏洞或恶意代码执行。该漏洞会影响使用 `get_file()` 处理 tar 归档文件的 Keras 安装,但不会影响使用适当过滤器参数保护此提取方法的版本。

漏洞分析/复现

漏洞分析

keras.utils.getfile调用了extract_archive

image.png



使用python tarfile.extractall() 方法,但是没有配置filter="data" 参数

image.png



虽然使用了filter_safe_paths进行过滤

image.png



PATH_MAX 符号链接解析错误发生在路径过滤之前,于是可以绕过检查

image.png



漏洞复现

使用pip安装相关依赖

搭建一个测试场景,平台允许用户上传测试数据集,处理数据集时会调用keras.utils.get_file()提取归档文件,从而触发漏洞

image.png



创建恶意文件

image.png



上传恶意文件

image.png



当点击处理数据集时会调用getfile来处理,从而触发漏洞,成功写入文件

image.png



https://pngtree.com/

教程
创建新的账号 / 该账号未订阅过!

随机点击任何一张素材,顶部会出现 “Try for Free” 横幅。

点击领取,他会要求绑卡验证(随便个虚拟卡也行)。最后点击 Pay 他会验证,然后就自动领取 1 个月素材库会员了。

记得获得后取消帮卡!

(最近刚好在做一些东西需要素材所以刚好。。。也分享一下


📌 转载信息
原作者:
dkly2004
转载时间:
2026/1/14 10:37:52

后端部分

前端部分

(只需要听歌可以只部署前端的音乐播放器部分)
rua!~


📌 转载信息
原作者:
lionsky
转载时间:
2026/1/14 10:37:44