
做“AI监督我学习”这类项目时开发者的技术判断点往往不在“能不能接到大模型”而在于“这段对话能不能长期跑下去且token消耗不失控”。B站AI创造公开赛里有创作者用“AI德国军官监督我学习”作为项目主题表面看是角色扮演玩法本质上却是一个典型的角色化AI陪伴型Agent它同时涉及人设设定、多模态感知、语音交互、长期记忆、任务调度和成本控制。这套架构如果拆开看每个模块都不算难但组合在一起token消耗会快速膨胀。一个能稳定运行的学习监督Agent70亿token并不是夸张的数字。这篇文章不是去评价某个具体作品而是把这类项目背后的技术链路拆清楚为什么这类应用如此消耗token完整系统要怎么搭角色人设怎么做多模态学习状态怎么检测语音和Agent主循环怎么串起来以及token成本如何治理。如果你也想做类似的产品、参赛项目或自用工具可以直接按这套思路落地。1. 为什么“AI监督学习”这类应用必须重视Token要理解“70亿token”这个量级先要理解token是什么。token是大模型处理文本和图片时的最小计算单位。不同模型的分词器不同通常来说一个中文汉字可能对应1个或几个token一个英文单词也会被拆成若干token。在视觉模型里一张图片会被切分成固定尺寸的Patch再转换成一组token参与计算。如果只做一轮问答token消耗并不高。但学习监督Agent不是一轮对话它是一个“永远在线”的循环系统这是token消耗失控的根源。1.1 普通聊天机器人与学习监督Agent的差异对比项普通客服机器人学习监督Agent交互频率用户触发一轮答完结束每10秒到几分钟自动检查一次上下文长度单轮或短会话长会话需要携带历史和记忆输入类型纯文本摄像头画面、屏幕截图、语音、时间表输出方式文字角色话语、语音、桌面通知失败成本答错最多重问状态误判会直接干扰用户学习一个学习监督Agent每次检测都会产生“感知→判断→生成提醒”的完整链路。假设每10秒触发一次调用每次调用消耗约500个输入token、100个输出token一天就是约8640次调用单日token消耗超过500万。连续运行三个月仅基础调用就会超过4亿token。如果再叠加语音对话、每日总结、视频画面多模态分析、异常重试和误触发70亿token确实是可以达到的量级。所以这类项目的核心工程问题不是“能不能做出一个会说话的军官”而是“如何让这个军官每时每刻都在线但token账单不爆炸”。这也是本文真正想解决的核心问题。1.2 判断一个Agent设计好不好的关键角度很多开发者第一次做Agent只关注“模型会不会按角色说话”忽略了token消耗。但从工程角度看设计一个长期运行的Agent至少要看四个指标平均每次任务消耗多少token。历史上下文是全部保留还是做了摘要和裁剪。哪些判断必须交给大模型哪些可以用本地规则或轻量模型完成。当模型调用失败或超时时系统有没有降级方案。一个健康的学习监督Agent应该让大模型只负责“生成角色化表达和复杂决策”把“有没有人在书桌前”“屏幕内容是不是学习页面”这些判断尽量交给本地检测和规则逻辑。2. 核心概念与整体架构2.1 Agent在本文中的含义这里说的Agent不是简单的“调一次大模型接口”而是一个有感知、有决策、有输出、能循环执行目标任务的程序。它的核心循环可以理解为感知获取当前状态。理解将多模态输入压缩成结构化描述。决策判断当前是否需要提醒。表达用符合角色设定的语言和语音输出提醒。记忆把关键信息写入短期或长期记忆供下一轮使用。这种循环式Agent和普通对话式AI最大的区别在于它需要为“不确定状态”做出决策。比如画面里没人是去上厕所还是已经逃学了屏幕上有视频窗口是在看网课还是在刷短视频这些状态判断如果全部交给大模型token消耗会非常高如果全部交给规则又会显得机械。2.2 整体架构分层一个学习监督Agent可以拆成四层层级模块职责感知层摄像头、屏幕截图、麦克风、系统日程采集真实学习状态理解层人脸检测、OCR、语音识别、状态压缩把原始数据转成短文本决策层规则引擎、Agent规划、大模型调用判断是否提醒、如何提醒表达层文本生成、TTS、桌面通知输出军官风格监督话语这种分层最大的好处是可以单独替换某一层。比如今天用开源OCR明天切换商用OCR今天在线调用大模型明天换本地部署模型都不会影响其他模块。2.3 一个完整的监督循环以一个典型场景为例摄像头每15秒抓一帧画面。人脸检测模型发现画面中没有人。规则引擎判断“离席时间超过3分钟”。大模型根据这句话生成一句符合军官人设的提醒。TTS播放语音同时桌面弹窗提示。在这个循环里大模型不是每次都参与完整判断的。大多数“人还在书桌前”的帧只经过本地检测不会调用大模型。真正调用大模型的节点是“需要生成有角色感的表达”的时候。这个设计决策直接决定了70亿token会不会白烧。3. 关键模块一角色人设与System Prompt设计“德国军官”这类角色设定本质上是在System Prompt里定义了一个高权威、低幽默、目标明确的监督人格。好的角色设定不是单纯把语气写得严厉而是要让模型在“监督学习”这个具体场景下始终保持一致的决策风格。3.1 System Prompt设计原则在设计监督类角色的System Prompt时核心要素有四块身份你是谁你的职责边界是什么。语气你用什么语句风格与人对话。策略看到什么状态时给出什么反应。边界哪些事绝对不能做。下面是一份可参考的System Prompt# config/director_prompt.py DIRECTOR_SYSTEM_PROMPT 你是“学习监督官”一个设备端运行的学习效率监督AI。 身份 - 你负责监督用户完成当天学习计划。 - 你的风格是冷静、直接、严格但关注用户长期进步。 语气规则 - 用简短指令句不要使用大量口语化表达。 - 对用户开小差或离席给出明确、克制的提醒。 - 如果用户完成一个阶段目标可以给出简短肯定。 监督策略 - “focusing”安静等待不打扰。 - “left_desk”先提醒一次提醒两次后要求用户说明原因。 - “distracted”指出偏离学习计划的事实并提示回到当前任务。 边界 - 你的职责是监督学习不能处理与学习无关的请求。 - 不能输出违法违规内容不能承诺你无法执行的操作。 - 如果发现用户连续久坐超过50分钟应主动提醒休息。 这段提示词的关键在于“策略”部分。它把状态行为直接映射成监督动作让模型在每一轮对话里都能基于明确的规则做判断而不是靠自己猜“现在该不该凶人”。3.2 为什么角色化Agent容易“出戏”很多角色化Agent跑几轮就变得不像角色原因往往有三个System Prompt里只写了“你是谁”没有写“你在什么情况下做什么”。对话历史里混入了大量非角色语境比如调试数据、报错信息、多轮状态码。温度过高模型为了“有趣”偏离了角色设定。解决方式也很直接把状态判断和表达层拆开。状态判断结果只以一句结构化的文本进入上下文例如“检测到离席5分钟”模型只需要负责把这句话转化为角色的表达。这样既能保持角色一致性又能减少送入模型的无关信息从而节省token。4. 关键模块二多模态学习状态检测学习状态检测是整个系统里最容易被低估的模块。很多人以为“看一眼摄像头画面让大模型判断人在不在”就行。这个方案理论可行但实践中会带来两个问题一是摄像头图像直接传给远程模型每次图片token消耗很高二是图像识别结果带有不确定性大模型容易把画面里的玩偶、背影误判成真人。更合理的做法是在本地完成基础检测只把结构化结果传给大模型。4.1 基于OpenCV的人脸在席检测先看一个最基础的人脸检测模块。它通过OpenCV的Haar级联分类器检测画面中是否有人脸从而实现“在席/离席”判断# modules/study_detector.py import cv2 import time class StudyDetector: def __init__(self, camera_index0, min_size(80, 80)): self.cap cv2.VideoCapture(camera_index) self.face_cascade cv2.CascadeClassifier( cv2.data.haarcascades haarcascade_frontalface_default.xml ) self.min_size min_size self.last_face_time 0.0 def grab_frame(self): ret, frame self.cap.read() if not ret: return None return frame def analyze(self, frame): if frame is None: return camera_error gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces self.face_cascade.detectMultiScale( gray, scaleFactor1.1, minNeighbors5, minSizeself.min_size, ) if len(faces) 0: self.last_face_time time.time() return focusing # 给一个宽限期避免摄像头偶尔漏帧导致误报 if time.time() - self.last_face_time 3: return focusing return left_desk这个模块的优点是纯本地计算不消耗大模型token。它会快速判断“人是否在屏幕前”。它的局限也很明显只能判断“人在”不能判断“人在干什么”。所以还需要另一层检测。4.2 屏幕内容检测与分心判断要判断用户是否走神常见做法是定时对屏幕截图再做OCR识别把屏幕文字抽出来与分心关键词做匹配。这里的OCR可以换成任意外部接口或本地模型代码以你使用的SDK文档为准# modules/screen_scorer.py DISTRACT_KEYWORDS [视频, 游戏, 漫画, 购物, 综艺] def get_screen_text(): 截取当前屏幕并进行OCR识别。 这里的OCR实现可以根据你安装的库替换。 # 伪代码示意screen_img mss.grab(monitor) # 真实项目里请替换为 PaddleOCR / Tesseract / 商用OCR return def judge_attention(screen_text, face_state): if face_state left_desk: return left_desk if not screen_text: return focusing for keyword in DISTRACT_KEYWORDS: if keyword in screen_text: return distracted return focusing这才是“监督”的关键判断一个人有没有真的坐在书桌前并不是最终目标判断他是否在做“学习相关的事”才是。如果一个学生开着视频网站背单词就属于“人在心不在”。这个判断规则不需要很复杂但需要在真实场景里持续调优。4.3 为什么不建议把完整画面直接丢给大模型视觉大模型可以看懂画面但代价是每次调用都会把图片切分成大量token。对于每15秒检测一次甚至更频繁的监督场景直接把画面传给远程模型成本会快速失控。更稳妥的做法是所有高频检测都在本地做只有需要角色表达时才调用大模型。大模型拿到的输入不是图片而是一个已经压缩好的状态描述比如“用户离开书桌5分钟”或“屏幕出现游戏画面”。这既降低了token消耗也减少了隐私暴露。5. 关键模块三语音交互与Agent主循环角色化监督员如果只有文字提示效果会弱很多。“口语化提醒”才是角色感的直接来源。因此完整项目还需要语音交互链路和Agent主循环。5.1 统一的大模型调用入口为了方便切换不同模型服务建议把大模型调用封装成一个统一入口。下面这个示例假设你使用兼容OpenAI格式的模型服务# llm_client.py import os from openai import OpenAI client OpenAI( api_keyos.getenv(LLM_API_KEY), base_urlos.getenv(LLM_BASE_URL), ) def chat_once(user_message, system_promptNone, historyNone, modelNone): history history or [] messages [] if system_prompt: messages.append({role: system, content: system_prompt}) messages.extend(history) messages.append({role: user, content: user_message}) response client.chat.completions.create( modelmodel or os.getenv(LLM_MODEL), messagesmessages, temperature0.7, ) return response.choices[0].message.content封装之后上层模块不需要关心具体是哪个模型、哪个供应商只需要调用chat_once就能拿到角色化回复。切换模型时只需要改环境变量或model参数。5.2 语音交互接口语音交互链路可以拆成录音 → 语音识别ASR→ 大模型回复 → 语音合成TTS。下面是一个简化版FastAPI接口示例只展示“文本进文本出”的后端逻辑# api/voice.py from fastapi import FastAPI from pydantic import BaseModel from llm_client import chat_once from config.director_prompt import DIRECTOR_SYSTEM_PROMPT app FastAPI() class VoiceRequest(BaseModel): text: str user_id: str default app.post(/voice) def voice_interact(req: VoiceRequest): reply chat_once( user_messagereq.text, system_promptDIRECTOR_SYSTEM_PROMPT, ) return {reply: reply}在实际项目中ASR和TTS会分别放在这个接口的前后。用户在麦克风前说“今天我不想学了”语音识别成文本后进入这个接口大模型生成一句符合监督官身份的回答再通过TTS播放出来。5.3 Agent主循环与最小可运行示例Agent主循环是让系统“有时间感知”的关键。它通过定时轮询检测模块只有在状态发生变化时才调用大模型。下面是一个最小可运行的主循环# study_supervisor.py import argparse import time from modules.study_detector import StudyDetector from config.director_prompt import DIRECTOR_SYSTEM_PROMPT from llm_client import chat_once def build_reminder(state: str) - str: if state left_desk: return 用户已经离开书桌一段时间请用监督官身份提醒他立刻回到学习位置。 if state distracted: return 检测到用户正在做与学习无关的事情请用监督官身份提醒他切回学习任务。 return 用户当前状态正常不需要打扰。 def run_once(detector: StudyDetector, dry_run: bool False): frame detector.grab_frame() state detector.analyze(frame) print(f[STATE] {state}) if state in (left_desk, distracted): reminder build_reminder(state) if dry_run: print(f[DRY_RUN] {reminder}) return reply chat_once( user_messagereminder, system_promptDIRECTOR_SYSTEM_PROMPT, ) print(f[AI] {reply}) def main(): parser argparse.ArgumentParser() parser.add_argument(--once, actionstore_true, help只检测一次用于验证) parser.add_argument(--dry-run, actionstore_true, help不调用大模型只打印提醒内容) parser.add_argument(--interval, typeint, default15, help检测间隔单位秒) args parser.parse_args() detector StudyDetector() if args.once: run_once(detector, dry_runargs.dry_run) return while True: run_once(detector, dry_runargs.dry_run) time.sleep(args.interval) if __name__ __main__: main()这个主循环的逻辑非常明确先看状态状态变了才提醒状态没变就不打扰。这样既符合“监督员”的角色逻辑也避免了无意义的token消耗。如果你不想让AI频繁说话这本身也是一种成本控制策略。6. Token成本治理70亿Token是如何烧掉的很多开发者在做Agent时最大的困惑是“我明明没写多少代码为什么token消耗这么快”。其实token消耗通常不是单次调用造成的而是长期循环和上下文膨胀叠加出来的。6.1 四个常见的Token浪费来源第一历史上下文无限制增长。如果每一轮都把上一次完整对话送给模型上下文会越来越长。到了第100轮光历史就可能占几千token。第二高频检测全部调用大模型。如果每一次画面检测都让大模型看图片多模态token消耗会迅速放大。第三失败后无限重试。模型接口偶发超时或限流如果代码不断重试每次重试都在烧token。尤其是一个死循环里埋着一个重试逻辑最终会变成一种“token泄漏”。第四调试信息混入对话。很多初学者为了方便调试把报错内容、日志片段、环境变量直接塞进prompt。在大模型看来这些都是上下文都会占用token。6.2 引入Token预算管理要控制成本第一步是先建立统计和预算机制。下面是一个简单的Token预算类# utils/token_budget.py class TokenBudget: def __init__(self, max_tokens1_000_000): self.max_tokens max_tokens self.prompt_tokens 0 self.completion_tokens 0 self.call_count 0 def add_usage(self, prompt_tokens: int, completion_tokens: int): self.prompt_tokens prompt_tokens self.completion_tokens completion_tokens self.call_count 1 property def total_tokens(self): return self.prompt_tokens self.completion_tokens def is_over_budget(self): return self.total_tokens self.max_tokens def summary(self): return { call_count: self.call_count, prompt_tokens: self.prompt_tokens, completion_tokens: self.completion_tokens, total_tokens: self.total_tokens, max_tokens: self.max_tokens, }在每次调用chat_once时解析返回值里的usage.prompt_tokens和usage.completion_tokens然后累加到TokenBudget中。一旦超预算就触发降级策略比如切换到规则文案不再调用大模型。6.3 Token优化对照表优化方向优化前做法优化后做法历史记忆每轮完整保存所有对话保留最近3轮超过部分生成摘要图像处理把摄像头画面直接发给远程视觉模型本地做人脸检测、OCR只传状态文本检测频率每秒查一次大模型先本地检测状态变化次数降低后再调用失败重试失败后立即重试指数退避最多重试2次超限降级为规则提醒角色提示词每轮都传超长提示词系统提示词压缩到必要范围细节放进配置文件从成本治理的角度看一个经验法则是能用规则判断的不用轻量模型能用轻量模型判断的不用大模型需要用大模型时只传最少的必要上下文。70亿token如果是这个系统真实消耗量那说明它在“感知”和“决策”环节掺入大模型的比重明显偏高。7. 运行验证与效果评估系统搭好之后不要急着让它7×24小时运行。先做短链路验证再逐步放开。7.1 验证命令与预期输出如果你按照上面的模块拆分先验证摄像头检测是否正常python study_supervisor.py --once --dry-run预期输出示例[STATE] focusing如果摄像头没有被占用且人坐在镜头前会输出focusing如果人离开会输出left_desk。这一步验证的是感知层。接着验证大模型调用层python study_supervisor.py --once这会在状态不是focusing时真正调用一次大模型并打印角色化提醒。你可以故意离开书桌看输出是否变成一句有监督官风格的话。7.2 效果评估指标一个学习监督Agent是否好用不能只看“能不能跑通”。建议从五个维度评估指标说明目标状态识别准确率人是否在书桌前判断是否准确不低于95%角色一致性AI说话是否符合监督官人设抽样人工评分响应延迟从触发状态到发出提醒的时间3秒以内可接受单日均token消耗每天消耗token总量根据预算设定用户坚持度用户是否按系统建议完成学习计划趋势上升在实际项目中最容易出问题的是状态识别准确率。灯光变化、摄像头角度、戴口罩、背对摄像头等因素都会导致误判。建议在进入正式使用前先记录几天的状态日志按时段分析误报比例。8. 常见问题与排查思路问题现象可能原因排查方式解决方案人脸检测频繁误报摄像头角度偏、光线差、Haar级联模型较弱查看抓帧日志观察连续状态变化调整摄像头位置换成更鲁棒的人脸检测模型增加离席宽限期大模型回复“出戏”System Prompt缺少状态到动作的映射回看最近几轮上下文和系统提示词按“身份语气策略边界”重写System Prompttoken每天消耗过高状态没变化也调用大模型或历史不裁剪打印每次调用的usage字段改成事件驱动调用减少轮询调用API返回403或限流密钥权限不足、配额超限、当前网络环境不支持该服务查看接口返回码和配额选择当前环境合规可用的模型服务配置指数退避重试语音提醒延迟大ASR模型过大、TTS在线合成慢分别统计ASR和TTS耗时换流式识别启用TTS缓存摄像头调用失败摄像头被其他程序占用或设备索引不对检查设备索引和系统权限设置camera_index重启程序前释放摄像头资源这里特别提醒一点如果大模型接口返回了与“地区或区域限制”相关的403错误不要试图用绕过方式解决。对开发者来说更稳妥的做法是选用你所在地区可合法访问的模型服务并仔细检查API密钥权限、配额和接口地址配置。9. 最佳实践、总结与后续学习方向9.1 工程落地建议这类角色化监督Agent看起来轻量真要稳定运行工程复杂度不低。以下几点建议来自实际项目里的通用经验第一做好权限与隐私设计。摄像头、屏幕截图、麦克风都属于敏感信息。要让用户明确知道哪些数据会被采集尽量将数据留在本地处理。不要把摄像头画面随意上传到第三方服务。第二建立“先降级再失败”的机制。大模型接口超时或者不可用时不要让Agent静默死机。可以降级为一条固定规则文案比如“检测到离席请尽快返回学习位置”。至少保证监督功能的核心链路不中断。第三把提示词和检测逻辑分开配置。角色语气、提醒话术、分心关键词、检测间隔都放到独立配置文件中方便调整也方便做A/B实验。不要改一行提示词就重发一次代码。第四日志里记录“状态机事件”而不是记录全部原始视频。比如记录“状态从focusing变为left_desk持续3分20秒”这样既方便复盘又能控制日志体积和隐私风险。第五严格控制上下文膨胀。长期运行的Agent建议把对话历史转成结构化摘要再配合最近几轮原始消息而不是把全部历史原样送入大模型。9.2 如果你想从零开始做类似项目不要一上来就追求完整版的多模态、语音、Agent规划。建议先用一条最简单链路跑通闭环摄像头本地检测人脸是否在席。状态变化时调用大模型生成一句监督提醒。用命令行或桌面通知输出提示。记录每天的token消耗和状态事件。这条闭环跑通之后再逐步加入屏幕OCR识别、语音交互、学习计划任务、长期记忆和可视化看板。角色化Agent最难的地方并不在“角色提示词写得好不好”而在于它是否能在长期运行中保持稳定同时让token消耗处于可控范围。70亿token听起来是一个巨大的数字但在一个7×24小时运行、每15秒检测一次、不断积累记忆的角色化监督系统中这个量级并非不可能。真正值得思考的是这70亿token里有多少花在了“理解用户状态”上又有多少花在了“重复处理无关信息”上。把token花在真正影响用户体验的地方才是这类Agent工程的核心竞争力。