src/step3_qa_agent/agent/ 负责问题理解、复用、接地、规划、执行审查和回答;
retrieval/ 负责受控 Neo4j 查询。答案必须来自实际检索结果,评估报告不能充当业务事实。
understand 每次执行时读取 output/meta_graph_schema.json,建立本轮快照。
问题理解提示词包含节点、属性、主键、合并/多值规则、关系方向/连接字段/判断方法和统计,
同时提供可用数据评估的目录;此时不放入评估正文。data_needs: {datasets: [节点id], relations: [关系id]}。
LLM 结合当前问题与历史对话选择所需数据;跨节点关系自动纳入两端数据集。
未知节点、未知或歧义关系会被排除并记录,不能扩大到全部评估。data/production/analysis/qa_data_context.json 中选取对应评估,
核对 source_sha256 与当前生产工作簿,以及字段集合与本轮元图谱是否一致。
只使用 Step1 精简评估;不会读取完整分析报告的原始数据,也不会临时重跑评估。AgentState.qa_context。规划、能力拓展、复用判断、审查、反思和回答均读取同一快照。
提问间重新加载;同一轮重试或中断恢复期间不静默换版。问题理解未返回有效 data_needs 时,仅以本轮问题和槽位做关键词兜底并记录该状态, 不继承上次选择。有效的空选择保持为空。闲聊也带本轮元图谱,但不注入业务评估正文。
agent/question_context.py:本轮快照、语义选择、关系端点补全和评估有效性校验。agent/data_context.py:评估摘要格式化,保留原有关键词调用兼容。agent/schema_context.py:优先从本轮状态组装提示词,保留旧独立调用入口。agent/nodes.py / agent/state.py:问题理解输出、状态传递及历史版本标记。uv run python scripts/test_qa_question_context.py
uv run python scripts/test_data_analysis.py
uv run python scripts/test_step2_production.py
本轮10+3+13项本地测试通过;新测试使用模拟 LLM,并实际运行 LangGraph “问题理解→规划”的状态传递路径。覆盖多轮隔离、同轮快照、跨图谱版本复用、 选中数据集与关系端点、缺失/过期评估、闲聊及元图谱加载失败。
真实本地文件核验:人员选择仅加载人员评估;服务于关系加载人员与项目评估, hash和字段校验均通过。没有发送真实业务问题或评估内容到外部模型,尚不能据此 宣称真实问答准确率提高;需在查询工具适配后执行业务问题集验收。
生产 Schema v2 在一次问题理解中同时提取可选的 simple_query。仅支持单数据集的
节点去重计数、属性查询和明细列表,最多6个明确文本等于/包含的 AND 条件。
候选必须声明完整、与原始 data_needs 单数据集/无关系选择一致,筛选值必须来自本轮问题,
已抽取的实体及筛选槽位不能遗漏,字段和结构必须通过现有只读编译器校验。
计数、关系或字段口径不明确时应返回 null。模型对完整性的判断仍需真实问题验收。
命中时程序构造计划,省去单独规划模型调用;仍经过中文计划确认、版本检查、 实际 Neo4j 查询、空结果候选及溯源回答。跨关系、时间/数值条件、分组排序、复杂表达、 历史指代、未知字段、缺失候选、修改反馈都回退完整规划。每次理解重置候选,旧会话不继承。
理解与完整规划均要求紧凑 JSON,不输出额外分析文字;省略空数组/空槽位、默认 limit=50、
重复 fields/depends。所有实际筛选、关联、分组、排序、返回字段和统计口径必须保留。
简单计数候选最小结构为:
{"kind":"count","dataset":"人员信息","complete":true}。
最终答案仍按原有规则生成,不对 JSON 使用可能导致截断的硬性 token 上限。
可选环境变量(未设置时默认 true,支持 true/false/1/0):
| 变量 | 作用 |
|---|---|
QA_FAST_PLAN_ENABLED |
单数据集快速规划 |
QA_COMPACT_INTERMEDIATE_ENABLED |
理解与完整规划的紧凑输出 |
两个都设为 false 可恢复原理解/规划提示词及两次调用路径。修改配置或代码后重启服务, 无需重新取数或构图。
api_log 中 state.trace 的 understand/production_plan 新增 duration_ms;
production_plan.mode 为 simple 或 llm,reason 说明命中或回退原因,validation_attempts
记录完整计划结构校验次数。llm_calls 每项记录一个 SDK 流的耗时、首次 chunk/文本等待、
输入/输出字符数及可用的 usage。JSON 解析重试计为不同流;SDK 内部网络重试只能计入耗时,
不能据此声称记录了精确网络请求次数。usage 缺失为 null,不把字符数当 token 数。
耗时不包含人工等待;节点耗时减去流耗时包含上下文、SDK创建等本地处理。
离线回归:
uv run --frozen python -X utf8 -m unittest scripts.test_simple_plan scripts.test_qa_question_context scripts.test_value_clarification scripts.test_plan_presentation scripts.test_runtime_workflow -q。
真实测试脚本 scripts/benchmark_qa_planning.py 交替关闭/开启两个优化,记录理解和规划耗时;
它会调用真实配置的外部模型,须取得授权后运行。加 --execute 执行受控只读图谱查询,
仅保存结果行数、截断标记及按字段/操作归一的结果哈希,不保存原始查询行。
输出默认在 .runtime/qa/planning-benchmark.json。哈希比较需确认同一 build_id、
同一口径且结果未截断;随机模型输出和无排序明细可能不同。单次样本不能代表 P95。
网页版完成生产图谱回答后,在答案下显示“答案正确,加入缓存”。只有点击此按钮, 服务端才把该答案加入独立 Redis 索引;查询计划的“确认”或自动确认不会写入缓存。 保存成功后可“撤销缓存”,缓存命中显示“已复用你确认的历史答案”。
索引使用 ka:qa:approved:v1: 前缀的 Redis String,与上下文 checkpoint 分离。
键包含问题文本哈希、thread_id 隔离范围、最近对话摘要哈希、数据版本、Schema版本、
模型名及缓存策略版本。只去除问题两端空白,不去除否定、数字、标点或实体内部空格;
不做相似度直接复用。新提问先作一次有超时限制的 GET;未命中继续原问答流程。
同一问题连续重复时沿用原问题的上下文签名;中途问过其他问题会重新计算签名,
因此可能不命中。带明确历史指代或相对时间的问题保守地不缓存。
命中后直接复用用户确认的答案、查询计划和来源,跳过模型及图谱查询, 但仍追加本轮历史并保存新的 checkpoint;关闭页面“历史复用”可强制正常问答。 目前API没有真实鉴权,缓存仅按会话隔离,清空/新建会话不会共享已有缓存。 未来接入鉴权后须把实际用户/租户/权限版本纳入键,不能使用当前固定 user_id=api 共享。
仅接收答案ID及checkpoint ID,不接收浏览器提交的答案正文。服务端读取该会话不可变 checkpoint并校验完成状态,所以旧答案按钮也不会误确认后来的另一条回答。 过期checkpoint无法确认。错误、闲聊、不支持、截断/缺失结果、待澄清、 修改过计划/替换过候选、依赖上下文/相对时间及超过256KB的记录不入缓存。 保存查询结果与来源仅用于复用已确认内容,不把评估报告当作事实。
缓存永久保存(不设置TTL),数据版本发布成功后删除全部旧数据版本缓存。写入通过Redis原子脚本核对当前数据版本,不设置过期时间。数据或Schema更新、模型名、 策略版本变化均不再命中旧键。数据版本发布成功后,仅扫描独立问答索引并删除全部非当前数据版本记录,不影响会话上下文及其他Redis数据。清理失败记录警告,服务启动及运行期间每60秒重试;启动时将尚存的当前版本旧TTL记录转为永久。 撤销使用比较后删除,避免并发时删除另一条新确认记录。 缓存读超时/损坏时回退正常问答;写入/撤销失败会明确提示,不显示成功。 底层Redis整体不可用仍会影响原有checkpoint持久化,不承诺整个服务脱离Redis运行。
| 配置 | 默认 | 用途 |
|---|---|---|
QA_ANSWER_CACHE_ENABLED |
true | 独立答案缓存及确认入口开关 |
QA_ANSWER_CACHE_TTL_SECONDS |
已停用 | 旧配置被忽略;问答缓存永久保存 |
QA_ANSWER_CACHE_POLICY_VERSION |
1 | 提示词/权限/回答策略变化时主动递增以失效旧缓存 |
修改代码或配置后重启服务,无需取数或构图。现有缓存不会自动认定旧上下文中的答案正确, 历史答案必须由用户通过对应按钮确认。
主要实现:src/step4_web/answer_cache.py、api.py、
html/answer_cache.js、html/answer_cache.css;根页iframe版本同步更新。
离线验证:
uv run --frozen python -X utf8 -m unittest scripts.test_answer_cache -q;
页面:node scripts/test_answer_cache_ui.cjs(需Playwright和Edge)。