人一次只看一个数字

solve.md中记录的人的解法是这样的。读取盘面,选一个数字。把已经有这个数字的行、列、宫划掉,就会出现只剩一个位置的宫或线。那个格子100%就是答案。填上之后再重新读取盘面。

所以我们把交给VLM的工作缩减成了两项。

  1. 读取盘面:原样读出81个格子的数字。
  2. 确定一个数字,写出所有不能填这个数字的格子。

我们不问哪个格子是正确答案。划掉不能填的格子后剩下的位置是否只有一个,由代码来数。

划掉不能填数字1的格子后剩下的位置
划掉不能填数字1的格子后剩下的位置

训练前的8B

模型换成了Qwen3-VL-8B-Instruct。在不加LoRA的情况下做了20道题,坐标100%答对,但81个格子全部读对的盘面只有20%(按格子计为83%)。不能填数字1的格子按格子计答对了91%,但一个不漏地全部答对的盘面一个也没有。

分阶段训练

两项工作分别进行训练。每个周期都进行测试,加上答错的题再重新训练。

  • 读取盘面:2B从17.5%经过4个周期达到100%,8B从25.2%经过2个周期达到100%。
  • 不能填数字1的格子:2B在9个周期中提高到69.5%,8B在15个周期时提高到97.6%。按格子计为99.98%。

与第2篇不同,这次越训练越提高。因为这是正确答案的依据就在盘面之内的工作。

读取盘面和不能填数字1的格子一个不漏地全部答对的比例
读取盘面和不能填数字1的格子一个不漏地全部答对的比例

这一步的实际代码

这是给VLM的提示词。让它确定一个数字,只写出不能填的格子。

vlm_prompt.pypython · 28 行
def build_digit_illegal_prompt(
    board: list[list[int]],
    given_mask: list[list[bool]],
    digit: int,
    *,
    retry_hints: list[str] | None = None,
) -> str:
    """digit 의 비합법수(둘 수 없는 모든 칸) 나열."""
    board_txt = board_to_text(board, given_mask)
    placed = sum(1 for r in range(9) for c in range(9) if board[r][c] == digit)
    prompt = (
        "You are solving Sudoku like a human (solve.md digit tracking).\n"
        f"{_COORD_HEADER}"
        f"{_HUMAN_RULES}"
        "The board has been read. Now track ONE digit on the number pad.\n"
        f"TARGET DIGIT: {digit} (already placed {placed} times on the board).\n\n"
        f"Task: list EVERY cell where digit {digit} CANNOT be placed.\n"
        "A cell is ILLEGAL for this digit if:\n"
        f"- it is filled with a digit other than {digit}, OR\n"
        f"- it is EMPTY but its row, column, or 3×3 box already contains {digit}.\n"
        f"Do NOT list cells that already contain {digit} — those are legal placements.\n"
        "You MUST include ALL illegal cells. Do not omit any.\n"
        "Use compact JSON (no spaces) to fit all cells.\n\n"
        f"Text grid:\n{board_txt}\n\n"
        f'Return ONLY JSON: {{"digit":{digit},"illegal":[[row,col],...]}}\n'
        f'Example: {{"digit":{digit},"illegal":[[0,3],[1,5],[2,8]]}}\n'
    )
    return _append_retry_hints(prompt, retry_hints)

这是生成训练用正确答案的函数。只根据行、列、宫中是否已经有该数字来确定。

vlm_board.pypython · 18 行
def illegal_cells_for_digit(
    board: list[list[int]], digit: int
) -> list[tuple[int, int]]:
    """digit 을 둘 수 없는 모든 칸 (이미 digit 인 칸은 제외)."""
    from sudoku_human_solver import cell_candidates

    out: list[tuple[int, int]] = []
    for row in range(9):
        for col in range(9):
            v = board[row][col]
            if v == digit:
                continue
            if v != 0:
                out.append((row, col))
                continue
            if digit not in cell_candidates(board, row, col):
                out.append((row, col))
    return out

下一篇是“4. 训练前后的解法对比”。