尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

用LLM构建成瘾行为干预助手:从戒烟到通用实践

用LLM构建成瘾行为干预助手:从戒烟到通用实践 解决尼古丁依赖是一个需要长期坚持的过程而 LLM 能在其中扮演一个全天候、可对话的行为管理助手。本文会从开发者的角度围绕“How I used an LLM to kick my nicotine addiction”这个场景展示如何用大语言模型构建一个成瘾行为干预辅助工具。它不是一个医疗系统也不提供治疗承诺而是一个支持动机访谈、渴求预警、认知重构和习惯追踪的对话应用。读完这篇文章后你可以获得一套可复现的最小项目结构、Prompt 设计方法和数据评估思路并能够根据实际需求替换成戒烟、戒糖、减少久坐等不同目标。这个主题很容易被误解成“用 AI 治病”。实际项目里更准确的定位是LLM 负责对话生成和内容组织用户负责真实行为决策开发者负责控制边界和记录数据。本文不讨论医学建议只关注如何用技术手段把认知行为方法中的常见策略转换成一个可运行的 LLM 应用。1. 理解 LLM 在成瘾行为干预中的角色边界1.1 这不是 AI 医生而是行为改变助手尼古丁依赖通常涉及生理依赖和心理依赖。单纯给用户推送“不要抽”的提醒很难产生持续效果。真正有意义的是通过高频对话帮助用户识别诱因、记录情绪、设计替代行为并在复吸后减少自我否定。LLM 适合承担这个角色因为它能理解自然语言能根据上下文生成有同理心的回复也能按照固定规则切换对话策略。但它没有医学诊断能力也无法感知用户真实的生理状态。因此系统定位必须是“行为管理助手”而不是“戒断治疗 AI”。在实际项目中我通常把系统边界写成三条可以聊当前情绪、渴望程度、触发器、替代行动。可以记触发时间、地点、压力值、是否复吸。不建议做开药、诊断疾病、替代医生建议、保证成功率。这条边界要在 System Prompt 中反复强调也可以在客户端展示“AI 不能提供专业医学建议”的提示。1.2 为什么用 LLM 而不是简单提醒 App传统习惯追踪应用能记录打卡时间但交互方式固定无法根据用户当下的情绪灵活调整。例如用户深夜加班时产生强烈渴求普通推送只能说“坚持住”。LLM 应用则能结合历史记录说出“你今天已经成功应对了两次类似场景这次我知道很难受要不要先离开座位接杯水”这种回应才更接近人的陪伴。LLM 的技术价值主要体现在三处上下文理解能读取用户前几轮对话识别复吸风险。策略切换根据渴望等级切换放松训练、转移注意力、认知重构等策略。数据处理把非结构化的对话转成结构化标签积累长期用户画像。但这不意味着要完全依赖 LLM 生成所有内容。高频渴求时回复速度重要答案长度反而次要。系统应设计为“短回复优先”避免用户等待。1.3 核心交互机制动机访谈、认知重构、紧急渴求应对行为干预中常用的方法可以映射成对话形态干预策略对话目标示例对话方向动机访谈探索改变的内在原因“你之所以想戒掉最主要的原因是什么”认知重构改变非理性信念“你刚才说‘没有烟就写不出代码’这个信念有证据吗”紧急渴求应对降低当下冲动“现在立刻做 3 次深呼吸然后告诉我第 4 次是否还强烈。”行为激活用替代行为填充时间“去洗杯子或站起来伸懒腰2 分钟后再回来聊。”复吸复盘降低自责提取经验“这次是几点、在哪、什么情绪下发生的下次可以调整哪一步”LLM 应用并不需要自己发明这些策略而是把策略写进 Prompt让模型按规则执行。后面会给出可复用的 System Prompt 模板。2. 技术选型本地模型还是云端 API2.1 两条技术路线与适用场景构建这类应用首先要决定 LLM 的部署方式。常见的两条路线是调用云端 API 和使用本地模型框架。云端 API 的好处是实现简单、回复质量稳定、不用考虑本机显卡。缺点是有网络延迟且健康类对话涉及隐私数据必须脱敏。本地模型的好处是数据不出本机延迟可控但需要一定显存模型能力也受参数规模限制。对比维度云端 API本地模型部署难度低注册服务即可中需要安装模型运行环境回复质量通常更高取决于模型规模和量化等级隐私控制需要额外脱敏和协议约束更可控运行成本按 token 计费主要看硬件电费延迟网络往返有波动本机推理更稳定适合场景快速原型、内部测试特定硬件环境、隐私要求高学习阶段建议先用云端 API 或本机的 Ollama 把流程跑通不要一开始就追求大参数模型。这里使用 OpenAI 兼容接口作为示例因为 Ollama 也支持同样的接口协议后续换模型只需要改base_url和model。2.2 环境准备与依赖安装本文示例使用 Python 3.10FastAPI 提供 HTTP 接口OpenAI SDK 负责调用模型。为了兼容本地模型我会把base_url设计成可配置项。创建一个项目目录mkdir llm-cessation-assistant cd llm-cessation-assistant python3 -m venv venv source venv/bin/activate安装依赖pip install fastapi uvicorn openai pydantic python-dotenv如果你使用本地 Ollama只需要先安装 Ollama再拉取模型ollama run qwen2.5:7b这一步会在本机启动一个兼容 OpenAI API 的服务默认地址是http://localhost:11434/v1。这样代码里只需要把base_url指向它不需要改其他逻辑。2.3 项目结构提前规划先规划一个最小但可扩展的结构llm-cessation-assistant/ ├── app.py # FastAPI 入口 ├── llm_client.py # 模型调用封装 ├── prompt.py # System Prompt 和工具函数 ├── models.py # Pydantic 数据模型 ├── habits.py # 简易内存状态存储 ├── .env # 环境变量不要提交到仓库 ├── requirements.txt └── README.md生产环境还要增加数据库、消息队列、日志采集和监控告警。这里先保证最小闭环能运行。3. 设计一个可落地的 Prompt 体系和对话状态管理3.1 需求拆解五类会话意图通过分析戒烟场景的对话可以把用户消息分为五类意图状态报告例如“现在特别想吸”“已经三小时没吸了”。情绪表达例如“太烦了”“工作压力好大”。触发器识别例如“同事递烟给我了”“路过便利店”。自我反思例如“我为什么总是戒不掉”。普通闲聊例如“今天天气不错”。不需要在代码里做严格的 NLP 分类而是让 LLM 在回复时自动判断当前应该使用哪个策略。但 Prompt 需要明确告诉模型这五类情况分别如何处理。3.2 System Prompt 设计模板好的 System Prompt 是整个应用效果的关键。下面是一个可以修改的模板核心是约束角色、边界、策略、格式和风险处理。SYSTEM_PROMPT 你是一个行为改变助手正在帮助用户减少尼古丁依赖。 你的目标不是批评用户而是在对话中帮助用户识别渴望、触发因素和替代行为。 行为准则 1. 保持简短回复。普通回复不超过 4 句话紧急渴求时不超过 2 句话。 2. 先共情再行动。不要一上来就讲大道理。 3. 识别到用户有强烈渴望时优先提供可立即执行的替代动作例如喝水、离开座位、深呼吸三次。 4. 不要承诺“一定能戒掉”不要夸大 AI 的能力。 5. 不要提供医疗诊断、用药建议或替代专业医生。 6. 如果用户表达自我否定先帮助降低自责再引导分析情境。 7. 如果检测到自伤、自杀或危险信号立即停止干预并建议联系专业机构或紧急服务。 回复格式 优先使用中文。可以使用列表但不要一次给太多选项。避免说教语气。 当前时间{current_time} 用户最近记录 {recent_context} 在这个模板中current_time和recent_context是两个动态填充的变量。前者帮助模型理解白天和夜间场景后者提供用户最近的经历。注意不要把“用户画像”“是否成功坚持”作为固定事实写进 Prompt。模型看到的上下文只是摘要应该用“用户上一次报告”这类表述避免模型产生错误记忆。3.3 对话状态与用户画像数据模型为了支撑上下文需要一个简单数据结构保存用户状态。这里用内存字典做演示生产环境可以替换为 Redis 或数据库。# models.py from pydantic import BaseModel from typing import Optional class UserEvent(BaseModel): event_type: str # craving / relapse / mood / action intensity: Optional[int] # 1-10 trigger: str note: str class MessageIn(BaseModel): user_id: str text: str# habits.py class HabitStore: def __init__(self): self.users {} def add_event(self, user_id: str, event: UserEvent): self.users.setdefault(user_id, []).append(event) def get_recent_context(self, user_id: str, limit: int 5): events self.users.get(user_id, [])[-limit:] return \n.join( f{e.event_type}: 强度{e.intensity or 无}, 触发源{e.trigger or 未知}, 备注{e.note} for e in events )实际生产环境不应该在 Prompt 里塞入过多历史否则 token 很快膨胀。推荐做法是先用模型或规则抽取对话中的关键字段再保存为结构化事件。这里先使用简单的event_type保存事件。3.4 紧急渴求时的时间提示策略渴求通常不会持续太久因此系统要引导用户拖延时间。Prompt 可以加入一个策略当用户报告“现在特别想”时先回应“试试 2 分钟策略”而不是直接长篇劝导。示例伪代码def build_user_message(text: str, user_id: str) - str: if any(k in text for k in [特别想, 受不了, 很冲动, 想抽]): return text \n[系统提示] 请先让用户进行 2 分钟替代动作再询问感受。 return text这里的[系统提示]是一种内部标记告诉模型优先执行紧急应对策略。因为模型是概率生成直接加指令比依赖它自己判断更稳定。4. 实现最小闭环从消息接受到回复生成4.1 FastAPI 入口和模型调用封装先实现 LLM 调用封装# llm_client.py import os from openai import OpenAI client OpenAI( api_keyos.getenv(OPENAI_API_KEY, ollama), base_urlos.getenv(OPENAI_BASE_URL, http://localhost:11434/v1), ) def get_chat_response(messages): response client.chat.completions.create( modelos.getenv(LLM_MODEL, qwen2.5:7b), messagesmessages, temperature0.7, max_tokens300, ) return response.choices[0].message.content这里把默认base_url指向 Ollama因此学习环境不需要申请 API Key。如果在生产使用云端 API在.env中改成对应地址和 Key 即可。然后实现 FastAPI 入口# app.py import os from datetime import datetime from fastapi import FastAPI from dotenv import load_dotenv from pydantic import BaseModel load_dotenv() from llm_client import get_chat_response from prompt import SYSTEM_PROMPT from habits import HabitStore app FastAPI() store HabitStore() class ChatRequest(BaseModel): user_id: str text: str app.post(/chat) def chat(req: ChatRequest): recent_context store.get_recent_context(req.user_id) system_content SYSTEM_PROMPT.format( current_timedatetime.now().strftime(%Y-%m-%d %H:%M), recent_contextrecent_context, ) messages [ {role: system, content: system_content}, {role: user, content: req.text}, ] reply get_chat_response(messages) return {user_id: req.user_id, reply: reply}这个/chat接口已经能完成一次对话。不过还没有保存任何信息所以下一步加入事件记录逻辑。4.2 记录用户事件并更新上下文可以在返回回复前从用户原文中尝试提取事件from models import UserEvent import re def extract_event(text: str): event_type note intensity None trigger if re.search(r特别想|很想|冲动|渴望, text): event_type craving m re.search(r(\d{1,2})\s*分, text) if m: intensity int(m.group(1)) elif re.search(r抽了|吸了|没忍住|复吸, text): event_type relapse elif re.search(r开心|心烦|焦虑|烦躁|压力, text): event_type mood if 因为 in text: trigger text.split(因为)[-1][:30] return UserEvent(event_typeevent_type, intensityintensity, triggertrigger, notetext[:100])这只是一个启发式规则不一定完美。更准确的方式是使用 LLM 做一次结构化抽取但会增加一次额外调用。学习阶段先用规则后续再升级。把它接入接口app.post(/chat) def chat(req: ChatRequest): recent_context store.get_recent_context(req.user_id) system_content SYSTEM_PROMPT.format( current_timedatetime.now().strftime(%Y-%m-%d %H:%M), recent_contextrecent_context, ) messages [ {role: system, content: system_content}, {role: user, content: req.text}, ] reply get_chat_response(messages) event extract_event(req.text) store.add_event(req.user_id, event) return {user_id: req.user_id, reply: reply, recorded_event: event.event_type}现在用户每次发消息都会保存事件下一次对话时recent_context就会包含最近的经历LLM 就能在回复中引用这些信息。4.3 启动服务和验证在项目根目录创建.envOPENAI_API_KEYollama OPENAI_BASE_URLhttp://localhost:11434/v1 LLM_MODELqwen2.5:7b PORT8000启动服务uvicorn app:app --host 0.0.0.0 --port 8000 --reload使用 curl 验证curl -X POST http://localhost:8000/chat \ -H Content-Type: application/json \ -d {user_id: u1, text: 现在特别想抽一根压力太大了}如果本地 Ollama 正常运行会返回类似{ user_id: u1, reply: 我理解压力大的时候确实很难受。先试试做三次深呼吸或者站起来倒杯水2 分钟后再告诉我感受好吗, recorded_event: craving }第一步的目标不是完美回复而是整个链路能走通。后续再优化 Prompt 和事件抽取。注意不要把用户第一次回复就当作成功指标。真正需要观察的是连续多轮对话中用户是否持续愿意使用以及是否出现了结构化的行为记录。5. 用数据评价和优化提示效果5.1 记录关键事件没有数据就没有优化依据。生产环境至少要记录以下事件用户发送消息的时间戳。回复响应时间。模型返回的完整内容。用户是否在回复后继续发消息。事件类型例如 craving、relapse、action。紧急干预是否触发。示例日志格式{ timestamp: 2025-01-01T21:30:00, user_id: u1, event_type: craving, intensity: 8, trigger: 工作压力, reply_tokens: 84, latency_ms: 1200, follow_up: false }在/chat接口里可以增加响应时间统计并把日志写入结构化文件或数据库。import time start time.perf_counter() reply get_chat_response(messages) latency time.perf_counter() - start5.2 离线评估指标评估 LLM 对话质量不能只看“回复是否友好”。对行为干预场景有几个更实际的指标指标说明计算方式渴求响应率用户表达渴求后模型是否引导替代行为人工抽样或规则匹配“深呼吸/喝水/离开”等关键词回复平均长度是否保持简短对返回文本计算中文字符数用户留存率连续使用天数从日志聚合复吸后语气是否出现指责性语言人工抽样评分上下文引用率回复是否提到用户历史事件检查回复中是否包含最近事件关键词学习阶段可以构造 20 到 50 条测试用例分别覆盖“普通情绪”“强烈渴求”“复吸后悔”“闲聊”等场景然后为每个测试用例给模型回复打分。5.3 通过复盘迭代 Prompt当回复不符合预期时不要直接修改模型而是先定位是哪一层问题。排查顺序是不是输入信息缺失。比如没有把用户最近记录传给模型。是不是 Prompt 指令冲突。例如同时要求“简短回复”和“提供详细计划”模型会困惑。是不是模型能力不足。本地小模型可能无法严格遵循复杂指令这时要降低指令复杂度。是不是温度过高。temperature0.7会有随机性生产场景可以降到0.3到0.5。例如用户报告“昨晚没忍住抽了”模型回复“你不能这样要坚持”这属于“复吸复盘”策略失败。修正方法是把 System Prompt 中的准则 6 改成更明确的指令当用户提到复吸时第一句要表达理解不需要批判。然后问三个问题 1. 当时在哪里 2. 当时有什么情绪 3. 下次遇到同样情况你能做的一个不同动作是什么用这种具体指令替代“帮助降低自责”这种抽象描述。6. 常见问题和排查链路6.1 模型返回过长或答非所问现象用户说“现在很想抽”模型却回了一段 500 字的戒烟科普。原因System Prompt 没有强调短回复优先或max_tokens设置过高。检查# 查看返回的 token 数量 # 可以在日志中打印 usage 字段解决把max_tokens调到 150 到 300并把 Prompt 中的“普通回复不超过 4 句话”放在行为准则第一位。6.2 上下文窗口溢出现象多轮对话后接口报错提示超过 context length。原因每次请求都传了全部历史消息。解决只保留最近 5 到 10 条消息并把更早的对话摘要成结构化事件。不要把所有原文拼进上下文。6.3 API 延迟高用户等待时间长现象紧急渴求时用户发消息3 秒后才收到回复。原因模型较大、网络往返慢或max_tokens过大。解决紧急场景切换到较小模型。使用流式输出让用户先看到前面几个字。前端显示“正在输入”状态。设计本地快速回复兜底如果用户消息包含“特别想”先立即返回固定引导语再异步生成更多内容。6.4 模型输出不安全或不合适现象模型在用户表达沮丧时给出了“你可能需要药物治疗”等建议。原因System Prompt 边界不够强硬。解决增加安全指令并设置输出过滤。至少检查回复中是否包含“药物”“诊断”“治愈”等敏感词命中就替换成安全提示。SAFE_GUARD_KEYWORDS [药物, 治疗, 治愈, 保证] def safety_filter(text: str) - str: for kw in SAFE_GUARD_KEYWORDS: if kw in text: return 关于医疗相关问题建议你咨询专业医生。我可以继续陪你聊聊当下的感受。 return text6.5 本地模型与云端 API 切换不生效现象修改了.env中的base_url但请求仍打到旧地址。原因load_dotenv()加载时机不对或进程未重启。检查python -c from dotenv import load_dotenv; load_dotenv(); import os; print(os.getenv(OPENAI_BASE_URL))解决确保.env文件与app.py同目录修改后重启 uvicorn。6.6 用户隐私风险现象日志中记录了完整对话原文包含姓名、电话或心情细节。原因没有做脱敏处理。解决日志只保留必要字段对文本做规则脱敏。生产环境要考虑数据加密和最小化存储。涉及健康数据的应用最好咨询法律合规意见。7. 从学习环境走向生产环境7.1 安全与伦理边界这个项目最大的风险不是技术而是边界模糊。LLM 容易生成听起来专业的句子但内容可能有误。因此生产环境必须固定以下边界不以“治愈率”“成功率”作为宣传。不存储不必要的敏感信息。在界面明显位置显示“AI 辅助工具不能替代专业医疗建议”。如果用户连续多天情绪低落或出现危险表达应触发人工帮助入口。Prompt 中加入危险信号识别规则命中时返回固定语术并建议联系亲友或专业机构。7.2 架构演进路径学习版只是单服务加内存存储。进入生产环境后建议按下面顺序演进把内存存储改成 PostgreSQL 或 Redis。增加用户认证和会话隔离。把日志写入 ELK 或其他日志系统。使用消息队列异步记录事件。增加模型调用异常重试和熔断。建立 Prompt 版本管理每次修改都要回归测试。增加监控面板关注 token 消耗、延迟、错误率。依赖版本说明上面的示例依赖openaiSDK、fastapi和pydantic。落地时先确认版本兼容性再锁定 requirements。fastapi0.115.* uvicorn0.30.* openai1.* pydantic2.* python-dotenv1.*7.3 最佳实践清单以下是这类 LLM 健康辅助应用的可复用清单明确产品边界定义哪些话 AI 可以说哪些不能说。System Prompt 使用动态变量禁止把用户敏感原始数据直接拼接。设置最大回复字数尤其是紧急场景。记录所有模型返回用于后续抽样评估。每次修改 Prompt 后用固定测试集回归。不把 LLM 作为唯一决策者保留人工入口。使用结构化事件存储对话摘要而不是无限保存原文。部署前检查.env、模型名称、API 地址和日志脱敏规则。对模型输出做安全过滤不能只依赖 Prompt。关注延迟紧急渴求场景必须提供秒级响应。8. 扩展方向从戒烟到通用行为干预这套结构不限于尼古丁依赖。替换目标行为后可以用同一套系统帮助用户减少久坐、改善睡眠、控制甜食摄入。只需要调整触发词和替代行为体系。PROGRAM_CONFIG { quit_smoking: { triggers: [想抽, 烟, 尼古丁], alternative_actions: [深呼吸, 喝水, 离开座位, 嚼口香糖], relapse_keywords: [抽了, 没忍住], }, reduce_sitting: { triggers: [坐了很久, 腰酸, 没站起来], alternative_actions: [站起来伸展, 走几步, 调整坐姿], relapse_keywords: [又坐了一天], }, }实际开发中把行为策略作为配置项LLM 只负责生成符合配置的回复这样的系统更容易维护和评估。对比两类模型部署方式如果用户隐私要求高建议选择本地模型如果追求回复质量且隐私保护做得好云端 API 是更快的方案。没有最好只有最合适。结尾想强调一点技术能完成“陪伴”“记录”“提醒”这些事但真正让行为发生改变的是用户自身。作为开发者我们最应该做的是让模型知道什么时候沉默、什么时候提问、什么时候给出一个具体的两分钟行动。这比把大模型参数调得更大更重要。
返回列表