状态:已定稿,作为后续开发的唯一权威依据;任何变更需同步更新本文档。 最后更新:2026-08-14(与当前 15 类数据模板、两阶段构建器、检索层实现保持一致)
将市场部、财务部、人事部、运营部、采购部的业务台账以「项目」为核心联通为知识图谱, 通过 LLM Agent 提供自然语言问答,答案必须可溯源到原始数据。
| 数据 | 体量 | 来源 |
|---|---|---|
| 项目 | 305(214 服务中已匹配 + 34 服务中未体现 + 57 历史;121 有金蝶编码) | 市场部项目梳理表 + 项目管理表 + 业务大表 |
| 人员 | 256~281(工号 KWL 前缀) | 人事部考勤/排班表 |
| 设备 | 858 | 运营部设备台账(青浦工业园区、徐汇环境监测中心) |
| 考勤记录 | 882(员工×月) | 人事部考勤表 |
| 排班记录 | 986(员工×月) | 人事部排班文件(48 个组织代码) |
| 财务收费明细 | 262 行(合同×月) | 财务部收费进程表 |
| 业务大表(项目扩展) | 282 行(121 父项目 + 80 子项目 + 81 服务点) | 龚天晓字段补充:业态/区域/合同明细/从属关系 |
| 项目档案目录 | 165 项目目录 / 623 合同期文件夹 / 276 续签衔接 | E:\申勤物业项目资料(区域→项目→合同期) |
| 档案文档 | 4657 文件(PDF 3415 为扫描件需 OCR、Word 684、Excel 88、图片 241) | 合同/招标/投标/中标通知书/验收等全生命周期 |
| 员工档案 | 全员花名册(五级公司=项目、岗位名称、职级、司龄、组织) | 人事部员工信息.xlsx |
| 岗位编制 | 项目 × 全岗位编制(预算/项目/标准工时/在岗) | 运营部 9-项目岗位编制汇总.xlsx |
| 维保/采购 | 维保合同台账、耗材汇总、固定资产台账 | 采购部常用表格 |
| 岗位薪资标准 | 区域 × 岗位 税前/税后区间 | 各管理处岗位薪资1.xlsx(系统侧参考,不进模板) |
| 人员证书 | 一人多行(华东区域/申勤两 sheet),含 工号/职位/专业类别/证书类别/证书名称 | 合景悠活职称及资格证书管理登记表(2026年第一季度) |
| 数据模板 | 15 类、每份一个数据表(+填写说明),含必填唯一标识与字段依据 | data/templates |
| 测试数据 | 15 类与模板一一对应(16 项目/34 人员/33 考勤/9 证书等) | data/test_data |
| 时间覆盖 | 2026-01 ~ 2026-04(科创含 25.12) | — |
| 层 | 选型 | 说明 |
|---|---|---|
| 环境 | uv(pyproject.toml + uv.lock) | Python 3.11+,依赖锁版本 |
| 图存储 | Neo4j 5+(neo4j://127.0.0.1:7687) | 单一全时间图,不做时间切片 |
| LLM | DeepSeek(deepseek-v4-flash,OpenAI 兼容) |
base_url / key / model 存 .env,模型名可配置;SDK:openai |
| 语言 | Python 3.14(当前 venv) | pandas / openpyxl / xlrd / neo4j / python-docx / pypdf / openai |
状态:🟧 橙色 = 规划中(DMS 模型元数据已识别,但数据值拉取脚本、
merge_dms_to_templates合并脚本、人工实际填写补填清单尚未完成);🟩 绿色 = 已实现(15 类模板与测试数据、graph/reader字段名写死校验、两阶段构建、引用存在性校验,测试数据端到端跑通)。
问答层各节点职责:
| 编号 | 节点 | 输入 → 输出 | 关键逻辑 |
|---|---|---|---|
| ① | 问题理解 understand |
用户问题 + 最近对话 → 分类 + 槽位(一次 LLM 调用) | 闲聊走 chat 结束;图谱检索进入接地;否认确认时携带反馈重新理解 |
| ①′ | 上下文复用 reuse_check + answer_reuse |
当前问题 + 历史轮次 → 能否直接推导 | 仅在有历史轮次时触发;历史包含全部事实则直接回答(标注"基于前序对话推导"),省去重新检索;缺数据/新实体/新筛选 → 走完整检索 |
| ② | 实体接地 ground |
原始槽位 → 接地槽位(锚点→主键/标签/置信度、时间归一化、意图→候选边) | 词法精确/包含/相似度 → Qwen3-Embedding 兜底;高置信度替换主键 |
| ③ | 能力拓展 capability |
问题点 + 元知识图谱结构 → 派生规则(概念/来源字段/表达式/建议参数) | 量化条件/未接地实体/筛选值未对齐 三类问题点触发;LLM 动态推理换算关系,结果注入计划器 |
| ③a | 能力拓展检查 cap_check + cap_llm_check |
派生规则 → 检查报告 | 规则层:表达式可解析/参数与概念一致/表达式引用来源字段;LLM 层仅对未注册新概念审查;不合格带反馈重试(≤3 次) |
| ④ | 人工确认 confirm |
接地槽位 → 自然语言确认文案 | LangGraph interrupt 中断等用户回复;否认带反馈回 ② |
| ⑤ | 制定计划 plan |
问题 + 接地槽位 + 元图谱结构 → 查询步骤图(工具/参数/返回字段/依赖) | 一次 LLM 调用完成规划+字段规划;必须包含派生换算来源字段;执行层再强制并入兜底 |
| ⑤a | 计划确认 confirm_plan |
查询计划表 → 人工确认/修改 | 首次计划与用户修改后的计划需确认;系统自动重试(执行检查/审查反馈)直接执行不打断;派生参数标注换算来源(如 年龄上限 ← 出生日期) |
| ⑥ | 执行子查询 run |
步骤图 → 检索子图(每步实参/丢弃参数/Cypher/行数/溯源) | 参数别名映射 + 工具签名校验;内置 聚合统计 / 缺勤分析;--debug 可看实际查询 |
| ⑥a | 执行检查 run_check + run_llm_check |
子图 → 检查报告 | 规则层:执行失败/未知工具/参数被丢弃/空结果;LLM 层:结果可信度与问题契合度审查;问题带反馈回 ⑤ 重规划(≤3 次) |
| ⑦ | 审查契合度 reflect |
子图 + 接地槽位 → 契合判定 | 规则前置(子图非空且锚点置信≥0.5 直接契合)→ LLM 审查;不契合回 ⑤(≤3 次) |
| ⑧ | 范围收缩 scope |
子图行数 > 30 → 用户选项 → 回 ⑤ | interrupt 中断;同一问题只询问一次(scope_offered 防死循环) |
| ⑨ | 综合回答 answer |
检索子图 + 用户问题 + 图谱结构 → 带溯源回答 | 只依据子图事实,缺失明确说"数据中未找到" |
实现:src/step3_qa_agent/agent/graph.py(LangGraph 有向图);scripts/ask.py --debug
可查看每步实际执行的参数与 Cypher(见 6.4)。
| 节点 | 来源部门 | 属性(与模板一致) |
|---|---|---|
| 项目(枢纽) | 市场部 | 编号* / 名称* / 简称 / 上级项目编号 / 续签前项目编号 / 起止时间 / 省份 / 市(区)/ 细分业态 / 汇总业态 / 委托方式 / 计费方式 / 合同面积 / 合同金额 / 年化合同额 / 合同额具体信息 / 服务期限 / 合同状况 / 首次服务日期 / 年化合同收入 / 客户满意度 / 客户投诉 / 甲方名称 / 项目地址 / 项目负责人 / 项目负责人工号* / 实际毛利率 / 服务状态 / 备注 |
| 人员 | 人事部 | 姓名* / 工号* / 组织名称 / 岗位名称* / 当前服务项目编号 / 服务起始-结束年月 / 职级 / 员工层级 / 所属组织名称 / 入职-离职日期 / 司龄 / 性别 / 出生日期 / 政治面貌 / 联系电话 / 备注 |
| 片区 | 运营部 | 片区编号* / 片区名称* / 片区负责人工号* |
| 财务 | 财务部 + 人事部 | 财务类型(项目财务/人员财务)/ 年月;项目财务:开票金额、收款金额、口径;人员财务:餐费/加班超时费/国定加班费/值班费/税后工资 |
| 投标记录 | 市场部 | 项目编号 / 年份 / 投标类型 / 中标结果 / 中标日期 / 投标金额 / 文档路径 |
| 岗位编制 | 运营部 | 项目编号 / 岗位 / 预算编制 / 项目编制 / 标准工时人数 / 在岗人数 |
| 设备 | 运营部 | 项目编号* / 设备编号* / 设备类型 / 名称 / 规格型号参数 / 制造厂商 / 安装位置 / 出厂-启用-入项目-退项目日期 / 出厂编号 / 完好状况 / 原值 / 设备责任人工号* |
| 考勤 | 人事部 | 工号 / 项目编号 / 年月 / 1-31 日状态 / 上班天数 / 加班小时 / 餐费 / 加班费 / 值班费 / 税后工资 |
| 排班月报 | 人事部 | 工号 / 项目编号 / 年月 / 1-31 日班次 |
| 供应商 | 采购部 | 名称 / 编号 / 类别 |
| 维保合同 | 采购部 | 项目编号 / 名称 / 类别 / 合同年限 / 金额 / 月份或日期 |
| 采购记录 | 采购部 | 记录类型(耗材采购/固定资产)/ 项目编号 / 名称 / 数量 / 单价 / 金额 / 状态 |
| 检查记录 | 运营部 | 项目编号 / 检查类型 / 检查日期 / 检查得分 / 问题描述 / 整改情况 / 整改完成日期 |
| 证书 | 人事部 | 工号 / 证书名称 / 专业类别 / 证书类别(保安员(三级)、高压电工作业、物业管理师等) |
月度快照作为第 15 类模板保留,用于项目月度总览;字段可由考勤、排班和项目月度财务聚合生成,也允许固化后导入图谱。
| 起点 | 终点 | 关系 | 时间语义 | 关联键 |
|---|---|---|---|---|
| 人员 | 项目 | 服务于 |
期间边(开始/结束) | 工号 + 项目编号(直接填写 + 考勤/排班连续月份推导) |
| 项目 | 项目 | 包含 |
静态层级 | 上级项目编号(父→子) |
| 项目 | 项目 | 续签自 |
续签链 | 续签前项目编号(新-续签自-旧) |
| 项目 | 片区 | 属于片区 |
M:N 静态 | 项目编号 + 片区编号 |
| 片区 | 人员 | 负责人 |
静态 | 片区负责人工号 |
| 项目 | 人员 | 负责人 |
静态 | 项目负责人工号 |
| 项目 | 投标记录 | 投标 |
年份 | 项目编号 + 年份 |
| 投标记录 | 人员 | 经办人 |
静态 | 投标经办人工号 |
| 项目 | 采购记录 | 采购 |
月份/日期 | 项目编号 |
| 采购记录 | 供应商 | 供应商 |
静态 | 供应商名称 |
| 采购记录 | 人员 | 经办人 |
静态 | 经办人工号 |
| 项目 | 维保合同 | 维保 |
合同期 | 项目编号 |
| 维保合同 | 供应商 | 维保单位 |
静态 | 维保单位名称 |
| 维保合同 | 人员 | 经办人 |
静态 | 经办人工号 |
| 项目 | 岗位编制 | 编制 |
静态/年度 | 项目编号 + 岗位 |
| 人员 | 考勤 | 有考勤 |
月事实边 | 工号 + 年月 |
| 考勤 | 项目 | 所属项目 |
月事实 | 项目编号 + 年月 |
| 人员 | 排班月报 | 有排班 |
月事实边 | 工号 + 年月 |
| 人员 | 证书 | 持有 |
静态 | 工号 + 证书名称(一人多张) |
| 排班月报 | 项目 | 所属项目 |
月事实 | 项目编号 + 年月 |
| 项目 | 财务 | 月度财务 |
月事实边 | 项目编号/工号 + 年月 |
| 人员 | 财务 | 月度财务 |
月事实边 | 工号 + 年月 |
| 财务 | 人员 | 经办人 |
静态 | 财务经办人工号 |
| 项目 | 检查记录 | 检查 |
检查日期 | 项目编号 |
| 检查记录 | 人员 | 检查人 |
静态 | 检查人工号 |
| 设备 | 项目 | 所属项目 |
静态/入项目日期 | 项目编号 + 设备编号 |
| 设备 | 人员 | 责任人 |
静态 | 设备责任人工号 |
服务于)表达人员跨项目流动:一人多条边,如
KWL2307024 = 赵巷镇政府(2026-01~02) + 青浦-北斗园区(2026-03~04)。开始 <= 目标月 AND 结束 >= 目标月;月度事实用年月相等。财务类型 + 年月 为时间键;考勤/排班以 工号 + 项目编号 + 年月 为键。(项目)-[:属于片区]->(片区) 边;片区负责人挂在片区节点上,避免在每个项目行重复填写。5 父、5-1 子)+ 委托方式(全委/多项单委/单一单委 = 正式项目行)。续签自 边;档案目录
(区域→项目→合同期文件夹)由 scripts/analyze_renewal_folders.py 扫描生成
output/续签链-档案目录.csv 回填,目录编号与金蝶编码存在对应规律(102 → XMSQ0102)。| 模板 | 必填列 | 字段依据 |
|---|---|---|
| 项目信息 | 项目编号 / 项目名称 / 项目负责人工号 | 字段依据表标注来源文件+列名;新增融合字段标注"新增(融合)" |
| 项目月度财务 | 项目编号 / 年月 / 财务经办人工号 | 同上 |
| 投标记录 | 项目编号 / 年份 / 投标经办人工号 | 同上 |
| 人员信息 | 姓名 / 工号 / 岗位名称 | 同上 |
| 考勤与人员财务 | 姓名 / 工号 / 项目编号 / 年月 | 同上(1-31 日状态代码含义见说明) |
| 排班月报 | 姓名 / 工号 / 项目编号 / 年月 | 同上(1-31 日班次代码 + 常用对照见说明) |
| 岗位编制 | 项目编号 / 岗位 | 同上 |
| 设备信息 | 项目编号 / 设备编号 / 设备责任人工号 | 同上 |
| 采购与维保 | 记录类型 / 项目编号 / 经办人工号 | 同上(供应商/维保合同/耗材采购/固定资产四类共用) |
| 检查记录 | 项目编号 / 检查人工号 | 同上 |
| 片区信息 | 片区编号 / 片区名称 / 片区负责人工号 | 同上 |
| 项目片区关系 | 项目编号 / 片区编号 | 同上 |
| 人员证书 | 工号 / 证书名称 | 同上(登记表申勤 sheet 缺工号,需按姓名在花名册补全) |
模板生成脚本:scripts/generate_templates.py;字段来源说明由该脚本生成模板时维护。
当前 Step2 默认消费 Step1 的 production Excel,并动态读取 metadata 模板和 relation.xlsx。 模板目录由 METADATA_TEMPLATE_DIR 配置,关系目录由 RELATION_DIR 配置,关系文件名固定为 relation.xlsx;默认目录为 data/templates/metadata 和 data/templates。支持项目外绝对路径,相对路径以项目根目录为基准;统一更新的指纹、取数、评估和构图使用相同配置,独立入口保留参数覆盖。 人员按工号、项目按项目编号合并;主键声明位于 data/graph_keys.json。 重复主键统一合并;相同值去重,空值不覆盖非空值,不同非空值保留为列表,保留来源行号。 关系按任意值对匹配,同规则同节点对去重;评分边取最高命中分。主键值缺失的记录逐行警告并跳过,其他有效记录继续构图;warnings 保留文件、行号及缺失字段,skipped_rows 单独统计,不计入 merged_rows。主键配置、表头及关系规则错误仍停止。
关系完全由 relation.xlsx 五列确定:相等、双向 Python in 包含、Qwen3 本地余弦评分。 空值不连边;评分严格大于 .env 中 STEP2_RELATION_SIMILARITY_THRESHOLD(当前 0.8)。 不额外沿用旧构建器中的服务期推导、固定边或标签别名。
LLM 仅接收真实字段、规则与聚合统计并生成元图谱语义说明;结构和计数由代码校验。 先校验及生成元图谱,再分阶段写入 Neo4j,回读数量后发布前端 JSON。 默认入口 scripts/build_graph.py,预检加 --check-only;详细契约、恢复方法和限制见 Step2 生产构图。以下 5.1~5.5 保留为历史实现参考。 Step3 固定查询工具尚未适配新生产标签,不能将本阶段完成等同于生产问答通过。
用户提供 13 个文件数组(每类可多份 Excel),如 data/config_example.json:
{"项目信息": [文件...], "项目月度财务": [...], ...};CLI:uv run python scripts/build_graph.py 配置.json [--clear]。
display 显示属性;当前模板驱动数据模式不再由 Step1 生成月度派生文件;本节仅保留为历史设计参考,不属于当前取数流程。
groupby(项目, 年月),在岗人数 = 当月去重工号数;最终目标:DMS 已有数据 + 员工人工补填 → 生成 15 类模板样式文件 → 图谱构建。
模板字段按来源分三类(见 docs/DMS字段映射_最新范围.md):
| 来源类型 | 含义 | 处理方式 |
|---|---|---|
| DMS来源 | DMS 模型中有对应字段 | 系统自动拉取(需有效 token) |
| 源Excel搬运(DMS未建模型) | 源 Excel 有、DMS 无模型(考勤/排班/设备/检查/月度财务等) | 人工从源文件搬运到模板 |
| 新增融合 | DMS 与源 Excel 均无,为图谱关联/可读性添加 | 人工确认填写(工号/项目编号/片区编号等) |
人工补填清单:data/manual_fill/DMS补数字段清单.csv
(生成脚本 scripts/generate_dms_supplement.py),列出全部需人工补填字段:
51 个源Excel搬运 + 25 个新增融合,逐字段标注来源文件+列、与 DMS 的对齐键、是否必填。
合并流程:
step0_pre_prepare.DmsTokenManager 使用 DMS_USERNAME / DMS_PASSWORD 自动登录;运行期 token 仅存内存,DMS 返回 212 时刷新并重试一次。graph/reader.py——字段名写死校验、必填校验、引用存在性校验(缺项目/人员/片区/经办人/责任人给出警告清单)。scripts/build_graph.py 读模板文件 → Neo4j 两阶段构建。已知数据坑:
Step4 的 /api/data-quality 只读当前发布版本的更新报告。摘要返回问题总数及按模板文件分组的计数;明细请求从该版本 production Excel 中读取全部问题行字段,包含文件名、工作表、行号、缺失字段及未入图说明。来源限制在当前生产目录,响应不缓存,读取期间版本变化返回409。正常行不返回,原始问题行不发送给LLM。
图谱页和独立聊天页顶部显示红色感叹号,点击弹窗按文件切换;每个文件单独计数,字段缺失高亮,保留全部字段(含空值及数值零)。嵌入聊天页不重复提醒;报告不可用有独立状态及重试,零问题则隐藏入口。每30秒及窗口激活刷新当前版本,页面显示已发布批次,不展示失败或未发布批次。
每次 understand 加载 Step2 发布元图谱快照,问题理解提示词包含完整核心结构及评估目录。 理解结果增加 data_needs(数据集/关系 id);程序校验后按需加载 Step1 精简评估, 关系选择自动补齐两端数据集,核对生产文件哈希和字段集合以排除过期报告。 本轮规划、重试、审查和回答共用 qa_context;不同提问重新加载,历史答案跨图谱版本不直接复用。 评估只辅助字段理解和规划,不能代替查询事实。真实检索工具适配仍未完成, 本次本地模拟测试不代表生产问答准确率已通过。详情见 Step3说明。
class QuerySlots(BaseModel):
raw_question: str
projects: list[str] # 原始项目表述
time_range: TimeRange | None # 归一化年月范围
intent: Intent # 考勤/排班/收费/设备/服务期/汇总对比/组合
granularity: Literal["汇总", "明细"] # 默认 汇总
filters: dict[str, str] # 部门/岗位/设备类型等
命名实体走相似度接地;时间走归一化+范围匹配(注入当前日期解析相对时间,图内覆盖 2026-01~04,超出诚实回答);意图/粒度走模板映射。槽位抽取会带入最近 4 条对话 上下文(上下文记忆);用户在确认环节否认时,反馈会回到槽位抽取重抽。
ambiguous);概念接地(agent/concepts.py):实体接地解决"具体名称→节点主键",概念接地解决
"泛化表述→图谱概念(节点/属性/工具)"。用户问题中的 员工/在职年限/人数 等表述,经
同义词子串精确命中 + embedding 相似度兜底,映射到 人员节点/司龄属性/人员工具+count,
结果注入计划器提示词(plan 节点的"用户问题概念解析"),避免"用户说法 ≠ 图谱命名"
导致工具选错(如把人数统计选成服务期)。概念与同义词集中维护在 concepts.py,可扩展。
值接地(agent/value_grounding.py):概念接地管"用户说法→节点/属性/工具",值接地管
"用户筛选词→字段枚举值"。用户说"管理岗"而图谱岗位名称是"项目经理",或"保安"对应
"保安_领班/保安_普保";值接地从图谱加载各字段 distinct 值,先精确/包含匹配,再用
embedding 相似度选 top-k 规范值(ground 节点输出 filters_grounded),计划器用规范值
调用工具(人员(岗位=["项目经理",...])),查询工具支持列表参数(IN 匹配)。
派生属性与能力拓展(agent/derived.py + nodes.extend_capability):图谱只存原始属性 (出生日期/项目起止时间),用户问派生概念(年龄/剩余服务期)。两条路径:
derived.py 声明式规则(RULES:年龄←出生日期、剩余服务期←项目起止时间),
数值条件解析把"50岁以下/3个月内到期/满10年"识别为逻辑参数(年龄上限=50);extend_capability
把"问题点 + 元知识图谱结构"交给 LLM,动态推理换算关系,输出 概念名/来源字段/表达式/建议参数;
计划器据此制定计划,执行层用通用表达式求值器 eval_expr(白名单 AST:today/years_between/
months_between/to_number/replace 与四则运算)在 Python 侧换算过滤。新派生概念无需预注册:已注册规则只是性能快速路径,未知概念(月均成本、续签次数、 人均产值…)由能力拓展节点动态发现;工具与提示词无需逐例修改。
按需返回字段(agent/nodes.py plan_fields + retrieval/templates.py TOOL_FIELDS):
查询工具的 RETURN 字段从写死改为按问题定制——字段规划节点(LLM,需元图谱结构)
为每个步骤选择必要字段,减少返回规模。与能力拓展配合:字段规划必须包含派生换算的
来源字段(source_fields),且执行层 make_and_run 会强制把派生参数对应的来源字段
并入返回列(如 年龄←出生日期 时"出生日期"必然返回),不依赖 LLM 正确性,避免漏字段。
SCHEMA_TEXT 单一事实源(meta/schema.py):问答 LLM 看到的节点/关系结构不再手写,
由 meta/schema.py 的 ENTITIES/RELATIONS 自动生成(未入图实体用 active=False 过滤)。
工具清单、派生清单、字段清单均挂接同一来源,改图谱模型只改 meta,问答提示词自动同步。
数据时间覆盖动态化:SCHEMA_TEXT 每次问答动态组装,年月覆盖从图谱实时统计
(考勤节点 min/max 年月,TTL 缓存 60s,Neo4j 不可用时回退静态默认值),
图谱重建到新月份后计划器自动感知,不再写死。
能力拓展触发泛化(nodes._capability_trigger):不再只限量化条件,三类问题点都会触发 能力拓展:① 量化条件(50岁以下/3个月内/满10年,纯时间如"3月"不触发);② 未接地实体 (锚点未能匹配图谱节点);③ 筛选值未对齐(filters 值接地低置信/ambiguous)。
| 工具 | 输入 | 输出 |
|---|---|---|
query_attendance |
项目编号/年月/工号 | 考勤记录 + 溯源 |
query_schedule |
项目编号/年月 | 排班月报 + 溯源 |
query_finance |
项目编号/年月/财务类型 | 项目/人员财务 + 溯源 |
query_equipment |
项目编号/设备类型 | 设备台账 + 溯源 |
query_service_period |
工号/项目编号 | 服务期边记录 |
query_project_children |
项目编号 | 从属子项目 |
query_project_renewals |
项目编号 | 续签链 |
query_certificates |
工号/项目编号/专业类别 | 持证记录 |
内置汇总工具(非图谱查询,由执行节点计算):聚合统计(sum/avg/count/max/min)、
缺勤分析(服务期在册 vs 考勤出勤,输出 在册/考勤/缺勤人数 + 人员明细)、
项目缺口分析(服务中但无人员服务记录的项目)、交集分析(跨步骤按条件过滤后
取关键字段交集,如 连续N个月都有加班 = 查N个月考勤 + 行过滤 平时加班>0 + 工号交集)。
时间谓词在 Cypher 内下推;输出结构化结果 + 来源说明;每次执行回传实际
Cypher 与参数(QueryResult.query/query_params),供 --debug 输出。
depends),参数用接地后的主键(项目编号/工号/年月);$ 引用 → 参数别名映射(项目编号→project_code 等)
→ 校验工具签名,记录 实参(executed_params)/ 被丢弃参数(dropped_params)
(被丢弃即字段名不匹配,可直接暴露检索问题)→ 执行并回传 Cypher/参数/行数/溯源;scope 中断给用户选项,选择后回计划重查
(同一问题只询问一次,scope_offered 防死循环);scripts/ask.py --debug 输出每步 实参/丢弃参数/Cypher/参数/首行,
状态全程记录 trace(classify/slots/ground/plan/run/reflect/answer 各节点日志)。interrupt 中断等用户确认;否认时带用户反馈回到槽位填充重抽;interrupt 打印给用户,
确认后执行;用户可直接说修改意见("去掉第2步""改用项目列表工具"),反馈回计划器重规划;
派生参数会标注换算来源("年龄上限=50(年龄由出生日期换算)"),中间换算逻辑由
计划器/能力拓展器内部消化,不暴露为多步;系统自动重试(执行检查/审查反馈)不再打断用户,
只在首次计划与用户主动修改后确认,避免重复确认;scope_offered 防死循环);reuse_check 判定可复用后由 answer_reuse
直接回答(标注"基于前序对话推导"),不重复检索,省 2~3 次 LLM 调用与全部查询;
历史数据不足或涉及新实体/时间/筛选时回退完整检索;uv run python scripts/ask.py 交互模式;--auto 自动确认(批处理/评测用);
--debug 显示每步实际执行的 实参/丢弃参数/Cypher/首行(检索字段名排查)。统一部署入口 deploy.sh 负责 uv/Python、锁定依赖、Qwen 模型下载及验证后直接执行更新与启动脚本;模型目录由 EMBEDDING_MODEL_DIR 统一供下载与 Step2/Step3 使用。部署说明见 deployment.md。
部署入口为项目根目录 deploy.sh:templates、dms 操作按顺序运行既有统一更新脚本和服务启动脚本,更新成功后自动启动;start 仅启动。更新失败阻止启动,启动失败不回滚已发布数据;服务在前台运行,已有实例需先停止。
启动:uv run python scripts/start_service.py;监听地址、端口由 .env 的 SERVICE_HOST、SERVICE_PORT 配置(示例 127.0.0.1:8000)。优先级为命令行 --host/--port > 进程环境变量 > 项目 .env;端口须为 1–65535 的整数,配置修改后重启生效。uv run python -m step4_web.api 使用相同配置。
| 接口 | 说明 |
|---|---|
POST /ask |
body {thread_id, query, auto_confirm};graph.astream 异步收集后返回完整结果;遇人工确认时按 auto_confirm 自动确认或返回 need_confirm |
POST /ask/stream |
SSE:event: progress(节点级进度,answer 节点带答案)、event: confirm、event: done(完整结果) |
POST /threads/{id}/resume |
body {reply};恢复被中断会话(确认或修改意见),再次中断则返回 need_confirm |
GET /threads/{id}/history |
返回该会话的 rounds(问题/实体/回答)与 messages |
设计要点:
agent/llm.py 的 chat_json/chat_text 全部用 llm.stream() 收集(无 invoke),
预留 on_chunk 回调,后续可升级为 token 级透传;_astream_run 事件流(节点级进度 + interrupt 循环),
resume 走 _astream_resume,同一套逻辑,无 invoke/stream 双实现;PlainRedisSaver,状态按 thread_id 隔离,
FastAPI async + graph.astream 天然支持多请求并发;PlainRedisSaver,原生 Redis 即可),进程重启不丢上下文;
上下文按 REDIS_CONTEXT_TTL_SECONDS 设置过期时间(默认 15 天);
CLI 的 scripts/ask.py 仍使用 InMemorySaver,仅用于本地调试。knowledge_agent/
├── pyproject.toml / uv.lock
├── .env # NEO4J_* / DEEPSEEK_* / REDIS_URL / TTL(不入库)
├── docs/技术方案.md
├── data/
│ ├── templates/ # 15 类单表模板(+填写说明)
│ ├── test_data/ # 与模板一一对应的测试数据
│ ├── manual_fill/ # 人工补填清单(DMS 无模型字段的填写任务)
│ └── config_example.json # 15 类文件数组示例配置
├── src/
│ ├── step0_pre_prepare/ # DMS 自动认证与构建前模板准备
│ ├── step1_data_aggregation/ # 模板取数、字段画像、LLM 质量评估
│ ├── step2_graph_building/ # 配置、Neo4j、元图谱及全量图谱构建
│ ├── step3_qa_agent/ # 实体检索与 LangGraph 问答 Agent
│ └── step4_web/ # FastAPI 服务
├── scripts/
│ ├── generate_templates.py / generate_test_data.py
│ ├── generate_dms_supplement.py # DMS 补数字段清单
│ ├── export_meta_schema.py # 元知识图谱 JSON 导出
│ ├── build_graph.py # CLI 图谱构建
│ ├── test_retrieval.py # 检索层冒烟测试
│ ├── ask.py # 交互式问答 CLI(含确认/范围收缩)
│ └── analyze_*.py # 档案/排班归属等分析
└── output/ # 生成物(续签链清单等,gitignore)
| 阶段 | 内容 | 状态 |
|---|---|---|
| P0 数据准备 | 项目别名表、组织代码映射、档案目录续签链、口径清单 | 部分完成(续签链/排班归属分析脚本就绪) |
| P1 全量图谱 | 15 类模板解析 + 两阶段构建 + 校验 + CLI | ✅ 已实现,测试数据端到端跑通 |
| P2 实体索引 | 内存索引 + 分层匹配 + 置信度 + 服务中优先 | ✅ 已实现 |
| P3 模板工具集 | 查询函数 + 聚合统计/缺勤分析 + LLM 槽位抽取(DeepSeek JSON) | ✅ 已实现(6.1-6.3) |
| P4 Agent 检索 | Plan/Execute/Reflect + trace + 槽位确认/范围收缩/上下文记忆 + --debug | ✅ 已实现(CLI 交互 + --auto/--debug) |
| P5 接口评测 | CLI→API、评测集(20-30 题:正确率/溯源率/空答率) | 待做 |
| P6 安全与权限 | 权限检查接线、敏感字段脱敏、审计日志、API 认证与限流 | 部分就绪(见第 11 章) |
| 风险 | 应对 |
|---|---|
| 项目名写法不统一/续签同名 | 项目编号唯一键 + 别名表 + 接地"服务中优先" |
| 人员重名 | 工号唯一键 + 当事人工号必填 |
| 岗位名称不统一 | 与岗位编制统一规范名 + 岗位字典校验 |
| 排班组织代码归属推断误差 | 按月细分 + 架构表补全;低置信度询问 |
| 财务口径不确定 | 开票/收款分字段存储并标注 |
| 模板字段名与数据不一致 | 字段名写死校验,缺失即报错 |
| 引用工号/编号不存在 | 构建后引用存在性校验 + 警告清单 |
| 图片数据未入库 | 该域问题明确回答"数据未入库" |
本方案独立成章,后续新增安全处理统一在此登记,并在 11.2 威胁表中补一行、在 11.5 扩展清单中补一项。 每项安全控制必须标注状态:✅ 已实现 / 🟡 待接线(代码或配置已就绪,未接入流程)/ ⬜ 规划中。
guest 仅开放考勤/排班);敏感意图单独管控。| 威胁 | 场景 | 防护措施 | 状态 |
|---|---|---|---|
| 数据篡改 | 用户诱导 LLM 修改/删除图谱数据 | SYSTEM_SAFETY 注入所有问答提示词 + Neo4j 只读账号(规划) | ✅ / ⬜ |
| 越权查询 | staff 查询工资、收费数据 | 权限矩阵拦截(permission_check 节点)+ 敏感字段脱敏 | 🟡 |
| 敏感信息泄露 | 回答中出现税后工资/电话/政治面貌/身份证 | 敏感意图 + 敏感字段双级脱敏 | 🟡 |
| 提示词注入 | 用户输入试图覆盖系统指令 | 系统提示词边界 + 输入规范化 + 输出校验(规划) | ⬜ |
| 幻觉/错误回答 | LLM 编造图谱外数据 | answer 只依据检索子图 + 溯源 + 缺失明说"数据中未找到" | ✅ |
| 凭据泄露 | .env / DMS token 进入仓库或日志 | gitignore + 环境变量;运行期 token 仅内存缓存,不输出、不写回 .env |
✅ |
| 接口滥用 | 高频调用刷接口、恶意批量查询 | API 认证(JWT)+ 限流/配额(规划) | ⬜ |
src/step3_qa_agent/agent/llm.py;chat_json / chat_text 对所有问答 LLM 调用自动拼接安全约束,禁止修改/写入/删除图谱或业务数据;agent/nodes.py answer 节点;.env(gitignore 排除)+ src/step2_graph_building/config.py;.env,config.py 仅做类型化读取;.env.example 仅含占位值。配置文件:data/permissions.json(角色、用户、敏感项,JSON 便于后续接入 API 时按请求携带权限)。
角色 × 意图矩阵:
| 角色 | 考勤 | 排班 | 设备 | 服务期/从属/续签 | 证书 | 收费 | 工资 |
|---|---|---|---|---|---|---|---|
| admin | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| manager | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| staff | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
| guest(默认) | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
sensitive_intents:收费、工资(命中即需权限校验);sensitive_fields:税后工资、加班超时费/国定加班费/值班费、餐费、联系电话、政治面貌、身份证号码等(无权限时从结果中剔除)。待接线内容:
permission_check / denied 节点已定义(agent/nodes.py),尚未挂入 LangGraph 图;confirm 节点之后、plan 之前(意图级拦截);PermissionManager.strip_sensitive 在 answer 前对检索子图执行字段脱敏;user_id 传递链:CLI --user → API 化后由调用方从 token 解析传入。| 编号 | 项目 | 目标 | 位置 | 状态 |
|---|---|---|---|---|
| S-1 | 权限检查接入图 | 意图级授权拦截,未授权返回明确拒绝信息 | graph.py + nodes.permission_check/denied |
🟡 |
| S-2 | 敏感字段脱敏接线 | 无收费/工资权限的角色看不到敏感金额与个人信息 | nodes.answer 前调用 strip_sensitive |
🟡 |
| S-3 | 审计日志 | 每次问答落盘(thread/问题/槽位/接地/计划/子图/回答/耗时/提问人),支持回放与追责 | scripts/ask.py / API 层 + output/audit/ |
⬜ |
| S-4 | 输入安全 | 防提示词注入:输入规范化、恶意指令检测、系统提示词边界强化 | agent/llm.py + 输入预处理 |
⬜ |
| S-5 | 输出安全 | 回答后校验:不输出敏感字段、不产生"已修改/已删除"类误导 | nodes.answer 后处理 |
⬜ |
| S-6 | Neo4j 只读账号 | 问答用只读凭据,构建用写凭据,账号级隔离写权限 | .env + config.py + retrieval/templates.py |
⬜ |
| S-7 | DMS token 管理 | 用户名密码自动登录;可选旧 token 校验;212 时刷新并单次重试;token 不落盘 |
src/step0_pre_prepare/dms_auth.py |
✅ |
| S-8 | API 认证与限流 | FastAPI + JWT,user_id 从 token 取;角色映射;限流/配额 |
API 层(规划) | ⬜ |
| S-9 | 敏感字段清单维护 | 集中维护敏感字段/意图,增删一处生效 | data/permissions.json |
✅ |
| 文件 | 作用 |
|---|---|
data/permissions.json |
权限配置:角色/用户/敏感意图/敏感字段 |
src/step3_qa_agent/agent/permissions.py |
PermissionManager:角色解析、意图校验、字段脱敏 |
src/step3_qa_agent/agent/llm.py |
SYSTEM_SAFETY 只读约束 |
src/step3_qa_agent/agent/nodes.py |
permission_check / denied 节点(待接线) |
.env / .env.example |
凭据(Neo4j / DeepSeek / DMS 用户名密码;DMS token 仅作可选启动兜底) |
scripts/check_dms_auth.py |
DMS 自动登录与 serviceId=2 token 校验(不输出、不落盘) |
部署补充:deploy.sh restart 在准备后通过本机认证控制通道请求受管Uvicorn优雅退出,再启动新实例;持有跨平台实例锁,30秒退出超时不强杀。旧版未受管实例需手动停止一次。
生产查询确认从已校验的查询结构确定性生成中文说明,展示对象、关联、全部筛选条件、分组/去重统计、排序与结果上限,不向用户展示 JSON;确认和修改反馈仍沿用原流程。旧分支也将参数表改为中文条目。聊天页使用本地 Marked 17.0.5 解析 Markdown,白名单 DOM 重建过滤可执行内容;流式以原文缓冲重绘,完成事件采用完整答案,确认后回答复用同一入口。
统一更新版本目录使用北京时间年月日、时分秒、微秒和UUID;清单与报告记录带时区时间。更新成功或失败结束后,在update_lock内保留最多10份受管运行目录,优先保护当前清单全部引用和本轮目录,按创建时间清理最旧版本;旧UUID目录回退最后修改时间。dms更新原样复制元图谱到新版本目录,schema_version不变,消除对旧目录的依赖。清理失败仅提醒并记录retention,后续更新重试;拒绝越界、符号链接和目录联接,不清理未知目录。
生产问答空结果或count/count_distinct为零时,对文本eq/contains条件逐一移除该条件、保留其余过滤和关系,从当前版本图谱取得实际值;标量/列表展开后按包含和字符相似度排序,最多8次探查、每次200组、最多5项候选。用户明确选择后仅替换该条件并重新查询;自动确认不代选,支持都不是、无效回复重选及版本变化拒绝。
生产分支流程为执行→候选澄清→回答;有选择则回执行。候选来自图谱受控查询,不用评估中的历史计数,探查记录与截断标记写入subgraph.suggestions。接口沿用confirm/options/resume;clarify_value不受auto_confirm或CLI --auto跳过。取消保留原查询;答案说明当前条件未匹配,不能断言业务中无人。规划提示词要求把级别和证书名称等复合条件拆到实际属性。
未运行真实LLM规划/答案和浏览器全链路,未重启业务服务。候选使用字符相似度而非语义向量,不保证召回无字面重合的同义词;仅处理文本eq/contains,一次替换一个条件,不自动放宽其他条件。旧版schema分支保持原实现。
clarify_value 支持一个或多个候选。前端候选按钮可切换选中状态,“都不是”与实际候选互斥;接口透传确认类型,普通计划确认仍为单选。后端接受逗号、中文逗号、顿号、分号分隔的序号/完整值及 JSON 字符串数组,严格解析且不做模糊代选。同一查询条件选择多个真实值时编译为 in,不同条件分别替换并继续使用 AND 组合,未选择的筛选和关系保持不变。
生产 Schema v2 问题理解增加可选 simple_query:单数据集计数/属性/列表的完整候选通过字段、原词、槽位覆盖和只读编译校验后,确定性构造计划;复杂/不完整候选及修改反馈继续完整规划。人工确认、版本隔离、查询和候选澄清保持原流程。理解与完整规划改为紧凑 JSON,省略空数组、默认值及重复字段,禁止遗漏条件。两个优化由 QA_FAST_PLAN_ENABLED / QA_COMPACT_INTERMEDIATE_ENABLED 独立控制,默认 true。trace 保存节点耗时、模型流次数、首次输出及字符/可用 token 统计;尚未取得真实性能结果。详见 Step3。
新增独立Redis索引ka:qa:approved:v1,仅在前端点击“答案正确,加入缓存”后保存;确认查询计划不等于确认答案。按精确问题、会话、上下文、数据/Schema/模型/策略版本匹配,永久保存,数据版本发布成功后物理删除全部旧数据版本缓存。缓存命中跳过模型和图谱,保留来源并追加会话。用户可撤销;超时未命中回原流程。checkpoint和答案ID绑定,拒绝客户端答案正文;错误、截断、未完成、改过条件/候选及明确上下文/相对时间问题不缓存。现阶段不跨会话共享、不按语义相似度直接复用;详见Step3及API说明。