first commit

This commit is contained in:
zhuyongxin
2026-03-24 17:01:35 +08:00
commit 1dfae8ca19
68 changed files with 3898 additions and 0 deletions
+229
View File
@@ -0,0 +1,229 @@
# Summary Loop 工作说明
## 1. 目的
本文档说明 `scripts/run_summary_loop.py` 如何把以下流程串成一个最小闭环:
`提取 JSON -> LLM 生成摘要 JSON -> validator 校验 -> 失败则修复重试`
这个脚本的目标不是单次调用 LLM,而是让 LLM 输出进入一个“生成后验收”的自动反馈回路。
---
## 2. 核心思路
这个闭环由三部分组成:
- 提取结果
- 来自 `outputs/*.extracted.json`
- 提供标题、链接、正文、质量标记
- LLM 摘要
- 基于提取结果和 prompt 生成 `result.json`
- validator
- 检查 `result.json` 是否符合结构和业务规则
- 不通过则返回错误列表
因此,LLM 不是“自己知道哪里错了”,而是脚本在每轮生成后用 validator 明确指出错误,再要求它修复。
---
## 3. 运行顺序
脚本入口:
- `scripts/run_summary_loop.py`
执行顺序如下:
1. 读取提取结果 JSON
2. 读取摘要 prompt 模板
3. 从提取结果中裁剪出摘要真正需要的输入字段
4. 调用 LLM 生成摘要 JSON
5. 解析模型输出中的 JSON
6. 将结果写入输出文件
7. 调用 validator 校验结果
8. 如果通过,结束
9. 如果失败,构造修复 prompt,再次调用 LLM
---
## 4. 输入来源
脚本主要使用两个输入文件:
- 提取结果:`outputs/read-flow-2026.extracted.json`
- 摘要 prompt:`outputs/llm-summary-prompt.txt`
提取结果不会整包无差别塞给 LLM,而是先裁剪成更小的摘要输入:
- `article.title`
- `article.url`
- `article.plain_text`
- `article.quality_flags`
- `warnings`
这样做是为了减少 payload,降低模型超时概率,也让 prompt 更聚焦。
---
## 5. 首次生成
第一次生成时,脚本会把:
- prompt 模板
- 结构化文章输入
拼成一个完整请求,发给 LLM。
对应函数:
- `build_initial_prompt(...)`
- `call_llm(...)`
模型返回后,脚本会尝试提取一个 JSON 对象。
如果模型输出不是合法 JSON:
- 这一轮直接视为失败
- 会生成一份失败校验结果
- 然后进入修复重试
---
## 6. validator 如何加入流程
validator 不是附加步骤,而是主流程里的硬门槛。
对应函数:
- `validate_llm_result(...)`
校验内容包括两层:
### 6.1 结构校验
- 必填字段是否存在
- 字段类型是否正确
- `category` 是否属于允许枚举
- `summary` 长度是否合规
- `highlights` / `keywords` / `topics` 数量是否合规
### 6.2 业务校验
- `keywords` 和 `topics` 不能重复
- 列表内部不能重复
- `title` 和 `url` 必须与提取结果一致
只有 validator 返回 `valid: true`,这一轮结果才会被接受。
---
## 7. LLM 如何知道自己生成错了
LLM 本身不会主动知道哪些字段不合规。
脚本会在校验失败后,把以下信息重新发给 LLM:
- validator 错误列表
- 原始提取结果
- 当前错误的摘要 JSON
然后要求它:
- 只修复错误字段
- 保留已经正确的字段
- 继续只输出合法 JSON
对应函数:
- `build_repair_prompt(...)`
也就是说:
- prompt 负责提出目标
- validator 负责判错
- repair prompt 负责把错误喂回模型
这三者一起形成自动修复回路。
---
## 8. 时序图
```text
Extracted JSON
|
v
run_summary_loop.py
|
|-- 读取 prompt 模板
|-- 构造摘要输入
|
v
LLM
|
v
摘要 JSON
|
v
validator
|
|-- valid = true ------> 结束
|
|-- valid = false
| |
| |-- errors
| v
| repair prompt
| |
+------> LLM 重试
```
---
## 9. 生成的中间文件
每次执行脚本都会在输出目录下留下调试痕迹:
- `result.loop.attempt-1.raw.txt`
- 模型原始输出
- `result.loop.attempt-1.json`
- 提取出的 JSON 结果
- `result.loop.attempt-1.validation.json`
- 这一轮的校验报告
- `result.loop.json`
- 当前最终结果
这些文件的价值是:
- 可以回看模型原始输出
- 可以定位是 JSON 解析失败还是 schema 校验失败
- 可以看每轮修复到底修了什么
---
## 10. 当前脚本参数
当前脚本支持:
- `--extracted`
- `--prompt`
- `--output`
- `--max-retries`
- `--timeout`
- `--api-key`
- `--model`
- `--api-url`
这意味着它既可以走环境变量,也可以直接用命令行传入模型配置。
---
## 11. 一句话结论
`run_summary_loop.py` 的本质不是“调一次 LLM”,而是“让 LLM 生成结果后必须经过 validator 验收,失败就拿着错误清单继续修”,直到结果通过或达到重试上限。