Commit e5b0f01f by luoqi

fix(助手): 排版重做 —— 段落成块 / 标题独立一层 / 标点全角

产品:「输出抓不住重点」。查下来不是字体的事,四条:

① **段落根本没成块**:`p` 是 my-1.5(6px),而行距 22px —— 段距只有行距的 1/4,
   段与段比行与行还紧。→ 14px(通则:段距 ≥ 1.5 倍行距)。
② **加粗身兼三职**(段落标题 / 句中强调 / 后果警示),还同时跳字重和颜色
   (semibold + slate-900),一屏 7 处黑块 = 没有重点。
   → strong 降成 font-medium;**标题交给 h3**。
③ **标题层此前是死的**:h1-h3 定义得好好的,模型一个 `#` 都没输出过 ——
   它只有粗体一件武器,所有层级都拿它凑。
   → h3 改成小节标签(12px / slate-500 / 加字距 / mt-4),与正文靠**字号+颜色**分层,
      不靠字重(靠字重就又跟句中加粗抢)。
④ 行长 50+ 字(中文舒适区 25–35)→ max-w-[34em];数字加 tabular-nums。

🔴 **标点:模型是在照抄提示词**。实测提示词里中文之间用半角逗号/分号 91 处、
   全角 0 处 —— 光加一条"要用全角"的规则等于跟例子对着干。
   ⇒ 提示词自身的标点也一并改了(逗号 176 / 分号 18 / 冒号 92 / 括号 78 处),
     再加一条第 0.5 条「怎么写」。改完实测:**半角残留 0**。

️ 但**只给规则,结构没跟上**:第一轮实测标点全对、`###` 一个没出、仍旧三大段 + 6 处加粗。
   ⇒ 按本仓既有方法论(「靠模型算对的一律降级成照抄」)补了一个**成品骨架**
     (要您定的 / 已排好 / 怎么排的),并把那次失败写进提示词当反例。
   第二轮实测:h3 三块齐、加粗降到 2 处、要主管动手的那块排到了最前面。

️ 两条断言里的半角标点改成 `[,,]` 兼容 —— 把测试钉死在某种标点上,
   失败与被测行为无关,只是噪音。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
