Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Documentation/Validation.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# 1.8.0 validation · 2026-10-02

- 72 Swift tests pass. New coverage: a full review calls the synthesizer once and a quick review never does; the synthesized report keeps the app's median score and diagnostics, keeps only corrections anchored in the answer, and becomes the conclusion, feedback and improved version; usage includes the synthesis call; a failed synthesis leaves the marks intact, falls back to the local merge and records why; English I translation totals are the sum of per-sentence medians; the synthesis prompt carries the fixed marks and forbids mentioning examiners.
- The consolidated review page was rendered offscreen. No paid live grading was run.

# 1.7.0 validation · 2026-10-02

- 69 Swift tests pass. New coverage: English I translation totals reconcile to the sum of segment marks (allowing a single 0.5 typo deduction), out-of-range segments are dropped, segments are cleared for other tasks; per-sentence marks come from the median reviewer with every judge's mark; legacy reports without segments decode; prompts require segments only for English I translation and the Codex schema includes them; numbered underlined segments are extracted from the question.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ platforms/android/ Android Studio 项目与手机界面
scripts/ 构建、图标生成与显式联调脚本
```

macOS 自动化测试覆盖 69 个案例,Android 有 5 个领域测试与 2 个实际设备服务测试;Windows 通过评分、持久化、字段校验及实际 OCR 自检。GPT-6 Astra/MAX 已通过实际 Swift 子进程完成样例评卷;DeepSeek 已验证官方模型接口连接。后台、流式输出及版本路径已用隔离测试验证;本次未执行完整付费三评,需用户填入有效 Key 后使用。
macOS 自动化测试覆盖 72 个案例,Android 有 5 个领域测试与 2 个实际设备服务测试;Windows 通过评分、持久化、字段校验及实际 OCR 自检。GPT-6 Astra/MAX 已通过实际 Swift 子进程完成样例评卷;DeepSeek 已验证官方模型接口连接。后台、流式输出及版本路径已用隔离测试验证;本次未执行完整付费三评,需用户填入有效 Key 后使用。

<details>
<summary><strong>更多文档</strong></summary>
Expand Down
7 changes: 7 additions & 0 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# 1.8.0 · 三评汇总成一份报告

- 三位评审独立评完后,由一位“主考官”把三份意见汇总成一份报告:一个结论,一份优点、不足和下一稿建议,一份逐句修改,一篇参考改写;英一翻译每句一个分数、一条评语和一组采分点。重复的意见合并,有分歧的地方由主考官核对原文后取舍。
- 分数仍由本机计算,汇总只整理文字、不改分:总分取三人的中位数;英一翻译每句取三人中位分,总分为各句之和(错别字扣分同样取中位)。
- 评阅页只呈现这一份报告;三位评审的原始意见收进页面底部默认折叠的“原始评审意见”。复制的评审结果也是一份。
- 汇总多一次请求(优先 DeepSeek,否则 Codex),可在 Settings 关闭。关闭或汇总失败时,由本机合并三份意见,评分不受影响。快速单评不汇总。

# 1.7.0 · 英一翻译逐句得分与踩点给分

- 英一翻译按大纲规则逐句评分:每句 2 分,拆成 3–4 个意群作为采分点,逐点给分;译文与原意明显不符的句子最多 0.5 分;同一处给出多种译法、其中有错的按错误给分;错别字整题累计满 3 个扣 0.5 分。准确(信)第一、通顺(达)第二,不要求“雅”。
Expand Down
12 changes: 8 additions & 4 deletions WriteBench.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
9EA1454709EC8802D43C7CA4 /* HistoryFolderEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 419DE9ABE9E26F14128B9B1E /* HistoryFolderEditor.swift */; };
A104DD7F66FF512A80BA9717 /* QuestionImportTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29453693001AD3ECDE2185B6 /* QuestionImportTests.swift */; };
A54BFAD371DEB925FCA1963B /* PracticeLoopTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F5E568498AF07D6BF8C1335 /* PracticeLoopTests.swift */; };
A64FFC28FE1B55A2B44B2E4B /* ReportSynthesis.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99093337348356622254C44E /* ReportSynthesis.swift */; };
A840C857A17F4257AD96C9AF /* ReviewTextExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 029A81F1C48B2FAE8914576B /* ReviewTextExporter.swift */; };
A97D07FFDB81393349741F89 /* PracticeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F1B7E5BB4C4D7689B346DE /* PracticeView.swift */; };
C118D4E53ACEC303DE46EA50 /* CorrectionMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1020C698B4DA8C1C47852419 /* CorrectionMatcher.swift */; };
Expand Down Expand Up @@ -131,6 +132,7 @@
89F9C06884EE52989CB4491F /* EssayFolderMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EssayFolderMetadata.swift; sourceTree = "<group>"; };
8F892E2F452C5999A8C08043 /* OCRService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OCRService.swift; sourceTree = "<group>"; };
9238EAE831BF69E5F210D72A /* WorkspaceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspaceView.swift; sourceTree = "<group>"; };
99093337348356622254C44E /* ReportSynthesis.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportSynthesis.swift; sourceTree = "<group>"; };
9AA993471F8CFF92BD9B2956 /* BrandArt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrandArt.swift; sourceTree = "<group>"; };
9B600C2CBBB1B477ABB65C42 /* ImmersiveWritingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImmersiveWritingView.swift; sourceTree = "<group>"; };
9F5E568498AF07D6BF8C1335 /* PracticeLoopTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PracticeLoopTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -244,6 +246,7 @@
1020C698B4DA8C1C47852419 /* CorrectionMatcher.swift */,
CFD296324E8F63CD0B525458 /* GradingProvider.swift */,
634F608AF0DF72D3DE51E438 /* GradingService.swift */,
99093337348356622254C44E /* ReportSynthesis.swift */,
E6B358A2E1043646F5A65FA4 /* UsageCost.swift */,
);
path = Grading;
Expand Down Expand Up @@ -563,6 +566,7 @@
3FF816DF3394076A7EAFCA8E /* QuestionLibraryView.swift in Sources */,
6C3E278AD86905C7C0A63BCE /* QuestionText.swift in Sources */,
17236CF00A287A2737947138 /* QuestionTextReader.swift in Sources */,
A64FFC28FE1B55A2B44B2E4B /* ReportSynthesis.swift in Sources */,
042D9A8FAAC22D71BDF6F7E4 /* ReviewCard.swift in Sources */,
DB3DEEA68D82D9F8BECF108E /* ReviewOutline.swift in Sources */,
A840C857A17F4257AD96C9AF /* ReviewTextExporter.swift in Sources */,
Expand Down Expand Up @@ -659,7 +663,7 @@
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 12;
CURRENT_PROJECT_VERSION = 13;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_NS_ASSERTIONS = NO;
Expand All @@ -674,7 +678,7 @@
GCC_WARN_UNUSED_VARIABLE = YES;
GENERATE_INFOPLIST_FILE = YES;
MACOSX_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 1.7.0;
MARKETING_VERSION = 1.8.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
Expand Down Expand Up @@ -755,7 +759,7 @@
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 12;
CURRENT_PROJECT_VERSION = 13;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
Expand All @@ -776,7 +780,7 @@
GCC_WARN_UNUSED_VARIABLE = YES;
GENERATE_INFOPLIST_FILE = YES;
MACOSX_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 1.7.0;
MARKETING_VERSION = 1.8.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
Expand Down
2 changes: 1 addition & 1 deletion WriteBench/Features/Review/BackgroundGradingView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ struct GradingProgressView: View {
}.padding(24)
ScrollView {
VStack(alignment: .leading, spacing: 16) {
Text(job.phase == .completed ? (job.isQuick ? "快速单评已完成,结论与分数已保存到历史。" : "三位独立评审已完成,完整结论与总分已保存到历史。") : job.phase == .failed || job.phase == .cancelled ? "本次未生成总分。已收到的评语片段不作为最终评分,提交原稿保留在下方。" : job.isQuick ? "快速单评:只请一位评审,速度更快、花费更少,没有评审一致性。" : "进度按实际完成的评审计数。下方为实时评语,总分将在三位评审全部完成后生成。").font(.system(size: 12)).foregroundStyle(WB.secondary)
Text(job.phase == .summarizing ? "三位评审已完成,正在把三份意见汇总成一份报告;分数已按中位数确定,汇总不改分。" : job.phase == .completed ? (job.isQuick ? "快速单评已完成,结论与分数已保存到历史。" : job.session?.report?.synthesis != nil ? "三位独立评审已完成,并已汇总成一份报告保存到历史。" : "三位独立评审已完成,完整结论与总分已保存到历史。") : job.phase == .failed || job.phase == .cancelled ? "本次未生成总分。已收到的评语片段不作为最终评分,提交原稿保留在下方。" : job.isQuick ? "快速单评:只请一位评审,速度更快、花费更少,没有评审一致性。" : "进度按实际完成的评审计数。下方为实时评语,总分将在三位评审全部完成后生成。").font(.system(size: 12)).foregroundStyle(WB.secondary)
ForEach(job.activeJudges) { judge in
Card(padding: 20) {
VStack(alignment: .leading, spacing: 12) {
Expand Down
4 changes: 2 additions & 2 deletions WriteBench/Features/Review/ReviewOutline.swift
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import SwiftUI

enum ReviewSection: String, CaseIterable, Identifiable {
case overview, progress, segments, feedback, examiners, corrections, improved, rewrite
case overview, progress, segments, feedback, corrections, improved, rewrite, examiners
var id: String { rawValue }
func title(isTranslation: Bool) -> String {
switch self {
case .overview: "总分与结论"
case .progress: "与上一稿对比"
case .segments: "逐句得分"
case .feedback: "优点与不足"
case .examiners: "评审意见"
case .examiners: "原始评审意见"
case .corrections: "逐句修改"
case .improved: isTranslation ? "参考改译" : "改进作文"
case .rewrite: "重写"
Expand Down
13 changes: 4 additions & 9 deletions WriteBench/Features/Review/ReviewTextExporter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,21 @@ import Foundation
var sections = [
"WriteBench · \(session.task.fullTitle)\n\(session.date.formatted(date: .abbreviated, time: .shortened))",
"\(report.isDemo ? "演示评分 · " : "")最终得分:\(report.finalScore.scoreText) / \(Int(session.task.maxScore))\n" + (report.gradingMode == .quick ? "评阅方式:快速单评(一位评审)" : "置信度:\(report.confidence.rawValue)\n评审分差:\(report.spread.scoreText)"),
"评阅结论(中位分评审)\n\(report.conclusion)",
"评阅结论\n\(report.conclusion)",
feedback("写得好的地方", report.strengths),
feedback("不足的地方", report.weaknesses),
feedback("下一稿怎么改", report.improvements),
"评分维度(诊断分 / 10)\n\(session.task.isTranslation ? "译义与完整性" : "任务完成度"):\(report.dimension(\.taskCompletion).scoreText)\n语言:\(report.dimension(\.language).scoreText)\n连贯性:\(report.dimension(\.coherence).scoreText)\n语域:\(report.dimension(\.register).scoreText)"
]
if !report.segmentScores.isEmpty {
sections.append("逐句得分(中位分评审)\n" + report.segmentScores.map { item in
sections.append("逐句得分\n" + report.segmentScores.map { item in
(["(\(item.segment.number)) \(item.segment.score.scoreText) / \(item.segment.maxScore.scoreText) \(item.segment.comment)"]
+ item.segment.points.map { " · \($0.source):\($0.earned.scoreText) / \($0.max.scoreText)\($0.note.isEmpty ? "" : "," + $0.note)" }).joined(separator: "\n")
}.joined(separator: "\n"))
}
let reviewers = report.reviewers.map { result in
var lines = ["\(result.judge.title) · \(result.judge.role) · \(result.response.score.scoreText) / \(Int(session.task.maxScore))",
"\(result.provider?.title ?? result.model) · \(result.model)", result.response.summary]
if !result.response.majorErrors.isEmpty { lines.append(feedback("主要问题", result.response.majorErrors)) }
if !result.response.minorErrors.isEmpty { lines.append(feedback("次要问题", result.response.minorErrors)) }
return lines.joined(separator: "\n")
if report.reviewers.count > 1 {
sections.append("三位评审独立评分:" + report.reviewers.map { "\($0.judge.title) \($0.response.score.scoreText)" }.joined(separator: ",") + "(取中位数)")
}
sections.append((report.gradingMode == .quick ? "评审意见" : "三位评审的独立意见") + "\n\n" + reviewers.joined(separator: "\n\n"))
let corrections = report.corrections.enumerated().map { index, correction in
"\(index + 1). \(correction.category.rawValue) · \(correction.severity == .major ? "主要" : "次要")\n原句:\(correction.original)\n修改:\(correction.corrected)\n说明:\(correction.explanation)"
}
Expand Down
Loading
Loading