Files
xk-ai-agent/internal/tool/agent_tools.go
2026-08-14 21:50:48 +08:00

200 lines
6.2 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
// ========================================================================
// ========== 工具1MaxKB 知识库检索 ==========
// 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)
}
// ========== 工具2HIS 系统查询 ==========
// 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