💻 源码
# 第一步:命令行实现打标

## 一、第一步要达到什么

第一步**不做网页**,只用命令行跑通三种金融文本打标。

学生跑完代码后,终端应看到**清晰的分段结果**,而不是一大段难读的 JSON。

学生只需要知道 5 件事:

1. 配置文件在哪里 → `config.yaml`
2. 提示词文件在哪里 → `prompts/`
3. 测试数据文件在哪里 → `data/`
4. 结果文件在哪里 → `outputs/`
5. 怎么运行 → PyCharm 选 **Label Classify**,或 `python label.py --task classify`

**第一步不要求**:理解 FastAPI、写前端、搭网页。

## 二、PyCharm 运行(推荐)

1. 用 PyCharm 打开 **`financial-labeling-project` 文件夹**(作为项目根目录)。
2. 右上角运行配置选 **Label Classify / Label Extract / Label Match**。
3. 在 **Run → Edit Configurations → Environment variables** 配置 `DASHSCOPE_API_KEY`。
4. 打开 `README.md`,`bash` 代码块左侧有绿色运行按钮(PyCharm 2023.1+)。

项目含 `bootstrap.py`,会自动切换工作目录,一般不需要手动 `cd`。

## 三、学生上手三步走(课堂直接照做)

### 第 1 步:安装依赖

```bash
pip install -r requirements.txt
```

### 第 2 步:确认环境变量

确认本机或 PyCharm Run Configuration 中已设置:

```text
DASHSCOPE_API_KEY
```

### 第 3 步:运行一条命令

PyCharm 选 **Label Classify**,或 README / 终端执行:

```bash
python label.py --task classify
```

看到终端出现**分段摘要 + 汇总表**,并且 `outputs/classify_results.json` 有内容,就算第一步跑通。

## 四、标准项目结构

```text
financial-labeling-project/
  config.yaml
  label.py
  requirements.txt
  README.md
  backend/
    __init__.py
    config.py
    llm_client.py
    label_service.py
    cli_output.py
  prompts/
    classify.txt
    extract.txt
    match.txt
  data/
    classify_samples.json
    extract_samples.json
    match_samples.json
  outputs/
```

学生第一步**只需要关心**:

| 文件/文件夹 | 作用 |
| --- | --- |
| `config.yaml` | 模型名、温度、输出格式、目录路径 |
| `prompts/*.txt` | 提示词,改这里影响模型行为 |
| `data/*.json` | 测试数据,改这里换待打标文本 |
| `outputs/` | 完整 JSON 结果,便于前后对比 |
| `label.py` | 入口,运行打标 |

`backend/` 里的代码可以先**不用细读**,知道「它负责读配置、调大模型、格式化输出」即可。

## 五、config.yaml 规范

项目根目录必须有 `config.yaml`,示例:

```yaml
llm:
  model: qwen-plus
  temperature: 0
  base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
  system_prompt: 你是一个严谨的金融文本分析助手。

paths:
  prompts_dir: prompts
  data_dir: data
  output_dir: outputs

cli:
  output_mode: summary
  show_raw_output: false
  text_preview_length: 60
  save_json: true
```

说明:

| 配置项 | 作用 |
| --- | --- |
| `llm.model` | 默认模型 |
| `llm.temperature` | 采样温度,课堂建议 0 |
| `llm.base_url` | DashScope 兼容接口地址 |
| `cli.output_mode` | 终端输出:`summary` / `json` / `quiet` |
| `cli.show_raw_output` | 是否在终端显示模型原始返回 |
| `cli.text_preview_length` | 输入文本预览长度 |
| `cli.save_json` | 是否保存 JSON 到 outputs |

**API Key 不放 config.yaml**,只从环境变量 `DASHSCOPE_API_KEY` 读取。

## 六、命令行输出规范(必须遵守)

### 默认模式:summary