parent afd4d8cc
......@@ -418,7 +418,9 @@ describe('待分配的批量处置 —— 决定他做,执行你做', () => {
});
test('⭐⭐ 提示词要点名禁止「我不能替您做这个决定」这种甩锅', () => {
expect(PROMPTS).toMatch(/决定他做,执行你做/);
// ⚠️ 标点写成 `[,,]`:提示词的中文标点 2026-08-11 统一改成全角了(模型在照抄它),
// ⛔ 别把断言钉死在某一种标点上 —— 那种失败与被测行为无关,只是噪音。
expect(PROMPTS).toMatch(/决定他做[,,]执行你做/);
expect(PROMPTS).toMatch(/甩锅/);
// 同时仍然保留"不许自作主张"那一半 —— 两半缺一不可
expect(PROMPTS).toMatch(/不许自作主张/);
......@@ -733,8 +735,8 @@ describe('确认单指令 —— 三个正交的轴', () => {
});
test('🔴🔴 提示词教的是**三问怎么拼**,⛔ 不是一串要背的短语', () => {
expect(SVC).toMatch(/选谁\(select\)· 干什么\(action\)· 给谁\(to\)/);
expect(SVC).toMatch(/别去背短语,按这三问填/);
expect(SVC).toMatch(/选谁[((]select[))]· 干什么[((]action[))]· 给谁[((]to[))]/);
expect(SVC).toMatch(/别去背短语[,,]按这三问填/);
// 举例可以有,但必须写明"别当成清单" —— 否则模型只会照抄例子,没列到的组合又不敢拼
expect(SVC).toMatch(/⛔ 别当成清单/);
// 把实测栽的那次写成反例:抽象规则模型容易绕过,具体反例不会
......
......@@ -95,21 +95,47 @@ function toolMeta(tool: string) {
return TOOL_META[tool] ?? { label: tool, running: `正在调用 ${tool}` };
}
// ── LLM 文本 → markdown 渲染(用户要求:不复用设计的结构卡,改 markdown 解析)──
/**
* LLM 文本 → markdown 渲染(用户要求:不复用设计的结构卡,改 markdown 解析)。
*
* ═══ 2026-08-11 排版走查改的四件事(产品:「输出抓不住重点」)═══════════════
* ① **段落要成块**:原来 `p` 是 `my-1.5`(6px),而行距 22px —— 段距只有行距的 1/4,
* 段与段比行与行还紧,三段话在眼里是一坨。排版通则:段距 ≥ 1.5 倍行距。
* ② **加粗不再身兼三职**:原来 `strong` 是 `semibold + slate-900`(字重与对比度同时跳),
* 模型又拿它当标题使 —— 一屏 7 处黑块,等于没有重点。
* 现在 strong 降成 `font-medium`,**标题交给 h3**(见下)。
* ③ **标题与正文靠字号/颜色分层,不靠字重**:h3 = 12.5px slate-500 的小节标签
* (「要您定的」「已排好」),⛔ 别做成"更大更黑的一行" —— 那会跟句中加粗抢。
* ④ **限行长 + 数字等宽**:13.5px 中文在宽面板里一行能排 50+ 字(舒适区 25–35),
* `max-w-[34em]` 收住;`tabular-nums` 让数字竖着对齐,扫视时自己跳出来。
* ⛔ 别把这些值改回去而不改提示词:样式与「模型输出什么结构」是一对
* (见 assistant-prompts 的「怎么写」一节)。
*/
function Markdown({ text }: { text: string }) {
return (
<div className="text-[13.5px] leading-relaxed text-slate-700">
<div className="max-w-[34em] text-[13.5px] leading-relaxed text-slate-700 [font-variant-numeric:tabular-nums]">
<ReactMarkdown
remarkPlugins={[remarkGfm]}
components={{
h1: ({ children }) => <h1 className="mb-2 mt-3 text-[16px] font-semibold text-slate-900">{children}</h1>,
h2: ({ children }) => <h2 className="mb-1.5 mt-3 text-[15px] font-semibold text-slate-900">{children}</h2>,
h3: ({ children }) => <h3 className="mb-1 mt-2.5 text-[14px] font-semibold text-slate-800">{children}</h3>,
p: ({ children }) => <p className="my-1.5">{children}</p>,
ul: ({ children }) => <ul className="my-1.5 ml-4 list-disc space-y-1">{children}</ul>,
ol: ({ children }) => <ol className="my-1.5 ml-4 list-decimal space-y-1">{children}</ol>,
h1: ({ children }) => <h1 className="mb-1.5 mt-4 text-[14px] font-semibold text-slate-900">{children}</h1>,
h2: ({ children }) => <h2 className="mb-1.5 mt-4 text-[13.5px] font-semibold text-slate-900">{children}</h2>,
/**
* ⭐ h3 是**模型实际会用的那一档**(提示词让它用 `###` 分块)。
* 做成**小节标签**:比正文小半号 + 次级色 + 加字距,与正文拉开的是**字号和颜色**。
* ⛔ 别做成"更大更黑" —— 那跟句中加粗是同一种编码,又会糊成一片。
* ⚠️ `mt-4` 是段落成块的另一半:标题上方的空白才是"新的一块开始了"的信号。
*/
h3: ({ children }) => (
<h3 className="mb-1 mt-4 text-[12px] font-semibold tracking-wide text-slate-500 first:mt-0">
{children}
</h3>
),
p: ({ children }) => <p className="my-3.5">{children}</p>,
ul: ({ children }) => <ul className="my-2.5 ml-4 list-disc space-y-1.5">{children}</ul>,
ol: ({ children }) => <ol className="my-2.5 ml-4 list-decimal space-y-1.5">{children}</ol>,
li: ({ children }) => <li className="leading-relaxed">{children}</li>,
strong: ({ children }) => <strong className="font-semibold text-slate-900">{children}</strong>,
/// ⚠️ `font-medium` 不是 semibold:句中强调而已,⛔ 别再同时跳字重和颜色
strong: ({ children }) => <strong className="font-medium text-slate-900">{children}</strong>,
a: ({ children, href }) => (
<a href={href} className="text-brand-700 underline" target="_blank" rel="noreferrer">
{children}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment