# 简历 · 大模型应用开发工程师（法律行业版）

> 由中医知识图谱项目迁移。同一套架构（爬取 → LLM 抽取 → 图谱 → Agent → 服务 → 前端 → 微调），
> 换成法律法规检索场景。带 `<>` 的请按实际情况替换。

---

## 基本信息

| | |
|---|---|
| 姓名 | `<姓名>` |
| 岗位 | 大模型应用开发工程师 / 法律 AI |
| 经验 | 4 年（其中大模型应用方向 2 年） |
| 电话 / 邮箱 | `<电话>` / `<邮箱>` |
| 学历 | `<学校>` `<专业>` `<本科/硕士>` |

---

## 技能栏

**大模型应用**
- LangGraph 有状态编排：多节点条件路由、带上限的重试环、多轮对话记忆、流式增量产出
- LangChain 链式调用与结构化输出，OpenAI 兼容端点（DeepSeek / vLLM 自部署）
- Prompt 工程：schema 注入、否定式约束、多级降级解析；法条引用的强制溯源约束

**知识图谱与检索**
- Neo4j：Cypher 读写、批量事务导入、唯一约束、`EXPLAIN` 静态校验、schema 元数据导出
- FAISS + bge-large-zh-v1.5 向量召回，按实体类型过滤
- 法律术语标准化：口语诉求到法定概念的映射（"老板不给钱" → "拖欠劳动报酬"）

**模型微调**：Alpaca 数据集构建、LoRA 微调、vLLM multi-LoRA 部署

**工程能力**：Python 3.10、FastAPI（异步 / 流式 / 线程池）、Streamlit、并发爬虫、
`asyncio.Queue` 跨线程反压、Git / Black

---

## 项目经验

### 法律法规智能问答与检索系统 `<公司/独立项目>`

**角色**：技术负责人，负责整体方案与全链路实现，带 1 名实习生做法条校对

**背景**：用户咨询法律问题时用的是生活语言（"公司让我加班不给加班费怎么办"），而法律检索
需要的是法定概念和精确的法条编号。通用大模型回答法律问题时最危险的行为是**编造法条**——
凭空生成一个"《劳动法》第某条"，条号和内容都似真非真，用户无从辨别。目标是把法律条文变成
可查询的知识图谱，答案中的每一条引用都能回溯到真实条文。

**技术栈**：Python 3.10 / LangGraph / LangChain / Neo4j / FAISS / sentence-transformers /
FastAPI / Streamlit / vLLM

**七阶段流水线：**

**1. 数据采集**（法律法规全文 + 司法解释 + 指导案例）
- 从国家法律法规数据库、裁判文书网采集，按**文档顺序**遍历 DOM 而非按已知字段名查找 ——
  法规的层级结构（编/章/节/条/款/项）因法而异，按顺序走才能保住条文的从属关系。
  这一点在法律场景比在其他领域更关键：条文脱离所属章节，含义会变
- 断点续传做在文件系统层：目标文件存在即完成；写入走 `.part` 再原子替换，
  中断不留半截文件。法规修订后需要重采，续传让增量更新只做缺失部分
- 并发采集，Session 按线程隔离；对政府站点保留请求间隔，避免触发封禁

**2. 条文结构化抽取**（LLM 结构化输出）
- Pydantic 定义 schema 作为全链路契约：实体为**法律法规 / 条文 / 法律概念 / 主体 /
  行为 / 法律后果**；关系为**规定 / 适用于 / 违反则 / 引用 / 上位法 / 例外**
- 属性挂在实体上：条文带条号、生效日期、失效日期、修订版本；法规带效力层级
  （法律 > 行政法规 > 部门规章 > 地方性法规）
- Prompt 显式要求"单条文无关联时返回空结果" —— 法律场景下模型最容易犯的错就是
  为了填满格式而虚构条文之间的引用关系，空结果是正确输出
- **抽取时不允许改写条文原文**：`条文内容` 字段必须逐字保留，任何摘要或改写都会
  让后续的引用失去法律意义
- 成本约束：单次调用耗时取决于**输出**规模，长条文（如民法典分则）显著更慢；
  失败不写文件因此天然留在待办列表，重跑即重试；空结果要写，否则重跑在重复付费

**3. 图谱构建**
- **先在内存聚合再写库**：同一个法律概念（如"不可抗力"）出现在数百个条文里，
  逐条 MERGE 等于把同样的工作重做几百遍
- **节点主键是 `(类型, 名称)` 而非名称**：法律领域同名不同义极其常见 ——
  "定金"作为法律概念与作为合同条款行为是两类节点，"第十条"必须绑定所属法规才唯一。
  只按名称去重会把不同法规的同号条文错误合并，这是致命错误
- 标签、关系类型、属性键先过白名单再拼接；值一律走参数化，防注入
- 属性用 `MERGE ... SET n += $attrs`：法规修订后属性变化，写进 MERGE 模式会匹配失败
  并创建重复节点，`ON CREATE SET` 又永远不会回填已有节点
- **时效性建模**：条文节点带生效/失效区间，查询时按时间点过滤 ——
  法律问答必须回答"当时适用哪一版"，而不是只给最新版

