尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于OpenClaw框架构建AI中医技能:从Agent原理到飞书集成实践

基于OpenClaw框架构建AI中医技能:从Agent原理到飞书集成实践 1. 项目缘起当剥龙虾遇上AI Agent最近在折腾一个叫OpenClaw的AI Agent框架边剥龙虾边琢磨突然就冒出一个想法能不能用这玩意儿结合我这些年对中医经方的一点业余爱好做个有意思的“AI中医技能”出来一来是验证一下OpenClaw这类工具在垂直领域的落地能力二来也是想探索一种新的内容创作形式或许能成为一个不错的“起号”方向。毕竟现在单纯讲理论或者展示代码已经不够看了大家更想看的是如何把前沿技术像AI Agent实实在在地用到一个具体的、有生活气息的场景里解决点实际问题。这个项目的核心就是利用OpenClaw框架构建一个具备基础中医问诊和经方推荐能力的AI智能体。它不是一个严肃的医疗诊断工具——这点必须再三强调而是更像一个结合了传统中医知识和现代AI交互的趣味助手或学习伴侣。你可以把它想象成一个懂点中医的、永远在线的“数字朋友”能和你聊聊身体的小状况根据一些简单的症状描述参考像倪海厦老师等经方家推崇的思路给出一些养生建议或经典方剂的科普性介绍。整个过程从环境搭建、技能设计、到与飞书等办公软件的集成我会把每一步的实操、踩的坑和心得都记录下来。这不仅仅是一个技术实现更是一次关于如何将AI技术平民化、场景化的实践。2. 核心思路与工具选型为什么是OpenClaw中医2.1 项目定位与边界设定首先必须明确我们做的是“技能”不是“神医”。它的目标是科普、辅助学习和趣味互动绝对不涉及任何真正的医疗诊断、处方开具或健康建议。所有输出内容都必须包含明确的免责声明提示用户内容仅供参考不能替代专业医师面诊。这个边界是红线也是在设计所有对话流程和知识库时的首要原则。我们的AI技能更像是一个智能化的“中医古籍索引器”和“症状-方剂关联分析器”它基于我们喂给它的、经过筛选的公开知识如《伤寒论》条文、一些公开的经方应用经验进行模式匹配和语言组织而不是进行创造性的诊断。2.2 为什么选择OpenClaw作为实现框架在众多AI Agent框架如LangChain、Semantic Kernel、Dify等中选择OpenClaw主要基于以下几点考量“技能”理念的契合度OpenClaw的设计哲学核心就是“技能”Skill。它允许开发者以模块化的方式封装各种能力比如调用搜索引擎、查询数据库、执行计算等。这与我们想打造一个独立“中医技能”的想法不谋而合。我们可以把“舌象分析”、“症状收集”、“经方查询”等功能分别封装成不同的技能再由一个主控Agent来协调调用逻辑非常清晰。对国产模型和开源生态的良好支持OpenClaw对国内主流大模型如通义千问、DeepSeek、GLM以及开源模型特别是通过Ollama部署的Llama、Qwen等系列的支持很友好。这对于希望使用本地化或低成本模型进行开发的个人开发者来说是一个巨大的便利。我们完全可以在本地用Ollama跑一个7B参数的模型来驱动这个技能实现完全自主可控。易于集成与扩展项目文档中提到了接入飞书这说明它具备较好的外部系统集成能力。我们的技能最终可以封装成一个API服务轻松接入到飞书机器人、微信公众号、独立网页等渠道使用场景灵活。社区热度与问题可见性从提供的网络热词可以看到“openclaw安装教程”、“openclaw接入飞书”等搜索词频繁出现说明有很多开发者正在探索和实践社区相对活跃。遇到问题时更容易找到相关的讨论或解决方案降低了学习成本。注意网络热词中出现的错误信息如“openclaw llamap svr operator(): got exception: { “error“: { “code“: 400这很可能是在部署或调用API时遇到的典型报错。这提醒我们在后续部署环节需要特别注意环境配置和API请求格式。2.3 中医知识体系的数字化处理思路中医知识尤其是经方体系结构化程度高但语境依赖性强。我们不能简单地把《伤寒论》全文扔给AI。我的处理思路是知识切片与结构化将经典方剂如桂枝汤、麻黄汤及其主要条文“太阳病头痛发热汗出恶风桂枝汤主之”拆解成结构化数据。包括方剂名称、组成、主治、关键症状关键词如“发热”、“恶寒”、“汗出”、禁忌等。构建症状-方剂映射网络这不是简单的关键词匹配。例如“怕冷”这个症状在“恶寒无汗”时可能指向麻黄汤在“恶风有汗”时则指向桂枝汤。我们需要在知识库中建立这种带有上下文关系的映射逻辑可以通过添加权重和关联症状来实现。设计渐进式问诊流程AI技能不会一次性问完所有问题。它应该模仿中医“十问歌”的思路由核心症状如“您最主要哪里不舒服”开始逐步深入“怕冷吗出汗情况怎样口渴吗”动态决定下一个问题从而逐步缩小可能方剂的范围。这非常适合用Agent的决策流来实现。3. 实操搭建从零构建“AI中医技能”全流程3.1 基础环境准备与OpenClaw部署我们选择在Linux系统Ubuntu 22.04上使用Docker进行部署这是最稳定和可复现的方式。# 1. 确保系统已安装Docker和Docker Compose sudo apt update sudo apt install docker.io docker-compose -y # 2. 拉取OpenClaw的官方Docker镜像请以OpenClaw官方仓库最新说明为准 # 假设官方镜像为 openclaw/openclaw:latest docker pull openclaw/openclaw:latest # 3. 创建项目目录并编写docker-compose.yml mkdir tcm-ai-agent cd tcm-ai-agent cat docker-compose.yml EOF version: 3.8 services: openclaw: image: openclaw/openclaw:latest container_name: tcm-openclaw restart: unless-stopped ports: - “3000:3000“ # OpenClaw Web管理界面 - “8080:8080“ # API服务端口 volumes: - ./data:/app/data # 挂载数据卷持久化配置和知识库 - ./logs:/app/logs environment: - LLM_PROVIDERollama # 指定使用本地Ollama服务 - OLLAMA_BASE_URLhttp://host.docker.internal:11434 # 关键配置连接宿主机Ollama - TZAsia/Shanghai EOF这里有一个关键细节OLLAMA_BASE_URL的设置。如果Ollama安装在宿主机上在Docker容器内需要通过http://host.docker.internal:11434来访问宿主机的服务。这比使用bridge网络和IP地址更简单稳定。部署Ollama及中医领域微调模型 OpenClaw本身是大脑Agent框架需要大模型作为思考引擎。我们选择在宿主机上运行Ollama加载一个适合中文和推理的模型。# 在宿主机上安装Ollama curl -fsSL https://ollama.com/install.sh | sh # 下载一个合适的模型例如Qwen2.5-7B-Instruct它在中文和指令跟随上表现不错 ollama pull qwen2.5:7b-instruct # 为了更贴近中医领域我们可以考虑利用模型融合或轻量化微调LoRA的方式。 # 这里提供一个高级思路使用公开的中医QA数据集对qwen2.5:7b-instruct进行LoRA微调。 # 这是一个简化示例实际过程需要更多步骤准备数据、安装Axolotl等工具 # ollama pull qwen2.5:7b # 使用Axolotl等框架基于《伤寒论》QA数据做LoRA微调生成一个适配后的模型文件。 # 最后创建一个Modelfile从基础模型加载并应用LoRA权重创建新模型‘qwen2.5-tcm-lora’。 # ollama create qwen2.5-tcm-lora -f ./Modelfile实操心得直接使用通用模型进行中医问答效果可能流于表面。如果条件允许花时间做一次针对性的LoRA微调哪怕是基于几百条高质量的“症状-方剂”问答对也能让AI技能的回答专业性和准确度提升一个档次。微调时指令模板要设计好例如“你是一个中医辅助学习助手根据用户的症状描述从以下经方知识库中匹配最相关的方剂并给出简要分析。知识库[结构化知识]。用户症状{user_input}”。3.2 设计并实现核心中医技能Skill在OpenClaw中技能通常以插件或特定格式的模块存在。我们需要创建两个核心技能SymptomInquirySkill症状收集技能和FormulaQuerySkill经方查询技能。假设OpenClaw的技能开发目录结构为skills/我们在此创建技能一SymptomInquirySkill (症状收集与解析)这个技能负责与用户进行多轮对话智能地收集症状信息。它内部维护一个“问诊状态机”。# 文件skills/symptom_inquiry/skill.py (示例逻辑) class SymptomInquirySkill: def __init__(self): self.inquiry_flow [ “chief_complaint“, # 主诉 “chills_fever“, # 寒热 “sweating“, # 汗出 “thirst“, # 口渴 “appetite“, # 食欲 “bowel_movement“, # 二便 … # 其他十问歌项目 ] self.current_step 0 self.collected_symptoms {} async def execute(self, user_input: str, context: dict) - dict: “““执行技能根据当前步骤和用户输入收集信息或判断下一步。“”“ if self.current_step 0: # 第一步询问主诉 self.collected_symptoms[“chief_complaint“] user_input self.current_step 1 return { “action“: “ask“, “message“: “了解。有怕冷或者发烧的感觉吗请描述寒热情况“, “collected_data“: self.collected_symptoms } elif self.current_step 1: # 处理寒热情况 self.collected_symptoms[“chills_fever“] user_input # 这里可以加入简单逻辑如果用户说“非常怕冷一点汗都没有”可以提前跳转到相关方剂询问 if “恶寒无汗“ in user_input: # 触发一个内部标记提示可能属于“麻黄汤证” self.collected_symptoms[“possible_formula“] “麻黄汤“ self.current_step 1 return { “action“: “ask“, “message“: “出汗情况怎么样容易出汗还是不出汗“, “collected_data“: self.collected_symptoms } # … 后续步骤 # 当所有步骤完成或提前满足某个条件时 if self.current_step len(self.inquiry_flow): return { “action“: “complete“, “message“: “症状信息已收集完毕正在为您分析。“, “final_symptoms“: self.collected_symptoms }技能二FormulaQuerySkill (经方知识库查询)这个技能对接我们事先准备好的结构化中医知识库例如一个SQLite数据库或JSON文件。// 知识库示例data/formulas.json [ { “name“: “桂枝汤“, “composition“: “桂枝、芍药、甘草、生姜、大枣“, “indication“: “太阳中风证。发热汗出恶风脉缓。“, “key_symptoms“: [“发热“ “汗出“ “恶风“ “脉缓“], “contraindication“: “伤寒表实证无汗、脉紧忌用。“ }, { “name“: “麻黄汤“, “composition“: “麻黄、桂枝、杏仁、甘草“, “indication“: “太阳伤寒证。头痛发热身疼腰痛骨节疼痛恶风无汗而喘。“, “key_symptoms“: [“发热“ “恶寒“ “无汗“ “身痛“ “喘“], “contraindication“: “表虚自汗、血虚、阴虚者慎用。“ } ]FormulaQuerySkill的工作就是接收SymptomInquirySkill输出的final_symptoms然后进行匹配计算。匹配算法可以很简单比如计算症状描述中与每个方剂key_symptoms的重合度也可以复杂一些引入权重和症状群逻辑。# 文件skills/formula_query/skill.py class FormulaQuerySkill: def __init__(self, knowledge_base_path): with open(knowledge_base_path, ‘r‘ encoding‘utf-8‘) as f: self.formulas json.load(f) async def execute(self, symptoms_data: dict) - dict: “““根据症状数据匹配经方“”“ user_symptoms_text symptoms_data.get(“final_symptoms“ {}).get(“chief_complaint“ ““) “ “ “ “.join([v for k, v in symptoms_data.get(“final_symptoms“ {}).items() if k ! ‘chief_complaint‘]) user_symptoms_list self._extract_keywords(user_symptoms_text) # 一个简单的关键词提取函数 matches [] for formula in self.formulas: score self._calculate_match_score(user_symptoms_list, formula[“key_symptoms“]) if score 0: # 设置一个阈值 matches.append({ “formula“: formula[“name“], “score“: score, “indication“: formula[“indication“], “composition“: formula[“composition“], “contraindication“: formula[“contraindication“] }) # 按匹配度排序 matches.sort(keylambda x: x[“score“], reverseTrue) return {“matches“: matches[:3]} # 返回匹配度最高的前三个3.3 构建主控Agent与工作流在OpenClaw的Web管理界面或配置文件中我们需要定义一个主控Agent它将协调上述两个技能。Agent配置我们创建一个名为TCM_Consultant的Agent。工作流设计触发用户输入如“我感觉有点不舒服”。步骤1调用SymptomInquirySkill开始多轮对话收集症状。该技能内部管理对话状态直到收集完成或主动中断。步骤2SymptomInquirySkill返回完成信号和收集到的症状数据。步骤3主控Agent将症状数据传递给FormulaQuerySkill。步骤4FormulaQuerySkill返回匹配的方剂列表和分析。步骤5主控Agent将结果格式化并附上强烈的免责声明返回给用户。提示词工程为主控Agent和技能编写清晰的系统提示词System Prompt至关重要。例如主控Agent的提示词必须包含“你是一个中医知识科普助手。你的任务是根据用户描述引导其完成症状收集并从知识库中匹配相关中医经方进行介绍。你提供的所有信息都来源于公开古籍和资料仅供学习和参考不能作为医疗诊断依据。在任何回复的开头或结尾必须明确提示‘内容仅供参考如有不适请及时就医’。”3.4 集成与发布接入飞书机器人将我们的AI技能对外提供服务飞书是个不错的起点。OpenClaw通常提供HTTP API。在OpenClaw中暴露API确保我们的TCM_ConsultantAgent 可以通过一个特定的API端点如/api/tcm/consult被调用接收用户输入返回Agent的响应。创建飞书自定义机器人在飞书开放平台创建一个企业自建应用启用“机器人”能力。获取app_id和app_secret。编写中间件服务可选但推荐虽然可以直接在飞书机器人配置中填写OpenClaw的API地址但编写一个简单的中间件如使用Python Flask更有弹性。这个中间件负责验证飞书推送过来的请求签名。将飞书的用户消息转发给OpenClaw的API。将OpenClaw返回的文本格式化成飞书支持的富文本或卡片消息。处理可能的多轮对话会话IDSession ID管理。配置事件订阅与消息回调在飞书应用后台配置“消息与事件”中的“接收消息请求地址”填写你的中间件服务公网URL。飞书会在用户机器人时将消息POST到该地址。# 一个极简的Flask中间件示例 from flask import Flask, request, jsonify import requests import hashlib import hmac import base64 import json app Flask(__name__) OPENCLAW_API_URL “http://localhost:8080/api/tcm/consult“ FEISHU_VERIFICATION_TOKEN “your_verification_token“ # 飞书后台获取 app.route(‘/feishu/webhook‘ methods[‘POST‘]) def feishu_webhook(): data request.json # 1. 验证签名略实际必须实现 # 2. 如果是url验证挑战直接返回challenge if data.get(‘type‘) ‘url_verification‘: return jsonify({‘challenge‘: data.get(‘challenge‘)}) # 3. 处理用户消息 if data.get(‘type‘) ‘message‘: user_input data[‘event‘][‘message‘][‘content‘] session_id data[‘event‘][‘sender‘][‘sender_id‘][‘open_id‘] # 调用OpenClaw API payload {“session_id“: session_id, “query“: user_input} resp requests.post(OPENCLAW_API_URL, jsonpayload, timeout30) ai_response resp.json().get(‘response‘ ‘分析中请稍候。‘) # 必须添加免责声明 final_response f“{ai_response}\n\n---\n**重要提示**以上内容基于公开中医知识库生成仅为文化科普与学习交流之用不构成任何医疗建议。个体情况千差万别如有健康问题请务必咨询专业执业中医师。“ # 这里需要调用飞书API将final_response发回给用户 # send_to_feishu(user_id, final_response) return jsonify({‘code‘: 0}) return jsonify({‘code‘: 1 ‘msg‘: ‘unknown event‘}) if __name__ ‘__main__‘: app.run(host‘0.0.0.0‘ port5000)4. 核心细节解析与避坑指南4.1 中医知识库构建的“质”与“量”质大于量初期不需要覆盖所有方剂。精选20-50个最经典、辨证要点清晰的经方如《伤寒论》前50条方把每个方剂的“症候群”描述准确。一个精准的“桂枝汤证”描述比模糊的100个方剂条目更有用。结构化字段设计除了方名、组成、主治一定要设计“核心症状群”key_symptom_clusters字段。例如“key_symptom_clusters“: [ [“发热“ “恶风“ “汗出“ “脉浮缓“] [“鼻鸣“ “干呕“] ]这表示第一组症状是主要辨证要点第二组是或然证。匹配算法可以优先匹配主要症候群。引入“禁忌”与“类似方鉴别”字段这是体现实用性和安全性的关键。在输出时AI技能应能主动说明“此方适用于XX情况若出现YY症状则不宜使用”并提示“需与ZZ方同样治疗AA症状但BB不同进行鉴别”。这能极大提升内容的专业感和可信度。4.2 Agent多轮对话状态管理的挑战OpenClaw等框架提供了会话管理的基础但针对中医问诊这种有固定流程的场景需要精细设计。状态持久化必须将SymptomInquirySkill的current_step和collected_symptoms与用户的session_id绑定并存储到Redis或数据库中。否则用户下次说话时对话状态就丢失了。异常流程处理用户可能不按常理出牌比如在回答“寒热”时突然说“我还有点拉肚子”。技能需要能处理这种“跳跃式”输入并尝试将其归类到对应的症状类别中如将“拉肚子”归入“二便”同时更新问诊流程避免重复提问。设置对话超时与重置如果用户超过10分钟没有回复应自动重置问诊状态避免旧会话数据干扰新咨询。4.3 提示词工程约束AI的“发挥”大模型容易“幻觉”或过度发挥。必须通过严格的提示词将其约束在知识库范围内。系统提示词示例 “你是一个严谨的中医知识查询助手。你的知识完全来源于我提供给你的结构化知识库。对于用户的症状描述你必须严格按照以下步骤工作1. 解析症状关键词。2. 在知识库中寻找包含这些关键词的方剂。3. 计算匹配度并排序。4. 输出时首先声明‘根据您描述的症状在知识库中匹配到以下可能相关的方剂仅供参考’。然后严格按‘方剂名称’、‘组成’、‘匹配到的症状关键词’、‘原文主治’、‘使用注意’的格式列出。严禁编造知识库中没有的方剂、症状或功效。如果知识库中没有匹配项请直接回答‘您描述的症状在现有知识库中未能找到高度匹配的经典方剂建议咨询专业医师。’”在每次调用模型的用户消息前动态插入上下文【当前问诊已收集信息】主诉头痛。寒热怕冷无汗。汗出无汗。 【知识库摘要】麻黄汤主治太阳伤寒证头痛发热身疼腰痛骨节疼痛恶风无汗而喘... 【指令】请根据以上收集的信息和知识库判断是否需要继续询问如下一步询问“身痛情况”还是可以给出初步匹配建议。如果给出建议请严格按知识库信息输出。4.4 性能优化与成本控制模型选择本地部署时7B参数模型如Qwen2.5-7B-Instruct在推理速度和精度上比较平衡。如果使用API如GPT-4成本较高可以通过以下方式优化缓存对常见症状组合如“感冒 流清涕 怕冷 无汗”的查询结果进行缓存。知识库前置过滤在调用大模型进行最终语言组织前先用简单的关键词匹配在本地知识库中筛选出候选方剂比如前5个然后将这5个方剂的详细信息作为上下文喂给大模型让它做精炼和总结。这大大减少了模型的“思考”负担和输入token数量。技能异步化SymptomInquirySkill的对话等待是阻塞的。在实际部署中应该采用异步事件驱动架构。当技能进入“等待用户回复”状态时释放资源。收到用户回复后再根据session_id恢复状态继续执行。OpenClaw的架构通常支持这种异步处理。5. 常见问题与排查实录在实际搭建和测试过程中你几乎一定会遇到下面这些问题。这里是我的排查记录问题1OpenClaw容器无法连接宿主机Ollama服务报错“Connection refused”。现象OpenClaw日志显示调用LLM失败。排查在宿主机执行curl http://localhost:11434/api/tags确认Ollama服务正常。进入OpenClaw容器内部docker exec -it tcm-openclaw bash尝试curl http://host.docker.internal:11434/api/tags。如果失败说明容器内无法解析该主机名。解决方案A推荐在docker-compose.yml中为OpenClaw服务添加extra_hosts配置并改用宿主机的实际局域网IP。services: openclaw: ... extra_hosts: - “host.docker.internal:172.17.0.1“ # 172.17.0.1是Docker默认网桥网关可在宿主机用ip addr show docker0查看 environment: - OLLAMA_BASE_URLhttp://host.docker.internal:11434方案B使用network_mode: “host“让容器共享宿主机网络但这样会牺牲一些隔离性。问题2AI技能的回答天马行空脱离知识库甚至自己编造方剂。现象用户说“头痛”AI回复了一个知识库里没有的“自创方”。原因提示词约束力不够和/或知识库信息没有有效地作为上下文注入。解决强化系统提示词在提示词中明确“严禁编造”、“你的知识仅限于以下内容”。采用RAG检索增强生成模式不要一次性把整个知识库都塞给模型。先根据用户输入用向量数据库或关键词从知识库中检索出最相关的3-5条方剂记录。然后在给模型的指令中明确“请基于且仅基于以下提供的方剂信息进行回答[检索到的方剂1详情] [检索到的方剂2详情]...”。后处理校验在AI输出最终答案前加一个简单的校验步骤检查回答中提到的方剂名称是否在本次检索结果列表中如果不在则触发一个修正流程或直接返回“未找到匹配”。问题3多轮对话中AI忘记之前聊过的内容。现象用户回答了怕冷、无汗AI接着问“您哪里不舒服”状态丢失。原因Session管理失效。OpenClaw Agent的会话上下文没有正确传递或持久化。解决检查OpenClaw的Agent配置确保开启了会话记忆Conversation Memory功能并设置了合适的记忆窗口如最近10轮对话。在自定义技能中像SymptomInquirySkill必须将会话IDsession_id作为状态存储和读取的键。所有中间状态current_step,collected_symptoms都应存入一个外部存储如Redis键名为skill_state:{session_id}。在技能执行入口首先根据session_id读取历史状态执行完逻辑后立即将新状态写回。问题4飞书机器人收不到消息或无法回复。现象用户机器人无反应或中间件服务收到消息但发不回去。排查验证飞书配置检查“事件订阅”中的“请求网址”是否已通过飞书验证即你的服务正确处理了url_verification事件。检查网络确保你的中间件服务所在服务器可以被飞书服务器访问公网IP或内网穿透。查看日志在中间件服务中打印详细的接收和发送日志。检查是否成功从飞书拿到了tenant_access_token用于调用飞书发送消息API。权限检查飞书机器人应用是否已获取“获取用户ID”、“发送消息”、“以应用身份读取通讯录”等必要权限。解决按照飞书官方文档一步步调试认证和消息收发流程。使用Postman等工具模拟飞书服务器发送事件来测试你的中间件逻辑是否正确。这个项目从构思到实现就像剥龙虾需要耐心地一层层处理外壳环境部署、框架学习才能吃到里面的肉核心技能逻辑。最大的收获不是做出了一个多厉害的AI中医而是完整走通了一个AI Agent从技术选型、技能设计、知识处理、到最终集成上线的全链路。过程中对提示词工程、状态管理和错误处理的理解比读十篇教程都深刻。如果你也想用AI搞点有意思的事情不妨从一个这样有明确边界、有具体场景的小技能开始边做边学乐趣无穷。
返回列表