Opik SimulatedUser 实战指南:用 LLM 驱动的人物角色模拟器做多轮对话测试
Opik SimulatedUser 实战指南用 LLM 驱动的人物角色模拟器做多轮对话测试【免费下载链接】comet-llmDebug, evaluate, and monitor your LLM applications, RAG systems, and agentic workflows with comprehensive tracing, automated evaluations, and production-ready dashboards.项目地址: https://gitcode.com/GitHub_Trending/co/comet-llm导读SimulatedUser是 Opik Python SDK 中用于多轮对话仿真multi-turn conversation simulation的核心类它既可以通过任意受支持的 LLM 模型生成贴合上下文与人物设定的用户回复也可以使用预设的固定回复实现确定性测试。本文将基于官方 API 文档结合仓库源码逐层拆解其构造函数、generate_response方法、模型解析机制以及与run_simulation的端到端集成帮助你快速构建面向客服、产品答疑等场景的自动化对话测试。一、SimulatedUser 是什么在对话类应用客服机器人、RAG 问答助手、Agent 工作流的测试中人工扮演用户进行多轮对话既耗时又难以覆盖不同人群。SimulatedUser提供了一种自动化方案它模拟用户一侧的行为把对话历史交给它由它生成下一条用户消息。根据 simulated_user.py 的类注释该类的定位是A simulated user that generates responses using LLMs or fixed responses. The user simulator generates string responses that are then incorporated into the conversation by the application logic.即它只负责产出字符串形式的用户消息至于如何把这些消息拼进完整对话由上层应用如run_simulation或你自己的调用逻辑负责。这一设计让 SimulatedUser 与具体业务解耦可被灵活嵌入多种测试框架。其核心能力包括LLM 驱动的响应使用任意受 Opik 模型工厂支持的 LLM 模型生成带上下文的用户回复固定回复模式提供预设回复列表时按顺序循环使用用于确定性测试人物设定Persona通过 persona 描述用户性格与行为作为 system prompt 引导生成对话上下文感知基于完整对话历史生成回复保持多轮行为连贯。在 Opik 中的导入方式为from opik.simulation import SimulatedUser该模块同时导出run_simulation见 simulation/init.py。二、快速上手安装与第一个模拟用户SimulatedUser属于 Opik Python SDK 的一部分随opik包一起分发无需额外安装独立依赖。但若使用 LLM 生成模式需要确保对应模型提供方的依赖可用Opik 模型工厂内部基于 LiteLLMAnthropic 模型可选用原生 SDK。最小可用示例from opik.simulation import SimulatedUser # 创建一个愤怒的顾客人物 user_simulator SimulatedUser( personaYou are a frustrated customer who wants a refund for a broken product, modelopenai/gpt-5-nano ) # 基于对话历史生成一条用户回复 conversation [ {role: assistant, content: Hello, how can I help you today?}, {role: user, content: My product broke after 2 days, I want a refund.}, {role: assistant, content: Im sorry to hear that. What happened?} ] response user_simulator.generate_response(conversation) print(response) # 可能的输出It just stopped working! Ive barely used it...在__init__中即使你随后改用固定回复SDK 也会调用模型工厂创建模型实例见源码第 40 行self._llm get_model(model_nameself.model)因此传入的model名必须是模型工厂可解析的合法名称。三、构造函数与参数详解官方文档给出的构造函数签名如下SimulatedUser( persona: str, model: Optional[str] None, fixed_responses: Optional[List[str]] None )三个参数的实际语义与 simulated_user.py 的实现一一对应参数类型默认值说明personastr必填用户性格与行为的描述文本被拼接进 system prompt指导 LLM 生成符合该人设的回复modelstrNone用于生成回复的 LLM 模型名省略时由get_default_model_name()解析默认模型fixed_responsesList[str]None预设回复列表提供后generate_response将按顺序循环使用不再调用 LLMpersona用户人格设定persona不是简单标签而是一段完整的自然语言描述最终以 system prompt 形式注入。从源码第 66-70 行可以看到完整的提示词模板You are a simulated user with the following persona: {self.persona} Your task is to generate realistic user messages that this persona would send in a conversation. Respond as if you are the user, not as an assistant describing the user. Generate a single user message that fits your persona and the conversation context.值得注意的两点设计其一明确要求以用户身份说话而不是像助手一样描述用户其二每次只生成一条用户消息。写得越具体身份、情绪、诉求、语气生成的回复越稳定。model模型选择与默认值解析model省略时SimulatedUser会调用models_factory.get_default_model_name()最终读取OpikConfig().default_llm。在 config.py 中该配置项的默认值为openai/gpt-5-nano并支持通过环境变量OPIK_DEFAULT_LLM覆盖——这与文档中省略时默认使用OPIK_DEFAULT_LLM未设置时为openai/gpt-5-nano的描述一致。模型实例的创建统一走 models_factory.py 的get()工厂函数Anthropic 系列模型且环境中存在anthropicSDK 时使用原生AnthropicChatModel其余模型使用LiteLLMChatModel。工厂内部带有实例缓存_MODEL_CACHE以模型名、是否追踪、额外 kwargs 为键因此多次创建相同配置的模拟用户不会重复初始化模型后端。fixed_responses确定性回复列表fixed_responses提供后generate_response会以内部计数器_response_index对列表长度取模实现顺序循环、耗尽后从头再来源码第 53-58 行。这一机制在单元测试 test_simulated_user.py 中有完整覆盖三次调用依次返回Response 1/2/3第四次调用回到Response 1。四、核心方法 generate_response 深度解析官方文档给出的方法签名generate_response(conversation_history: List[Dict[str, str]]) - str参数conversation_history为消息字典列表每个字典必须包含role与content两个键role 取值通常为system、user、assistant。返回str即模拟用户产出的单条回复文本。注意返回的是字符串而非消息字典——把role: user包装回去的工作由上层调用方完成。行为分支对应源码第 42-61 行若fixed_responses非空直接按顺序循环取出固定回复并返回完全绕过 LLM否则调用_generate_llm_response()用 LLM 基于 persona 与对话历史生成回复LLM 调用抛出任何异常时返回兜底文案Im having trouble responding right now. ({错误信息})保证模拟流程不因单次模型故障中断。对话历史如何送入 LLM_generate_llm_response的内部流程源码第 63-87 行分为三步拼装消息列表构造[{role: system, content: persona提示词}]再把conversation_history原样追加到其后文本化转换调用_format_messages_as_text()把消息字典序列化为带角色前缀的纯文本——system→System:、user→User:、assistant→Assistant:未知角色用role.title()前缀各消息以换行拼接生成调用self._llm.generate_string(inputconversation_text)得到字符串回复。单元测试 test_simulated_user.py 验证了这一转换15 条消息的历史中User: Message 0与Assistant: Response 14都出现在最终输入里。关于历史截断需要留意的事实官方文档提到会自动将对话历史限制为最近 10 条消息以避免超出 token 限制。但从当前仓库源码看_format_messages_as_text并未对历史做截断conversation_history会被完整送入 LLM对应的长历史单元测试也断言从Message 0到Message 14全部包含在输入中。因此在实际使用中若对话轮次很长建议在调用侧自行控制传入历史长度不要依赖自动截断。五、固定回复模式确定性测试的正确姿势当测试需要可复现、不依赖外部模型时使用fixed_responsesfrom opik.simulation import SimulatedUser # 用预设回复做确定性测试 user_simulator SimulatedUser( personaTest user, fixed_responses[ I want a refund, This is taking too long, Can I speak to a manager?, Im not satisfied with this service ] ) # 回复按列表顺序循环 response1 user_simulator.generate_response([]) # I want a refund response2 user_simulator.generate_response([]) # This is taking too long response3 user_simulator.generate_response([]) # Can I speak to a manager?注意在此模式下conversation_history参数会被忽略源码直接短路返回适合构造完全确定的脚本化对话同时应意识到__init__仍会初始化 LLM 后端self._llm get_model(...)如需彻底离线运行需确保模型工厂初始化不会触发网络请求模型实例创建本身通常不发起调用。六、多 Persona 场景模拟不同类型的用户为覆盖更多真实用户行为可以为同一测试创建多个SimulatedUser实例from opik.simulation import SimulatedUser # 满意的顾客 happy_customer SimulatedUser( personaYou are a satisfied customer who loves the product and wants to buy more, modelopenai/gpt-5-nano ) # 困惑的新手用户 confused_user SimulatedUser( personaYou are a confused user who needs help understanding how to use the product, modelopenai/gpt-5-nano ) # 技术型用户 technical_user SimulatedUser( personaYou are a technical user who asks detailed questions about implementation and integration, modelopenai/gpt-5-nano )每个实例持有独立的 persona 与可选的独立模型可以并行用于同一被测应用横向对比应用对不同用户类型的表现。七、与 run_simulation 集成完整的端到端对话仿真SimulatedUser通常与run_simulation配合使用。run_simulation定义在 simulator.py其职责是驱动模拟用户 ↔ 被测应用的多轮对话循环并把整段对话以 thread 形式写入 Opik 便于评估。run_simulation 签名与参数run_simulation( app: Callable, # 处理消息的应用函数 user_simulator: SimulatedUser, # 模拟用户实例 initial_message: Optional[str] None, # 首条用户消息缺省由模拟器生成 max_turns: int 5, # 最大对话轮数 thread_id: Optional[str] None, # 线程 ID缺省自动生成 project_name: Optional[str] None, # Opik 项目名用于 trace 归类 **app_kwargs: Any, # 透传给 app 的额外关键字参数 ) - Dict[str, Any]返回字典包含三个键thread_id、conversation_history本轮完整消息列表、project_name。被集成应用函数的约定被测app的签名须为app(message: str, *, thread_id: str, **kwargs) - Dict[str, str]返回{role: assistant, content: ...}形式的字典。run_simulation会自动用track包装未追踪的应用并通过opik_args注入trace.thread_id与包含turn、project_name的 metadata从而把每一轮都归属到同一个线程下源码第 46-80 行。官方文档给出的完整集成示例from opik.simulation import SimulatedUser, run_simulation from opik import track track def customer_service_agent(user_message: str, *, thread_id: str, **kwargs): # 你的 Agent 逻辑内部管理对话历史 return {role: assistant, content: I understand your concern...} # 用多个人物做测试 personas [ You are a frustrated customer who wants a refund, You are a happy customer who wants to buy more products, You are a confused user who needs help with setup ] for i, persona in enumerate(personas): simulator SimulatedUser(personapersona) simulation run_simulation( appcustomer_service_agent, user_simulatorsimulator, max_turns5, project_namecustomer_service_evaluation ) print(fSimulation {i1} completed: {simulation[thread_id]})循环内部的容错与健壮性run_simulation对异常做了双层防护源码第 82-100 行若app调用抛出异常会用{role: assistant, content: Error processing message: ...}兜底仿真不中断若app返回的不是含role/content的字典会强制转成{role: assistant, content: str(...)}空返回则使用No response。第一轮的消息取自initial_message未提供时由user_simulator.generate_response([])生成之后每一轮都由模拟器基于累计的对话历史生成用户消息——注意这里run_simulation会维护一份conversation_history供模拟器使用而业务侧的完整历史由 app 自己通过thread_id管理两者职责分离。八、最佳实践把 Persona 写细写具体包含身份、情绪、诉求、语言风格的人设描述能显著提升行为一致性过于笼统的描述容易产生漂移。按场景选模型快速冒烟测试用速度快的模型降低成本需要逼真对话时换用能力更强的模型SimulatedUser的模型参数按实例独立配置方便分组测试。确定性测试优先用固定回复需要严格复现回归场景时用fixed_responses关闭 LLM 随机性需要评估 LLM 应用在开放式对话中的表现时再切回模型生成。主动管理上下文长度当前实现不会自动截断历史长对话请自行限制传入generate_response的消息条数避免超出模型 token 上限。善用兜底机制LLM 失败时类会返回Im having trouble responding right now. (...)而run_simulation也会对 app 异常兜底——把这两层机制当作仿真稳定性的默认保障不必自行再包一层 try/except。九、源码级注意事项Notes模型一致性SimulatedUser通过 Opik 的模型工厂models_factory.py创建 LLM 后端与 Opik 评估器如 LLM-as-judge 指标使用同一套模型解析与追踪机制保证配置和追踪行为一致。返回值是纯字符串generate_response返回的是文本而非{role: user, ...}字典组装消息结构是上层应用的责任run_simulation已代为处理。persona 即 system promptpersona 文本被完整拼接进 system prompt因此其中的指令性描述如说话简短先抱怨再提诉求会被 LLM 遵循。固定回复循环取模内部计数器_response_index自增并对len(fixed_responses)取模列表耗尽后从头开始且该计数器在实例生命周期内持续累计。对应测试用例行为细节可对照单元测试 test_simulated_user.py构造、固定回复循环、LLM 调用、异常兜底、长历史、test_simulator.py 以及集成测试 test_simulation_integration.py 进一步验证。十、小结SimulatedUser把用户这一抽象实体封装为一个可配置、可复用的对象用persona控制行为风格用model接入模型工厂用fixed_responses保证确定性再配合run_simulation的自动追踪与多轮驱动即可低成本地为客服、RAG 问答、Agent 等对话应用搭建自动化仿真与评估流水线。结合仓库源码可以看出其实现刻意保持简单——字符串输入输出、显式系统提示词、异常兜底这些细节共同保证了它在生产级评估链路中的稳定与可预测。【免费下载链接】comet-llmDebug, evaluate, and monitor your LLM applications, RAG systems, and agentic workflows with comprehensive tracing, automated evaluations, and production-ready dashboards.项目地址: https://gitcode.com/GitHub_Trending/co/comet-llm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

