boardx / boardx/workspacex

已裁决·暂缓:消息级上下文指示器 —— 真正缺的是 citation 写入方,等 LangGraph 编排层落地后重开

Open
#714 1 comment 0 reactions 0 assignees View on GitHub
area:chat backlog
Dominant language
TypeScript
Stars
0
Forks
0
Avg merge
1h 7m
Merged PRs (30d)
969

Description

## 背景

延续 #654 / #708 / #712 系列 chat UI/UX 十项改进调查。第 7 项:消息级上下文指示器("已引用 3 项上下文")。

此前一轮调查怀疑「现有 `DurableMessage` 契约没有 citations/contextPack 字段,这些字段存在于一个疑似遗留/未接线的旧 `GetThreadDetailOut` 消息形状里」。本 issue 是对这句话的**逐条核实结果**——结论与原猜测不完全一致,先把两边契约的真实关系钉清楚,再请人类裁决该怎么走。

## 核实结论

**`GetThreadDetailOut` 这个类型名在本仓库不存在**(`rg -n "GetThreadDetailOut" .` 全仓零命中,含 `apps/`、`packages/`、`docs/`、`phases/`)。之前的怀疑对不上号,实际情况是:

### 生产用的两套消息形状,其中一套(不是猜测中那套)确实带 citations 字段,但字段被硬编码为空

1. **`chat.operations.getThread.out.messages`**(`packages/contracts/src/chat.ts:339-371`,类型是 `Message`,`chat.ts:253-263`)——**这个形状本身就带 `citations: z.array(Citation)` 字段**,`Citation` 是完整的三段式结构(`citationId, index, sourceFullName, anchor{kind,page,range,messageId}`,`chat.ts:225-235`)。`getThread` 是生产 `/chat` 屏正在调用的真实端口(`chat-read-screen.tsx` 的 `loadSelectedThread`),**但**它的实现 `apps/api/src/application/chat/get-thread.ts:117-127`(`toMessage()`)把 `citations` **硬编码为 `[]`**,代码自己的注释写着这是"已登记的契约缺口":
```
`chat.Message` 是 `.strict()` 且**没有正文字段**——没有 `body` / `text`。
于是「一条消息说了什么」在契约上无处安放……正文另需契约补字段。
**契约由人改**(ADR-020),此处只登记。
```
进一步查:迁移 `apps/api/migrations/20260731170746_f111_chat_citations.sql` 建了 `chat_citations` 表,但**生产代码里没有任何一处 `INSERT INTO chat_citations`**(唯一命中是测试 fixture `apps/api/tests/support/chat-db.ts:85`)——写入侧本身不存在,`citations: []` 不是偷懒,是如实反映"这张表现在没有任何持久化落点"。

顺带一提:`getThread.out.messages` 用的 `Message` 形状**也没有正文字段**(没有 `body`/`text`)——这也是同一处代码头部登记的已知缺口,与 citations 缺口是同一个"消息模型需要人补字段"的问题的两个症状。

2. **`chat.operations.listMessages.out.messages`**(`DurableMessage`,`chat.ts:83-93`,生产实际渲染消息气泡用的那个端口,`ChatLiveMessagePanel` 调)——**这个形状完全没有 `citations` 字段**,连空数组占位都没有。它有正文(`text`),是产品实际展示消息内容的地方。

也就是说:**真正带正文的消息形状(`DurableMessage`)没有 citations 字段;带 citations 字段的消息形状(`Message`)没有正文**。两套形状目前各缺一半,不是"一套有一套没有、复用哪套的问题",而是**两套都不完整**,且它们服务的是两个不同的端口(`listMessages` vs `getThread`),语义定位也不同(`getThread.messages` 目前实际只用来算 `rightTabs` 计数和越权判定,`chat-read-screen.tsx` 从不渲染它的 `messages` 字段本身)。

### 另一个独立契约:`context-pack` 束

`packages/contracts/src/context-pack.ts` 定义了自己的 `ContextPack`/`Citation`(`context-pack.ts:232-235`,`{segmentId, artifactVersionId}`,字段与上面 `chat.Citation` **结构不同**)与 `assembleContextPack`/`replayContextPack` 操作。这套有 `apps/api/src/application/context-pack/*` 的应用层实现,但**没有任何控制器把它接到 HTTP 路由**(`rg -ln "context-pack" apps/api/src/interface/controllers` 零命中)。`chat.ts` 自己也声明了一个 `replayContextPack`(`chat.ts:760-775`,`GET /chat/agent-runs/:agentRunId/context-pack`),同样没有控制器路由,只有生成的前端 mock(`apps/web/lib/generated/context-pack.mock.ts`)在用它。

`docs/architecture/context-engine.md:252-268` 定义了目标 `ContextPack` 形状;`phases/phase-00-shared-kernel/design-coherence.md:487-541` 记录了 `contextPack.Anchor` 与 `artifact.AnchorKind` 之间的形状分歧,是待人类裁决的已知问题。

## 需要人类裁决的问题

1. "消息级上下文指示器"要展示的"已引用 N 项",应该来自哪一套?
- (a) 补完 `chat.Message.citations` 的写入侧(先把 `chat_citations` 真正接上写路径),复用它作为唯一的消息级引用计数来源;还是
- (b) 复用/对接 `context-pack` 束的 `Citation`(先给它接上控制器路由),语义上更接近"这条回复用了 Context Pack 里的哪几条证据";还是
- (c) 两者是不同层级的概念(`chat.Citation` 是"这条结论落地为 Artifact 时的出处回链",`context-pack.Citation` 是"这次模型调用真正读到的证据段"),应该分别建两个指示器,不是一个字段的问题。
2. 如果选 (a) 或 (b),写入侧(谁在什么时机往哪张表插入 citation 行)由谁设计——这本身是一次不小的后端工作(需要模型调用时真的记录用到的证据 span),不是纯前端能力所及。

在人类给出选择之前,本 issue **不配 PR**——第 7 项按人类此前的指示"如果调查后仍然无法确定该复用还是新建,开一个 issue 把选项和你的调查结论写清楚,PR 就先不做这一项"处理。

Contributor guide

No contributing guide indexed for this repository

Research direction

Read packages/contracts/src/chat.ts, apps/api/src/application/chat/get-thread.ts, and packages/contracts/src/context-pack.ts first; run the cited repository searches to verify the current routes and write paths. This issue is done only after the citation semantics and source contract are decided and the responsible persistence path is designed, so no implementation is currently defined.

Written by the indexing model from the issue text.

Assessment

Tech stack
sql, typescript
Domain
api, backend, databases
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
20/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.