# SPEC: Markdown-First 物理落盘与来源分级架构
> **版本**: v1.0
> **前置依赖**:
> - [SPEC_deepview_metadata_scoping_v3.md](./SPEC_deepview_metadata_scoping_v3.md) (Prosumer-First 沙箱隔离)
> - [SPEC_tri_domain_knowledge_architecture.md](./SPEC_tri_domain_knowledge_architecture.md) (三元知识域)
> - [DESIGN_PHILOSOPHY_AI_NATIVE.md](./DESIGN_PHILOSOPHY_AI_NATIVE.md) (AI-Native 产品心法)
> **状态**: 草案 (Proposal)
---
## 1. 问题陈述
当前管线中存在三个结构性隐患,它们共享同一根源——**AI 产出物与原始素材在文件系统中无法区分**。
### 1.1 Ghost Response 问题
Hermes Agent 在执行"分析素材并生成报告"类任务时,天然地会通过 `write_file` 工具将分析结果写入物理文件,而 `final_response` 仅返回"操作完成"的告知性消息。如果管线代码从 `final_response` 取值,拿到的是一段无意义的摘要而非真正的分析内容。
这不是 Agent 的 bug——这是 Agent 作为**自主工作者**的正确行为。人类分析师也不会口述 2000 字报告给你,他会"写完放这里,告诉你一声"。
| 管线 | 当前取值方式 | 风险 |
|------|-------------|------|
| 录音笔记 Stage 1 | `result.get("final_response")` | 碰巧可靠(Agent 恰好未写文件),但不稳定 |
| 客户档案 Stage 1 | `result.get("final_response")` | **已验证失效** — Agent 将内容写入 `profile.md`,response 仅 401 字符 |
### 1.2 幻觉复利效应
当前 `profile.md` 既是 Agent 的**输入素材**(读取既往档案),又是 Agent 的**输出目标**(生成新档案后覆盖写入)。文件系统中没有任何标记区分:
- `asr.md`(L0 原始转写,可信度最高)
- `profile.md`(L2 AI 聚合产出,可能包含错误推断)
当 Agent 下次执行时,它 `read_file("profile.md")`——无法判断这是人类录入的事实,还是上一轮 AI 的推测。如果上一次的 profile.md 含有错误推断(如"客户属于价格不敏感型"),Agent 会将其当作已有事实继续叠加推理。
**错误被放大而非纠正——这是幻觉的复利。**
### 1.3 Token 消耗悖论
一位客户积累 50 次面诊后:
- **暴力拼接所有 asr.md**(每条 1-3 万字)→ 100 万字 → 上下文直接爆炸
- **只读 report.md 摘要**(每条 3000 字)→ 15 万字 → 可控
但前提是:**Agent 必须知道 report.md 是 AI 生成的一阶摘要(有损),asr.md 才是无损证据。当需要考据时,应回溯 asr.md 原文,而非引用 report.md 中的话当作原声。**
可靠的中间产出物反而减少 Token 消耗;不可靠的中间产出物则制造幻觉。
---
## 2. 核心原则:Markdown-First 物理落盘
### 2.1 总则
> **管线各阶段之间的接口契约,必须是物理文件,不是内存态变量。**
| 接口方式 | 韧性 | 可审计性 | Agent 兼容性 |
|----------|------|---------|-------------|
| `final_response`(内存态) | ❌ 进程崩溃即丢 | ❌ 需翻 session 日志 | ❌ 与 Agent 的 `write_file` 行为冲突 |
| 物理 `.md` 文件 | ✅ 落盘即不丢 | ✅ `cat` 即可审 | ✅ 顺应 Agent 自然行为 |
### 2.2 两段式管线的物理接口
```
Stage 0 (ASR/上传) Stage 1 (Hermes Agent) Stage 2 (Qwen JSON)
│ │ │
▼ ▼ ▼
asr.md ──read───► Agent ──write──► report_draft.md ──read──► LLM ──► DB JSON
(L0 原始) (L1 AI 产出)
```
- **Stage 0 → Stage 1**:Agent 通过 `read_file` 自主读取 `asr.md`(及其他素材)
- **Stage 1 → Stage 2**:管线代码从 Agent 写入的物理文件读取内容,传入 Stage 2
- **Stage 2 → DB**:JSON 写入数据库(已实现,无变更)
### 2.3 Agent 指令约定
Agent 的 user_message 必须明确指定**输出目标路径**,让 Agent 将分析结果写入该路径。管线代码执行完 Agent 后,从该路径读取内容。
```python
# ✅ 正确:明确输出路径,Agent 写入后管线读取
result = agent.run_conversation(
user_message=f"分析 {historyDir}/ 下的素材,将全景档案写入 {outputPath}",
system_message=systemPrompt,
)
with open(outputPath, "r") as f:
mdReport = f.read()
# ❌ 错误:依赖 final_response(Ghost Response 风险)
mdReport = result.get("final_response", "")
```
---
## 3. 来源分级标记 (Provenance Levels)
### 3.1 三级分类
| 级别 | 标识 | 含义 | 可信度 | 文件命名约定 | 示例 |
|------|------|------|--------|-------------|------|
| **L0** | `source: human` | 物理世界直接采集的原始素材 | 最高——可回溯到原声/原件 | `asr.md`, `asr_raw.m4a`, 用户上传照片 | 录音转写文本(ASR 原始输出) |
| **L1** | `source: ai-single` | AI 基于**单个** L0 素材生成的一阶分析 | 高——有损摘要,但来源明确 | `report_draft.md` | 单条录音的面诊 X 光片报告 |
| **L2** | `source: ai-aggregate` | AI 基于**多个** L0/L1 素材聚合生成的产物 | 中——叠加推理,幻觉风险最大 | `profile.md` | 客户全景档案(跨录音合成) |
### 3.2 文件头元数据标注
所有 AI 生成的 `.md` 文件,必须在文件头以 HTML 注释形式嵌入来源元数据:
```markdown
# 面诊沟通X光片深度分析报告
...
```
```markdown
# 👤 客户全景档案:测女士
...
```
L0 文件(asr.md)**不需要**标注——没有标注即为 L0。这是"无标记即原始"的默认原则。
### 3.3 Agent 行为约束(注入 SKILL.md)
在 Agent 的 SKILL 指令中,追加来源分级感知规则:
```markdown
## 📋 来源分级阅读规则
1. **L0 文件**(asr.md, asr_raw.m4a)是不可变的原始证据,所有核心论断必须以 L0 文件为最终考据依据。
2. **L1 文件**(report_draft.md)是单次录音的 AI 摘要,可用于快速浏览历史,但引用原声时必须回溯 L0 验证。
3. **L2 文件**(profile.md)是跨录音的 AI 聚合分析,优先级最低。如果 L2 中的结论与 L0 原文冲突,以 L0 为准。
4. **引用格式**:所有洞察必须标注来源文件名和大致行号,并注明来源级别。
- ✅ `(来源: rep_bc439351/asr.md L0, 125 行)`
- ❌ `(来源: profile.md)` ← 禁止引用 L2 作为唯一证据
```
---
## 4. 文件系统约定
### 4.1 录音笔记管线(单条录音 → 报告)
**当前**:
```
inbox/{asrId}/
├── asr.md ← L0 原始转写
└── asr_raw.m4a ← L0 原始音频
```
**改造后**(归档前):
```
inbox/{asrId}/
├── asr.md ← L0 原始转写
├── asr_raw.m4a ← L0 原始音频
└── report_draft.md ← L1 AI 单次分析(Agent 写入,管线读取后传入 Stage 2)
```
**改造后**(归档后):
```
clients/{clientId}/history/{reportId}/
├── asr.md ← L0 不可变
├── asr_raw.m4a ← L0 不可变
└── report_draft.md ← L1 随迁(审计留痕)
```
### 4.2 客户档案管线(多条录音 → 全景档案)
```
clients/{clientId}/
├── profile.md ← L2 AI 聚合产出(每次生成时覆盖)
└── history/
├── {reportId_A}/
│ ├── asr.md ← L0
│ ├── asr_raw.m4a ← L0
│ └── report_draft.md ← L1
├── {reportId_B}/
│ ├── asr.md ← L0
│ └── report_draft.md ← L1
└── ...
```
### 4.3 不可变性规则
| 文件 | 可读 | 可写/覆盖 | 可删除 |
|------|------|----------|--------|
| `asr.md` (L0) | ✅ Agent + 管线 | ❌ 上传后不可变 | ❌ |
| `asr_raw.m4a` (L0) | ✅ 审计用 | ❌ | ❌ |
| `report_draft.md` (L1) | ✅ Agent + 管线 | ✅ 重新生成时覆盖 | ❌ |
| `profile.md` (L2) | ✅ Agent + 管线 | ✅ 每次归档后重新生成 | ❌ |
---
## 5. 代码改造清单
### 5.1 录音笔记管线 (`_handleReportGenerate`)
| 步骤 | 现状 | 改造 |
|------|------|------|
| Agent user_message | 未指定输出路径 | 新增 `report_draft.md` 输出路径指令 |
| Stage 1 取值 | `result.get("final_response")` | 优先读取 `inbox/{asrId}/report_draft.md`,兜底 `final_response` |
| report_draft.md 写入 | 无 | Agent 自行写入(prompt 引导),或管线兜底写入 |
| 文件头标注 | 无 | Agent prompt 中要求写入 `` 标注 |
### 5.2 客户档案管线 (`_generateClientProfile`)
| 步骤 | 现状 | 改造 |
|------|------|------|
| Agent user_message | 含 `profileMdPath` 路径 | 保持(Agent 已自然写入此路径) |
| Stage 1 取值 | ~~`result.get("final_response")`~~ **已修复** → 读取 `profile.md` | ✅ 已完成 |
| 文件头标注 | 无 | 需追加 `` |
### 5.3 SKILL.md 追加
| 文件 | 变更 |
|------|------|
| `skills/deepview_assistant/SKILL.md` | 追加"来源分级阅读规则"章节 |
| `skills/deepview_profile/PROMPT_stage1.md` | 要求输出文件头带 `` 标注 |
---
## 6. Agent 信息获取策略(规模化场景)
当客户积累大量面诊录音后,Agent 应自主规划信息获取策略,而非被管线代码预先读取所有文件:
### 6.1 分层阅读策略
```
Agent 收到任务: "生成 p_b1232417 的全景档案"
│
├─ Step 1: search_files → 扫描 history/ 目录结构,确认有 N 条录音
│
├─ Step 2: read_file(profile.md) → 读取 L2 既有档案(如果存在)
│ └── Agent 识别 provenance: ai-aggregate → 知道这是上一轮 AI 自己的产出,仅供参考
│
├─ Step 3: 逐条读取 report_draft.md (L1) → 快速掌握每条录音的核心结论
│ └── Token 消耗: N × ~3000 字(远小于 N × ~20000 字的 asr.md)
│
├─ Step 4 (按需): 对关键论断回溯 asr.md (L0) 验证
│ └── 仅在需要考据原声时才读取完整 ASR,避免全量加载
│
└─ Step 5: write_file(profile.md) → 生成新的 L2 档案,覆盖旧版
```
### 6.2 Token 经济模型
| 录音数量 | 暴力全读 (asr.md) | 分层阅读 (report_draft.md + 按需 asr.md) | 节省比 |
|----------|-------------------|----------------------------------------|--------|
| 5 条 | ~10 万字 | ~1.5 万字 + 按需 ~2 万字 | ~65% |
| 20 条 | ~40 万字 | ~6 万字 + 按需 ~4 万字 | ~75% |
| 50 条 | ~100 万字 (上下文爆炸) | ~15 万字 + 按需 ~6 万字 | **可行 vs 不可行** |
---
## 7. 与已有 SPEC 的关系
```mermaid
graph TB
A["DESIGN_PHILOSOPHY_AI_NATIVE
先有价值,后有治理"] --> B["SPEC_metadata_scoping_v3
Prosumer-First 沙箱隔离"]
B --> C["SPEC_tri_domain_knowledge
三元知识域"]
B --> D["SPEC_md_first_persistence
📄 本规范"]
D --> E["来源分级标记"]
D --> F["管线物理接口"]
D --> G["Agent 分层阅读策略"]
```
- **V3 SPEC** 定义了"数据在哪"(`users/{userId}/` 沙箱)
- **三元域 SPEC** 定义了"知识从哪来"(平台/机构/个人三级注入)
- **本 SPEC** 定义了"文件怎么流"(物理落盘、来源标记、Agent 感知证据层级)
三者合力构成了完整的数据治理体系:**沙箱隔离 × 知识分域 × 来源追溯**。
---
## 8. 验证计划
### 8.1 自动化测试
```bash
# 1. 录音笔记管线:验证 report_draft.md 是否被生成
test -f inbox/{asrId}/report_draft.md && echo "✅ L1 文件存在"
head -1 inbox/{asrId}/report_draft.md | grep "provenance" && echo "✅ 来源标记存在"
# 2. 客户档案管线:验证 profile.md 来源标记
head -1 clients/{clientId}/profile.md | grep "ai-aggregate" && echo "✅ L2 标记正确"
# 3. 归档后文件完整性
ls clients/{clientId}/history/{reportId}/ | grep -c ".md" # 应 >= 2 (asr.md + report_draft.md)
```
### 8.2 人工审计
- 对比 `profile.md` 中的引述与 `asr.md` 原文,确认无二次幻觉放大
- 检查 Agent session 日志,确认分层阅读行为(先读 L1 摘要,按需回溯 L0)
---
> *本规范定义了深维面诊助理的 Markdown-First 物理落盘与来源分级架构。*
> *核心信念:物理文件是管线各阶段之间的唯一接口契约。AI 产出必须标注来源级别,原始素材不可变。*
> *审阅确认后,将指导两条管线的统一改造。*