Super Productivity 收件箱视图(Inbox View)完全指南:任务的默认归宿、快速捕获与渐进式整理

Super Productivity 收件箱视图(Inbox View)完全指南:任务的默认归宿、快速捕获与渐进式整理

Super Productivity 收件箱视图(Inbox View)完全指南:任务的默认归宿、快速捕获与渐进式整理 【免费下载链接】super-productivity Super Productivity is an advanced todo list app with integrated Timeboxing and time tracking capabili…

2026/9/13 18:08:27 阅读更多 →
Appium 客户端(Client)完全解读:客户端-服务器架构、WebDriver 协议与多语言客户端库实战

Appium 客户端(Client)完全解读:客户端-服务器架构、WebDriver 协议与多语言客户端库实战

Appium 客户端(Client)完全解读:客户端-服务器架构、WebDriver 协议与多语言客户端库实战 【免费下载链接】appium Cross-platform automation framework for all kinds of apps, built on top of the W3C WebDriver protocol 项目地址: ht…

2026/9/13 18:08:27 阅读更多 →
brpc 高性能哈希表 FlatMap 深度解析:接近原生数组查找速度的 C++ 实现原理与实战

brpc 高性能哈希表 FlatMap 深度解析:接近原生数组查找速度的 C++ 实现原理与实战

