Files
AgentLab/backend/app/api/inquiry.py
T
ThearchyHelios 7df224106d 审批质询(后端):运行停在审批时,审批人针对报告里的一句话提问,只读 agent 现查现答,问答写进运行、随运行封存
- engine/inquiry.py:开放条件(停在审批且问的是待处理的那张卡;或已封存、核对一致的探索运行,追加在封存之后;
  正式运行封存后、设置关闭、还在执行一律 409);同一运行同时只答一条(进程内登记);回答交给 run_verify_agent
  (不传 emit,数据版本取运行记录,上限和模型按核查设置),任务里给被问的那句、照 judge.prepare 取的证据摘录
  (遮罩照旧、原报告编号标明)、选中的原文和问题,数据源取这次运行用过的查询工具;回答由 compose_doc 组装成文档
  落工件,数字可以引用自己的查询格子或报告已有的指标、运行输入,裸数字被标出;引用到遮罩列的格子渲染成「已遮罩」,
  回答正文和查询清单里出现的遮罩列的值换成「已遮罩」;写入走 append_events,运行在回答期间变了就不写、照实说明,
  触顶和失败照样记一条
- api/inquiry.py:POST 提问、GET 列表(按报告、句子筛选)、GET 质询工件(只认质询引用过的,遮罩照证据面板);
  输入不合法回 {detail, field}
- api/runs.py:质询正在回答时,审批回复(批准 / 驳回)和继续运行回 409 inquiry_busy,拦在记签批人之前
2026-10-05 18:51:51 +08:00

62 lines
2.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""审批质询:审批人针对待审报告里的一句话提问,只读 agent 现查现答(规则和做法见 engine/inquiry.py)。
- POST /api/runs/{run_id}/inquiries:{report, unit, quote?, question, approval_id?} → {inquiry, recorded, note}
- GET /api/runs/{run_id}/inquiries?report=&unit=:{items: [Inquiry]},封存前后的都算,按先后
- GET /api/runs/{run_id}/inquiries/artifacts/{artifact_id}:质询引用过的工件,查询快照的遮罩列照证据面板遮好
不受理:409 {detail, code}(inquiry_not_open / inquiry_busy / inquiry_disabled);输入不合法 422 {detail, field},
field 指明是哪一项(前后端约定的形状,不是 FastAPI 自己那种校验错误数组,所以请求体自己解析)。
"""
from __future__ import annotations
from typing import Any
from fastapi import APIRouter, Header, HTTPException, Request
from fastapi.responses import JSONResponse
from app.api.coded import CodedHTTPException
from app.api.runs import actor_of
from app.engine import inquiry
from app.engine.inquiry import InquiryRefused
router = APIRouter(prefix="/api/runs", tags=["inquiry"])
def _refused(e: InquiryRefused) -> Any:
"""不受理 → HTTP 响应。422 照约定只回 {detail, field};其余带机读码的回 {detail, code}。"""
if e.status == 422:
return JSONResponse(status_code=422, content={"detail": e.message, "field": e.field})
if e.code:
raise CodedHTTPException(e.status, e.message, e.code)
raise HTTPException(e.status, e.message)
@router.post("/{run_id}/inquiries", response_model=None)
async def ask_inquiry(run_id: str, request: Request, x_actor: str | None = Header(default=None)) -> Any:
"""问一个问题。回答要等 agent 查完库(最长约为设置里的时长上限再加半分钟收尾),接口一直等到回答完才返回。"""
try:
body = await request.json()
except (ValueError, UnicodeDecodeError):
body = None
try:
return await inquiry.ask(run_id, inquiry.parse_ask(body), actor=actor_of(x_actor))
except InquiryRefused as e:
return _refused(e)
@router.get("/{run_id}/inquiries", response_model=None)
async def list_inquiries(run_id: str, report: str | None = None, unit: str | None = None) -> Any:
try:
return {"items": await inquiry.list_inquiries(run_id, report=report or None, unit=unit or None)}
except InquiryRefused as e:
return _refused(e)
@router.get("/{run_id}/inquiries/artifacts/{artifact_id}", response_model=None)
async def inquiry_artifact(run_id: str, artifact_id: str) -> Any:
try:
return await inquiry.inquiry_artifact(run_id, artifact_id)
except InquiryRefused as e:
return _refused(e)