这两年AI圈最热的一个词就是Agent。我自己做应用开发,前后接触了不少Agent项目,也踩过不少坑。最近这一个月做的一个项目,让我觉得最值得拿出来分享——从零到一开发一个英语情景教学Agent。简单说,它就是一个能模拟各种真实场景(机场通关、餐厅点餐、面试、就医、租房)陪你练口语的AI私教:用户开口说英语,Agent扮演场景角色实时回应,对话过程中自然引导,结束后生成纠错和学习报告。它解决的问题很真实:很多人英语读写还行,一开口就卡壳,因为身边没有人陪练,请外教又贵,普通语音助手对真实对话场景几乎帮不上忙。这篇文适合两类人看:一是想搞清楚Agent到底是什么、能干什么的普通用户;二是准备上手Agent开发的技术同学,我会把从需求拆解、架构设计、框架选型到实操踩坑的全过程摊开讲,不藏着掖着。
1. 内容整体设计与思路拆解
1.1 先把"英语情景教学"拆成四个核心能力
做技术的第一件事不是写代码,是需求拆解。我没有急着选框架、调模型,而是先把"英语情景教学"这件事掰开揉碎,拆成四个核心能力。
第一个是情景模拟。用户要练的不是孤立的单句对话,而是特定场景下完整的交流。比如"预订酒店",你一开始想的可能是"Can I book a room?",但真实场景里前台会追问你日期、房间类型、有没有会员卡,你还可能遇到"抱歉,标准间没了,要不要升级行政房"这种突发。所以Agent不能只是"你说一句我回一句",它要能撑起一个场景的完整剧情,肚子里得有这个场景的知识库——通常聊什么、有什么规矩、有哪些高频和低频表达、有哪些意外情况。
第二个是对话引导。一个合格的教学Agent,核心是"教",不是"聊"。用户说出"I go to airport yesterday"这种时态错误,Agent不能像真人朋友一样忽略过去继续聊,但也不能一错就打段,那体验极差。合理做法是让一个回合自然走完,对话不被打断,本体属于哪个回合的工商指出问题所在,然后继续剧情。这个平衡怎么拿捏,我会在核心模块部分细讲。
第三个是水平适配。同一个餐厅点餐场景,零基础用户需要一个词一个词蹦,中级学习者要挑战更复杂的句型,高级学习者要考虑文化语境和地道表达。Agent要能判断用户当前水平,动态调整对话难度和反馈深度。我一开始没做这层,结果一个做过雅思口语的用户和一个刚开始启蒙的用户,反馈方式完全一样,效果都不好。
第四个是学习闭环。练完不能白练。Agent要把整场对话打包成一份报告:语法错误、卡壳点、词汇扩展建议、下一轮练习建议。这是很多AI产品容易忽略、但用户特别买账的功能。实际开发中,这个报告由单独的评估Agent生成,不占对话主流程的上下文。
1.2 为什么必须做成"Agent",而不是一次API调用
标题里用了Agent这个词,不是赶时髦,是这个产品形态决定的。它不能是一个简单的LLM接口封装。
核心原因在于状态管理。情景对话是长流程、多轮交互,系统需要知道:用户在哪个场景、进行到哪个环节(刚开局还是已经触发突发事件)、哪些表达已经掌握、哪些错误反复出现。传统LLM API调用是无状态的,你当然可以把历史消息全塞进context,但那只是"对话历史",不是"教学状态"。Agent框架把对话拆成"状态+动作":Agent随时知道自己处于哪个教学阶段,该调用什么工具(查词典、查场景知识库、触发评分),该更新什么记忆。这个区别是本质性的。
其次是工具调用。纯LLM对话不能可靠地触发外部系统,但教学Agent需要。用户语音进来,要触发语音识别;说出某个词,可能触发发音评测;错误反复出现,要查错题数据库;练习结束,要记录学习进度。Agent框架天然支持"模型决定调哪个工具"——模型读完用户的话,自己决定"这句话有发音问题,调评测工具查一下",而不是靠死板的业务规则判断。这就是Agent的自主性。
最后是多Agent协作。实际项目里我做了但没做太重:一个"教师Agent"负责对话和引导,一个"监督Agent"负责错误记录,一个"评估Agent"在对话结束后生成报告。职责拆开后,每个Agent的Prompt好写,系统行为可预测,出了问题也知道去哪里查。如果全塞进一个Agent里,Prompt会膨胀到失控。
1.3 整体架构:五层模型,反复重构后沉淀下来的
经过两轮重构,我的最终架构是五层:
- 交互层:负责语音输入输出和文字输入。Web端用浏览器语音API,App端用原生SDK,后端统一走WebSocket推送事件。
- 教学核心层:情景引擎加对话管理。情景引擎负责场景剧情的推进和突发事件触发;对话管理负责多轮会话状态,决定"该引导了、该纠错了、该打分了"。
- Agent执行层:Agent框架的主战场,编排工具调用、记忆读写和模型推理循环。这一层是心跳,每轮用户消息都在这里完成"思考—决定—执行"。
- 记忆层:短期记忆存当前会话上下文,长期记忆存用户画像、历史错误、学习进度,场景知识库存静态资料或向量化内容。
- 评估与数据层:纠错记录、口语评分、学习报告生成、后续的数据分析。
这个五层结构不是我一开始就设计出来的。第一版我图省事,把教学逻辑全写在Prompt里,结果一遇到复杂场景就失控——模型不知道自己现在该干嘛,上一秒还在当酒店前台,下一秒就开始上语法课。后来把状态管理收拢到Agent框架,教学逻辑下沉到引擎层,系统才稳定下来。这个教训我后面还会反复提到:别把流程控制交给Prompt,要让代码和Agent框架管状态,让模型只干它擅长的事——语言理解和生成。
2. 工具选型与Agent框架对比
2.1 主流Agent框架横向盘点
做Agent开发,第一道坎就是选框架。今年市面上主流的框架我基本都实测过一轮,直接说结论。
LangGraph:状态图驱动,每个节点是一个函数,边是状态转移。胜在可控性强,适合我这种"教学流程要精确控制"的场景——必须先把引导做完,才能触发纠错;评分完成后才进入下一关。缺点是上手曲线陡,图逻辑复杂之后Debug很难受,你得像查地图一样顺着边在节点里打断点。
AutoGen:微软出的,核心是多Agent对话模式,Agent之间互相发消息,灵活但"自主性"太强,在你需要严格控制流程的时候反而不好使。做自由聊天够用,做教学这种有明确阶段流程的场景,它容易跑飞——两个Agent聊嗨了,早把教学大纲忘到脑后。
CrewAI:主打角色分工,定义Role/Goal/Backstory就能创建Agent,上手极快。但CrewAI更适合任务型多Agent协作,比如一个Agent写文案、一个Agent做图,不适合"同一个Agent和用户长时间保持多轮关系对话"的场景。它的记忆和状态管理对我来说太轻了。
自研编排:对于简单的"单场景、少轮次"玩玩可以,完全不建议用于正式项目。一旦你要加长上下文管理、工具回退、多Agent协同,自己造的轮子会花掉你80%的时间在维护上。
2.2 我的选择:LangGraph做骨架,自研引擎做教学控制
综合对比后,我选了LangGraph做Agent执行层,理由很直接:教学流程是强状态流程。
以"餐厅点餐"为例,流程大致是:开场问候→推荐菜品→用户点餐→追问烹饪方式→上菜→餐后反馈→建议小费。这个流程里,每一步之间有明确的先后关系,部分环节还有分支(比如用户说"我是素食者",就要立刻切换推荐逻辑)。LangGraph的状态图正好能把这种流程显式表达出来,一个节点一个环节,节点完成就切状态,模型的自由度被限制在"当前节点内如何组织语言",而不是"整个对话随便走"。
实际开发中,我还做了一个补充:把情景剧本引擎放在教学核心层,LangGraph每个节点其实是在执行剧本引擎下发的一个"子任务"。这样的好处是,产品经理想调流程,改剧本配置就行,不用动图结构。目前这个项目迭代了三个月,图结构只改过两次,剧本配置改了不下几十次——这个设计太值了。
2.3 MCP在这个项目里扮演的角色
现在Agent开发绕不开MCP(Model Context Protocol,模型上下文协议)。我在这个项目里的定位是:MCP负责连通外部系统,不负责业务逻辑。
具体来说,我通过MCP服务器接了三个外部能力:发音评测服务、词典查询服务、用户学习记录数据库。模型在对话中如果需要查一个词的用法、或者要对某句话做发音打分,就通过MCP工具去调用。
我的实际心得是:MCP最适合的场景是"标准化的外部资源接入",它把"模型怎么调用外部工具"这件事统一了。但不要把复杂业务逻辑塞进MCP server里,因为MCP server本质上是工具层,它不感知你的教学状态。我在早期犯过这个错——写了一个"判断何时该纠错"的MCP工具,结果它收到的参数是零散的对话片段,没有上下文状态,判断质量很差。后来把这个逻辑移回Agent执行层的状态节点里,问题才解决。
3. 核心模块实现细节
3.1 情景剧本结构设计:把场景变成配置文件
情景剧本是整个产品的灵魂,我把它设计成了JSON+模板的结构。每个剧本包含五个部分:场景元信息、角色设定、剧情节点、突发事件、词汇库。
场景元信息很简单:场景ID、场景名称(中英)、难度系数、预计时长。角色设定是这个场景里Agent扮演什么角色,比如"酒店前台,礼貌、专业、语速稍慢"。剧情节点是一个有序列表,每个节点包含:节点ID、节点触发条件、Agent需要做的事情、可用的句式模板。突发事件是可选分支,比如餐厅场景里的"隔壁桌打翻杯子"、机场场景里的"航班取消",用于制造真实感和挑战性。
词汇库是我比较自豪的设计:每个剧本自带一个场景词库,按"必须掌握"和"锦上添花"两级分类。用户对话中用到"锦上添花"级别的词,Agent会在反馈里特意表扬;如果用户连"必须掌握"的都没用上,反馈里会提示补充。这个设计让词汇教学从枯燥的背单词变成了场景内自然发生。
剧本是用纯JSON写的,不掺任何代码逻辑。我举一个简化版的例子:
{ "scenario_id": "restaurant", "name": "餐厅点餐", "difficulty": 2, "role": { "name": "服务员", "persona": "友好、耐心、语速中等,会根据顾客要求推荐菜品" }, "nodes": [ {"id": "greeting", "trigger": "start", "action": "打招呼并递上菜单"}, {"id": "recommend", "trigger": "user_asks_menu", "action": "推荐今日特价菜"}, {"id": "order", "trigger": "user_reads_order", "action": "确认点单并问烹饪方式"} ], "events": [ {"id": "vegan", "trigger": "user_says_vegan", "action": "切换推荐素食菜品"} ], "vocab": { "must": ["menu", "order", "bill", "recommend"], "bonus": ["appetizer", "well-done", "doggy bag"] } }这套JSON设计大概用了两周打磨,真正带来的好处是:运营人员和英语老师不需要懂代码,就能新增一个"健身房办卡"或"银行开户"的场景,我只用写一个校验脚本保证JSON字段合法。
3.2 记忆系统设计:短期、长期、场景知识三级记忆
记忆系统是Agent区别于普通聊天机器人的关键。我只做了必要记忆,没有过度设计。
短期记忆直接复用Agent框架的对话历史,但做了一个裁剪策略:只保留最近20轮对话。超过20轮的内容会被压缩成一个"对话摘要节点"存进短期记忆里。这个策略解决了一个大问题——长对话会让context爆炸,模型越聊越"飘",忘了早前的关键信息。实测下来,20轮的窗口加上摘要,既能保证教学的连续性,又不会把上下文塞满。
长期记忆存的是用户画像和学习轨迹,存在数据库里,不塞进每次对话。包括:用户当前等级(CEFR级别A1到C1)、历史错误Top10、已完成场景列表、每个场景掌握程度。每次会话开始时,Agent会读取这些信息,生成一个"教学简报",塞进系统Prompt的开头。比如"用户是B1级别,餐厅场景已经练过2次,'order'这个动词用法还有问题,今天的重点是多引导他用礼貌请求句式"。
场景知识库分为两部分:一部分是静态规则(比如机场场景的流程是固定的),另一部分是向量化的场景表达库。最开始我以为场景知识要用向量库做语义检索,后来发现静态规则就够了80%的场景,向量检索只用在"用户提出一个场景延伸问题"的时候,比如用户问"如果我说牛排要七分熟,英文该怎么说",这时候向量检索能快速找到相关表达模板。
记忆这块我的经验是:先写死,再向量化。千万别第一版就上向量数据库,那会增加系统复杂度,而且查不准的问题比不查更难受。
3.3 纠错与反馈机制:怎么"教"而不"打断"
这是整个产品里最需要拿捏的部分。我设计了四级纠错策略:
- 第一级:不打断,自然重复。用户说"I yesterday go to museum",Agent如果判断是轻微口误,就不直接纠正,而是在下一句回复里自然带上正确用法:"Oh, you went to the museum yesterday? How was it?"。人类老师就是这么教的,效果最好。
- 第二级:对话结束后整句纠正。中等程度的错误,比如句式混乱、动词搭配不对,会在当前轮次结束后给出明确批注:"刚才这句更地道的说法是...".
- 第三级:即时提示。用户完全卡壳,说不出话,Agent会给出提示词:"可以用'Could I have...'开头试试"。这个提示只给开头,不给完整句子,逼用户自己完成。
- 第四级:严重障碍时的教学暂停。用户连续三次表达失败,Agent会跳出角色,切换成"老师模式",用中文或用英文详细讲解一个语言点,讲完再回到角色。这个切换状态由Agent执行层控制,触发条件写死在状态图里。
判断用哪级策略,不是简单看有没有语法错误,还要看用户等级和当前状态。B1用户说错一个介词,我一般用第一级;A1用户如果整句话语法都对但一个高级词汇都没用,反馈里反而要鼓励为主。这个动态判断逻辑一开始放在模型Prompt里,效果不稳定,后来改成了规则引擎+模型判断混合:规则引擎先做粗分类(有没有触发词、有没有卡壳),模型在粗分类框架内做细判断。
3.4 语音链路与发音评测
语音输入我用Web端浏览器的MediaRecorder做录音,后端调用语音识别服务转成英文文本,然后走Agent推理,输出文本后用语音合成播放。这里有个小坑:语音识别的错误会直接污染Agent的判断。用户明明说的是"I'd like a table for two",语音识别成"I'd like a table for true",Agent就会莫名其妙地当用户提了什么奇怪要求。
我处理的方法是:Agent对用户文本先做一次"宽容化预处理"——对明显的识别错误(比如冠词、介词混乱)不进入语法纠错判断;只有当错误是"语义层面"的才触发纠错。另外,发音评测我单独接了一个服务,只对用户完整说出的句子做评分,不参与对话过程中的即时判断,避免评测延迟拖慢对话节奏。
发音评分报告在最终学习报告里单独呈现,按"元音、辅音、连读、重音、语速"五个维度给细分。维度的划分不是我自己发明的,是参考了常见的英语口音评测标准,然后让评估Agent根据评测服务的原始数据生成描述性建议——"你的th发音偏像s,建议发舌尖轻抵上齿背"这样的具体指导,比一个总分有用得多。
4. 实操过程:从零到一搭建
4.1 环境与依赖准备
我假设你已经有一定Python基础,能跑普通的FastAPI项目。我的开发环境是这样:
# 项目目录结构 english_agent/ ├── agent/ # LangGraph执行层 ├── engine/ # 教学核心层/剧本引擎 ├── memory/ # 短期/长期记忆管理 ├── schemas/ # Pydantic数据模型 ├── scenarios/ # 情景剧本JSON ├── mcp_server/ # MCP工具服务 ├── web/ # 前端页面 └── tests/ # 单元测试和集成测试依赖方面,核心就几个:LangGraph做Agent编排,LangChain Core做工具层(如果你不介意绑定,直接用原生Python context manager也行),OpenAI或Anthropic的SDK做模型调用,sqlite3或PostgreSQL存长期记忆,向量库我用的轻量级方案,场景不多的时候甚至可以不用向量库。
起步阶段不要追求完美架构,先把最小闭环跑起来:一个脚本,一次能进来用户文字,Agent回一句话,能识别剧本节点。我用了一天时间搭出了这个最小闭环,然后才逐步加语音、记忆、评测这些模块。这算是老生常谈,但我真的见过很多同事第一步就铺开所有模块,最后三个月连对话都跑不通。
4.2 核心代码实现:LangGraph状态图
我直接贴状态图的核心代码,注释写得比较详细。这个图包含三个节点:teaching(正常教学对话)、feedback(回合结束纠错)、report(生成学习报告)。
from langgraph.graph import StateGraph, END from typing import TypedDict, Optional from pydantic import BaseModel class AgentState(TypedDict): user_id: str scenario_id: str current_node: str # 剧本当前节点ID turn_count: int user_text: str agent_reply: str errors: list # 本回合错误列表 needs_teaching_pause: bool # 是否触发第四级教学暂停 def teaching_node(state: AgentState) -> AgentState: """教学对话节点:调用LLM生成回复,期间判断是否需要纠错、是否卡壳""" # 1. 从剧本引擎取当前节点指令 scenario = load_scenario(state["scenario_id"]) node_meta = get_current_node_meta(scenario, state["current_node"]) # 2. 构建Prompt(含角色人设、场景词库、当前节点任务、用户画像简报) prompt = build_teaching_prompt(state, node_meta) # 3. 调用LLM并获取结构化返回:agent_text + 是否触发特殊动作 result = llm_with_tools(prompt) state["agent_reply"] = result["text"] # 4. 触发工具调用(查词、评测等),结果合并回回复 # 5. 剧本引擎推进节点状态 state["current_node"] = advance_scene(state) return state def feedback_node(state: AgentState) -> AgentState: """回合末纠错节点:对用户上一句话做细致反馈""" state["agent_reply"] += generate_feedback(state) return state def report_node(state: AgentState) -> AgentState: """生成学习报告并存储""" save_report(state) return state # 状态图编排 graph = StateGraph(AgentState) graph.add_node("teaching", teaching_node) graph.add_node("feedback", feedback_node) graph.add_node("report", report_node) graph.add_edge("teaching", "feedback") graph.add_edge("feedback", "report") graph.add_edge("report", END)这段代码看着简单,真正花时间的是build_teaching_prompt和advance_scene这两个函数。Prompt构建要拼用户画像、剧本节点、动态事件,字段顺序都要讲究,否则模型总是忽略关键指令。剧本推进要处理各种分支条件,比如用户提了素食、用户要求换菜、用户想走人,走不同的分支。
4.3 教师角色Prompt设计实战
Prompt设计是这个项目的灵魂之一。我提供一个经过实际调优的"教师角色Prompt"模板框架,你照这个思路写,基本不会跑偏。
你是一位专业又亲切的英语口语教师,同时扮演{角色人设}。 当前场景:{场景名},难度:{用户等级}。 【教学原则】 1. 始终保持角色,不要跳出情景讲解语法,除非设定等级的教学暂停被触发。 2. 对话语言全英文,根据用户水平调整句长和用词。 3. 优先让对话自然推进,纠错按四级策略执行。 【当前任务】 你现在要做的是:{当前剧本节点指令}。 用户已经到达本场景的第{轮次}轮,共通的关键词使用情况:{已掌握词汇}。 【语言风格】 回复控制在2-4句内,多用提问引导对方输出,少用陈述。这个模板里最有价值的三个设计是:第一,"始终保持角色"这句看似简单,实际决定了Agent不会突然变身语法老师;第二,"多用提问引导"让Agent始终把说话机会让给用户,避免它长篇大论讲单口相声;第三,"已掌握词汇"让Agent知道哪些词已经用过,可以巧妙地在后续对话里自然复现,实现间隔复习。
我实测发现一个大坑:Prompt里如果同时写了"保持角色"和"给出纠错",模型会很矛盾。解决办法是把纠错的触发条件完全放到状态图节点里——回复文本生成时,模型只管角色对话;纠错文本由专门的feedback_node在用户回合结束后单独生成,两段文本拼接输出。这样模型在生成角色回复时没有任何杂念,纠错质量也稳定了。
4.4 端到端联调与效果调优
最小系统跑通之后,调优花了最多时间。分享几个实测有效的手段。
第一是建立一个内部评测集。我准备了30条典型用户对话轨迹,覆盖了常见错误、卡壳、突发事件,每轮改完Prompt或代码,都拿这30条轨迹回归测试。没有这个测试集,你会陷入"改好一个案例、弄坏另一个案例"的死循环,而且自己毫无知觉。
第二是打开LangGraph的完整轨迹记录。LangGraph每一步的状态转移都能记录下来,我在开发环境打开trace,看模型在每一步选择什么工具、状态如何转移。调试效率一下提升了十倍,很多"玄学"问题其实是某个节点返回了空值,或者某个边的条件写错了。
第三是对模型输出做结构化约束。我使用Pydantic定义模型返回的JSON结构,要求LLM返回{"text": "...", "action": "query_vocab"}这种格式。没有结构化返回之前,模型偶尔会在对话文本里偷偷夹带纠错内容,导致纠错重复、格式混乱。结构化之后这个问题消失了。
调优过程中还有个心态问题要提醒:不要追求一次调完美。先让对话流畅,再让纠错精准,最后才做发音评分。顺序反了,你会在不稳定基础上反复返工。
5. 常见问题与排查技巧实录
5.1 "Agent execution terminated due to error"的排查套路
开发Agent的人应该都见过这个报错,英文原文是"Agent execution terminated due to error."。我第一次遇到时以为是什么神秘的系统级错误,查了很久,后来才明白这行的残酷——这基本就是"Agent的执行循环因为某个异常被打断了",具体原因得自己一层层扒。
我的排查套路分三步。第一步,看LangGraph的trace,确定是哪个节点抛的异常。大概率是工具调用或LLM输出格式问题。第二步,如果是工具调用,八成是MCP server返回了非预期的空值或错误类型,一定要给MCP server加统一的try-except和错误格式返回。第三步,如果是LLM输出问题,检查结构化输出的JSON是否符合Pydantic约束,最常见的就是模型把字段名拼错或者说字段缺失,我后来在调用层加了一个二次清洗函数,JSON不合法就重试一次。
这个报错背后还有一个容易被忽略的原因:Agent循环的步数限制或上下文长度超限。我自己设置过最大迭代10步,联网搜索类工具一多,10步根本不够,莫名其妙就报这个错。排查时先把步数调大,确认不是卡在步数限制上再说。
5.2 上下文爆炸与记忆遗忘
教学对话动辄五六十轮,不加控制的话,30分钟课程就能把8K到16K的上下文窗口塞满。塞满之后有两种症状:一是模型开始重复刚才说过的话,二是早期对话里的关键信息被"挤"出了注意力范围,Agent忘记了用户最开始说自己是素食者,又推荐了牛排。
我的方案前面提过:短期记忆只保留最近20轮,超过20轮压缩成摘要。这里有一个实践细节——摘要本身也要定期重写,不是简单地把旧消息拼接成一段话,而是让一个专门的摘要Agent基于之前的摘要和最新几轮对话,生成一份更新版摘要。这样摘要始终保持精炼且聚焦。
还有一个容易犯的错误是:把记忆全部塞进系统Prompt的前部。模型对Prompt开头和结尾的注意力最强(所谓的位置偏见),核心的"当前教学指令"必须放在Prompt的后部,靠近新输入的位置。我把用户画像放在中前部,把"当前节点任务"和"最近摘要"放在后部,效果立刻好了很多。
5.3 模型总是不按教学流程走
这是我最头疼的类问题。明明剧本节点是"点餐确认",模型却突然开始推荐甜点,或者本该保持服务员角色,却因为用户说了一句"I'm tired"就开始安慰起用户来。
根本原因在于流程控制和内容生成混淆了。解决方案就是我在架构里坚持的:剧本节点切换由代码控制,模型只是"在给定的节点内生成符合角色的话术"。但即便这样,模型仍然可能"跑出"当前节点的范围,因为它本质上是个语言模型,不是确定性状态机。
我最后的兜底方案是在节点出口加校验层:LLM生成回复后,用一个小模型或规则检查回复内容是否偏离当前节点任务。比如当前节点是"确认订单",规则校验回复中是否包含"confirmation"类动作词语,没有就重新生成一次。重试2次仍不合格,就降级用模板话术。这个兜底审计机制让系统的稳定率从大概86%提升到了97%,效果非常明显。
5.4 响应延迟与成本控制
对话类产品最敏感的就是延迟。我实测发现,一次完整的"用户说话→语音识别→Agent推理→反馈→语音合成"链路,如果所有环节串行,总耗时经常超过8秒,用户早就等得不耐烦了。我的优化手段是异步并行:语音合成可以在Agent还没完全结束时就预加载音频;发音评测的调用不阻塞主对话回复——先让教学回复回去,评测结果算完了再异步补进下一轮的消息里。
成本控制也是实打实的痛点。一个30分钟的高强度对话,光模型调用成本可能就接近1美元甚至更多。我的经验是三板斧:一是分级用模型,流程性判断用便宜的小模型(比如只要判断"用户这句话是不是点餐动作",用小型模型就够了),最终话术生成才用大模型;二是缓存高频回复,场景开场白、常见感谢语、模板纠错这些,能缓存就缓存;三是控制冗余重试,给每次LLM调用都设置合理max_tokens,避免模型放飞自我输出长篇的英语讲解。
5.5 发音评测不能不接,但也不能太当真
发音评测这块我从骨子里又爱又恨。爱的是它让产品有了"口语教练"的完整感;恨的是评测服务的分数经常和用户的主观感受对不上——用户觉得说得挺好的,评测给个65分,用户当场就不想练了。
后来我调了策略:评测分数不进对话、不进即时反馈,只在最终报告里呈现,而且用等级替代分数。"你的发音整体不错,重点是连读偏弱"比"发音65分"温和得多、有用得多。评测数据本身也不直接使用原始分,而是先做一次时间区间聚合,取稳定表现后的中位数,避免单次识别误差带来大波动。
另外一个技术细节:评测的文本要和语音严格对齐。有时候语音识别出来的文本和用户实际说的略有出入,评测服务按错误的文本打分,结果必然离谱。我在调用评测前会做一次对齐校验,识别置信度低于阈值的结果宁可丢弃也不给用户看,虚假的负面反馈比没有反馈更伤用户。
6. 总结与更多思考
这篇文写到这里,核心的东西都已经摊开了。最后聊几句个人体会。
我最大的感受是:Agent开发的核心难点不在模型,在于状态设计。模型的能力边界很清楚,但怎么把"教学这件事"的状态、流程、反馈、记忆组织好,才是真正决定产品成败的地方。我踩过最大的坑就是把教学逻辑全塞进Prompt里——那就像是让一个实习生同时当服务员、经理、培训师,他再努力也会手忙脚乱。把流程还给代码,把生成还给模型,这条原则帮我省了大量心力。
第二个体会是:好的Agent产品要靠"场景"来立骨架。英语教学Agent的场景剧本是产品灵魂,技术框架是骨架,没有好剧本,再强的Agent也只是一个"会聊天的空壳"。我后来花在写剧本上的时间远超写代码,但每一分钟都值得。
第三个建议,如果你也想做类似项目,别一上来就想着做个大而全的平台。先选一个你觉得最常用的场景(比如餐厅点餐),把"对话流畅、反馈有用、报告可读"这三件事跑通,再慢慢加场景、加语音、加发音评测、加多Agent协作。从0到1最快路径永远是:最小闭环跑通,然后有价值的迭代才会滚滚而来。这个项目我从第一行代码到第一个可用版本大概用了一周,但真正让我满意,是迭代到第四周的时候。做Agent产品,耐心比聪明重要。