brpc 高性能哈希表 FlatMap 深度解析:接近原生数组查找速度的 C 实现原理与实战 【免费下载链接】brpc brpc is an Industrial-grade RPC framework using C Language, which is often used in high performance system such as Search, Storage, Machine learning,…

2026/9/13 18:08:27 阅读更多 →

最新新闻

基于YOLOv8的网球场识别系统:数据集、训练与部署实战

基于YOLOv8的网球场识别系统:数据集、训练与部署实战

简介:面向计算机视觉方向毕业设计或课程设计,提供一套基于YOLOv8的网球场识别系统,功能完整、简单部署即可运行,尤其适合深度学习、目标检测相关专业学生作为毕设或课设基础。资源共97个文件,以70个Python脚本和12个py…

2026/9/13 18:53:48 阅读更多 →
分布式唯一 ID 生成算法:从雪花算法到号段模式的权衡

分布式唯一 ID 生成算法:从雪花算法到号段模式的权衡

分布式唯一 ID 生成算法:从雪花算法到号段模式的权衡 在海量分布式存储、分布式数据库分库分表、以及全链路追踪系统中,分布式全局唯一 ID(Distributed Unique ID Generator) 是所有业务数据实体的物理身份证。 一个理想的分布式…

2026/9/13 18:53:48 阅读更多 →
网易2025年财报分析:多元业务协同与利润增长

