Browse Source

docs: 补充坑 12/13 与契约「只做加法」纪律

deep_agent 1 month ago
parent
commit
3b9fd2cded
1 changed files with 22 additions and 0 deletions
  1. 22 0
      AGENTS.md

+ 22 - 0
AGENTS.md

@@ -156,6 +156,26 @@ WARNING 并把冲突键暴露给 `/health` 的 `config_conflicts`。
 
 **副作用一律放进 lifespan**,模块级只留纯定义。
 
+### 坑 12:工具卡必须按 `tool_call_id` 索引,不能按工具名
+
+同一轮里同一个工具会被调多次(实测 agent 会自己换关键词连查 4 次)。
+只按 `tool` 名索引卡片 → 后一次的结果挂到前一张卡上,界面看起来「结果和
+参数对不上」。
+
+1.5 的契约里没有 `tool_call_id`,2.0 补了它作为**附加字段**(`sse_events`
+的 tool_call / tool_result)。附加字段安全、改名不安全——见 §四的契约纪律。
+
+### 坑 13:`element.textContent += x` 会摧毁子元素
+
+`textContent` 的读是「所有后代文本的拼接」、写是「替换全部子节点」。所以
+`bubble.textContent += token` 这类写法会:① 把子元素(工具卡片)的文本一起
+读进来,② 赋值时把那些子元素整个删掉。表现是工具卡片被抹成一坨纯文本,
+而且每次追加都重新拼接全文(O(N²))。
+
+正确做法是给正文一个独立的文本节点,并在插入卡片时「断开」当前文本段,
+以保留时间顺序(见 `static/index.html` 的 `appendToken` / `addCardFor` /
+`breakSegment`)。
+
 ---
 
 ## 三、分层与依赖方向
@@ -183,6 +203,8 @@ main.py ──> api/ ──> agents/ ──> tools/
   新增开关的默认值不得改变既有行为。
 - **对外契约的字段名只在 `api/sse_events.py` 声明**。其他任何地方都不得
   手拼 `data: ` 字符串——字段改名漏掉一处,前端就静默渲染失败。
+- **契约只做加法**:新增字段安全(旧前端忽略未知字段),改名或删除不安全
+  (静默破坏渲染)。要改已有字段名,先确认 1.5 前端也一起改。
 - **函数式注释写「为什么」不写「是什么」**。行内注释只在代码本身无法表达
   约束时写(顺序要求、副作用、反直觉的取舍)。
 - **同步驱动(pymysql / sqlite3)在 async 路由里必须走 `asyncio.to_thread`**,