mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 04:11:58 +00:00
dde0927460
- 夹住:上一轮只在同一个条件里配对,把找值拆到 ON 和 WHERE、WHERE 和 HAVING、INTERSECT / EXCEPT 两支、包在 CASE / 函数 / 算式里、子查询和 CTE 里,完整手机号就照原样封存、回答正文也跟着不遮。现在整条 SQL 里配对,只有 CASE 的不同分支、IIF 的真假两支、SELECT 里不筛行的不同项(选出列、GROUP BY、ORDER BY)、UNION 的不同支各算各的(_apart,看最近公共祖先);同一个条件里照旧按合起来的真假细看。整数上 <= v、>= v 也隔开相邻的整数(age <= 24 OR age >= 26 就是 age <> 25);BETWEEN v AND v、>= v AND <= v、> v OR < v 条件里哪一层这样写都算(被更宽的范围吸收、旁边有矛盾的条件、隔着别的条件写也认得出),整数的半开区间仍只看整个条件,成百个相邻分档照旧不遮。条件的各层摊开一次反复用,600 档的耗时和上一轮相当 - 分档标签:CASE / IIF 结果全是写死常量的,标签文字是 agent 写在 SQL 里的,不收进遮罩值,规格原句的 SQL、回答里的档名照原样;当分组键(每一档一行)或套在汇总里按条件数个数(SUM(CASE … THEN 1 ELSE 0 END))不算派生值,格子照常显示,分得清哪一档几笔;逐行的标签列、汇总遮罩列本身的值照旧整列遮 - 质询工件的 SQL 检查结果:片段是检查器重新写出的表达式,单拿出来认不出在找哪一个值,改为按整条 SQL 里整个换掉的字面量换(简单 CASE、行值、BETWEEN v AND v 里的短的数也换掉,阈值照原样);说明里照引的取值(「没有码值「3」」)一样换;截断处半截的号码去掉 - 同一个短的数在别处换掉时,别的列上的阈值、LIMIT 照原样:一律换会回退第 2 轮起的「age = 25 AND amount > 25 … LIMIT 25」,写进已知局限并补测试钉住 - 模块说明的已知局限照实改写(分档拆进两个 CTE / FROM 子查询、同一选出列里两档汇总相除会多遮,拆进 CASE 分支 / 不同选出列认不出,运行详情的工件页签不经这里的遮罩) - 测试:端到端改回规格原句(文字档位、SUM(CASE …)、年龄段 CASE),补 ON/WHERE、WHERE/HAVING、INTERSECT、包在 CASE 里、CTE、相邻整数的端到端用例,单元补拆分写法、被吸收的 BETWEEN v AND v、分组标签、检查结果片段与说明;每处去掉实现都验证过变红
70 lines
3.8 KiB
Python
70 lines
3.8 KiB
Python
"""审批质询:审批人针对待审报告里的一句话提问,只读 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}:质询引用过的工件 {id, content, masked[, mask_note]}——
|
||
查询快照(queries[].artifact)照回答时的办法遮好:遮罩列(含起了别名、套了函数的)整列遮,别的列里整格是遮罩值的
|
||
格子也遮,masked 是有格子整格被遮掉的列名(界面把这几列整列画成「已遮罩」);一格文字里夹着遮罩值的(备注里写着
|
||
手机号),只把那一截换成「已遮罩」,这一列不记进 masked。content.sql 就是事件里记下的那条(遮罩值已换掉),两处
|
||
一字不差;content.checks(SQL 检查结果)的字段照证据面板,片段 sql_excerpt、说明 message 里的遮罩值照整条 SQL
|
||
的换法换掉(截断处半截的值去掉)。回答文档
|
||
(answer.doc_artifact)组装时就遮好了,原样给;回答里引用的报告指标(doc.catalog 里 m: 条目的 artifact,口径卡)
|
||
也从这里取,界面一律按 doc.catalog[alias].artifact 取。没写进运行记录的回答(recorded 为假)在本进程里留一小时,
|
||
这段时间里它引用的工件照样能取;之后、或者服务重启过,回 404。别的工件一律 404
|
||
|
||
不受理: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)
|
||
|