表助手的背景在宽表上不至于太长:这张表的目录最多列 600 项(表级的项、关联关系在前),其余写明还有几项没列出;导入时界面直接交来的文件原文(JSON 文字)也认,不是 JSON 的 422;导入冲突说明里的状态用界面的叫法

This commit is contained in:
ThearchyHelios
2026-10-14 08:26:05 +08:00
parent 65369b8a6e
commit 77ddbe96ea
4 changed files with 37 additions and 6 deletions
+11 -4
View File
@@ -79,9 +79,11 @@ TOOL_CHARS = 12_000
RETRY_DELAYS_S = (3.0, 10.0)
#: 等模型的时候每隔几秒发一行 SSE 注释
HEARTBEAT_S = 10.0
#: 背景里「目录里还没有中文名和含义的列」最多列几个;别的表指过来的关联关系最多列几条
#: 背景里「目录里还没有中文名和含义的列」最多列几个;别的表指过来的关联关系最多列几条;这张表的目录最多列几项
#: (几百列的宽表全部起草过有上千项,全列出来提示词就太长了:表级的项、关联关系在前,列的项在后)
UNNAMED_SHOWN = 30
INCOMING_SHOWN = 30
CATALOG_ITEMS_SHOWN = 600
QUERY_TOOL = "run_query"
PROPOSE_TOOL = "propose_changes"
@@ -166,6 +168,7 @@ ASSISTANT_PROMPT_LABELS = {
"catalog_empty": "这张表还没有数据目录。",
"item": "- {path}({label}):{value}〔{source},{status}〕",
"item_note": " 备注:{text}",
"catalog_more": "(还有 {n} 项没有列出:这张表的目录太长。要改的项照上面的路径写法提,核对时按当前目录算)",
"relation": "{columns} → {to_table}.{to_columns}{extra}",
"relation_extra": "({text})",
"cardinality": "基数 {value}",
@@ -357,10 +360,14 @@ def _catalog_lines(notes: Mapping[str, Any] | None, masked: set[str]) -> list[st
(剖析的备注里有取值范围)。"""
labels = ASSISTANT_PROMPT_LABELS
lines: list[str] = []
for key, item in catalog._slots(notes).items():
# 表级的项、关联关系在前,列的项在后(稳定排序,各自照目录里的顺序);遮罩列上的码值不算
slots = [(key, item) for key, item in sorted(catalog._slots(notes).items(), key=lambda kv: kv[0][0] == "c")
if not (key[0] == "c" and key[2] == "codes" and key[1].lower() in masked)]
for shown, (key, item) in enumerate(slots):
if shown >= CATALOG_ITEMS_SHOWN:
lines.append(labels["catalog_more"].format(n=len(slots) - shown))
break
hidden = _touches_masked(key, item, masked)
if hidden and key[0] == "c" and key[2] == "codes":
continue
if key[0] == "r":
label, value = labels["field_relation"], _relation_line(item)
else:
+9 -2
View File
@@ -30,6 +30,7 @@ tables: {表名: {notes, version}}},含全部状态的项,表结构里已经
from __future__ import annotations
import copy
import json
import re
from collections.abc import Mapping
from dataclasses import dataclass, field
@@ -51,7 +52,8 @@ _UNNAMED = "数据源"
#: 状态:照带的、只填空缺的
_LOCKED = ("confirmed", "rejected")
_STATUS_LABEL = {"proposed": "推断", "verified": "有确证", "confirmed": "已确认", "rejected": "已驳回"}
#: 状态的界面叫法(同 frontend/src/lib/terms.ts 的 CATALOG_STATUS_LABEL)
_STATUS_LABEL = {"proposed": "推断", "verified": "已验证", "confirmed": "已确认", "rejected": "已驳回"}
#: 写入一直撞上别人刚改过(重读重算三次仍冲突)
WRITE_CONFLICT_ERROR = "这张表的数据目录正被其他人修改,没有导入,请稍后重新导入"
@@ -106,7 +108,12 @@ class ImportFile:
def read_file(content: Any, *, include_guidance: bool) -> ImportFile:
"""导出的那个对象 → ImportFile。格式不对抛 TransferInvalid(说法给人看)。"""
"""导出的那个对象 → ImportFile。界面直接交来文件的原文(一段 JSON 文字)也认。格式不对抛 TransferInvalid(说法给人看)。"""
if isinstance(content, str):
try:
content = json.loads(content)
except ValueError as e:
raise TransferInvalid("文件内容不是合法的 JSON,请选择从 AgentLab 导出的数据目录文件") from e
if not isinstance(content, Mapping):
raise TransferInvalid("文件内容不是数据目录导出的格式,请选择从 AgentLab 导出的数据目录文件")
if content.get("format") != FORMAT:
+12
View File
@@ -351,6 +351,18 @@ async def test_background_has_structure_catalog_and_hides_masked_values(client,
assert "没开剖析" not in system
async def test_long_catalogs_are_cut_with_a_note(client, tmp_path, monkeypatch):
sid = await _source(client, tmp_path)
monkeypatch.setattr(catalog_assistant, "CATALOG_ITEMS_SHOWN", 2)
model = Scripted([Turn(text=["好。"])])
use(monkeypatch, model)
await _talk(client, sid, _ask("看看"))
system = model.seen[0][0].content
# 表级的项在前;遮罩列的码值本来就不算;剩下的说明还有几项
assert "- label(中文名):订单" in system and "- grain(粒度)" in system and "- kind(表类型)" not in system
assert "还有 3 项没有列出" in system
async def test_without_profiling_there_is_no_query_tool(client, tmp_path, monkeypatch):
sid = await _source(client, tmp_path, profile=False)
model = Scripted([Turn(text=["只能根据表结构推断。"])])
+5
View File
@@ -334,6 +334,11 @@ async def test_import_waits_for_draft_jobs_and_rejects_bad_files(client):
r = await client.post(f"{_base(local)}/import/preview", json={"content": body})
assert r.status_code == 422, body
assert any("一" <= ch <= "鿿" for ch in r.json()["detail"])
# 界面直接交来文件原文也认;不是 JSON 的 422
as_text = await client.post(f"{_base(local)}/import/preview", json={"content": json.dumps(content)})
assert as_text.status_code == 200 and as_text.json()["totals"]["tables"] == 2
not_json = await client.post(f"{_base(local)}/import/preview", json={"content": "{不是 JSON"})
assert not_json.status_code == 422 and "JSON" in not_json.json()["detail"]
too_long = {**content, "guidance": "长" * (catalog_jobs.GUIDANCE_MAX + 1)}
r = await client.post(f"{_base(local)}/import/preview", json={"content": too_long, "include_guidance": True})
assert r.status_code == 422 and "起草要求" in r.json()["detail"]