网易2025年财报分析:多元业务协同与利润增长

1. 网易2025年财报核心数据解读2025年对网易而言是标志性的一年,全年营业利润达到358亿元,同比增长21%。这个数字背后反映的是网易在游戏、电商、音乐、教育等多元业务的协同发力。作为从业十余年的互联网分析师,我将从业务结构、增长驱动力和…

2026/9/13 18:53:48 阅读更多 →
Linux密码修改无效排查:认证源、缓存与脚本全解析

Linux密码修改无效排查:认证源、缓存与脚本全解析

前几天处理了一个挺典型的账户问题,现象一句话就能说清:某台设备里有个叫ctxsys的系统账户,运维按规范用passwd改了密码,命令也是正常执行完的,但结果完全没影响——用新密码登录被拒,旧密码却能进&#xf…

2026/9/13 18:53:48 阅读更多 →
基于 Google Cloud 语音识别与合成的实战指南:Chirp 3、Gemini TTS 与 Gemini 3.5 Transcribe 全解析

基于 Google Cloud 语音识别与合成的实战指南:Chirp 3、Gemini TTS 与 Gemini 3.5 Transcribe 全解析

基于 Google Cloud 语音识别与合成的实战指南:Chirp 3、Gemini TTS 与 Gemini 3.5 Transcribe 全解析 【免费下载链接】generative-ai Sample code and notebooks for Generative AI on Google Cloud, with Gemini Enterprise Agent Platform 项目地址: https://g…

