# 简历 · 大模型应用开发工程师（畜牧业版）

> 由中医知识图谱项目迁移。同一套架构（采集 → 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；弱网环境下的移动端适配

---

## 项目经验

### 畜禽疫病诊断与养殖知识图谱问答系统 `<公司/独立项目>`

**角色**：技术负责人，负责整体方案与全链路实现，与 `<N>` 名执业兽医协作做内容审核

**背景**：中小养殖户遇到畜禽异常时，最近的兽医可能在几十公里外，而疫病扩散以小时计。
养殖户描述症状用的是方言口语（"猪不吃食耷拉着""鸡拉白屎"），而兽医知识库里是标准病名。
通用大模型的问题是会编造用药方案，尤其是**给出错误剂量或忽略休药期** —— 后者直接导致
畜产品药残超标，整批货被拒收。目标是把疫病手册、饲养规程、兽药说明书变成可查询的
知识图谱，让诊断建议可回溯，且用药方案强制携带休药期。

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

**七阶段流水线：**

**1. 数据采集**（疫病诊断手册 + 兽药说明书 + 饲养管理规程 + 防疫政策）
- 按**文档顺序**遍历文档结构而非按已知字段名查找 —— 不同来源的疫病资料章节命名与层级
  完全不统一（临床症状/病理变化/剖检变化/鉴别诊断混用），按顺序走开新章节才能完整保留。
  漏掉"鉴别诊断"章节会让相似症状的疫病无法区分
- 断点续传做在文件系统层：目标文件存在即完成；写入走 `.part` 再原子替换。
  防疫政策更新频繁，续传让增量更新只做缺失部分
- 并发采集 + Session 按线程隔离

**2. 疫病知识抽取**（LLM 结构化输出）
- Pydantic 定义 schema 作为全链路契约：实体为**畜种 / 疫病 / 症状 / 病因 / 兽药 /
  防治措施**；关系为**易感于 / 表现为 / 由...引起 / 治疗 / 预防 / 传播途径**
- 属性挂在实体上：疫病带传染性、是否强制上报、潜伏期、死亡率；兽药带剂量、
  给药途径、**休药期**、适用畜种、配伍禁忌；措施带成本和实施窗口
- **休药期是不可省略字段**：抽取时强制要求，缺失即视为抽取失败而非空值。
  这是本项目定义的 P0 约束 —— 漏掉休药期的用药建议会造成药残超标和整批产品报废
- **法定强制上报疫病单独标记**：非洲猪瘟、高致病性禽流感这类必须触发上报提示而非
  仅给治疗建议，答案生成时置顶输出
- 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 直连生产库，两个独立位置设闸**：① 关键词词边界扫描 + 必须有 RETURN
  且以 MATCH/WITH 开头；② `EXPLAIN` 逐条静态校验
- **绝不把用户原话放进 WHERE**：养殖户说"猪站不起来"，图里存的是"后肢瘫痪"。
  术语匹配节点弥合这个差距 —— 方言口语与标准病名几乎没有字面交集，
  这是本项目里 embedding 召回价值最大的一环
- **答案生成节点的四条硬约束**：
  ① 结论必须携带来源（手册章节 / 说明书），来源只能来自图谱查询结果；
  ② 用药建议**必须显示休药期**，无休药期数据则不给该药；
  ③ 命中强制上报疫病时置顶提示上报，且不以治疗建议替代上报；
  ④ 检索不到时明确告知并建议联系执业兽医，绝不编造用药方案
- JSON 解析多级降级；Cypher 逐条执行而非批量事务，出错补空列表保持下标对齐

**6. 服务化**
- FastAPI 三接口：健康检查、同步问答、流式问答
- 图是懒加载的模块级单例，`lifespan` 启动预热（FAISS 索引 + embedding 模型约 50 秒）；
  预热失败只记日志不阻止启动
- `graph.invoke` 同步阻塞，走 `run_in_threadpool`，否则事件循环被占死、服务退化成单并发。
  疫情期间并发咨询量会陡增
- 流式接口用生产者-消费者：节点在工作线程产出，有界 `asyncio.Queue`（32，形成反压）
  跨回事件循环。养殖场网络条件差，流式让首个反馈在 1 秒内到达，
  比等 30 秒后一次性返回的体验好得多，也更耐受连接不稳

**7. 前端与微调**
- Streamlit 多页：常规问答 + 流式页。流式页展示检索过程（识别的畜种和症状、候选疫病、
  引用来源）。针对养殖户优化：字号加大、移动端适配、支持语音输入转文字
- 抽取结果顺带产出 Alpaca 数据集用于微调；两条写入路径 —— 抽取时追加 JSONL
  （并发追加整行不会破坏性交错），另有不调 LLM 的幂等重建脚本覆盖续传跳过的历史条目
- LoRA 微调后 vLLM 部署，multi-LoRA 单服务挂多个畜种适配器（猪 / 禽 / 牛羊）按需切换

**成果**
- 覆盖 `<N>` 个畜种、`<M>` 种疫病、`<K>` 份兽药说明书，schema 6 类实体 6 类关系
- **用药建议 100% 携带休药期**，无休药期数据的药不进入推荐，从源头规避药残超标
- 强制上报疫病命中时置顶上报提示，不以治疗建议替代上报
- 方言口语到标准病名的召回是核心能力，弥合养殖户表达与专业术语的鸿沟
- 接口层 27 条离线用例（替换假图，不触达 LLM/Neo4j/FAISS，2 秒内跑完）+ 15 条 live；
  前端 49 条离线 + 11 条 live

---

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

1. **休药期不可省略**：缺失即视为抽取失败，漏掉直接导致药残超标和整批产品报废
2. **畜种入主键**：同名病在猪和禽上用药完全不同，很多兽药禁用于特定畜种
3. **强制上报疫病**：非洲猪瘟等命中即置顶上报提示，不以治疗建议替代
4. **禁用药硬拦截**：禁用清单入图，在答案生成层拦截而非依赖 prompt 约束
5. **方言召回**："猪站不起来" → "后肢瘫痪"，字面无交集，embedding 召回价值最大的一环
6. **转写节点补全畜种**：养殖户常省略主语，补错会导致跨畜种用药
7. **弱网适配**：流式让首个反馈 1 秒内到达，比一次性返回更耐受不稳定连接
8. 定位为辅助工具，诊断与处方须执业兽医确认
