fix: isolate payment amount evidence
This commit is contained in:
@@ -3,8 +3,9 @@ using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Http.Json;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using MiaoJiZhang.Api.Services;
|
||||
using System.Text.Json;
|
||||
using MiaoJiZhang.Api.Controllers;
|
||||
using MiaoJiZhang.Api.Services;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
@@ -859,6 +860,29 @@ public sealed class RecognitionBatchActionParserTests
|
||||
Assert.Equal(1, action.Confidence);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class RecognitionAmountUpdateEvidenceTests
|
||||
{
|
||||
private static readonly IReadOnlyDictionary<string, string?> EvidenceOwners =
|
||||
new Dictionary<string, string?>
|
||||
{
|
||||
["evidence-a"] = "candidate-a",
|
||||
["evidence-b"] = "candidate-b"
|
||||
};
|
||||
|
||||
[Fact]
|
||||
public void AmountUpdateRequiresLinkedEvidenceAndHighConfidence()
|
||||
{
|
||||
Assert.False(ParseController.CanApplyAmountUpdate(
|
||||
"candidate-a", null, 0.99, EvidenceOwners));
|
||||
Assert.False(ParseController.CanApplyAmountUpdate(
|
||||
"candidate-a", "evidence-a", 0.89, EvidenceOwners));
|
||||
Assert.False(ParseController.CanApplyAmountUpdate(
|
||||
"candidate-a", "evidence-b", 0.99, EvidenceOwners));
|
||||
Assert.True(ParseController.CanApplyAmountUpdate(
|
||||
"candidate-a", "evidence-a", 0.9, EvidenceOwners));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class BudgetRecommendationValidationTests
|
||||
{
|
||||
|
||||
@@ -364,6 +364,9 @@ public class ParseController(AppDbContext db, ILlmClient llm, AgentService agent
|
||||
{
|
||||
var candidateIds = manifest.Candidates.Select(item => item.CandidateId).ToHashSet();
|
||||
var evidenceById = manifest.Evidence.ToDictionary(item => item.EvidenceId);
|
||||
var evidenceOwners = evidenceById.ToDictionary(
|
||||
entry => entry.Key,
|
||||
entry => entry.Value.CandidateId);
|
||||
var selected = modelActions
|
||||
.Where(action => action.CandidateId != null && candidateIds.Contains(action.CandidateId))
|
||||
.GroupBy(action => action.CandidateId!)
|
||||
@@ -401,7 +404,19 @@ public class ParseController(AppDbContext db, ILlmClient llm, AgentService agent
|
||||
transferDirection = candidate.TransferDirection;
|
||||
reason = "转账方向不明确,已保留本地结果";
|
||||
}
|
||||
var amount = model?.Amount is > 0 ? model.Amount.Value : candidate.Amount;
|
||||
var proposedAmount = model?.Amount is > 0 ? model.Amount.Value : candidate.Amount;
|
||||
var amountChanged = proposedAmount != candidate.Amount;
|
||||
var amountUpdateAllowed = !amountChanged ||
|
||||
model?.Action == "update" && CanApplyAmountUpdate(
|
||||
candidate.CandidateId,
|
||||
model.EvidenceId,
|
||||
model.Confidence,
|
||||
evidenceOwners);
|
||||
var amount = amountUpdateAllowed ? proposedAmount : candidate.Amount;
|
||||
if (amountChanged && !amountUpdateAllowed)
|
||||
{
|
||||
reason = "金额修改证据不足,已保留本地金额";
|
||||
}
|
||||
var category = FindCategory(
|
||||
categories,
|
||||
type == "income" || transferDirection == "in"
|
||||
@@ -464,6 +479,18 @@ public class ParseController(AppDbContext db, ILlmClient llm, AgentService agent
|
||||
}
|
||||
return result.Take(20).ToList();
|
||||
}
|
||||
|
||||
internal static bool CanApplyAmountUpdate(
|
||||
string candidateId,
|
||||
string? evidenceId,
|
||||
double confidence,
|
||||
IReadOnlyDictionary<string, string?> evidenceOwners)
|
||||
{
|
||||
return confidence >= 0.9 &&
|
||||
!string.IsNullOrWhiteSpace(evidenceId) &&
|
||||
evidenceOwners.TryGetValue(evidenceId, out var owner) &&
|
||||
owner == candidateId;
|
||||
}
|
||||
|
||||
private async Task<bool> FeatureEnabled(string key)
|
||||
{
|
||||
|
||||
@@ -200,6 +200,7 @@ public partial class OpenAiVisionClient : ILlmClient
|
||||
只有截图明确显示失败、取消、待支付,或明确是同一 flowSessionId 的重复结果页时才可 drop。
|
||||
只有存在没有对应候选的 evidenceId 且截图明确显示交易成功时才可 create,并必须引用该 evidenceId。
|
||||
update/create 的 type 只能是 expense、income 或 transfer,amount 必须大于 0;transfer 必须返回 transferDirection=in|out。
|
||||
update 修改 amount 时必须引用属于该 candidateId 的 evidenceId,且 confidence 不低于 0.9;证据不足时保持候选金额。
|
||||
截图未明确显示时间时沿用候选时间,禁止猜测时间。reason 不超过 40 个汉字。
|
||||
""";
|
||||
var messages = new List<object>();
|
||||
|
||||
Reference in New Issue
Block a user