2026/9/13 18:53:48 阅读更多 →
可编辑生成结果:AI 产出与人工介入协同交互

可编辑生成结果:AI 产出与人工介入协同交互

可编辑生成结果:AI 产出与人工介入协同交互在生成式 AI 工具进入真实企业业务流时,一个普遍的误区是认为“大模型应该一键完成 100% 的工作”。 但在真实的专业创作、代码编写或合同起草场景中,无论模型多么强大,它生成的初稿往往…

2026/9/13 18:52:47 阅读更多 →

日新闻

AI SDK Harness 依赖更新指南:掌握 harness 包 SDK 依赖的升级、桥接同步与一致性校验

AI SDK Harness 依赖更新指南:掌握 harness 包 SDK 依赖的升级、桥接同步与一致性校验

AI SDK Harness 依赖更新指南:掌握 harness 包 SDK 依赖的升级、桥接同步与一致性校验 【免费下载链接】ai The AI Toolkit for TypeScript. From the creators of Next.js, the AI SDK is a free open-source library for building AI-powered applications and ag…

2026/9/13 0:00:24 阅读更多 →
Refine v5 Ant Design NumberField 组件实战:基于 Intl 的本地化数字格式化

Refine v5 Ant Design NumberField 组件实战:基于 Intl 的本地化数字格式化

Refine v5 Ant Design NumberField 组件实战:基于 Intl 的本地化数字格式化 【免费下载链接】refine A React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility. 项目地址: https://gitcode.com/GitH…

2026/9/13 0:00:24 阅读更多 →
Flutter应用改名全指南:从Android到iOS的配置与工具实践

Flutter应用改名全指南:从Android到iOS的配置与工具实践

刚接一个外包项目时,甲方要求把工程里临时用的应用名改成正式产品名。我本来觉得“改名”这种小事,打开配置文件改一行不就完了?结果真动手才发现,Flutter项目里“应用名称”根本不是一处配置,而是一整套散落在 Androi…

2026/9/13 0:00:24 阅读更多 →

周新闻

AI SDK Harness 依赖更新指南:掌握 harness 包 SDK 依赖的升级、桥接同步与一致性校验

AI SDK Harness 依赖更新指南:掌握 harness 包 SDK 依赖的升级、桥接同步与一致性校验

AI SDK Harness 依赖更新指南:掌握 harness 包 SDK 依赖的升级、桥接同步与一致性校验 【免费下载链接】ai The AI Toolkit for TypeScript. From the creators of Next.js, the AI SDK is a free open-source library for building AI-powered applications and ag…

2026/9/13 0:00:24 阅读更多 →
Refine v5 Ant Design NumberField 组件实战:基于 Intl 的本地化数字格式化

Refine v5 Ant Design NumberField 组件实战:基于 Intl 的本地化数字格式化

Refine v5 Ant Design NumberField 组件实战:基于 Intl 的本地化数字格式化 【免费下载链接】refine A React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility. 项目地址: https://gitcode.com/GitH…

2026/9/13 0:00:24 阅读更多 →
Flutter应用改名全指南:从Android到iOS的配置与工具实践

Flutter应用改名全指南:从Android到iOS的配置与工具实践

刚接一个外包项目时,甲方要求把工程里临时用的应用名改成正式产品名。我本来觉得“改名”这种小事,打开配置文件改一行不就完了?结果真动手才发现,Flutter项目里“应用名称”根本不是一处配置,而是一整套散落在 Androi…

2026/9/13 0:00:24 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/13 16:51:11 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/12 18:29:34 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/12 19:02:44 阅读更多 →