💻 源码
# 简历 · 大模型应用开发工程师(土木/建筑行业版)

> 由中医知识图谱项目迁移。同一套架构(采集 → 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>` 名注册结构/建造师协作做条文校核

**背景**:施工现场遇到技术问题要查规范,而规范体系庞杂(国标/行标/地标/图集),条文之间
互相引用、还有新旧版本替代关系。现场人员翻纸质规范效率极低,而通用大模型回答规范问题时
会**编造条号和限值** —— 给出一个"《混凝土结构设计规范》第某条规定保护层厚度为 X mm",
条号和数值都似真非真。规范限值错误直接对应结构安全与验收不合格。目标是把规范条文变成
可查询的知识图谱,让每一条引用和每一个限值都能回溯到真实条文。

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

**七阶段流水线:**

**1. 数据采集**(国标/行标/地标规范全文 + 标准图集 + 施工工艺标准 + 质量验收标准)
- 按**文档顺序**遍历文档结构而非按已知字段名查找 —— 规范的层级(篇/章/节/条/款/项)
  和条文说明的排布因标准而异,按顺序走开新章节才能保住条文的从属关系。
  这在规范场景尤为关键:**条文脱离所属章节含义会变**,同一句限值在"一般规定"下
  和在"抗震设计"下的适用范围完全不同
- **强制性条文单独标记**:黑体字条文(强条)必须与一般条文区分,它们不可通过论证变通
- 断点续传做在文件系统层:目标文件存在即完成;写入走 `.part` 再原子替换。
  规范修订后重采,续传让增量更新只做缺失部分
- 并发采集 + Session 按线程隔离

**2. 条文结构化抽取**(LLM 结构化输出)
- Pydantic 定义 schema 作为全链路契约:实体为**规范标准 / 条文 / 构件 / 材料 /
  技术指标 / 施工工序**;关系为**规定 / 适用于 / 引用 / 替代 / 前置工序 / 检验要求**
- 属性挂在实体上:条文带条号、是否强制性、实施日期、废止日期;技术指标带限值、
  单位、适用条件(环境类别、抗震等级、结构形式);工序带验收节点和隐蔽工程标记
- **限值必须连同适用条件一起抽取**:脱离条件的限值毫无意义且危险 ——
  保护层厚度随环境类别变化,同一个数值在不同环境下可能合规也可能违规。
  这是本项目定义的 P0 约束,限值与条件必须绑定为一个整体,不允许拆开存储
- **条文原文逐字保留,禁止改写**:任何摘要都会让引用失去技术依据意义
- **数值与单位逐字保留,禁止换算**:MPa/kN/mm 原样搬运,换算在下游显式处理
- Prompt 显式要求"条文之间无明确引用关系时返回空结果" —— 模型最危险的行为是虚构
  条文之间的引用,空结果是正确输出
- 成本约束:耗时取决于**输出**规模,长条文(如荷载规范)显著更慢;失败不写文件因此
  留在待办列表,重跑即重试;空结果要写,否则重跑在重复付费

**3. 图谱构建**
- **先在内存聚合再写库**:常见构件(如"梁""柱")出现在数千条条文里,
  逐条 MERGE 等于把同样的工作重做数千遍
- **节点主键是 `(类型, 名称)` 而非名称**:土木领域跨类型同名极其常见 ——
  "混凝土"既是材料也是分部工程,"沉降"既是技术指标也是质量缺陷现象。
  只按名称去重会把它们错误合并
- **条号必须绑定所属标准编号才唯一**:`第 4.1.3 条` 在几十本规范里都存在,
  只按条号去重会把不同规范的同号条文错误合并,这是致命错误。
  主键实际是 `(标准编号, 版本年份, 条号)`
- 标签、关系类型、属性键先过白名单再拼接;值一律参数化
- 属性用 `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**:现场说"钢筋绑得太稀",图里存的是"箍筋间距超限"。
  术语匹配节点弥合这个差距
- **答案生成节点的四条硬约束**:
  ① 结论必须携带标准编号 + 版本年份 + 条号,且只能来自图谱查询结果,
     宁可回答"未检索到相关条文"也不编造条号;
  ② **限值必须连同适用条件一起输出**,不允许给出无条件的裸数值;
  ③ 命中强制性条文时明确标注"强条";
  ④ 涉及已废止版本时提示现行有效版本
- JSON 解析多级降级;Cypher 逐条执行而非批量事务,出错补空列表保持下标对齐

**6. 服务化**
- FastAPI 三接口:健康检查、同步问答、流式问答
- 图是懒加载的模块级单例,`lifespan` 启动预热(FAISS 索引 + embedding 模型约 50 秒);
  预热失败只记日志不阻止启动
- `graph.invoke` 同步阻塞,走 `run_in_threadpool`,否则事件循环被占死、服务退化成单并发
- 流式接口用生产者-消费者:节点在工作线程产出,有界 `asyncio.Queue`(32,形成反压)
  跨回事件循环。工地网络条件差,流式让首个反馈在 1 秒内到达,也更耐受连接不稳

**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. **限值与适用条件绑定**:脱离环境类别/抗震等级的裸数值毫无意义且危险,P0 约束
2. **条号主键**:`第 4.1.3 条` 在几十本规范里都有,主键必须是 `(标准编号, 版本, 条号)`
3. **强制性条文标记**:黑体强条不可论证变通,答案中必须标注
4. **版本替代链入图**:在建项目按开工时规范执行,不能一律给最新版
5. **条文原文逐字保留**:改写会让引用失去技术依据意义
6. **转写节点补全适用条件**:现场提问常省略环境类别,不补全查出的限值就是错的
7. **跨类型同名**:混凝土既是材料也是分部工程,主键必须是 `(类型, 名称)`
8. 定位为规范检索辅助工具,设计与验收结论须注册执业人员签认