```text
============================================================
金融文本打标结果 | 任务: classify | 共 3 条
============================================================

[1/3] c_easy_1 | 难度: 简单
输入: 某上市公司发布公告称,预计上半年净利润同比增长 40%。

标准: category = 公司公告
模型: category = 财报解读
对比: 错误
状态: 成功

[2/3] c_medium_1 | 难度: 中等
输入: 受海外降息预期影响,今日 A 股三大指数集体高开...
标准: category = 市场行情
模型: category = 市场行情
对比: 正确
状态: 成功

[3/3] c_hard_1 | 难度: 困难
输入: 公司在业绩说明会上表示,一季度营收同比增 18%...但管理层同时强调...毛利率...不确定性
标准: category = 风险提示
模型: category = 财报解读
对比: 错误
状态: 成功

------------------------------------------------------------
汇总
------------------------------------------------------------
c_easy_1     简单   错误   成功   标准=公司公告 | 模型=财报解读
c_medium_1   中等   正确   成功   标准=市场行情 | 模型=市场行情
c_hard_1     困难   错误   成功   标准=风险提示 | 模型=财报解读

完整 JSON 已保存到: outputs/classify_results.json
```

### 不同任务的结果展示规则

| 任务 | 终端重点展示字段 |
| --- | --- |
| classify | `category` |
| extract | 各字段数组的非空项数量或关键值 |
| match | `is_match`、`score` |

### 提示词输出字段规范

当前阶段**不要求模型返回理由**,只返回打标结果本身:

| 任务 | 模型应返回字段 |
| --- | --- |
| classify | `category` |
| extract | `主体机构`、`时间周期`、`金额数值`、`财务指标`、`业务事件`、`风险因素`、`行业板块` |
| match | `is_match`、`score` |

后续如需可解释性,再在提示词中增加 `reason` 字段。

### 失败时的展示

解析失败时,终端应明确显示:

```text
状态: 失败
错误: 模型返回内容不是合法 JSON
原始返回: {...}
```

### 可选输出模式

```text
python label.py --task classify                  # 默认 summary
python label.py --task classify --output json    # 调试完整 JSON
python label.py --task classify --output quiet   # 只看保存路径
python label.py --task classify --difficulty easy  # 只跑简单样本
```

## 七、运行方式

安装依赖后,依次运行(PyCharm 可用右上角 **Label Classify** 等配置):

```bash
python label.py --task classify
python label.py --task extract
python label.py --task match
```

运行后:

- 终端:看 `summary` 分段结果
- 文件:看 `outputs/{task}_results.json` 完整记录

## 八、outputs 文件格式

保存到 `outputs/` 的 JSON 应**精简可读**,默认不包含重复的 `raw_output`(解析成功时):

```json
[
  {
    "id": "c_easy_1",
    "difficulty": "easy",
    "task": "classify",
    "input": {
      "text": "某上市公司发布公告称,预计上半年净利润同比增长 40%。"
    },
    "expected": {
      "category": "公司公告"
    },
    "result": {
      "category": "财报解读"
    },
    "is_correct": false,
    "status": "success"
  }
]
```

解析失败时才附加 `raw_output` 和 `error` 字段。

## 九、测试数据说明(已内置三档难度 + 标准标签)

`data/` 中每个任务文件都包含 **easy / medium / hard** 三条示例,并且必须带**标准答案**,方便和模型输出对比:

| 任务 | 标准答案字段 | 示例 |
| --- | --- | --- |
| classify | `expected_category` | `"公司公告"` |
| extract | `expected_result` | 中文字段 JSON 对象 |
| match | `expected_is_match` | `true` / `false` |

分类数据示例:

```json
{
  "id": "c_easy_1",
  "difficulty": "easy",
  "text": "某上市公司发布公告称,预计上半年净利润同比增长 40%。",
  "expected_category": "公司公告"
}
```

抽取数据示例(字段名与抽取值均使用中文,便于金融分析):

```json
{
  "id": "e_medium_1",
  "difficulty": "medium",
  "text": "比亚迪公告称,受新能源汽车销量增长影响,公司预计上半年净利润同比增长 20% 至 35%。",
  "expected_result": {
    "主体机构": ["比亚迪"],
    "时间周期": ["上半年"],
    "金额数值": [],
    "财务指标": ["净利润同比增长20%至35%"],
    "业务事件": ["新能源汽车销量增长"],
    "风险因素": [],
    "行业板块": ["新能源汽车"]
  }
}
```

