拓冰建站拓冰建站
首页 / 资讯中心 / 正文

基于OpenClaw框架构建AI Agent:从零实现中医方剂推荐技能

1. 从剥龙虾到开药方一次跨界AI Agent的实践探索最近在折腾一个挺有意思的事儿一边剥着小龙虾一边琢磨着怎么让AI也能“望闻问切”开个中医方子。这听起来像是两个八竿子打不着的领域但核心逻辑其实是一致的如何让一个智能体Agent去理解复杂的现实世界任务并拆解、执行它。剥龙虾需要识别关节、用力技巧、避免被刺伤中医开方需要辨证、选药、组方、考虑禁忌。这两件事本质上都是“感知-决策-执行”的链条。我这次实践的主角是近期在开发者圈里热度颇高的OpenClaw一个开源的AI Agent框架。我的目标很明确不满足于让它只是回答“当归补血”而是真正构建一个能模拟中医辨证论治过程的“技能”让AI Agent具备初步的、可交互的方剂推荐能力。你可能会问为什么是OpenClaw市面上Agent框架不少比如LangChain、LlamaIndex还有Coze、Dify这类低代码平台。OpenClaw吸引我的点在于它的“轻量”和“直接”。它不像一些大而全的框架需要你先理解一大堆抽象概念而是试图用更简洁的方式将大模型LLM的能力“工具化”、“技能化”。它的设计哲学是让开发者能快速地将一个想法封装成一个可被调用的“技能”Skill并且这个技能能与其他技能、工具、甚至是现实世界的API进行组合。这对于我这种想快速验证“中医AI助手”这个想法的人来说再合适不过了。所以这篇文章就是一次完整的记录。我会带你从零开始理解OpenClaw的基本概念部署一个可用的环境然后重点分享如何构思并实现一个“中医方剂技能”。这个过程里有环境配置的坑有提示词Prompt设计的反复调试也有对中医知识如何被AI理解的思考。无论你是对AI Agent开发感兴趣还是对传统知识与现代技术的结合有想法希望这篇“剥着龙虾搞出来”的实践笔记能给你带来一些实实在在的参考。2. OpenClaw初探它是什么以及为什么选它在深入代码之前我们得先搞清楚手里的“工具”到底是什么。OpenClaw根据其官方描述和社区讨论它是一个开源的大模型智能体LLM Agent框架。它的核心目标是降低构建基于大模型的、具备复杂任务执行能力的智能体的门槛。你可以把它想象成一个“乐高底座”大模型是提供想象力和逻辑的“大脑”而OpenClaw则提供了各种标准化的“接口”和“连接件”让你能轻松地把“大脑”的想法通过调用工具、访问数据、执行代码等方式在现实世界中实现出来。2.1 OpenClaw的核心组件与工作流与一些更偏向于应用编排的平台如Coze不同OpenClaw更贴近开发者强调代码的可控性和灵活性。它的架构通常围绕以下几个核心概念展开Agent智能体这是任务执行的最高层级实体。你定义一个Agent就相当于定义了一个具备特定目标和能力的“虚拟角色”。在我们的场景里这个Agent就是“中医助手”。Skill技能这是Agent能力的具象化单元。一个Skill就是一个封装好的、可重复使用的功能模块。比如“查询药材功效”可以是一个Skill“辨证分型”可以是另一个Skill。我们的核心目标就是创建一个“中医方剂推荐”Skill。Tool工具比Skill更原子化的操作。可以是调用一个外部API如查询天气、执行一段系统命令、或者操作一个数据库。Skill内部可以组合使用多个Tool来完成更复杂的任务。Planner规划器与Executor执行器这是Agent的“思考-行动”循环。Planner负责理解用户请求并将其分解成一系列Skill或Tool的调用序列一个计划。Executor则负责按顺序执行这个计划并处理每一步的结果和可能的异常。它的典型工作流是这样的用户输入一个问题如“我最近失眠、心烦、口干该吃什么药” - Agent的Planner通常由大模型驱动分析问题决定需要调用哪些Skill - 依次执行“辨证Skill”判断为“心火亢盛”和“方剂查询Skill”匹配“朱砂安神丸”等 - 将各步骤结果整合生成最终回答返回给用户。2.2 为何选择OpenClaw进行此次实验面对众多选择我基于以下几点考量最终敲定了OpenClaw开源与可控性代码完全开放意味着我可以深入其内部机制根据中医领域的特殊需求进行定制化修改。比如我可以调整Skill之间传递信息的格式或者为辨证过程设计专门的推理逻辑这在闭源或SaaS平台上是难以实现的。轻量级与开发友好它的设计似乎避免了一些框架的“过度设计”问题。上手文档和示例相对直接依赖清晰让我能更快地进入“实现想法”的阶段而不是陷入“学习框架哲学”的泥潭。技能Skill的抽象很契合“中医方剂推荐”本身就是一个非常典型的“技能”。OpenClaw以Skill为中心的模型让我能很自然地将这个复杂问题模块化。我可以先实现一个基础的方剂查询技能再逐步增加辨证、加减药材、禁忌检查等子技能迭代起来非常清晰。社区热度与潜力从提供的热搜词可以看到“openclaw安装”、“openclaw部署”、“agent开发”等词条有相当的搜索量说明它正处于一个活跃的上升期。选择一个有活力的项目意味着在遇到问题时更有可能找到解决方案或同行讨论。当然它并非没有缺点。作为一个较新的项目其文档的完整性、社区的成熟度可能不如LangChain等老牌框架。部署和配置过程中可能会遇到一些“坑”但这恰恰也是实践的一部分。接下来我们就从把这个框架“跑起来”开始。3. 实战部署在Docker中搭建OpenClaw运行环境理论清楚了就要动手。为了环境纯净和可复现我强烈推荐使用Docker进行部署。这也是社区里最常见的做法。下面是我一步步搭建环境的过程其中包含了一些官方文档可能没细说但实际会遇到的问题。3.1 前期准备与踩坑预警在拉取镜像之前有件事必须明确OpenClaw需要一个大模型后端作为其“大脑”。它本身只是一个框架不提供模型能力。这意味着你需要准备一个可访问的大模型API比如OpenAI的GPT系列、Anthropic的Claude或者开源的、通过Ollama、vLLM等工具本地部署的模型如Llama 3、Qwen等。我的选择是在本地通过Ollama运行一个轻量级的模型如qwen2.5:7b这样响应速度快且没有网络延迟和费用顾虑。你需要先确保你的机器上已经安装了Docker和Docker Compose并且有足够的资源至少8GB可用内存来运行模型。第一个坑来了网络配置。如果你像我一样使用本地Ollama那么OpenClaw的Docker容器需要能访问到主机上的Ollama服务。这里不能简单地用localhost因为在容器内部localhost指的是容器自己。我们需要使用宿主机的特殊DNS名称host.docker.internal在Mac/Windows的Docker Desktop上可用或者直接使用宿主机的IP地址。3.2 使用Docker Compose一键部署OpenClaw社区通常提供了示例的docker-compose.yml文件这极大简化了部署。下面是一个我调整后的版本重点配置了模型端点version: 3.8 services: openclaw: image: your_openclaw_image:latest # 这里需要替换为实际的OpenClaw镜像名例如 openclaw/openclaw:latest container_name: openclaw_server ports: - 8000:8000 # 将容器的8000端口映射到主机的8000端口 environment: - OPENAI_API_BASEhttp://host.docker.internal:11434/v1 # 关键指向主机Ollama的API地址 - OPENAI_API_KEYollama # Ollama不需要真正的key但有些框架要求非空任意填写即可 - MODEL_NAMEqwen2.5:7b # 指定使用的模型名称 - LOG_LEVELINFO volumes: - ./skills:/app/skills # 挂载本地目录用于存放我们自定义的技能代码 - ./storage:/app/storage # 挂载存储目录用于持久化数据 restart: unless-stopped networks: - openclaw-net networks: openclaw-net: driver: bridge关键点解析OPENAI_API_BASE这是最重要的配置。我将其设置为http://host.docker.internal:11434/v1。host.docker.internal让容器能访问宿主机服务11434是Ollama的默认端口/v1是OpenAI兼容API的路径。OPENAI_API_KEY对于本地Ollama可以随意填写一个非空字符串如ollama。MODEL_NAME必须与你在Ollama中拉取和运行的模型名称完全一致。volumes挂载skills目录至关重要。这样我们可以在宿主机上编写和修改技能代码容器内能实时生效无需每次重建镜像。在包含这个docker-compose.yml文件的目录下运行docker-compose up -dOpenClaw服务就应该启动起来了。你可以通过docker logs -f openclaw_server来查看日志确认没有报错并且成功连接到了模型。3.3 验证部署与常见问题排查部署完成后访问http://localhost:8000/docs如果映射了其他端口则替换应该能看到OpenClaw的API文档页面这是一个好迹象。可能遇到的错误及解决openclaw llamap svr operator(): got exception: { error: { code: 400, ...这类错误通常指向模型API调用失败。请按以下顺序检查模型服务是否运行在宿主机上执行curl http://localhost:11434/api/tags看是否能列出Ollama中的模型。容器内能否访问主机进入容器docker exec -it openclaw_server sh执行curl http://host.docker.internal:11434/api/tags进行测试。API路径和密钥确认OPENAI_API_BASE路径正确特别是/v1MODEL_NAME拼写无误。模型是否加载在Ollama中确保指定模型已通过ollama run qwen2.5:7b等方式正确拉取和加载。技能加载失败如果日志提示找不到技能或技能初始化错误请检查挂载的./skills目录权限是否正确以及目录内是否有正确的技能文件结构。当服务稳定运行API可以正常调用后我们的“舞台”就搭好了。接下来就是给这位AI“演员”编写“中医”这个剧本也就是创建自定义技能。4. 核心实现设计并编码“中医方剂推荐”技能这是整个项目最核心、也最有挑战性的部分。我们的目标不是创建一个包罗万象的“AI老中医”而是一个有限但实用的技能根据用户描述的症状进行初步的辨证分析并推荐一个或多个经典方剂同时给出简要的方解和注意事项。4.1 技能设计思路分而治之一个复杂的技能直接让大模型“端到端”生成答案往往效果不稳定且难以控制输出格式和知识准确性。我采用“分而治之”的策略将技能拆解为两个核心阶段对应两个子技能或一个技能内的两个步骤辨证分析阶段输入是用户自然语言描述的症状如“头痛、发烧、怕冷、无汗、脉浮紧”。目标是输出结构化的辨证结果例如{证型: 风寒感冒, 治法: 辛温解表, 关键症状: [头痛, 发热恶寒, 无汗, 脉浮紧]}。这一步主要依靠大模型的自然语言理解和推理能力。方剂匹配与生成阶段输入是上一步的结构化辨证结果。目标是查询一个本地的“方剂知识库”找到匹配的方剂并生成格式友好的回答。这一步可以结合规则匹配和模型生成。为什么这么设计首先解耦。辨证和方剂匹配可以独立优化。其次可控。我们可以为辨证阶段设计更精细的提示词Prompt引导模型输出我们需要的结构化数据。最后可扩展。未来我们可以很方便地替换“方剂知识库”或者增加“药材查询”、“禁忌检查”等新的阶段。4.2 构建本地方剂知识库在编码技能之前我们需要数据。一个高质量的、结构化的方剂知识库是基础。我创建了一个简单的JSON文件formula_database.json存放在挂载的目录下[ { name: 麻黄汤, pinyin: Mahuang Tang, composition: [麻黄, 桂枝, 杏仁, 甘草], functions: 发汗解表宣肺平喘, indications: 外感风寒表实证。恶寒发热头身疼痛无汗而喘舌苔薄白脉浮紧。, contraindications: 表虚自汗、外感风热、体虚者慎用。 }, { name: 桂枝汤, pinyin: Guizhi Tang, composition: [桂枝, 白芍, 生姜, 大枣, 甘草], functions: 解肌发表调和营卫, indications: 外感风寒表虚证。头痛发热汗出恶风鼻鸣干呕苔白不渴脉浮缓或浮弱。, contraindications: 外感风寒表实无汗者禁用。 }, { name: 银翘散, pinyin: Yinqiao San, composition: [金银花, 连翘, 桔梗, 薄荷, 竹叶, 生甘草, 荆芥穗, 淡豆豉, 牛蒡子], functions: 辛凉透表清热解毒, indications: 温病初起。发热无汗或有汗不畅微恶风寒头痛口渴咳嗽咽痛舌尖红苔薄白或薄黄脉浮数。, contraindications: 风寒感冒者不宜。 } ]这个库虽然小但包含了方名、拼音、组成、功效、主治、禁忌等关键字段足以演示技能逻辑。在实际应用中这个库可以扩展得非常庞大和详细。4.3 编写OpenClaw技能代码接下来我们在挂载的skills目录下创建我们的技能文件例如tcm_formula_skill.py。OpenClaw的技能通常继承一个基类并实现execute方法。import json import os from typing import Dict, Any from openclaw.skill import BaseSkill # 假设的导入路径根据实际框架调整 class TCMFormulaSkill(BaseSkill): 中医方剂推荐技能 def __init__(self, name: str, description: str, **kwargs): super().__init__(name, description, **kwargs) # 加载方剂知识库 db_path os.path.join(os.path.dirname(__file__), data, formula_database.json) with open(db_path, r, encodingutf-8) as f: self.formula_db json.load(f) async def execute(self, input_data: Dict[str, Any], context: Dict[str, Any]) - Dict[str, Any]: 执行技能。 :param input_data: 输入数据应包含 symptoms 字段 :param context: 执行上下文 :return: 执行结果 user_symptoms input_data.get(symptoms, ) if not user_symptoms: return {error: 请输入症状描述} # 阶段1: 调用大模型进行辨证分析 syndrome_result await self._differentiate_syndrome(user_symptoms, context) if error in syndrome_result: return syndrome_result # 阶段2: 基于辨证结果匹配方剂 formula_suggestions self._match_formula(syndrome_result) # 整合结果 final_output { user_input: user_symptoms, syndrome_analysis: syndrome_result, recommended_formulas: formula_suggestions, advice: 以上建议仅供参考具体用药请咨询专业中医师。 } return final_output async def _differentiate_syndrome(self, symptoms: str, context: Dict) - Dict: 利用大模型进行辨证分析 # 构造一个精心设计的Prompt引导模型输出结构化数据 prompt f 你是一位经验丰富的中医师。请根据患者描述的症状进行中医辨证分析。 患者症状描述{symptoms} 请严格按照以下JSON格式输出分析结果不要输出任何其他解释性文字 {{ 证型: 例如风寒感冒、风热感冒、肝阳上亢等, 治法: 例如辛温解表、辛凉解表、平肝潜阳等, 关键症状: [症状1, 症状2, ...], 舌脉推测: 基于常见情况推测如舌淡红苔薄白脉浮紧 }} 注意证型和治法请使用标准的中医术语。 # 通过context中的LLM客户端调用模型 llm_client context.get(llm_client) if not llm_client: return {error: LLM客户端未配置} try: response await llm_client.chat.completions.create( modelcontext.get(model_name, qwen2.5:7b), messages[{role: user, content: prompt}], temperature0.1, # 降低随机性使输出更稳定 response_format{ type: json_object } # 如果模型支持强制JSON输出 ) result_text response.choices[0].message.content # 解析JSON return json.loads(result_text) except Exception as e: return {error: f辨证分析失败: {str(e)}} def _match_formula(self, syndrome_data: Dict) - list: 根据辨证结果匹配方剂 syndrome_type syndrome_data.get(证型, ) treatment_method syndrome_data.get(治法, ) key_symptoms set(syndrome_data.get(关键症状, [])) matched [] for formula in self.formula_db: # 简单的关键词匹配逻辑实际应用可更复杂如向量检索 indications formula.get(indications, ) # 检查证型或治法是否在主治中提及或症状有重叠 if (syndrome_type and syndrome_type in indications) or \ (treatment_method and treatment_method in indications): matched.append(formula) else: # 或者检查关键症状是否出现在主治描述中 symptom_overlap any(symptom in indications for symptom in key_symptoms if symptom) if symptom_overlap and len(key_symptoms) 0: matched.append(formula) # 去重并排序可以根据匹配度评分 seen set() unique_matched [] for f in matched: if f[name] not in seen: seen.add(f[name]) unique_matched.append(f) return unique_matched[:5] # 返回最多5个匹配方剂代码要点与心得结构化输出是王道在_differentiate_syndrome方法中Prompt明确要求模型输出JSON格式。这是控制大模型行为、方便后续程序处理的关键。response_format{ type: json_object }参数如果模型支持能极大提高输出格式的稳定性。温度Temperature设置对于辨证这种需要确定性和准确性的任务我将温度设为较低的0.1以减少模型的“胡言乱语”。匹配策略的简化_match_formula方法使用了简单的关键词匹配这在真实场景中远远不够。更优的方案是向量化检索将方剂的主治、功效等文本转化为向量同样将用户症状描述向量化进行相似度计算。知识图谱构建中医证型-症状-方剂的知识图谱进行图谱查询。规则引擎编写更复杂的辨证规则。这里为了演示采用了最简单的方式。错误处理代码中加入了基本的错误处理比如模型调用失败、JSON解析失败等确保技能不会因为意外错误而完全崩溃。4.4 注册并测试技能技能代码写好后需要在OpenClaw中注册它。这通常通过一个配置文件或在一个主应用文件中导入并注册技能类来完成。具体方式取决于OpenClaw项目的版本和结构可能需要修改某个skills/__init__.py或主app.py文件。注册成功后重启OpenClaw服务。然后我们就可以通过其提供的API通常是/api/agent/run或类似的端点来测试我们的技能了。发送一个POST请求Payload可能如下{ skill: tcm_formula_skill, input: { symptoms: 我最近感觉头痛有点发烧特别怕冷没有汗喉咙有点痒。 } }理想的返回结果应该类似于{ user_input: 我最近感觉头痛有点发烧特别怕冷没有汗喉咙有点痒。, syndrome_analysis: { 证型: 风寒感冒, 治法: 辛温解表, 关键症状: [头痛, 发热, 恶寒, 无汗, 喉咙痒], 舌脉推测: 舌苔薄白脉浮紧 }, recommended_formulas: [ { name: 麻黄汤, pinyin: Mahuang Tang, composition: [麻黄, 桂枝, 杏仁, 甘草], functions: 发汗解表宣肺平喘, indications: 外感风寒表实证。恶寒发热头身疼痛无汗而喘舌苔薄白脉浮紧。, contraindications: 表虚自汗、外感风热、体虚者慎用。 } ], advice: 以上建议仅供参考具体用药请咨询专业中医师。 }看到这样的结果就说明我们的技能基本跑通了它成功地将自然语言症状通过大模型辨证转换成了结构化的中医诊断并匹配到了经典的“麻黄汤”。5. 优化、反思与未来可能的方向技能能跑起来只是第一步要让其真正可用、可靠还有很长的路要走。在调试和测试过程中我遇到了不少问题也总结出一些优化思路。5.1 当前实现的主要问题与优化点辨证准确性依赖Prompt和模型能力这是最大的瓶颈。当前简单的Prompt只能处理典型症状。对于复杂、模糊或兼夹证型的描述模型很容易出错。优化方向Few-shot Prompting在Prompt中提供几个高质量、覆盖不同证型的辨证示例引导模型学习输出模式。思维链Chain-of-Thought要求模型先一步步推理“患者有A症状提示B有C症状提示D综合来看证型可能是E”再输出最终结论。这能提升推理过程的可靠性。专用微调模型如果有足够的中医病历数据可以微调一个专门的“中医辨证模型”这将是效果最好的方式但成本也最高。方剂匹配过于粗糙关键词匹配召回率和准确率都低。优化方向引入向量数据库如前所述使用Sentence-BERT等模型将方剂主治和症状描述向量化存入ChromaDB、Qdrant等向量数据库实现语义相似度检索。增加过滤和排序规则在向量检索初步结果后加入基于“证型”、“治法”的规则过滤以及根据症状匹配数量、方剂经典程度等进行排序。缺乏交互与追问真实的问诊是一个交互过程。当前技能是“一次性”的。优化方向设计多轮对话Skill让Agent具备主动追问的能力。例如当症状描述不完整时只说“肚子疼”Skill可以触发一个“症状追问子技能”询问“疼痛是胀痛还是刺痛喜按还是拒按大便情况如何”等将收集到的信息补充到上下文再进行辨证。安全性与免责声明医疗建议容错率极低。必须在每一次输出中以醒目的方式强调“仅供参考不能替代专业医疗诊断用药需咨询医师”。甚至可以设计一个前置检查如果症状描述涉及“胸痛剧烈”、“昏迷”、“大出血”等危重关键词直接拒绝回答并强烈建议立即就医。5.2 技能组合与智能体Agent的构建单一的“方剂推荐”技能价值有限。OpenClaw的强大之处在于技能的组合。我们可以围绕“中医助手”这个Agent设计一系列协同工作的技能症状采集与澄清技能负责与用户进行多轮对话结构化收集症状信息。辨证分析技能即我们上面实现的核心。方剂查询与匹配技能基于辨证结果进行检索和匹配。药材百科查询技能当用户对某个推荐方剂中的某味药感兴趣时可以调用此技能查询该药材的性味归经、功效、禁忌等。经典医案参考技能匹配历史上类似病案的记载和治疗思路提供参考。禁忌与注意事项检查技能结合用户可能提供的简易个人信息如“孕妇”、“儿童”对推荐方剂进行安全过滤。然后通过OpenClaw的Planner让Agent能够自动规划这些技能的调用顺序。用户问“我感冒了怎么办”Planner可能规划为[症状采集] - [辨证分析] - [方剂匹配] - [禁忌检查] - [整合输出]。这样一个功能相对完整的“AI中医助手”原型就初具雏形了。5.3 关于“剥龙虾”的启示回到标题里的“剥龙虾”这不仅仅是一个比喻。它提醒我们设计AI Agent处理复杂任务时要有“拆解”的思维。剥龙虾需要识别结构虾头、虾身、虾壳、运用工具手、剪刀、遵循流程去头、剪边、剥壳。我们设计中医技能也一样拆解“辨证论治”这个复杂过程为“信息收集望闻问切”、“病机分析辨证”、“治法确定”、“选方用药”、“随症加减”等可执行的步骤并为每个步骤找到合适的“工具”大模型推理、知识库查询、规则判断。这次实践从部署OpenClaw到实现一个初步可用的技能让我深刻体会到AI Agent的开发不再是空中楼阁。借助这些日益成熟的框架我们可以将领域知识无论是中医、法律、金融还是剥龙虾有效地“编码”成AI可理解和执行的技能。虽然前路仍有诸多挑战——知识的准确性、推理的可靠性、交互的自然性——但方向是清晰的。或许有一天每个人身边都能有一个这样专业、贴心的AI助手而我们现在做的正是在为那个未来剥开一层层技术的“硬壳”。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门