先说结论:这一篇“鸿蒙智能体实战记录 16”,我给它起名叫“禅心(Zen Heart)”,核心就两件事——极简表达和留白控制。如果你也被大模型那句“好的,我来帮你详细介绍一下”折磨过,那这篇应该能救你。
这个项目要解决的问题特别具体:我们把智能体从“话痨”调教成“话少”,从“什么都替用户说完”改成“点到为止,把空间还给用户”。适用场景包括手机端的语音助手、手表上的快捷问答、甚至耳机里的低功耗交互,这些场景有一个共同特点——屏幕小、时间碎、用户没耐心听长篇大论。
先交代一下背景。我是做鸿蒙生态智能体开发的,这个系列已经写到第16篇,前几篇聊过意图识别、工具调用、记忆管理,都是偏“能力”的构建。但做到后面我发现一个比能力更棘手的问题——生成内容的“表达气质”。模型默认的说话方式太“完整”了:一个三句话能说清的问题,它非要给六句;一个用户自己就能得出结论的场景,它非要总结陈词。这种“完整”在聊天窗口里勉强能忍,放在手表屏幕上,直接溢出三屏。
所以禅心这个项目,目标不是让模型更聪明,而是让模型更克制。这篇文章我会把整个调优过程拆开讲:从提示词设计、采样参数调优、输出后处理,到鸿蒙端的流式截断和多设备适配,全部是实测记录,不是概念科普。
1. 整体设计思路:先把“说少”这件事拆成三层
1.1 项目起源:用户不是来听讲座的
禅心这个项目的起因是一次实测翻车。我们在鸿蒙平板上跑一个生活助手智能体,用户问“附近有什么适合周末去的地方”,模型巴拉巴拉输出四百多字,从气候分析到出行建议再到穿搭提醒,最后还来了句“祝您周末愉快”。测试群里一个用户直接说:“我问一句话你们回我一篇作文?”
这句话成了压垮骆驼的最后一根稻草。我们复盘了一下,发现这其实是普遍问题,不是个例。大模型的训练目标让它在“续写”时倾向于输出高概率的延续,而“高概率延续”往往等于“更完整的描述”,所以它天然就是个话痨。要让智能体学会克制,等于要跟这个惯性对着干。
于是我们立了一个项目,代号“禅心”。目标很朴素:同一个问题,回答字数压到原来的三分之一,信息量不降,并且在该停的地方停下来。这个目标听着简单,落地才发现要动的地方特别多——不是改一句提示词就完事,而是要从表达的内容结构、交互行为到工程链路全部配合。
1.2 极简表达的三个层次:语言、结构、信息
动手之前,我们先把“极简表达”拆成了三个层次,避免笼统地“让它少说点”:
第一层是语言层。主要是指去掉口头禅、去掉铺垫、去掉“好的”“没问题”“当然可以”这类礼貌性冗余。这一层最简单,靠提示词约束就能解决大半。
第二层是结构层。模型默认的回答结构是“总—分—总”,先给结论,再展开,再总结。极简表达要求它砍掉总结、合并条目、能用一句话说完的分点绝不分三行。这一层需要对输出格式做约束。
第三层是信息层。这是最难的一层。模型分不清“用户需要的全部信息”和“用户此刻需要的信息”,它倾向于把前者全给出来。信息层的要求是:只回答用户当前问的那个点,相关但没被问到的内容,要么藏起来等追问,要么直接用一句话点明“这块如果需要我再展开,要我继续说吗”。
把这三层放在一张表里对照着看会更清楚:
| 层次 | 问题表现 | 解决手段 |
|---|---|---|
| 语言层 | “好的,我来帮您介绍一下” | 提示词禁令 + 句式过滤 |
| 结构层 | 总-分-总三段式,重复收尾 | 输出模板约束 + 解析后处理 |
| 信息层 | 把所有相关内容全塞给用户 | 留白规则 + 交互行为引导 |
三层逐个击破,才能达到“话少但不失信息”的效果,否则就会出现另一个极端——话是少了,但用户想问的也没了。
1.3 留白控制:不是“不说”,而是“知道什么时候停”
留白这件事比极简更微妙。极简是“少说”,留白是“刻意不说该说的部分,把空间留给用户”。
我们定义了三种留白类型,对应三种不同的控制策略:
第一种是信息留白。用户只问到步骤A,模型忍住不讲步骤B和C,只提一句“后面两步做的时候遇到问题可以再问我”。这利用了“用户的追问是更精准的上下文”这一特性——与其猜,不如等。第二种是情感留白。用户明显在倾诉或犹豫的时候,模型不去给解决方案,只回应情绪,然后停住。这个在传统对话系统里很少见,但对大模型来说反而容易,关键在于识别“该不该继续输出”。第三种是交互留白。在给选项的场景里,列完选项就闭嘴,不替用户做决定,不追加“如果您需要更详细的信息请告诉我”这种废话。
三种留白加在一起,给用户的感觉就是:这个智能体“接得住话”,但“不抢话”。说起来有点玄,但用户调研时确实有人反馈“它不像以前那么啰嗦了,像是有个人陪着而不是一个资料库”。
这三种留白策略我们最终都固化成了提示词里的一条条规则,下面就会讲到具体怎么写。
2. 提示词体系与“留白控制”的核心实现
2.1 系统提示词里的“表达宪章”
我先贴一份最终版本的系统提示词,这是整个禅心项目一切约束的源头。实际使用时已做了敏感信息脱敏:
你是禅心,一个追求克制表达的智能体。 【表达准则】 1. 能用一句话说完,绝不用两句。 2. 先给结论,再给理由;理由最多两条,除非用户明确要求更多。 3. 禁止以下句式和词汇:好的、没问题、当然可以、让我为您、希望以上内容对您有帮助、总的来说、总而言之、如果您有任何问题请随时告诉我。 4. 不要重复用户的问题,不要复述用户的信息。 【留白规则】 1. 回答结束在用户下一步最需要思考的地方。 2. 用户只问了一个点,就只交代这个点;牵连内容用不超过10个字概括,并明示“需要再问”。 3. 给选项时,列完选项立即结束,不做推荐词、不追加说明。 4. 用户表达情绪或倾诉时,先接住情绪,不要急着给方案。如果用户没问“怎么办”,就不主动展开方案。 【格式约束】 1. 3个分点以内使用单行,超过3个分点才允许使用列表。 2. 每个段落不得超过两行。 3. 禁止使用“首先/其次/最后”等顺序词。这份提示词我们内部叫“表达宪章”,后面每一次模型调优、AB测试都基于这份宪章改。你可能会问,第一条“一句话说完绝不用两句”会不会太强硬?实测下来不会——模型为了满足这条会自己压缩句式,而且压缩之后信息密度反而上来了。
但注意一个细节:禁止句式列表里我们写的是“禁止以下句式和词汇”,而不是“请尽量少用”。大模型对“尽量”“建议”这类软约束的遵从度很低,必须用“禁止”“必须”这种强指令词。这是提示词工程里被反复验证过的经验。
2.2 留白触发规则:不是所有场景都该留白
提示词里的留白规则写出来容易,但真正的问题是:模型怎么知道“现在该停了”?我们在测试中发现,笼统的“该停时停”没有任何效果——模型不会理解抽象指令。
所以我们把留白规则改写成了可判定的触发条件。每一个留白行为背后都绑定一个明确的输入模式,比如:
- 用户文本里出现“吗”“哪些”“怎么”这类疑问词,且文本中只有单一问题——只回答该问题,不扩展相关联问题。
- 用户文本中出现“好累”“烦”“难受”这类情绪词,且没有“怎么解决”“该怎么办”——只接情绪,不输出方案。
- 用户文本中出现两个及以上选项关键词(或者我们主动给选项的场景)——列完项就停。
- 用户上一次回复里存在“要不要我详细说”这类句子,且用户这次没有追问——默认用户不需要,继续极简。
这些规则写进提示词后,留白问题才真正解决。这说明一个道理:对大模型的控制的粒度必须具体到“输入特征”层面,不能停留在“意图”层面。
2.3 采样参数调优:从温度到top_p再到min_p
提示词之外,解码参数对表达风格的影响远超很多人想象。我们花了大概三周做参数调优,这里直接给结论。
最开始我们用默认参数,temperature 0.7,top_p 0.9,出来的回答虽然被提示词压住了废话,但“表达倾向”还是偏啰嗦——模型会在合规的前提下尽可能多的补充信息。这很好理解:temperature高了,随机性大,模型有更多空间去发挥;top_p宽了,候选范围大,模型更容易选到那些“展开说”的句子。
我们的最终参数组合:
| 参数 | 默认值 | 禅心最终值 | 效果说明 |
|---|---|---|---|
| temperature | 0.7 | 0.3 | 降低发散度,输出更稳定、更收敛 |
| top_p | 0.9 | 0.85 | 微收窄候选,去掉低概率的冗余候选 |
| max_tokens | 无/大 | 192 | 强制压缩上限,配合后处理使用 |
| frequency_penalty | 0 | 0.8 | 惩罚重复句式,避免车轱辘话 |
| presence_penalty | 0 | 0.3 | 轻微鼓励新表达,防止单调 |
temperature 调到 0.3 之后效果变化很明显,但不是越低越好。我们也试过 0.1,结果回答变得又短又生硬,经常是光秃秃一句话,连必要的上下文都没有,用户根本不知道你在说什么。0.3 是个平衡点,短而自然。
frequency_penalty 这个值很容易被忽略,我们一开始也没管,结果发现模型即使遵守了禁止句式,还是会在两句话里用相同的句法结构——“建议您……”“建议您可以……”这种排比式冗余。开了 0.8 后明显改善。
2.4 后处理兜底:长度与句式的硬约束
提示词和参数都调好之后,还是要加一道后处理工序,我们内部叫“刹车器”。原因很简单:模型偶尔会不听话,而且这种不听话在流式输出场景下更明显。
后处理做了三件事。第一件是长度截断。我们在鸿蒙端拿到了流式输出的 token 回调,设置一个硬阈值,超过阈值就强制打断生成。阈值按设备类型区分:手机 120 token,手表 48 token,耳机 24 token。这个值不是拍脑袋定的,而是统计了用户阅读习惯——手表上超过 48 token 的文字基本没人会看完。
第二件是句式过滤。用一个轻量关键词名单做正则匹配,拦截“好的”“让我为您”“总的来说”这类词。注意,如果提示词已经把概率压住了,后处理正则基本是空转的,但“空转”也值得,因为它是最后一道保险。
第三件是安全截断点。不能直接按 token 数硬切,那样会把句子切成半截。我们的方案是把输出缓冲在内存里,每到一个句号、问号、换行符时,标记一个“安全断点”,只有整句完整落入阈值内才放行,否则在最近安全断点处截停。
这套“提示词+参数+后处理”三层结构,才构成完整的极简表达链路。
3. 鸿蒙端工程落地:流式输出与多设备适配
3.1 ArkTS侧的流式捕获与截断策略
禅心的宿主平台是鸿蒙,我们用 ArkTS 做 UI 层,智能体引擎通过流式接口向上抛增量文本。这一节聊一下端侧代码层面的实现思路。
先贴一段核心截断逻辑的示意代码,已做了简化,实际工程里还要处理异步竞态和内存回收:
// 流式输出捕获与安全截断示意 let outputBuffer: string = ''; let lastSafeCut: number = 0; const LIMIT = isWatch ? 48 : isPhone ? 120 : 240; agent.onStreamUpdate((delta: string) => { outputBuffer += delta; // 记录最近的安全断点 const cutIndex = findLastSafeChop(outputBuffer); if (cutIndex !== -1) { lastSafeCut = cutIndex; } // 超过硬阈值时截断 if (outputBuffer.length >= LIMIT && lastSafeCut > 0) { const finalText = outputBuffer.slice(0, lastSafeCut); render(finalText); agent.cancelStream(); return; } render(outputBuffer); }); function findLastSafeChop(text: string): number { const punctuations = ['。', '?', '!', '\n']; let last = -1; for (const p of punctuations) { const idx = text.lastIndexOf(p); if (idx > last) last = idx; } return last; }这段代码的核心思想是“先缓冲、后落笔”。流式输出时 UI 要实时刷新,但如果边接收边展示,截断时就容易出现残句。我们选择把增量先攒在 buffer 里,每次渲染前判断是否越界,越界就在安全断点切断,同时调用 cancel 终止后续生成。
这里有一个工程上的坑:鸿蒙端流式接口在 cancel 之后,有时候还会再抛一两个残留 delta,必须在上层再做一次开关校验,不然截断后的最后一句会被重复渲染或者被覆盖成残句。
3.2 端侧模型与云侧API的分工
禅心的极简控制里,最纠结的决策是:用端侧小模型还是云侧大模型。鸿蒙端侧推理目前能跑的模型参数量相对有限,但优势是响应快、无网络依赖、具备低功耗待机。云侧大模型能力强、表达灵活,但首字延迟高,网络不稳就断片。
我们的分工方案是:端侧模型做主回答链路,云侧模型做兜底和复杂理解。具体来说,意图识别、情绪判断和短回答生成全部走端侧,端侧模型参数量小而快,配合上面的提示词和后处理,能扛住 80% 的日常问答。只有识别到用户问题复杂度高、端侧模型置信度低于阈值时,才把上下文转发到云侧,生成结果再回到端侧统一执行截断策略。
这套分工让禅心在手机上的平均首字响应时间压到了 600ms 以内,手表上更短一些,因为端侧模型加载的是更小的量化版本。
3.3 多设备形态适配:手表、耳机、手机的不一样
鸿蒙生态最麻烦也最有趣的地方在于设备形态差异巨大。同一套“极简+留白”策略,在不同的屏幕上完全是不同的实现要求。
手机上的极简是“一段话三行内”,可以接受列表,可以接受轻微的结构。手表上的极简是“两屏内必须结束”,列表基本不能用,一句话能表达完就不给两句话。耳机上的极简是“十条以内说完”,没有屏幕可以回看,所以要额外追加一条规则——关键信息必须在最前面出现。
我们在工程里做了一个设备能力配置表,针对每个形态调整 LIMIT、提示词追加段落和留白触发条件:
| 设备 | 输出上限 | 提示词追加规则 | 留白策略 |
|---|---|---|---|
| 手机 | 120 token | 无 | 完整三种留白均生效 |
| 手表 | 48 token | 禁止列表,单段落 | 信息留白为主 |
| 耳机 | 24 token | 关键信息前置 | 只输出结论,不提供选项 |
耳机形态最极端,我们直接把“给选项”这个行为禁掉了——耳机场景用户根本没法做选择操作,留白在这里反而是负担。这个例子可以说明,极简和留白不是固定策略,而是一组可以被形态化调整的参数。
3.4 性能开销:减了话,也减了算力
有个意外收获:极简表达直接降低了端侧算力消耗。同样的上下文长度,回答 token 数少了大概 60%,推理时间、内存占用、电池消耗全部跟着下降。
我们拿同一批问题做了对比测试,调优前后模型在端侧的推理数据如下:
| 指标 | 调优前 | 调优后 |
|---|---|---|
| 平均回答 token 数 | 342 | 118 |
| 端侧 P99 推理延迟 | 820ms | 430ms |
| 单轮峰值内存 | 无法统计(已超限) | 1180MB |
token 少了之后,端侧小模型的压力显著降低,甚至很多原本要上云的问题,端侧自己就能搞定。这算是一个意外的性能红利——虽然最开始做极简只是为了体验,但最后发现它也成了成本优化手段。
4. 实战过程中的坑与排查记录
这一节把踩过的坑集中整理一下,基本都是工程里最常见的几个问题,给后面做类似项目的读者避避雷。
4.1 问题一:提示词写死了,模型还是一堆口头禅
我们第一次上“表达宪章”时,想象中模型会立刻变成惜字如金,结果第一轮测试就直接翻车——“好的,我来帮你分析一下这个问题”照样出现。排查后确认不是提示词没生效,而是我们把它放在了 system 角色的最后,前面还有一大段能力描述和工具定义,模型在长上下文里对后段指令的遵从度被稀释了。
解决方法:把表达准则提到 system prompt 的顶部,紧跟在角色定义之后,并且把“禁止口头禅”的策略词改为“重复出现时判定为严重错误”。改完之后口头禅出现率从 34% 降到了 6%。
这个经验可以推广:对大模型的约束强烈程度,不完全取决于措辞强弱,还取决于在上下文中的位置。越靠前、越独立,约束力越强。
4.2 问题二:温度调太低,回答干瘪但还在说废话
调参过程中有个迷惑现象——temperature 降到 0.1 后,回答变短了,但短的内容反而全是废话。比如用户问“现在几点能到”,模型回“根据当前路况,您到达的时间可能会受到多种因素影响,高峰期的话会有波动”。一句有用的都没有。
后来我们在调试台逐 token 看概率分布才发现,temperature 太低导致模型只挑最高概率的那个延续路径,而高概率路径在训练语料里往往是最“安全”的套话。说白了,低温度不是让模型“变聪明”,而是让模型“变保守”。
最终把 temperature 回调到 0.3,同时靠留白规则去约束它什么时候该停,而不是靠低温去压表达。这个坑值得记住:温度和极简是两个维度,极简靠规则,不靠低温。
4.3 问题三:留白过度,用户以为智能体坏了
留白控制上线后一度被用户投诉“是不是程序 bug 了,怎么说一半就不说了”。复盘后我们发现,问题出在信息留白规则上——用户问 A,模型答了 A 之后确实只给了十个字以内的连带提到的提示,但用户根本没意识到“还可以追问”,直接以为对话结束了。
后来我们改了一版:连带提示必须使用明确追问引导句式,比如“需要我接着讲后两步吗”,而不是给一句模糊的“相关步骤可能也有影响”。别看就差几个字,前者让用户明确知道还有内容可选,后者让用户一头雾水。留白的前提是用户能感知到“这里有空档”,不然留白就变成了失联。
4.4 问题四:流式截断导致语义残废
截断逻辑出过两次事故。第一次是硬截断把“删除文件后建议立即清空回收站,以免……”截成“删除文件后建议立即清空”,后半的警示信息丢了,这个很危险。第二次是安全断点误判,把引号里的句号当成了断点,结果拆出来一截引文,语义完全散掉。
最终修了两个地方:一是断点只认句号、问号和换行符,不认引号内逗号句号;二是截断前必须做一次括号配对检查,如果当前文本里引号或括号处于未闭合状态,就继续往前找更早的断点。这两处修完,截止目前没有收到截断语义残废的报告。
4.5 效果前后对比:同一问题的两种答案
这里放一组前后对比,非常直观:
用户提问:“周末想找个安静的地方看书,有什么推荐?”
调优前:“好的,我来为您推荐几个适合阅读的安静去处。首先,您可以考虑城市公共图书馆,那里环境安静,藏书丰富,还有专门的自习区域。其次,某些商业书店设休息阅读区,工作日人少,周末可能略多,建议上午前往。此外,一些创业咖啡馆也提供安静空间,不过消费较高。最后,祝您周末愉快!”
调优后:“公共图书馆最稳,安静且有座位。书店阅读区也可以,但周末下午人较多,建议上午去。需要我按距您最近的地方排序吗?”
字数从一百多字压缩到五十字左右,但该给的核心信息都在,最后还用一句留白把选项权还给了用户。测试群里的反馈是“这个答案像是人说的”。
5. 用数据说话:极简表达的实际效果
5.1 三个关键指标的变化
客观效果需要数据支撑,我们跑了一轮三周的小规模用户灰度,主要看三个指标:
| 指标 | 调优前 | 调优后 | 变化 |
|---|---|---|---|
| 平均回答字数(字) | 246 | 91 | -63% |
| 用户追问率 | 27% | 61% | +34% |
| 对话满意度(5分制) | 3.4 | 4.6 | +1.2 |
追问率这个数据最能说明留白的价值。以前模型一次性把话说满,用户没有追问的理由;现在模型给出关键信息和一句引导,用户天然就顺着往下问了。对话从一次性的问答,变成了有来有回的连续交互,这对智能体的体验是个质的变化。
5.2 不同场景下的表现差异
按场景拆开看也有意思。信息查询类场景(问天气、问时间、问路况)提升最明显,极简表达完全匹配需求。建议推荐类场景(求书单、求路线)留白效果好,用户明显更愿意从选项里挑一个接着聊。闲聊情绪类场景最复杂,不能一味极简,有时候需要多说两句照顾情绪。
所以最终我们在禅心的提示词里给“情绪场景”额外开了一个口子:“用户明显处于负面情绪且主动倾诉时,可突破一条规则:先给情绪回应,允许第二段展开,但依然不输出解决方案。”这说明规则要有弹性,极简的核心是知道何时简化,而不是永远简化。
5.3 后续还可以这样扩展
禅心目前的版本解决了“表达克制”的问题,但还有一个方向没做透——个性化留白。不同用户对留白的接受度完全不一样。有人觉得“点到为止”是高级,有人觉得“话不说全”是敷衍。
我们已经在灰度一个基于用户反馈的留白偏好系数。用户在对话里主动追问“然后呢”的次数越多,系统就把留白阈值调低一点;用户连续几次不追问,就把留白阈值调高一点。相当于给智能体装了一个“话痨旋钮”,顺着用户的使用习惯自动调整。这个版本还在测试,等数据跑通了我再专门写一篇。
最后再分享一个我在整个实战过程中的体会:做极简表达,最大的阻力从来不是技术,而是我们作为开发者自己对“信息密度”的执念。总担心少说了就损失了,但真实用户根本不这么想。用户需要的从来不是“最多的信息”,而是“在合适的时机获得合适的少量信息”。禅心这个项目做到后面,对我来说已经不只是提示词调优的事,更像是在教一个 AI 学会“看人下菜碟”的分寸感。这个分寸感,就是大模型从“能用”走向“好用”的关键一步。