抽取字段说明:

| 字段 | 含义 | 分析用途 |
| --- | --- | --- |
| 主体机构 | 公司、银行、基金、监管机构 | 识别分析对象 |
| 时间周期 | 报告期、日期、统计时段 | 时间序列对齐 |
| 金额数值 | 金额、规模(保留原文单位) | 量化分析 |
| 财务指标 | 营收、净利润、同比、毛利率等 | 核心财务分析 |
| 业务事件 | 并购、分红、产销变化等 | 事件驱动分析 |
| 风险因素 | 风险点、不确定性 | 风险监控 |
| 行业板块 | 行业、细分赛道 | 行业对比 |

| 难度 | 特点 | 适合做什么 |
| --- | --- | --- |
| easy | 意图明显 | 验证代码是否跑通 |
| medium | 信息点较多 | 测试提示词是否够用 |
| hard | 多重歧义/指代/嵌套,程序校验保证难度 | 分析提示词缺陷、观察模型边界 |

## 十、学生课堂练习(10 分钟就能完成)

### 练习 A:跑通(必做)

```text
python label.py --task classify
```

确认终端有分段输出和汇总表。

### 练习 B:改配置(必做)

打开 `config.yaml`,把 `cli.output_mode` 改成 `json`,再运行一次,观察终端变化。

改回 `summary` 后继续。

### 练习 C:改提示词(必做)

打开 `prompts/classify.txt`,删掉「只能从给定类别中选择」,保存后重新运行。

对比终端汇总表和 `outputs/classify_results.json` 的变化。

## 十一、常见问题

| 问题 | 解决办法 |
| --- | --- |
| 提示 `DASHSCOPE_API_KEY 未设置` | 检查环境变量是否配置 |
| 终端输出一大段 JSON 看不清 | 检查 `config.yaml` 中 `cli.output_mode` 是否为 `summary` |
| 返回不是合法 JSON | 终端会显示「状态: 失败」和原始返回 |
| 不知道改哪里 | 改 `config.yaml`、`prompts/` 或 `data/` |

## 十二、给 AI 编程工具的生成计划(可直接复制)

```text
请帮我开发一个「金融文本大模型打标」的命令行入门项目,要求标准 Python 项目结构、配置可控、终端输出清晰。

一、项目目标
学生通过运行 Python 命令,使用 DashScope 大模型对金融文本做三种打标:
1. 金融文本分类
2. 金融文本信息抽取
3. 金融文本匹配

二、项目结构(标准 Python 布局)
financial-labeling-project/
  config.yaml
  label.py
  requirements.txt
  README.md
  backend/
    __init__.py
    config.py
    llm_client.py
    label_service.py
    cli_output.py
  prompts/
  data/
  outputs/

三、config.yaml 要求
1. llm 段:model、temperature、base_url、system_prompt
2. paths 段:prompts_dir、data_dir、output_dir
3. cli 段:output_mode(summary/json/quiet)、show_raw_output、text_preview_length、save_json
4. API Key 不写进 config,只从环境变量 DASHSCOPE_API_KEY 读取

四、命令行要求
1. label.py 支持 --task classify/extract/match
2. 支持 --output 覆盖 config 中的 output_mode
3. 支持 --difficulty easy/medium/hard 可选过滤
4. 默认终端输出 summary 模式:每条样本分段展示 + 末尾汇总表
5. 禁止默认直接 print 整段嵌套 JSON

五、核心功能
1. config.py 统一加载 config.yaml
2. llm_client.py 从配置读取 model/temperature/base_url
3. label_service.py 从配置读取路径,完成打标逻辑
4. cli_output.py 负责格式化终端输出
5. 解析成功时 outputs JSON 不含 raw_output;失败时才保留 raw_output 和 error
6. 分类只返回 category;匹配只返回 is_match 和 score;不要求 reason 字段
7. data 文件必须包含标准答案字段,终端和 outputs 要展示标准 vs 模型对比及 is_correct

六、测试数据
每个 data 文件 3 条,含 id、difficulty 字段。

请生成完整可运行代码。
```