AI智能体开发新范式:用TDD工程化方法构建可靠LLM应用

news2026/5/15 18:39:02
1. 项目概述当AI智能体遇上测试驱动开发最近在GitHub上看到一个挺有意思的项目叫agent-skill-tdd。光看名字就能嗅到一股“新老结合”的味道——一边是当下火热的AI智能体Agent另一边是软件工程领域经久不衰的经典实践测试驱动开发TDD。这个组合本身就充满了想象空间。简单来说这个项目探索的是如何用TDD的方法论来指导、规范和验证AI智能体技能的开发过程。这背后其实反映了一个更深层次的需求随着大语言模型能力的爆发基于LLM构建的智能体应用如雨后春笋般涌现。但很多开发过程还处于“手工作坊”阶段——写个提示词Prompt跑一下看看效果不行再调充满了不确定性和随机性。agent-skill-tdd试图将这种“炼丹”过程工程化引入TDD的“红-绿-重构”循环让智能体技能的开发变得可预测、可重复、可维护。它不仅仅是一个工具库更是一种开发范式的倡导适合所有正在或计划将LLM智能体投入实际生产环境的开发者、团队负责人和技术决策者。2. 核心理念拆解为什么智能体开发需要TDD2.1 智能体开发的现状与痛点在传统的软件开发中我们编写的是确定性的代码。给定输入必有确定的输出或者明确的异常。测试用例可以清晰地断言这些行为。但到了AI智能体领域尤其是基于大语言模型的智能体情况变得复杂。智能体的核心“逻辑”往往是一段自然语言描述的提示词Prompt它的输出是概率性的、开放性的文本。这带来了几个核心痛点行为不可预测微调一下提示词的措辞或者更换模型的版本智能体的输出风格、格式甚至逻辑都可能发生意想不到的变化。这种不确定性给集成和上线带来了巨大风险。回归测试困难当你为智能体增加一个新功能技能时如何确保原有的核心能力没有被破坏手动一个个场景去验证效率低下且容易遗漏。协作与沟通成本高一个复杂的智能体技能其提示词可能长达数百字包含各种约束、示例和指令。团队成员之间如何理解、评审和修改这份“需求文档”缺乏像单元测试那样清晰、可执行的规格说明。重构与优化胆怯你想优化提示词的结构让它更高效或更便宜消耗更少的Token但你怎么敢动手没有测试的保护任何修改都像是在走钢丝。agent-skill-tdd正是瞄准了这些痛点。它认为智能体的“技能”虽然表现形式是自然语言但其内在的“契约”是可以被定义的。这个契约就是给定特定的输入上下文Context智能体应该产生符合特定期望的输出Output。TDD提供了一套完美的框架来定义和验证这份契约。2.2 TDD范式在智能体场景的映射经典的TDD循环是“红-绿-重构”红先写一个失败的测试定义你期望的功能。绿编写最少量的代码让测试通过。重构优化代码结构同时保持测试通过。在agent-skill-tdd的语境下这个循环被巧妙地映射了红先写一个失败的“技能测试”。这个测试会描述一个场景输入上下文并定义你期望智能体做出的回应输出。例如测试“当用户问天气时智能体应询问城市”。绿编写或调整你的提示词可能结合一些工具调用逻辑让智能体在这个测试场景下能产生符合期望的回应。这时你的“产品代码”就是那段提示词和相关的控制逻辑。重构优化你的提示词。可能是精简字数、调整示例的顺序、改用更清晰的指令或者将部分逻辑抽离成可复用的模板。因为有测试套件的保护你可以放心大胆地进行这些优化确保核心行为不变。这种映射将智能体开发从“艺术”拉向了“工程”。测试用例成为了智能体技能的“活文档”和“安全网”。3. 项目核心架构与使用模式虽然我无法看到agent-skill-tdd项目具体的、最新的代码实现细节因为这是一个假设的深度解析但基于其理念和常见的工程实践我们可以推断并构建出其核心架构和典型的使用模式。这有助于我们理解如何在实际项目中应用这种思想。3.1 核心组件推断一个完整的agent-skill-tdd框架或实践很可能包含以下几个核心组件测试运行器Test Runner这是框架的引擎。它负责加载测试用例准备测试环境如初始化一个模拟的或真实的LLM调用客户端执行测试并比对结果。它需要处理与LLM API的交互管理测试的并发、超时和重试。测试用例定义TestCase Definition提供一套领域特定语言DSL或API让开发者能够方便地描述一个测试场景。关键要素包括skill_name: 被测试的技能名称。context: 测试的输入上下文。这可能包括用户的对话历史、当前查询、从外部工具获取的数据如数据库查询结果、API响应等。这部分需要被构造成智能体所能理解的“消息”格式例如OpenAI的messages数组。expected_behavior: 期望的行为。这比简单的字符串完全匹配更灵活可能包括文本断言检查回复中是否包含/不包含某些关键词或短语。正则表达式匹配验证回复是否符合某种格式如日期、JSON。函数调用断言验证智能体是否正确地调用了某个工具函数并且参数符合预期。结构化数据提取与验证从回复中提取出JSON或特定格式的数据然后进行字段级的断言。LLM评估对于更复杂、开放性的期望可以使用另一个通常更小、更便宜的LLM作为“裁判”来评估回复是否满足要求例如情感是否积极是否回答了问题核心。技能适配器Skill Adapter提供标准化的接口来封装一个具体的智能体技能。它接收测试运行器构造的上下文调用实际的智能体逻辑可能是本地函数也可能是远程服务并返回响应。适配器模式使得测试框架与具体的智能体实现解耦。Mock与Fixture系统智能体技能经常需要调用外部工具如搜索、查数据库、调用API。在单元测试中我们必须隔离这些外部依赖。框架需要提供便捷的方式来Mock这些工具调用返回预设的fixture数据从而确保测试的确定性和速度。报告与可视化生成清晰的测试报告显示通过率、失败用例的详细对比期望输出 vs 实际输出、耗时等。这对于持续集成CI流程至关重要。3.2 一个典型的工作流示例假设我们要为一个“餐厅推荐智能体”开发一个“根据口味和预算推荐菜品”的技能。以下是应用agent-skill-tdd的可能工作流# 步骤1 编写一个失败的测试红 # test_restaurant_recommendation.py import pytest from agent_skill_tdd import SkillTestCase, expect_contains, expect_tool_call class TestRestaurantRecommendationSkill: pytest.fixture def skill(self): # 返回我们要测试的技能适配器实例 return RestaurantRecommendationSkillAdapter() def test_recommend_dish_within_budget(self, skill): # 定义测试上下文用户表达了喜好和预算 context { user_query: “我喜欢吃辣的预算人均100元左右有什么推荐吗”, conversation_history: [], # 新对话无历史 available_tools: [query_restaurant_menu] # 模拟可用的工具 } # 执行技能 response skill.execute(context) # 断言期望行为 # 1. 智能体应该先调用工具查询菜单 expect_tool_call(response, tool_namequery_restaurant_menu) # 我们可以进一步断言工具调用的参数里应该包含“辣”和预算过滤条件这需要框架支持 # 2. 在最终的回复中应该包含推荐和价格信息 expect_contains(response.final_output, [推荐, 辣]) # 我们可以用一个简单的逻辑检查是否提到了价格并隐含在100元左右 # 更复杂的断言可以用LLM评估“判断回复是否在推荐菜品时考虑了预算限制”运行这个测试它肯定会失败因为我们还没有实现RestaurantRecommendationSkillAdapter和其背后的提示词逻辑。# 步骤2 实现技能让测试通过绿 # restaurant_recommendation_skill.py class RestaurantRecommendationSkillAdapter: def execute(self, context): # 这里是核心的提示词工程 prompt f 你是一个餐厅推荐助手。请根据用户的需求推荐菜品。 用户需求{context[user_query]} 你可以使用以下工具 - query_restaurant_menu: 根据口味和价格范围查询菜单。 请遵循以下步骤 1. 分析用户需求提取关键词如口味“辣”预算“人均100元”。 2. 调用合适的工具获取菜品信息。 3. 从结果中筛选出符合要求的菜品组织成友好的回复。 注意回复必须具体包含菜名和大致价格。 # 这里会调用LLM并处理工具调用的循环 # 假设我们有一个LLM客户端和工具执行器 llm_client LLMClient() response llm_client.chat(prompt, toolscontext[available_tools]) return response我们实现了适配器和简单的提示词。现在再次运行测试。测试框架会执行skill.execute(context)。当LLM返回的响应中包含工具调用时框架会拦截这个调用。因为我们没有提供真实的工具实现框架会使用我们在测试中预设的Mock数据。我们需要在测试中设置这个Mock。Mock的query_restaurant_menu工具返回一个预设的菜品列表Fixture。LLM根据这个Mock结果生成最终回复。框架检查最终回复是否通过了我们的断言包含“推荐”、“辣”等。通过精心设计提示词和Mock数据我们让测试变绿了。# 步骤3 重构与优化 # 观察测试通过后我们可能觉得提示词太长或者结构不好。 # 我们可以重构提示词例如将其模块化 system_prompt “你是一个餐厅推荐助手。” step_by_step_instructions “1. 分析需求... 2. 调用工具... 3. 筛选并回复...” # 或者使用更高效的few-shot示例代替冗长的指令。 # 在每次修改后我们运行整个测试套件。只要测试还是绿的我们就知道核心功能没有被破坏。 # 我们还可以增加更多测试用例覆盖边界情况比如用户没说预算、用户口味矛盾等让技能更加健壮。3.3 关键配置与参数解析在实际使用中有几个关键配置点决定了测试的可靠性和成本LLM客户端的配置模型选择测试时应该使用与生产环境相同或能力相近的模型吗成本可能很高。一个常见的折衷是在本地开发和非关键CI环节使用小型、快速的模型如GPT-3.5-turbo来快速获得反馈在发布前的关键测试阶段再用生产模型如GPT-4运行核心用例进行验收。框架应支持灵活配置模型。温度Temperature必须设置为0或接近0。测试需要确定性和可重复性高温带来的随机性会使得测试结果不稳定。最大Token数根据测试场景合理设置避免不必要的开销。断言策略的配置严格模式 vs 宽松模式对于格式严格的输出如生成的JSON、代码可以使用精确匹配或JSON Schema验证。对于开放性的文本回复则使用关键词包含、LLM评估等宽松断言。框架应支持多种断言器Assertor。LLM评估的配置如果使用LLM作为断言裁判需要为其配置独立的模型、提示词和判断标准。这部分本身也需要被测试和校准以避免“裁判”的不稳定。Mock数据的维护Mock数据Fixtures应该真实且有代表性。它们最好来源于生产环境数据的脱敏样本或者是精心设计的、覆盖了各种边界条件的样例。维护一套好的Fixture是保证测试有效性的基础。注意测试的非确定性即使温度设为0不同版本的模型、甚至同一版本模型在不同时间的微小更新仍可能导致输出有细微差别。因此对AI智能体的测试断言应该侧重于“语义正确”而非“字面完全一致”。这也是为什么需要灵活的断言策略。4. 实战构建一个简单的TDD智能体技能测试套件由于agent-skill-tdd可能是一个概念或尚未成熟的开源项目我们可以自己动手利用现有的测试框架如Pytest和LLM SDK实践这一理念。下面我们构建一个最小可行版本的测试框架。4.1 环境准备与基础框架搭建首先我们确定技术栈Python Pytest OpenAI SDK或其他LLM提供商。我们创建一个项目结构agent-skill-tdd-demo/ ├── skills/ # 智能体技能实现 │ ├── __init__.py │ └── weather_skill.py ├── tests/ # 测试目录 │ ├── __init__.py │ ├── conftest.py # Pytest全局配置和Fixture │ └── test_weather_skill.py ├── core/ # 核心测试框架 │ ├── __init__.py │ ├── assertors.py # 各种断言器 │ ├── skill_adapter.py # 技能适配器基类 │ └── test_runner.py # 简化版测试运行器 └── requirements.txtrequirements.txt内容pytest7.0.0 openai1.0.0 pydantic2.0.0 # 用于数据验证4.2 实现核心断言器断言器是测试的灵魂。我们在core/assertors.py中实现几种常用的# core/assertors.py import re import json import jsonschema from typing import Any, Dict, List from openai import OpenAI class AssertorBase: 断言器基类 def assert_response(self, expected: Any, actual: str) - bool: raise NotImplementedError class ContainsAssertor(AssertorBase): 检查实际回复是否包含预期关键词 def __init__(self, keywords: List[str], all_required: bool True): self.keywords keywords self.all_required all_required # 是否要求全部关键词都出现 def assert_response(self, expected: List[str], actual: str) - bool: if self.all_required: return all(keyword in actual for keyword in self.keywords) else: return any(keyword in actual for keyword in self.keywords) class RegexAssertor(AssertorBase): 使用正则表达式匹配回复 def __init__(self, pattern: str, flags: int 0): self.pattern re.compile(pattern, flags) def assert_response(self, expected: re.Pattern, actual: str) - bool: return bool(self.pattern.search(actual)) class JsonSchemaAssertor(AssertorBase): 验证回复是否能解析为JSON并符合指定Schema def __init__(self, schema: Dict): self.schema schema def assert_response(self, expected: Dict, actual: str) - bool: try: data json.loads(actual) jsonschema.validate(instancedata, schemaself.schema) return True except (json.JSONDecodeError, jsonschema.ValidationError): return False class LLMEvaluatorAssertor(AssertorBase): 使用另一个LLM作为裁判进行评估。成本较高用于复杂断言。 def __init__(self, client: OpenAI, eval_prompt_template: str, model: str gpt-3.5-turbo): self.client client self.eval_prompt_template eval_prompt_template self.model model def assert_response(self, expected: Dict, actual: str) - bool: # expected 里可能包含上下文、评估标准等 context expected.get(context, ) criteria expected.get(criteria, 回复是否合理且相关) prompt self.eval_prompt_template.format( contextcontext, actual_responseactual, criteriacriteria ) response self.client.chat.completions.create( modelself.model, messages[{role: user, content: prompt}], temperature0.0, max_tokens10 ) evaluation response.choices[0].message.content.strip().lower() # 假设裁判LLM输出 yes 或 no return evaluation.startswith(yes)4.3 实现技能适配器与测试装饰器我们在core/skill_adapter.py中定义一个基类并在tests/conftest.py中设置全局的Pytest Fixture来处理LLM客户端和技能加载。# core/skill_adapter.py from abc import ABC, abstractmethod class SkillAdapter(ABC): 技能适配器抽象基类 skill_name: str abstractmethod def execute(self, context: Dict) - str: 执行技能返回最终文本回复 pass def get_tool_calls(self, response): # 可选用于工具调用断言 从LLM响应中解析出工具调用请求 # 这里需要根据具体的LLM响应格式来解析 # 例如OpenAI的响应中tool_calls字段 return []# tests/conftest.py import pytest from openai import OpenAI from core.skill_adapter import SkillAdapter pytest.fixture(scopesession) def openai_client(): # 从环境变量读取API Key import os api_key os.getenv(OPENAI_API_KEY) if not api_key: pytest.skip(OPENAI_API_KEY environment variable not set) return OpenAI(api_keyapi_key) pytest.fixture def weather_skill(openai_client): # 动态导入并初始化具体的技能 from skills.weather_skill import WeatherSkill return WeatherSkill(clientopenai_client)4.4 编写并运行一个完整的技能测试现在我们实现一个简单的“天气查询”技能并为其编写TDD测试。首先编写测试红tests/test_weather_skill.pyimport pytest from core.assertors import ContainsAssertor, RegexAssertor class TestWeatherSkill: def test_ask_for_city_when_no_city_provided(self, weather_skill): 测试当用户未提供城市时技能应主动询问城市 context { user_query: “今天天气怎么样”, conversation_history: [] } response weather_skill.execute(context) # 使用断言器验证 assertor ContainsAssertor(keywords[哪个城市, 哪里, 城市], all_requiredFalse) assert assertor.assert_response(assertor.keywords, response), f预期回复应询问城市实际回复{response} def test_provide_weather_with_city(self, weather_skill, mocker): 测试当用户提供城市时技能应调用天气工具并给出答复模拟工具调用 # 使用pytest-mock来模拟工具调用 mock_tool_response { city: 北京, temperature: 22, condition: 晴, humidity: 65% } # 假设技能内部会调用一个 get_weather 函数 mocker.patch.object(weather_skill, _call_weather_api, return_valuemock_tool_response) context { user_query: “北京天气如何”, conversation_history: [] } response weather_skill.execute(context) # 断言回复中包含天气信息 assertor1 ContainsAssertor(keywords[北京, 天气], all_requiredTrue) assertor2 ContainsAssertor(keywords[22, 度, 晴], all_requiredFalse) # 温度单位可能不同 assert assertor1.assert_response(assertor1.keywords, response), f回复应提及北京和天气实际{response} # 注意由于模拟了工具回复中肯定有22和晴。实际中LLM的措辞可能不同这里断言可能不稳定。 # 更好的方式是使用LLMEvaluatorAssertor或更宽松的关键词。 print(f测试通过技能回复{response}) def test_response_contains_temperature_format(self, weather_skill, mocker): 测试回复中的温度格式大致正确数字单位 mocker.patch.object(weather_skill, _call_weather_api, return_value{temperature: 18, condition: 多云}) context {user_query: “上海天气”, conversation_history: []} response weather_skill.execute(context) # 使用正则表达式匹配“数字 度/°”的模式 assertor RegexAssertor(patternr\d\s*[度°C]) assert assertor.assert_response(assertor.pattern, response), f回复中应包含温度格式实际{response}然后实现技能绿skills/weather_skill.py# skills/weather_skill.py from core.skill_adapter import SkillAdapter from typing import Dict class WeatherSkill(SkillAdapter): skill_name weather_query def __init__(self, client): self.client client # 这是一个模拟的函数真实场景可能调用外部API self._call_weather_api self._mock_weather_api def execute(self, context: Dict) - str: user_query context.get(user_query, ) # 简单的逻辑如果查询中包含城市名则查询天气否则询问城市。 # 这里用一个极其简单的规则实际应用会用NLU或LLM来提取实体。 known_cities [北京, 上海, 广州, 深圳] city_in_query next((city for city in known_cities if city in user_query), None) if city_in_query: weather_data self._call_weather_api(city_in_query) # 构造提示词让LLM格式化回复 prompt f 用户询问{city_in_query}的天气。以下是获取到的天气数据 {weather_data} 请用一句友好、自然的话回复用户包含城市、温度和天气状况。 else: prompt “用户询问天气但没有指明城市。请用一句友好、自然的话询问用户想知道哪个城市的天气。” response self.client.chat.completions.create( modelgpt-3.5-turbo, # 测试使用成本较低的模型 messages[{role: user, content: prompt}], temperature0.0, # 测试时温度必须为0 max_tokens150 ) return response.choices[0].message.content def _mock_weather_api(self, city): # 模拟数据 import random return { city: city, temperature: str(random.randint(15, 30)), condition: random.choice([晴, 多云, 阴, 小雨]), humidity: f{random.randint(40, 80)}% }运行测试pytest tests/test_weather_skill.py -v。你会看到测试从失败红到通过绿的过程。之后你就可以安全地重构WeatherSkill.execute方法中的提示词或者优化城市提取的逻辑只要测试保持绿色即可。5. 高级话题与最佳实践将TDD应用于AI智能体开发是一个新兴领域在实践中会遇到一些独特挑战。以下是几个高级话题和对应的最佳实践建议。5.1 处理非确定性让测试稳定可靠LLM输出的非确定性是最大的挑战。除了设置temperature0还有以下策略断言语义而非字面这是最重要的原则。使用ContainsAssertor检查关键信息使用RegexAssertor检查格式使用LLMEvaluatorAssertor检查逻辑合理性。避免使用assert response “预期的完整句子”。设置置信度阈值对于ContainsAssertor可以计算关键词出现的密度或使用文本相似度如余弦相似度设定一个阈值如0.8超过阈值即认为通过。黄金标准数据集与模糊匹配维护一个“黄金标准”测试集包含输入和“理想”输出。测试时计算实际输出与每个“理想”输出之间的相似度如使用Sentence-BERT嵌入取最高分如果超过阈值则通过。这允许输出有多种正确的表达方式。重试与降级机制在测试运行器中对于失败的断言可以尝试让LLM“重答”一次使用相同的输入或者用一个更宽松的断言降级再检查一次。这可以缓解API偶发的不稳定。5.2 测试的分类与金字塔像传统软件测试一样智能体技能的测试也应该形成金字塔单元测试最多测试单个技能在特定上下文下的行为。Mock所有外部工具和API调用。目标是快速、廉价、高覆盖率。agent-skill-tdd主要聚焦于此层。集成测试中等测试多个技能之间的协作或者技能与真实工具如测试环境的数据库、沙箱API的集成。需要部分真实依赖。端到端E2E测试最少模拟真实用户与整个智能体系统的完整对话流。成本高、速度慢、脆弱但能发现交互和流程问题。应只覆盖最关键的用户旅程。最佳实践将开发时间的绝大部分投入到编写和维护高质量的单元测试上。利用agent-skill-tdd框架让单元测试的编写变得简单。5.3 测试数据的管理与生成Fixture管理将Mock数据如工具返回的JSON保存在独立的文件如tests/fixtures/weather_api_response.json或目录中便于管理和复用。测试用例生成可以利用LLM本身来生成边界测试用例。例如给定一个技能描述让LLM“想出10个可能让这个技能出错的用户提问”。这能帮助发现盲点。持续回归将测试套件接入CI/CD管道如GitHub Actions。每次提交代码或提示词都自动运行测试。这能第一时间发现因模型更新或提示词修改导致的回归问题。5.4 性能与成本考量频繁调用LLM运行测试成本可能成为问题。本地模型对于开发阶段尽可能使用本地部署的小模型如Llama 3.1 8B, Qwen2.5 7B来运行测试。虽然能力稍弱但零成本、速度快适合验证逻辑和格式。测试缓存对于相同的输入上下文其输出在temperature0时理论上是确定的。可以实现一个简单的缓存层如磁盘缓存或Redis将(model, prompt, parameters)的哈希值作为键存储响应结果。在非强制刷新的情况下优先使用缓存结果。注意当提示词或模型版本更新时必须清除相关缓存。分层测试运行在CI中可以设置两个测试阶段第一阶段用小型/本地模型运行全部测试快速反馈第二阶段如合并前只用生产模型运行核心的、高优先级的测试用例。6. 常见问题与排查技巧实录在实际应用TDD开发智能体技能时你会遇到一些典型问题。以下是一些实录和解决方案。6.1 测试时好时坏Flaky Tests问题同一个测试用例有时通过有时失败没有修改任何代码。排查与解决检查温度设置确保测试时LLM调用的temperature参数绝对为0.0。这是第一要务。审查断言策略你的断言是否过于严格是否要求完全一致的字符串立即改为语义断言或模糊匹配。例如将assert “温度是22度” in response改为assert “22” in response and any(word in response for word in [“度”, “°C”, “摄氏度”])。模型版本漂移云服务商可能在不通知的情况下更新模型。在测试中打印或记录下使用的模型ID和版本。如果发现大面积测试突然失败检查是否是模型版本变化导致。上下文窗口差异虽然概率极低但确保每次测试提供的上下文对话历史、系统提示是完全一致的。任何细微差别如多余的空格、换行符都可能影响输出。实现自动重试与标记对于非核心的、偶尔失败的测试可以配置自动重试如重试2次如果通过则标记为“通过但不稳定”并记录日志供后续分析优化。6.2 测试运行速度慢问题测试套件有上百个用例运行一次要十几分钟严重拖慢开发节奏。排查与解决Mock所有外部调用确保每个单元测试都完全Mock了网络请求、数据库查询、工具调用。使用pytest-mock或unittest.mock。一个真实的外部API调用可能耗时数百毫秒到数秒而Mock几乎是瞬时的。使用更小的模型在开发和本地测试中将模型从gpt-4切换到gpt-3.5-turbo甚至text-davinci-003如果可用响应速度会快很多成本也大幅下降。并行化测试使用pytest-xdist插件并行运行测试。大多数LLM API客户端是网络I/O密集型可以很好地并行。实施测试缓存如上文所述为测试添加缓存层。首次运行后后续运行将直接读取缓存速度极快。6.3 技能重构后大量测试失败问题你优化了提示词的结构结果跑测试发现一大片红。排查与解决这是正常现象也是TDD的价值所在它告诉你你的修改影响了既有功能。不要慌张。分类处理失败用例误报False Positive技能行为实际上仍是正确的但测试断言太脆弱如检查了具体的措辞。这时需要修改测试的断言使其更健壮转向语义断言。真阳性True Positive技能行为确实被改坏了。你需要分析是提示词的哪部分修改导致了问题并调整提示词。这正是重构环节要做的事。小步快跑重构时尽量做微小的、渐进的修改每改一点就运行一下相关的测试子集而不是全部改完再测。这能帮你快速定位是哪个改动引入了问题。6.4 如何测试复杂的、多轮对话的技能问题有些技能需要维护状态或者涉及多轮工具调用和用户交互。解决方案将对话历史作为上下文的一部分在测试用例的context中完整构建conversation_history列表包含之前的多轮用户消息和助手消息。这样你可以测试技能在对话中任何一点的状态。测试“对话片段”而非完整对话为多轮对话中的每一个关键转折点编写独立的测试。例如测试1用户首次提出复杂请求技能应如何拆解并询问第一个澄清问题。测试2用户回答了第一个问题后技能应调用哪个工具。测试3收到工具结果后技能应如何整合信息并回复或继续追问。使用状态机或流程测试框架对于非常复杂的对话流可以定义状态机并测试在每个状态下给定输入技能是否转移到正确的下一个状态并产生正确的输出。但这属于更高级的集成测试范畴。将TDD引入AI智能体开发初期会感觉有些繁琐需要额外编写测试代码。但一旦习惯它会成为你开发过程中最可靠的伙伴。它迫使你在编写提示词前就思考清楚技能的边界和行为它在你每次修改后给你即时的反馈它让你的智能体技能库随着时间推移越来越稳定而不是越来越脆弱。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2615731.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…