# 结果与诊断

四个求解入口都返回 `Solution`。先检查是否有可行解，再读取变量和派生表达式的值。

## 安全读取结果

```python
from optagent import OptModel

model = OptModel()
x = model.int(0, 10)
cost = 2 * x
model.constraint(x >= 3)
model.minimize(cost)
solution = model.solve(time_limit_s=1.0, log_level="off")

print(solution.status, solution.solver_name)
if solution.feasible:
    print(solution.objectives)
    print(x.value, cost.value)
else:
    print(solution.diagnostics)
```

| 字段 | 如何使用 |
| --- | --- |
| `feasible` | 是否取得可行赋值；不能用 `False` 单独判断无解 |
| `objectives` | 按模型声明顺序排列的目标值元组 |
| `status` | 终止状态；不同路径的字符串与大小写可能不同 |
| `solver_name` | 当前包装层标识：`search`、`highs`、`cpsat`、`mathopt` |
| `diagnostics` | 路径相关的诊断字典；用 `.get()` 读取可选字段 |
| `best_bound` | OptX 后端报告的单目标界限；未独立认证，不可用时为 `None` |
| `gap` | OptX 后端报告的相对间隙比值，`0.001` 表示 `0.1%`；不可用时为 `None` |
| `solve_time` | OptX 后端搜索运行秒数，包含后端重试；不可用时为 `None` |

`best_bound` 对最小化目标是下界，对最大化目标是上界。它与 `gap` 描述后端编译问题及其候选，
并不表示通过原模型最优性认证；数值修正后的 `objectives` 可能与后端候选目标有差异。
多目标模型只在 `diagnostics["backend_attempts"]` 中按 `objective_index` 提供各阶段指标，
不会将某一阶段的界限当成整个问题的 `best_bound`。其他求解路径当前不填充这三个便捷属性。
`solve_time` 不含模型转换、原模型验解、连续变量修正或独立认证；完整调用耗时可由业务端计时。

## OptX 的结论与后端信息

`status`、`feasible` 和 `objectives` 给出 OptAgent 本次求解的最终结论。
后端信息保留在 `diagnostics` 中，可按需查询，无需调用者另行选择验证层级：

```python
print(solution.status, solution.feasible, solution.objectives)
print(solution.diagnostics.get("backend_status"))
for attempt in solution.diagnostics.get("backend_attempts", []):
    print(attempt.get("status"), attempt.get("mip_feasibility_tolerance"))
```

`backend_status` 是后端最后一次运行的原始状态；`backend_attempts` 逐次记录有效数值参数、
目标、界限、gap 和耗时。`best_bound`、`gap`、`solve_time` 提供单目标的便捷读取方式。
参数配置及内部策略的记录范围见[求解与配置](../api/solve.md)。

## 无值、超时与取消

未求解或模型变更后读取 `.value` 会抛出 `RuntimeError`。重新求解会清除旧值，因此应在变更或下一次求解前，将所需数值复制到业务数据结构。

本地搜索可能在序列化预算耗尽时返回 `deadline`，外部评价取消时返回 `cancelled`。CP-SAT / MathOpt 在验证前预算耗尽时返回 `UNKNOWN` 且无值。不要把这些状态视为不可行证明。

## 定位耗时阶段

| 路径 | 诊断字段 | 单位 / 含义 |
| --- | --- | --- |
| 本地搜索 | `python_serialize_ns` | Python 序列化纳秒数 |
| 本地搜索 | `python_wire_bytes` | 序列化载荷字节数 |
| 本地搜索 | `python_expression_count` | 表达式数量 |
| CP-SAT / MathOpt | `exact_phase_ms` | 各阶段耗时字典，单位毫秒 |
| CP-SAT / MathOpt | `exact_total_ms` | 包装层总耗时，单位毫秒 |
| CP-SAT / MathOpt 超时 | `termination_phase` | 预算终止阶段说明 |

诊断字段取决于执行到达的阶段。业务控制流优先依赖 `feasible` 和公开结果字段，不要要求每次返回都带有相同的内部统计。

## 常见问题

**模型可以创建，为什么求解器拒绝它？** 统一建模层比单个后端的表达能力更广。检查[路径兼容性](solvers.md)，尤其是非线性表达式、有限域边界和展开规模。

**为什么增加一条表达式后旧值不可读？** 新表达式改变模型，旧解被失效。先完成全部建模，再求解和读值。

**为什么设置一秒预算后调用稍晚才返回？** 预算采用协作式取消，正在执行的操作和结果清理会增加延迟。

**找不到内核或 OR-Tools？** 检查安装所用解释器与运行解释器一致，并按[安装说明](../start/installation.md)安装匹配的包和 extra。
