package tool import ( "context" "encoding/json" "fmt" "log" "net/http" "strings" "time" "tcm-agent/internal/config" ) // ======================================================================== // Agent 工具集 // ======================================================================== // 工具是 Agent 的"手脚"——LLM 通过 Function Calling 自主决定 // 何时调用哪个工具。 // // 已注册工具: // - maxkb_retrieve :检索中医知识库(方剂/药典/指南) // - his_query :查询 HIS 患者数据 // - pharmacopoeia_query:查询《中国药典》单味药详情 // - rule_check :校验处方配伍禁忌 // // 新增工具只需:实现 types.Tool 接口 → 在 Runner 中 RegisterTool // ======================================================================== // ========== 工具1:MaxKB 知识库检索 ========== // MaxKBRetrieveTool 知识库检索工具 type MaxKBRetrieveTool struct { client *MaxKBClient } // NewMaxKBRetrieveTool 创建知识库检索工具 func NewMaxKBRetrieveTool(client *MaxKBClient) *MaxKBRetrieveTool { return &MaxKBRetrieveTool{client: client} } func (t *MaxKBRetrieveTool) Name() string { return "maxkb_retrieve" } func (t *MaxKBRetrieveTool) Description() string { return "检索中医知识库,获取权威的中医典籍、方剂、药典、病历书写规范等内容。输入应为检索关键词或问题。" } func (t *MaxKBRetrieveTool) Execute(ctx context.Context, params map[string]any) (string, error) { query, _ := params["query"].(string) if query == "" { return "", fmt.Errorf("检索关键词不能为空") } log.Printf("[工具] 知识库检索 | 关键词: %s", query) return t.client.Chat(ctx, query) } // ========== 工具2:HIS 系统查询 ========== // HISTool 医院信息系统查询工具 // // 实际项目对接真实的 HIS/PACS/LIS 系统。 // 这里用模拟数据演示。 type HISTool struct { cfg config.MaxKBConfig // 复用配置结构(实际应独立 HIS 配置) client *http.Client } // NewHISTool 创建 HIS 查询工具 func NewHISTool(cfg *config.MaxKBConfig) *HISTool { var c config.MaxKBConfig if cfg != nil { c = *cfg } return &HISTool{ cfg: c, client: &http.Client{Timeout: 10 * time.Second}, } } func (t *HISTool) Name() string { return "his_query" } func (t *HISTool) Description() string { return "查询医院信息系统(HIS)中的患者数据,包括既往病史、检查报告、用药记录、过敏史等。输入为患者ID或查询条件。" } func (t *HISTool) Execute(ctx context.Context, params map[string]any) (string, error) { patientID, _ := params["query"].(string) log.Printf("[工具] HIS 查询 | 患者 ID: %s", patientID) // ===== 实际项目这里调用 HIS 接口 ===== // 以下是模拟数据 mockData := map[string]string{ "P001": `患者 P001 既往病史: - 2023 年: 慢性胃炎 - 2024 年: 高血压(轻度) - 过敏: 青霉素 - 近期检查: 血常规正常, 肝功能正常`, "P002": `患者 P002 既往病史: - 2022 年: 子宫肌瘤手术 - 2024 年: 妊娠期糖尿病 - 过敏: 无 - 近期检查: 血糖偏高`, } result, ok := mockData[patientID] if !ok { result = fmt.Sprintf("未找到患者 %s 的 HIS 记录", patientID) } return result, nil } // ========== 工具3:药典查询 ========== // PharmacopoeiaTool 中国药典查询工具 // // 复用 MaxKB 客户端(药典内容已录入知识库)。 type PharmacopoeiaTool struct { client *MaxKBClient } // NewPharmacopoeiaTool 创建药典查询工具 func NewPharmacopoeiaTool(client *MaxKBClient) *PharmacopoeiaTool { return &PharmacopoeiaTool{client: client} } func (t *PharmacopoeiaTool) Name() string { return "pharmacopoeia_query" } func (t *PharmacopoeiaTool) Description() string { return "查询《中国药典》中某味中药的性味归经、功效主治、用法用量、禁忌事项。输入为药名。" } func (t *PharmacopoeiaTool) Execute(ctx context.Context, params map[string]any) (string, error) { herbName, _ := params["query"].(string) log.Printf("[工具] 药典查询 | 药名: %s", herbName) query := fmt.Sprintf("《中国药典》%s 的性味归经、功效、用法用量、禁忌", herbName) return t.client.Chat(ctx, query) } // ========== 工具4:规则引擎校验 ========== // RuleCheckTool 规则校验工具(供 Agent 自查) type RuleCheckTool struct{} // NewRuleCheckTool 创建规则校验工具 func NewRuleCheckTool() *RuleCheckTool { return &RuleCheckTool{} } func (t *RuleCheckTool) Name() string { return "rule_check" } func (t *RuleCheckTool) Description() string { return "校验处方是否符合中医配伍规则(十八反、十九畏、剂量范围、孕妇禁忌等)。输入为处方文本。" } func (t *RuleCheckTool) Execute(ctx context.Context, params map[string]any) (string, error) { prescription, _ := params["query"].(string) log.Printf("[工具] 规则校验 | 处方: %.50s...", prescription) // 十八反 conflicts18 := map[string][]string{ "甘草": {"甘遂", "大戟", "芫花", "海藻"}, "乌头": {"贝母", "瓜蒌", "半夏", "白蔹", "白及"}, "藜芦": {"人参", "沙参", "丹参", "玄参", "苦参", "细辛", "芍药"}, } // 十九畏 conflicts19 := map[string]string{ "硫黄": "朴硝", "水银": "砒霜", "狼毒": "密陀僧", "巴豆": "牵牛", "丁香": "郁金", "牙硝": "三棱", "川乌": "犀角", "草乌": "犀角", "人参": "五灵脂", "官桂": "赤石脂", } issues := make([]string, 0) // 检查十八反 for herb, conflicts := range conflicts18 { if strings.Contains(prescription, herb) { for _, conflict := range conflicts { if strings.Contains(prescription, conflict) { issues = append(issues, fmt.Sprintf("【十八反】%s 反 %s", herb, conflict)) } } } } // 检查十九畏 for a, b := range conflicts19 { if strings.Contains(prescription, a) && strings.Contains(prescription, b) { issues = append(issues, fmt.Sprintf("【十九畏】%s 畏 %s", a, b)) } } if len(issues) == 0 { return "处方校验通过,未发现配伍禁忌。", nil } return "校验发现问题:\n" + strings.Join(issues, "\n"), nil } // 确保 import 使用 var _ = json.Marshal