tmp
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
import { tool } from "@langchain/core/tools";
|
||||
import z from "zod";
|
||||
import { DBModel } from "../../../../models/index.js";
|
||||
|
||||
const hospitalInfoSetTool = tool(
|
||||
async ({ hospitalName, section, action, content, guides, entry, entryName, updatedBy }) => {
|
||||
try {
|
||||
if (!hospitalName) {
|
||||
return { success: false, error: "hospitalName 不能为空" };
|
||||
}
|
||||
|
||||
// 定位医院:名称必须唯一命中
|
||||
const orgs = await DBModel.Organization.find({ name: { $regex: hospitalName, $options: "i" } }).exec();
|
||||
if (orgs.length === 0) {
|
||||
return { success: false, error: `未找到名称包含「${hospitalName}」的机构,请确认医院名称后重试` };
|
||||
}
|
||||
if (orgs.length > 1) {
|
||||
return {
|
||||
success: false,
|
||||
error: `名称包含「${hospitalName}」的机构有 ${orgs.length} 家(${orgs.map((o) => o.name).join("、")}),请使用更精确的名称`,
|
||||
};
|
||||
}
|
||||
const org = orgs[0];
|
||||
const fa = org.forAgent ?? {};
|
||||
|
||||
const update = {};
|
||||
|
||||
if (section === "overview") {
|
||||
// 文本块全量替换
|
||||
if (typeof content !== "string") {
|
||||
return { success: false, error: "overview 需要提供 content 文本(写入前请先查询现有内容并合并)" };
|
||||
}
|
||||
update["forAgent.overview"] = content;
|
||||
} else if (section === "guides") {
|
||||
// 场景键值对浅合并,值为 null 表示删除该场景
|
||||
if (!guides || typeof guides !== "object" || Array.isArray(guides)) {
|
||||
return { success: false, error: "guides 需要提供对象,键为场景名,值为文本(值为 null 表示删除该场景)" };
|
||||
}
|
||||
const merged = { ...(fa.guides ?? {}) };
|
||||
for (const [key, value] of Object.entries(guides)) {
|
||||
if (value === null) {
|
||||
delete merged[key];
|
||||
} else {
|
||||
merged[key] = value;
|
||||
}
|
||||
}
|
||||
update["forAgent.guides"] = merged;
|
||||
} else if (section === "photos" || section === "departments" || section === "doctors") {
|
||||
// 列表区块:按锚点 upsert(新增或整条替换)/ remove
|
||||
const list = [...(fa[section] ?? [])];
|
||||
|
||||
if (action === "upsert") {
|
||||
if (!entry || typeof entry !== "object") {
|
||||
return { success: false, error: "upsert 需要提供 entry 对象" };
|
||||
}
|
||||
let idx = -1;
|
||||
if (section === "photos") {
|
||||
if (!entry.url) return { success: false, error: "photos 条目必须包含 url 作为锚点" };
|
||||
idx = list.findIndex((p) => p.url === entry.url);
|
||||
} else if (section === "doctors") {
|
||||
if (!entry.name || !entry.department) {
|
||||
return { success: false, error: "doctors 条目必须包含 name 和 department 作为锚点" };
|
||||
}
|
||||
idx = list.findIndex((d) => d.name === entry.name && d.department === entry.department);
|
||||
} else {
|
||||
if (!entry.name) return { success: false, error: "departments 条目必须包含 name 作为锚点" };
|
||||
idx = list.findIndex((d) => d.name === entry.name);
|
||||
}
|
||||
if (idx >= 0) {
|
||||
list[idx] = { ...list[idx], ...entry };
|
||||
} else {
|
||||
list.push(entry);
|
||||
}
|
||||
update[`forAgent.${section}`] = list;
|
||||
} else if (action === "remove") {
|
||||
if (!entryName) {
|
||||
return {
|
||||
success: false,
|
||||
error: `remove 需要提供 entryName(${section === "photos" ? "照片url" : section === "doctors" ? "医生姓名" : "科室名称"})`,
|
||||
};
|
||||
}
|
||||
if (section === "photos") {
|
||||
update[`forAgent.${section}`] = list.filter((p) => p.url !== entryName);
|
||||
} else {
|
||||
update[`forAgent.${section}`] = list.filter((d) => d.name !== entryName);
|
||||
}
|
||||
} else {
|
||||
return { success: false, error: "列表区块(photos/departments/doctors)需要指定 action: upsert 或 remove" };
|
||||
}
|
||||
} else {
|
||||
return { success: false, error: "未知的 section,可选值:overview / photos / departments / doctors / guides" };
|
||||
}
|
||||
|
||||
update["forAgent.updatedAt"] = new Date();
|
||||
if (updatedBy) {
|
||||
update["forAgent.updatedBy"] = updatedBy;
|
||||
}
|
||||
|
||||
const updated = await DBModel.Organization.findByIdAndUpdate(org._id, { $set: update }, { new: true }).exec();
|
||||
return { success: true, data: updated.forAgent };
|
||||
} catch (error) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "hospital_info_set",
|
||||
description:
|
||||
"录入/更新医院就医信息。按区块维护:overview(医院级文本,全量替换,先查后改避免丢失)、guides(场景指南键值对,浅合并)、photos/departments/doctors(列表,按锚点 upsert 或 remove)。医院名称必须唯一命中。",
|
||||
schema: z.object({
|
||||
hospitalName: z.string().describe("医院名称(需唯一命中,如 北京协和医院)"),
|
||||
section: z
|
||||
.enum(["overview", "photos", "departments", "doctors", "guides"])
|
||||
.describe("要更新的区块"),
|
||||
action: z
|
||||
.enum(["set", "upsert", "remove"])
|
||||
.optional()
|
||||
.describe("操作类型:overview/guides 用 set;photos/departments/doctors 用 upsert 或 remove"),
|
||||
content: z.string().optional().describe("overview 区块的完整文本(全量替换)"),
|
||||
guides: z
|
||||
.record(z.string(), z.string().nullable())
|
||||
.optional()
|
||||
.describe('guides 区块的场景键值对,如 {"住院流程": "..."};值为 null 表示删除该场景'),
|
||||
entry: z
|
||||
.object({
|
||||
name: z.string().optional().describe("科室名或医生名(锚点)"),
|
||||
department: z.string().optional().describe("医生所属科室(医生锚点之一)"),
|
||||
detail: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe("信息全文:科室(位置/电话/类型/提示)或医生(职称/擅长/号别/坐班停诊)"),
|
||||
photo: z.string().optional().describe("照片链接"),
|
||||
url: z.string().optional().describe("照片链接(photos 锚点)"),
|
||||
caption: z.string().optional().describe("照片说明"),
|
||||
})
|
||||
.optional()
|
||||
.describe("photos/departments/doctors 条目(upsert 时必填)"),
|
||||
entryName: z.string().optional().describe("remove 的锚点:科室名称 / 医生姓名 / 照片url"),
|
||||
updatedBy: z.string().optional().describe("操作人标识(用户ID或姓名),用于审计"),
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
export { hospitalInfoSetTool };
|
||||
Reference in New Issue
Block a user