**4. 检索层**
- Neo4j schema 元数据导出后注入 Cypher 生成 prompt；关系的中文描述人工补齐
  （含效力层级说明和适用范围），这段说明直接影响生成质量
- FAISS 索引覆盖全部实体类型，**索引本身不带类型**，召回后必须按类型过滤 ——
  否则"欺诈"可能召回一个法规名并落进"法律概念"槽位，生成出错误的 Cypher。
  超额取 `top_k * N` 再过滤，保证过滤后数量足够
- 分级健康检查守住索引与图谱的一致性：自检索（已知实体必须以距离≈0 返回 top-1）
  证明向量与映射未错位；与 Neo4j 对账防止法规更新后索引陈旧

**5. LangGraph Agent**（9 节点、2 条件边、1 个环、3 个出口）

```
START → 语义转写 → 法律意图识别
          ├─ 非法律 ─→ 直接回答 ──────────────────────→ END
          └─ 法律  ─→ 概念抽取 → 概念匹配 → cypher生成 ←──┐
                          → cypher检查                    │
                               ├─ 全部合法 ─→ 执行 → 引注答案 → END
                               ├─ 不合法且未超 3 次 ────────┘
                               └─ 额度用尽 ─→ 直接回答 → END
```

- **语义转写排在最前**：法律咨询天然是多轮的 —— 用户先问"加班费怎么算"，再问"那不给呢"。
  后一句单看没有任何信息，意图识别会判成无关。转写节点结合历史补全成完整问题，
  下游才有东西可用。全图只有转写节点读原始输入，其余节点一律读转写结果
- **重试环的刹车靠状态计数**：路由函数只能读 state 不能记账，重试次数必须由生成节点自增，
  上限 3 次。重试时把上一轮不合法的语句回喂给模型，否则模型输入不变会原样再生成一遍
- **生成的 Cypher 直连生产库，两个独立位置设闸**，prompt 里的禁止写操作不算其中之一：
  ① 关键词词边界扫描 + 必须有 RETURN 且以 MATCH/WITH 开头；② `EXPLAIN` 逐条静态校验
- **绝不把用户原话放进 WHERE**：用户说"老板不给钱"，图里存的是"拖欠劳动报酬"。
  概念匹配节点的存在意义就是弥合这个差距
- **答案生成节点强制引注**：回答必须携带法规全称 + 条号，且这些字段只能来自图谱查询结果，
  不允许模型自行补充。这是整个项目最核心的约束 —— 宁可回答"未检索到相关条文"，
  也不能给出一个编造的条号
- JSON 解析多级降级；Cypher 逐条执行而非批量事务，出错补空列表保持下标对齐

**6. 服务化**
- FastAPI 三接口：健康检查、同步问答、流式问答
- 图是懒加载的模块级单例，`lifespan` 启动预热（FAISS 索引 + embedding 模型约 50 秒）；
  预热失败只记日志不阻止启动
- `graph.invoke` 同步阻塞，走 `run_in_threadpool`，否则事件循环被占死、服务退化成单并发
- 流式接口用生产者-消费者：节点在工作线程产出，有界 `asyncio.Queue`（32，形成反压）
  跨回事件循环。推给前端的是**状态增量**而非全量状态
- 法律咨询的等待容忍度低，流式让"正在检索《劳动合同法》"这类进度可见

**7. 前端与微调**
- Streamlit 多页：常规问答 + 流式页。流式页展示检索过程（命中的法规、生成的查询、
  引用的条文），法律用户尤其需要看到检索依据而非只有结论
- 抽取结果顺带产出 Alpaca 数据集用于微调；两条写入路径 —— 抽取时追加 JSONL
  （并发追加整行不会破坏性交错），另有不调 LLM 的幂等重建脚本覆盖续传跳过的历史条目
- LoRA 微调后 vLLM 部署，multi-LoRA 单服务挂多个适配器（法律 / 中医 / 通用）按需切换

**成果**
- 覆盖 `<N>` 部法律法规、`<M>` 万条条文，schema 6 类实体 6 类关系
- **零编造条号**：所有引用均来自图谱边，检索不到时明确告知而非猜测
- 支持按时间点查询历史版本条文
- 接口层 27 条离线用例（替换假图，不触达 LLM/Neo4j/FAISS，2 秒内跑完）+ 15 条 live；
  前端 49 条离线 + 11 条 live

---

## 行业适配要点（面试可展开）

1. **效力层级入图**：法律 > 行政法规 > 部门规章 > 地方性法规，冲突时按层级取舍
2. **时效区间建模**：条文带生效/失效日期，回答"当时适用哪一版"
3. **同号条文的主键设计**：`第十条` 必须绑定所属法规才唯一，只按名称去重是致命错误
4. **强制引注**：答案里的条号只能来自图谱，宁可说"未检索到"也不编造
5. **条文原文逐字保留**：抽取时禁止改写，改写会让引用失去法律意义
6. 免责声明：系统输出为法律信息检索结果，不构成法律意见，需执业律师复核
