5.9 KiB
5.9 KiB
decisions.md — confidence-feedback
Question Pool(grill 阶段)
| # | 问题 | 模式 | 状态 |
|---|---|---|---|
| Q1 | 置信度由谁计算 | user-interview | 已确认 |
| Q2 | 反馈触发哪些后端操作 | user-interview | 已确认 |
| Q3 | CaseLibrary 结构化字段从哪里填 | evidence-driven | 已确认(方案变更) |
| Q4 | 验收口径 | user-interview | 已确认 |
Evidence-Driven 结论
Q3:CaseLibrary 内容来源
初始结论:从 agent_step.thought 提取(grill 阶段)
修正(apply 阶段讨论后):
- 代码证据:
agent_step.thought截断为 2000 字符,modelOutput截断为 500 字符,均不是完整答案 ChatService.executeChat第 269 行已有完整答案answer = response.getText(),但未持久化- 决策:给
DiagnosisSession加answer TEXT字段,Flyway V008 迁移,案例内容直接从session.answer取
User-Interview 确认记录
Q1 — 置信度由谁评估
- 用户原话(grill):"两者都要:规则兜底 + Verifier 主打分"
- apply 后修正:讨论后决定去掉 LLM 自评,仅用规则引擎(见"apply 阶段决策")
- 最终实现:
EvaluationService纯规则,预留llm_opinion扩展位
Q2 — 反馈触发操作
- 用户原话:"写入 DiagnosisSession.feedback 字段, not_useful → 打 BAD_CASE 标记"
- apply 后修正:BAD_CASE 不改 status,feedback 字段本身即为标记(见"apply 阶段决策")
- 最终实现:
FeedbackService只写 feedback + 可选写 case_library,不改 status
Q4 — 验收口径
- 用户原话:"端到端可验证:发一次 chat → 查 DB 看 selfEvaluation 有值 → 提交 feedback → 查 DB 看 feedback + case_library"
- 确认状态:已确认,未变化
Apply 阶段决策(post-grill 重要变更)
决策 A:DiagnosisSession 加 answer 字段
- 问题:案例沉淀需要完整答案,agent_step.thought 被截断,不可用
- 决策:新增
answer LONGTEXT字段,ChatService SUCCESS 分支写入 - 影响:V008 Flyway 迁移,CaseLibraryService 直接读 session.answer
决策 B:去掉 LLM 自评,只用规则引擎
- 问题:LLM 评估自己的答案系统性偏高分;多一次调用消耗 token;Verifier Agent 当前未实现
- 决策:MVP 阶段仅用基于 tool_invocation 的规则引擎
- 理由:规则可解释、可复现、不撒谎;Verifier 留待诊断全链路实现时再做
- 预留:
selfEvaluationJSON 结构保留llm_opinion扩展位,代码底部注释说明接入点
决策 C:BAD_CASE 不改 status 字段
- 问题:status 是执行状态语义(RUNNING/SUCCESS/FAILED),BAD_CASE 是质量标签,两个维度不同;覆盖 status 会破坏统计
- 决策:
not_useful通过feedback字段本身标识,查 BadCase 用WHERE feedback = 'not_useful'
决策 D:评分字段重命名为 evidence_score
- 问题:原名 confidence 容易误解为"答案准确性",实际衡量的是"证据收集充分度"
- 决策:重命名为
evidence_score,明确语义边界 - 边界说明:工具调用能证明 Agent 有尝试收集证据,但无法证明答案无幻觉;这个分数过滤最差情况(无工具调用就给答案),不能识别"调用了工具但结论仍错误"
决策 E:规则输入来源仅限 tool_invocation 事实
- 问题:DateTimeTools、QueryMetricsTools 等非检索工具调用未写入 tool_invocation
- 接受:evidence_score 定义本来就是检索证据充分度,非检索工具排除在外是合理的,不是 bug
- 已知限制:调用了时间工具但 evidence_score = 0 的 session 存在
架构审计记录
- 接口影响:
POST /api/feedback是新接口(L2);ChatService 主流程返回值不变(L1) - 时序验证:tool_invocation 在工具执行时同步写入,evaluate @Async 在 Agent 完成后触发,无竞态问题
- 已接受风险:
@Async失败时 selfEvaluation 保持 null,前端需处理 null- 案例结构化字段(faultCategory 等)暂时填 GENERAL,后续可人工补充
- LLM 自评预留但未实现,Phase 2 再迭代
决策 F:ChatResult record + ChatResponse.sessionId 回传
- 问题:
ChatService内部生成 8 位 sessionId,但从不返回给前端;前端用自己的 sessionId 调 feedback 接口,后端查不到 session(400) - 决策:新增
ChatResult(answer, sessionId)record,executeChatWithStrategy链路全部返回ChatResult;ChatResponse增加sessionId字段;前端读取并闭包绑定至对应消息的反馈按钮 - 影响:
ChatService三个方法签名变更(内部链路),ChatController调用方更新,前端app.js读取新字段
决策 G:反馈 sessionId 闭包绑定而非全局变量
- 问题:最初实现用
this.lastSessionId全局变量,多轮对话时点击早期消息的反馈按钮会提交最新 sessionId - 决策:
createFeedbackBar(sessionId)接收 sessionId 参数,submitFeedback(feedback, bar, sessionId)直接用传入值,不读全局状态 - 效果:每条 AI 回复绑定自己那轮的 sessionId,多轮对话下行为正确
项目技术栈清单
- ChatModel 注入:
@Autowired ChatModel chatModel,通过ModelRoutingConfig路由 - Repository:Spring Data JPA,
Optional<T>返回,方法命名约定 - DTO:独立文件放
dto/包 - 异步:新建
AsyncConfig.java加@EnableAsync(项目原无此配置) - 无 MQ,无加密,工具类直接用 UUID.randomUUID()
- 日志:SLF4J Logger,
LoggerFactory.getLogger() ToolInvocationRepository.findBySessionId已有,可直接用
参考实现文件
ChatService.java:executeChat/executeChatComplex 流程CaseLibraryRepository.findByDiagnosisId:幂等检查用DiagnosisSessionRepository.findBySessionIdToolInvocationRepository.findBySessionId