ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Qwen Code ACP 消息改写中间件:用 LLM 把终端编码 Agent 的输出改写成业务友好语言

Qwen Code ACP 消息改写中间件:用 LLM 把终端编码 Agent 的输出改写成业务友好语言 Qwen Code ACP 消息改写中间件用 LLM 把终端编码 Agent 的输出改写成业务友好语言【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-codeQwen Code 在 ACPAgent Client Protocol集成中提供了一套可选的消息改写中间件Message Rewrite Middleware原始消息原样透传同时把每个回合累积的思考与回复文本交给一个独立的 LLM 调用进行降噪改写再以_meta.rewritten: true标记的新agent_message_chunk追加下发。读完本文你将理解这套中间件的适用场景、messageRewrite配置项的完整含义、其在packages/cli/src/acp-integration/session/rewrite/下的源码实现回合缓冲、LLM 改写器、中间件编排以及下游渠道如何基于_meta.rewritten标记避免重复投递。适用场景垂直业务集成中的输出降噪当编码 Agent 被接入数据分析、运维、报表生成等垂直业务场景时原始输出里往往混杂着终端用户并不关心的技术细节文件路径、工具调用名、内部推理过程、代码片段、堆栈跟踪等。改写中间件的作用就是在不修改原始消息流的前提下额外生成一份面向业务用户的进度摘要。官方文档 README 在开篇特别强调⚠️ Temporary Solution — subject to change or removal at any time.这是一个过渡性实现stopgap。团队正在考虑一种基于 hook 的方案解耦和可扩展性会更好欢迎提出改进设计的想法。因此本文介绍的所有配置项与行为细节适用前提为当前仓库版本未来接口可能变化或被移除。工作原理透传优先回合末追加改写README 总结了四步核心流程下文结合源码逐条印证原始消息原样透传——不做任何修改回合结束时工具调用开始前 / 模型响应结束时把累积的 thought message 片段发给一次独立的 LLM 调用进行改写改写后的文本以新的agent_message_chunk追加下发并带_meta.rewritten: true客户端自行决定展示哪个版本——展示层根据_meta.rewritten选择原始或改写版。回合Turn的边界在哪里回合是改写的最小单元。从 TurnBuffer 的实现看缓冲区分两类累积thoughts来自agent_thought_chunk的文本片段模型的思考内容messages来自agent_message_chunk的文本片段模型回复内容。一个回合在两种情况下结束模型开始发起tool_call工具调用标志回合边界或模型停止生成。flush()会拼接并重置缓冲返回一个TurnContent结构定义见 types.tsexport interface TurnContent { thoughts: string[]; messages: string[]; hasToolCalls: boolean; }拦截、累积与异步改写MessageRewriteMiddleware 是这套机制的编排核心其interceptUpdate()方法处理每一条 ACP 更新遇到tool_call更新时先调用flushTurn()结算当前回合再放行原始更新遇到agent_thought_chunk/agent_message_chunk时先原样发送原始消息再根据target配置决定是否累积其他类型tool_call_update、plan、available_commands等一律直接透传。有两处值得注意的过滤逻辑来源排除agent_message_chunk若携带_meta.source为slash_command或vision_bridge_notice则透传但不累积——这类消息是系统通知性质不参与改写空回合跳过缓冲为空或改写输入文本短于 10 个字符时LlmRewriter.ts 中inputText.length 10直接跳过改写避免为过渡性短回合浪费一次 LLM 调用。改写本身是非阻塞的flushTurn()把改写任务塞入pendingRewrites队列在后台与工具执行并行运行。每次改写都会强制套用超时信号AbortSignal.timeout(this.timeoutMs)并与调用方传入的AbortSignal合并确保一次挂死的改写调用不会阻塞主流程。在主会话的调用链中可以看到几个触发点见 Session.ts模型响应结束、发出 usage 元数据时this.messageRewriter.flushTurn(pendingSend.signal)——注释明确写着Kick off rewrite in background (non-blocking, runs parallel to tools)回合完整结束、prompt 即将返回前await this.messageRewriter.waitForPendingRewrites()——保证会话返回前所有在途改写都已落地会话所有更新都经由sendUpdate()出口base-emitter.ts当messageRewriter存在时先走interceptUpdate(update)否则直接透传。waitForPendingRewrites()的实现采用循环抽干策略每次把当前队列整体取出、置空新数组等这批全部 settle 后再检查是否有新任务入队直到队列为空——注释解释了这样做的原因如果在 await 期间又有flushTurn入队一次性快照再清空会静默丢掉这些迟到任务。改写成功后中间件发出形如这样的更新{ sessionUpdate: agent_message_chunk, content: { type: text, text: rewritten }, _meta: { ...turnMeta, // 原回合内捕获的 _meta如 source 等 rewritten: true, // 客户端据此识别改写版 turnIndex: turnIdx, // 回合序号 }, }其中turnMeta来自回合内消息片段携带的_metacaptureTurnMeta()会累积合并。源码中还有一个显式的扩展点REWRITE_META_EXCLUDED_KEYS——目前为空集合注释说明它是向改写消息的_meta中剔除某个键的预留口子早期版本曾在这里剔除backgroundTask/source/qwenDiscreteMessage后来因下游离散消息路由依赖这些键而回退。改写失败超时、LLM 报错等不会中断会话LlmRewriter.rewrite()内部捕获异常并返回null中间件仅记录 debug 日志Turn N: rewrite failed: ...。配置settings.json 中的messageRewrite项按 README 的说明在settings.json中添加如下配置即可启用{ messageRewrite: { enabled: true, target: all, promptFile: .qwen/rewrite-prompt.txt, model: qwen3-plus, contextTurns: 1, timeoutMs: 60000 } }结合 types.ts 中的MessageRewriteConfig接口各字段含义如下字段类型默认值说明enabledboolean必填是否启用改写。Session.installRewriter()仅在rewriteConfig?.enabled为真时安装中间件targetmessage \| thought \| all必填累积哪些消息类型仅回复文本 / 仅思考内容 / 两者都改写promptstring—内联改写提示词作为改写 LLM 的系统提示词promptFilestring—改写提示词文件路径相对 CWD 解析同时配置时优先于promptmodelstring空改写使用的模型留空则沿用当前会话模型contextTurnsnumber \| all1携带几条历史改写结果作为上下文0不带N带最近 N 条all带全部timeoutMsnumber30000单次改写 LLM 调用超时毫秒。README 示例中设为60000配置的加载与信任边界配置读取逻辑在 config.ts 中loadRewriteConfig()从用户级与工作区的originalSettings中读取messageRewrite键且工作区配置仅在受信任trusted工作区中生效——其目的是防止不受信任的仓库通过自带工作区配置启用改写器、注入自定义提示词。优先级上工作区配置覆盖用户级配置。安装时机同样有讲究Session.ts 中installRewriter()的注释明确写道Must be called AFTER history replay to avoid rewriting historical messages——即中间件必须装完历史消息回放之后才安装避免把恢复会话时的历史内容也送去改写。自定义提示词与内置回退promptFile的解析在 LlmRewriter.ts 构造函数中完成采用优雅降级策略文件不存在记录Rewrite prompt file not found: ..., using default警告回退到内置默认提示词文件存在但不可读EISDIR、EACCES等——existsSync对目录也返回 true因此读操作本身仍可能失败同样降级而非抛错源码注释指出直接抛错会导致 ACP 会话启动崩溃关联上游 issue #9752。README 也给出了对应的排障手段设置QWEN_DEBUG_LOG_FILE环境变量可以捕获这类回退警告。内置默认提示词DEFAULT_REWRITE_PROMPT定义了改写的核心规则可作为自定义提示词的参照严格基于原文——不虚构 Agent 未陈述的细节、计划或结论保留目标、决策、关键发现、结果、影响用户的错误、状态更新丢弃文件路径、工具/技能名、内部工具选择推理、代码片段、堆栈跟踪、let me… / now Ill… 之类填充语进度型回合Agent 刚开始读文件、跑命令、探索代码输出一句简短说明避免用户面对静默输入本身已是结构良好的面向用户内容表格、列表、格式化结果时只做轻度清理保留结构纯内部操作改自己的代码笔误、重试失败的工具调用、建临时目录→ 返回空字符串数据原样保留——绝不改动数字、百分比、文件大小、错误码或引用输出。此外LlmRewriter会拼接上下文连续性指令若提供了Previous rewrite output上一轮改写结果用户已经看过不要重复、在其基础上推进本轮若无新增内容则返回空字符串。改写调用本身参数与输入构造改写通过核心包的runSideQuery()原语发起见 LlmRewriter.ts关键参数为purpose: acp-rewrite——标识这次旁路查询的用途modelrewriteConfig.model未配置时回退this.config.getModel()当前会话模型maxAttempts: 1——注释解释了原因这是尽力而为best-effort路径失败只会静默跳过用户完全无感知因此不在瞬时故障上烧掉 7 次重试systemInstruction: this.prompt——即配置或内置的改写提示词config: { temperature: 0.3, maxOutputTokens: 1024 }——低温度保证输出稳定上限 1024 token 限定摘要篇幅abortSignal——中间件传入的带超时合并信号。输入文本的构造分三段用中文标记分段拼接[内部推理]thoughts 用换行连接、[回复文本]messages以及最前面的[上一轮改写结果]由outputHistory提供多条之间以\n---\n分隔。其中outputHistory按contextTurns裁剪保留——all时全量保存有限值时只保留最近 N 条0时干脆不存——避免长会话中历史无界增长。输出侧还有两道过滤返回空或长度不足 5 字符的结果直接跳过返回null不发出改写消息异常统一吞掉并返回null。下游消费客户端如何对待_meta.rewritten改写版消息通过_meta.rewritten: true与原始消息区分客户端决定展示哪个版本并非只是纸面约定渠道层channels已有实际的去重逻辑。例如 DaemonChannelBridge.ts 与 AcpBridge.ts 在处理后台通知响应的离散消息qwenDiscreteMessage: true时判断条件包含meta[rewritten] ! true——即改写版的后台响应会被忽略避免同一条回复通过原始版 改写版两条消息重复投递对应测试用例名为ignores a rewritten background response to avoid duplicate delivery见 AcpBridge.test.ts、DaemonChannelBridge.test.ts。测试与验证该模块自带四个单元测试文件覆盖各组件行为边界LlmRewriter.test.ts——提示词加载回退、上下文裁剪、短输入/空输出跳过等MessageRewriteMiddleware.test.ts——拦截逻辑、回合边界、超时信号、_meta.rewritten标记TurnBuffer.test.ts——累积与flush()重置config.test.ts——配置加载与工作区信任判断。此外 Session.test.ts 中存在针对flushTurn与waitForPendingRewrites时序关系的集成断言确保改写结算发生在模型循环内部、且终端更新先于待决改写等待完成——防止回合标记错误地粘到下一回合的改写摘要上。局限与演进方向从源码注释和 README 的声明可以确认这套中间件当前的设计取舍包括临时定位官方明确其为 stopgap 方案正在评估基于 hook 的替代设计以获得更好的解耦与可扩展性配置项与行为随时可能变化或移除同步读文件promptFile在中间件构造时同步读取readFileSync失败即降级为默认提示词运行期不做热重载无持久化改写结果只作为一次性 chunk 下发不进入会话历史outputHistory也是内存态仅用于同会话内的上下文连续性。对于集成方而言当前可靠的做法是在受信任工作区中通过用户级或工作区settings.json的messageRewrite键启用功能、用promptFile定制业务化改写规则、通过QWEN_DEBUG_LOG_FILE观察改写输入输出与降级警告并在客户端依据_meta.rewritten与turnIndex消费改写版消息。【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表