chore(backup): 保存新版预审开发前的代码与文档基线
verify / test-and-build (push) Canceled after 0s

This commit is contained in:
2026-09-28 16:34:10 +08:00
parent b10093f9d8
commit 4d5a420671
37 changed files with 3890 additions and 10 deletions
+20 -5
View File
@@ -10,7 +10,7 @@ from typing import Literal
import httpx
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
from pydantic import BaseModel, ConfigDict, Field
from pydantic import BaseModel, ConfigDict, Field, field_validator
from graph.agent import agent_manager
@@ -31,6 +31,15 @@ class ModelResult(BaseModel):
requirement_response: str = Field(min_length=1, max_length=5000)
experience_proposal: "ExperienceProposal | None" = None
@field_validator("requirement_response", mode="before")
@classmethod
def normalize_requirement_response(cls, value):
if isinstance(value, dict):
return ";".join(f"{key}:{item}" for key, item in value.items())
if isinstance(value, list):
return ";".join(str(item) for item in value)
return value
class ExperienceProposal(BaseModel):
model_config = ConfigDict(extra="forbid")
@@ -49,16 +58,21 @@ ModelResult.model_rebuild()
def configuration():
endpoint = os.getenv("XINGBANG_LOCAL_MODEL_URL", "")
if not endpoint:
return {"available": False, "reason": "未配置内网模型,语义核查未执行"}
return {"available": False, "reason": "未配置受信模型,语义核查未执行"}
url = urlparse(endpoint)
allowed = url.hostname == "localhost"
official_deepseek = (
url.scheme == "https"
and url.hostname == "api.deepseek.com"
and url.port in (None, 443)
)
allowed = url.hostname == "localhost" or official_deepseek
try:
address = ipaddress.ip_address(url.hostname or "")
allowed = address.is_loopback or any(address in ipaddress.ip_network(net) for net in ("10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16") if address.version == 4)
except ValueError:
pass
if url.scheme not in ("http", "https") or not allowed or url.username or url.password or url.query or url.fragment:
raise ValueError("模型须为已批准的内网地址,不允许凭据或参数嵌入 URL")
raise ValueError("模型须为已批准的内网地址或 DeepSeek 官方 HTTPS 地址,不允许凭据或参数嵌入 URL")
return {"available": True, "endpoint": endpoint, "name": os.getenv("XINGBANG_LOCAL_MODEL_NAME", "local-model")}
@@ -69,7 +83,8 @@ PROMPT = """你是兴邦项目核查助手,必须通过受限工具读取本
输出一个JSON对象,只含 findings 和 requirement_response。findings 每项字段为 code(稳定英文码)、title、result
(pass/issue/unknown/not_applicable)、evidence_ids(真实证据ID数组)、fields(标准字段数组)、suggested_action。
没有依据不得判定pass/issue。缺失资料标unknown,证据只能引用冻结快照。不得引用其他项目经验中的客户信息。
requirement_response逐项说明是否完成用户要求,无法验证要明确说明。不要Markdown围栏。"""
requirement_response必须是一个普通字符串,不得输出对象或数组;逐项说明是否完成用户要求,无法验证要明确说明。
示例顶层结构:{"findings": [], "requirement_response": "已读取授权事实并完成核查。"}。不要Markdown围栏。"""
def invoke(skill, snapshot, checks, rules, requirement, experiences, model_config, budget, cancelled=lambda: False, feedback=None):
+9 -2
View File
@@ -602,10 +602,17 @@ class ProjectAssistantService(DocumentsMixin, ExperiencesMixin):
if current["data_domain"] != "demo":
raise ValueError("禁止重置正式项目")
records = [dict(r) for r in db.execute("SELECT * FROM assistant_objects WHERE project_id=? OR (kind='project' AND id=?)", (item["id"], item["id"]))]
put(db, "reset_archive", {"id": uid("reset"), "source_project_id": item["id"], "records": records, "actor": actor, "created_at": now()}, immutable=True)
events = [dict(r) for r in db.execute("SELECT * FROM assistant_events WHERE project_id=? ORDER BY seq", (item["id"],))]
time_events = [dict(r) for r in db.execute("SELECT * FROM business_time_events WHERE project_id=? ORDER BY started_at", (item["id"],))]
put(db, "reset_archive", {"id": uid("reset"), "source_project_id": item["id"], "records": records,
"events": events, "time_events": time_events, "actor": actor, "created_at": now()}, immutable=True)
db.execute("DELETE FROM assistant_jobs WHERE project_id=?", (item["id"],))
db.execute("DELETE FROM assistant_idempotency WHERE object_id IN (SELECT id FROM assistant_objects WHERE project_id=?)", (item["id"],))
db.execute("DELETE FROM assistant_objects WHERE project_id=? OR (kind='project' AND id=?)", (item["id"], item["id"]))
db.execute("DELETE FROM assistant_events WHERE project_id=?", (item["id"],))
db.execute("DELETE FROM business_time_events WHERE project_id=?", (item["id"],))
self._seed_project(db, item, "demo")
self._seed_timeline(db, item)
event(db, item["id"], "demo.reset", actor, {"recoverable": True})
return {"status": "reset", "data_domain": "demo", "projects": len(fixtures["projects"]), "recoverable": True}
return {"status": "reset", "data_domain": "demo", "projects": len(fixtures["projects"]),
"recoverable": True, "history_reset": True}