feat: patch_chapters工具(根治create_chapter_outline只增不改)+list_*项目上下文缺失改报错不静默空
This commit is contained in:
parent
6c87cd12b6
commit
fdd98266f8
@ -806,7 +806,9 @@ async def list_scoring_items(project_id, section=""):
|
||||
try:
|
||||
project_id = await resolve_project_id(project_id)
|
||||
except ValueError as e:
|
||||
return []
|
||||
# 2026-09-03:静默返回 [] 会让 LLM 误判「无数据」(实测 C-12 绑定阻塞根因),
|
||||
# 改为明确报错,提示补 project_id。
|
||||
return "ERROR: 项目上下文缺失(%s)。请显式传 project_id 后重试。" % e
|
||||
db, dbname = get_db()
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
sql = ("SELECT id, section, item_no, item_name, max_score, is_veto, "
|
||||
@ -828,7 +830,7 @@ async def list_doc_requirements(project_id, req_type=""):
|
||||
try:
|
||||
project_id = await resolve_project_id(project_id)
|
||||
except ValueError as e:
|
||||
return []
|
||||
return "ERROR: 项目上下文缺失(%s)。请显式传 project_id 后重试。" % e
|
||||
db, dbname = get_db()
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
sql = ("SELECT id, req_type, chapter_no, chapter_title, requirement, page_limit "
|
||||
@ -938,3 +940,98 @@ async def create_chapter_outline(project_id, chapters="", who=None, agent_id=Non
|
||||
who=who, agent_id=agent_id, detail="新增 %d 章" % created)
|
||||
return True, ("章节骨架已生成:新增 %d 章,已存在跳过 %d 章。"
|
||||
"系统将自动为每个章节派发编写任务。" % (created, skipped))
|
||||
|
||||
|
||||
async def patch_chapters(project_id, patches="", who=None, agent_id=None):
|
||||
"""按 chapter_no 增量更新已有章节(2026-09-03,根治 create_chapter_outline 只增不改)。
|
||||
|
||||
patches: JSON 数组,每项 {chapter_no, title?, outline?, section?,
|
||||
scoring_item_ids?, scoring_items_by_name?, max_score?}。
|
||||
- scoring_item_ids 直接覆盖(传真实 id 数组);
|
||||
- scoring_items_by_name: [{item_name, max_score?}] 按名称在 bid_scoring_items
|
||||
反查 id 绑定(item_name 为「商务N|名称|出处」格式时按包含匹配);
|
||||
- 白名单外的章节不动;不存在的 chapter_no 报错列出。
|
||||
"""
|
||||
try:
|
||||
project_id = await resolve_project_id(project_id)
|
||||
except ValueError as e:
|
||||
return False, str(e)
|
||||
items = json_loads(patches, []) if isinstance(patches, str) else (patches or [])
|
||||
if not items:
|
||||
return False, "patches 为空:传 JSON 数组 [{chapter_no, ...}]"
|
||||
db, dbname = get_db()
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
sitems = await sor.sqlExe(
|
||||
"SELECT id, item_name, max_score FROM bid_scoring_items WHERE project_id=${pid}$",
|
||||
{"pid": project_id})
|
||||
await sor.sqlExe("COMMIT", {})
|
||||
sc = rows_to_dicts(sitems)
|
||||
|
||||
def _bind_by_name(names):
|
||||
ids, miss = [], []
|
||||
for n in (names or []):
|
||||
nm = (n.get("item_name") or "").strip() if isinstance(n, dict) else str(n or "").strip()
|
||||
hit = None
|
||||
for s in sc:
|
||||
sn = s.get("item_name") or ""
|
||||
if nm and (nm == sn or nm in sn or sn in nm):
|
||||
hit = s
|
||||
break
|
||||
if hit:
|
||||
ids.append(hit["id"])
|
||||
else:
|
||||
miss.append(nm)
|
||||
return ids, miss
|
||||
|
||||
updated, missing, bound, unbound = [], [], 0, []
|
||||
for it in items:
|
||||
if not isinstance(it, dict):
|
||||
continue
|
||||
cno = str(it.get("chapter_no") or "").strip()
|
||||
if not cno:
|
||||
continue
|
||||
recs = await sor.sqlExe(
|
||||
"SELECT id, scoring_item_ids FROM bid_chapters "
|
||||
"WHERE project_id=${pid}$ AND chapter_no=${cno}$ LIMIT 1",
|
||||
{"pid": project_id, "cno": cno})
|
||||
await sor.sqlExe("COMMIT", {})
|
||||
if not recs:
|
||||
missing.append(cno)
|
||||
continue
|
||||
sets, p = [], {"pid": project_id, "cno": cno}
|
||||
if it.get("title"):
|
||||
sets.append("title=${t}$"); p["t"] = str(it["title"])[:250]
|
||||
if it.get("outline") is not None and it.get("outline") != "":
|
||||
sets.append("outline=${o}$"); p["o"] = str(it["outline"])[:20000]
|
||||
if it.get("section"):
|
||||
sets.append("section=${s}$"); p["s"] = str(it["section"])[:30]
|
||||
ids = list(it.get("scoring_item_ids") or [])
|
||||
if it.get("scoring_items_by_name"):
|
||||
b, m = _bind_by_name(it["scoring_items_by_name"])
|
||||
ids = b
|
||||
bound += len(b)
|
||||
unbound += m
|
||||
if it.get("scoring_item_ids") is not None or it.get("scoring_items_by_name"):
|
||||
sets.append("scoring_item_ids=${si}$")
|
||||
p["si"] = json.dumps(ids, ensure_ascii=False)
|
||||
if it.get("max_score") is not None:
|
||||
sets.append("max_score=${ms}$"); p["ms"] = to_float(it["max_score"], 0)
|
||||
if not sets:
|
||||
continue
|
||||
sets.append("updated_at=NOW()")
|
||||
sets.append("version=version+1")
|
||||
await sor.sqlExe(
|
||||
"UPDATE bid_chapters SET " + ", ".join(sets) +
|
||||
" WHERE project_id=${pid}$ AND chapter_no=${cno}$", p)
|
||||
updated.append(cno)
|
||||
await sor.sqlExe("COMMIT", {})
|
||||
await record(sor, project_id, "bid_chapters", project_id, "patch_outline",
|
||||
who=who, agent_id=agent_id,
|
||||
detail="更新 %d 章:%s" % (len(updated), ",".join(updated)[:200]))
|
||||
msg = "章节增量更新:成功 %d 章(%s),评分项按名绑定 %d 条" % (
|
||||
len(updated), ",".join(updated)[:200], bound)
|
||||
if missing:
|
||||
msg += ";不存在的章节:%s" % ",".join(missing)
|
||||
if unbound:
|
||||
msg += ";按名未绑定的评分项:%s" % ",".join(unbound)[:200]
|
||||
return True, msg
|
||||
|
||||
@ -96,7 +96,7 @@ async def list_qualifications(project_id, match_status=""):
|
||||
try:
|
||||
project_id = await resolve_project_id(project_id)
|
||||
except ValueError as e:
|
||||
return []
|
||||
return "ERROR: 项目上下文缺失(%s)。请显式传 project_id 后重试。" % e
|
||||
db, dbname = get_db()
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
sql = ("SELECT id, qual_name, is_mandatory, owner, match_status, kb_doc_id, "
|
||||
|
||||
@ -103,6 +103,13 @@ BID_ROLE_TOOL_SCHEMAS = {
|
||||
"params": {"project_id": "项目ID(可选,默认当前)", "chapters": "章节JSON数组(可选,默认自动生成)"},
|
||||
"required": [],
|
||||
},
|
||||
"patch_chapters": {
|
||||
"module": f"{_M}.bid_analysis_capability",
|
||||
"description": "按 chapter_no 增量更新已有章节(改标题/outline/挂载评分项/满分)。create_chapter_outline 只增不改,修正既有骨架必须用它。scoring_items_by_name 按名称反查绑定真实 id",
|
||||
"params": {"project_id": "项目ID(可选,默认当前)",
|
||||
"patches": "JSON数组[{chapter_no,title?,outline?,section?,scoring_item_ids?,scoring_items_by_name?,max_score?}]"},
|
||||
"required": ["patches"],
|
||||
},
|
||||
"list_scoring_items": {
|
||||
"module": f"{_M}.bid_analysis_capability",
|
||||
"description": "列出评分项与得分规则",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user