当前位置: 首页 > article >正文

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

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智能体开发初期会感觉有些繁琐需要额外编写测试代码。但一旦习惯它会成为你开发过程中最可靠的伙伴。它迫使你在编写提示词前就思考清楚技能的边界和行为它在你每次修改后给你即时的反馈它让你的智能体技能库随着时间推移越来越稳定而不是越来越脆弱。

相关文章:

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

1. 项目概述:当AI智能体遇上测试驱动开发最近在GitHub上看到一个挺有意思的项目,叫agent-skill-tdd。光看名字,就能嗅到一股“新老结合”的味道——一边是当下火热的AI智能体(Agent),另一边是软件工程领域经…...

Sora 2 + Premiere = 新一代“AI剪辑OS”?深度拆解其MediaCore架构、Timeline Graph API及动态权重调度算法

更多请点击: https://intelliparadigm.com 第一章:Sora 2 Premiere 新一代“AI剪辑OS”?概念演进与范式重构 传统视频编辑正经历一场静默但深刻的底层迁移——当 Sora 2 的原生时空建模能力与 Adobe Premiere Pro 的专业时间线引擎深度耦合…...

5000+明日方舟游戏素材库:解锁二次创作与游戏开发的完整资源解决方案

5000明日方舟游戏素材库:解锁二次创作与游戏开发的完整资源解决方案 【免费下载链接】ArknightsGameResource 明日方舟客户端素材 项目地址: https://gitcode.com/gh_mirrors/ar/ArknightsGameResource 您在二次创作时是否曾为素材不全而烦恼?开发…...

Allegro中Route Keepout、Design Outline和Cutout到底怎么用?一张图讲清PCB布局中的‘禁区’设置

Allegro中三大边界工具实战指南:Route Keepout、Design Outline与Cutout的精准运用 在PCB设计领域,边界定义如同城市规划中的红线,既决定了板卡的物理形态,又影响着电气性能的发挥。Cadence Allegro作为行业标准工具,提…...

5分钟完整指南:Sabaki围棋软件打造专业级对弈环境

5分钟完整指南:Sabaki围棋软件打造专业级对弈环境 【免费下载链接】Sabaki An elegant Go board and SGF editor for a more civilized age. 项目地址: https://gitcode.com/gh_mirrors/sa/Sabaki Sabaki是一款优雅的围棋棋盘和SGF编辑器,专为追求…...

AES换成SM4就够了吗?国密算法迁移踩坑实录,附SM4/SM2完整代码和等保自查清单

等保2.0测评中"仍在使用国际算法"是最常见的扣分项之一。但把AES换成SM4就真的合规了吗?密钥管理怎么办?签名算法怎么选?本文从等保条款出发,梳理国密算法完整迁移路径,提供可直接使用的 SM4/SM2 Java代码和…...

使用 Taotoken 后 API 调用延迟与稳定性体验分享

🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 使用 Taotoken 后 API 调用延迟与稳定性体验分享 作为一名日常需要频繁调用大模型 API 的开发者,服务的稳定性和响应速…...

基于CircuitPython与BLE的智能振动腕带:从硬件选型到代码实现

1. 项目概述:打造你的智能触觉腕上伴侣如果你和我一样,经常被淹没在手机通知的海洋里,或者在专注工作时完全忘记了时间,那么这个项目可能就是为你量身定做的。今天,我们来动手制作一个基于CircuitPython和蓝牙低功耗&a…...

昇腾平台上的异构编程

昇腾平台作为国产化 AI 算力核心底座,采用CPUNPU 异构计算架构,异构编程是充分释放昇腾硬件算力、实现高性能计算与 AI 推理融合加速的关键技术。异构编程指在同一计算任务中,协同调度通用处理器(CPU)与神经网络处理器…...

SIFT和ORB到底怎么选?图像配准实战对比,看完这篇你就懂了

SIFT与ORB图像配准实战指南:如何根据项目需求选择最佳算法 在计算机视觉领域,图像配准是许多应用的基础环节,从医疗影像分析到增强现实,从卫星图像处理到工业检测,都离不开高效准确的特征匹配技术。当开发者面对SIFT和…...

Matlab控制建模实战:从开环到闭环的传递函数构建

1. 从零开始认识传递函数 第一次接触控制系统的朋友可能会被"传递函数"这个概念吓到,但其实它就像是我们日常生活中的"快递单号"。想象一下,你在网上购物时,商家把货物(输入信号)交给快递公司&…...

网络安全新态势与应对策略

网络安全新态势与应对策略 在数字化浪潮席卷全球的今天,网络空间已成为国家竞争的新战场、经济发展的新引擎和社会生活的新空间。然而,伴随技术飞速发展的,是日益严峻和复杂的网络安全挑战。传统的边界防御模式在AI驱动的自动化攻击、无孔不…...

3分钟掌握百度网盘提取码智能查询:高效资源获取的终极完整指南

3分钟掌握百度网盘提取码智能查询:高效资源获取的终极完整指南 【免费下载链接】baidupankey 项目地址: https://gitcode.com/gh_mirrors/ba/baidupankey 你是否曾经遇到过这样的场景:朋友分享了一个百度网盘链接,却忘记了提供提取码…...

Wwise音频文件处理终极指南:3步完成游戏音效解包与替换

Wwise音频文件处理终极指南:3步完成游戏音效解包与替换 【免费下载链接】wwiseutil Tools for unpacking and modifying Wwise SoundBank and File Package files. 项目地址: https://gitcode.com/gh_mirrors/ww/wwiseutil 还在为游戏音频文件无法编辑而烦恼…...

3PEAK思瑞浦 TPA1811-S5TR SOT23-5 精密运放

特性 供电电压:4伏至30伏 低功耗:在25C时为55A(典型值) 低偏置电压:8V在25C(最大值) 零漂:0.01V/C 轨到轨输出 增益带宽积:500kHz 斜率:0.3V/us...

在嵌入式c项目中集成大模型能力taotoken的稳定api调用方案

🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 在嵌入式C项目中集成大模型能力:基于Taotoken的稳定API调用方案 应用场景类,针对嵌入式或资源受限的C语言开…...

ENVI处理SPOT影像避坑指南:波段选错、阈值设偏?手把手教你精准提取城市地物

ENVI处理SPOT影像避坑指南:波段选错、阈值设偏?手把手教你精准提取城市地物 城市地物精准提取是遥感应用中的基础性难题。当面对SPOT系列卫星影像时,许多用户会发现:明明按照标准流程操作,提取结果却总出现水体与阴影混…...

SAP ECC老司机避坑指南:FAGLGVTR和F.07年结操作,这5个细节不注意就白干了

SAP ECC年结实战:FAGLGVTR与F.07操作中的5个致命陷阱 每到年末,财务部门的紧张气氛总是格外浓厚。对于使用SAP ECC系统的企业来说,年结操作就像一场没有彩排的现场演出——任何一个小失误都可能导致数据混乱、报表错误,甚至影响整…...

Compose-Skill:为Jetpack Compose应用注入AI能力的组件化技能库

1. 项目概述:一个为Compose应用注入AI能力的技能库最近在折腾Jetpack Compose项目时,我一直在想,能不能让UI开发也“智能”一点?比如,用户输入一段模糊的描述,界面就能自动生成对应的组件布局;或…...

智能体化提示工程:从静态指令到动态协作的AI应用范式

1. 项目概述:从“提示词”到“智能体”的范式跃迁如果你和我一样,在过去一两年里深度使用过各类大语言模型,那你一定经历过这样的场景:为了完成一个稍微复杂的任务,比如写一份市场分析报告,你需要反复和模型…...

环境配置与基础教程:保姆级教程:在 Mac M 芯片上利用 MPS 加速 YOLO 训练与推理的完整环境搭建

写在前面:为什么你的 Mac 也能跑深度学习? 几年前,如果有人告诉你用 MacBook 训练深度学习模型,你大概会笑出声。那时候 Mac 上的 PyTorch 只能依赖 CPU 吭哧吭哧地算,训练一个小模型都要等到天荒地老。但自从 Apple Silicon 芯片(M1、M2、M3、M4,以及最新的 M5)横空出…...

Haystack框架实战:从零构建企业级智能问答系统

1. 项目概述:一个为构建智能搜索与问答系统而生的框架如果你正在为海量文档构建一个能“理解”问题并“找到”答案的智能系统,比如一个公司内部的知识库助手,或者一个能检索技术文档并给出精准回复的客服机器人,那么你很可能已经听…...

别再只会用StegSolve了!深入理解LSB隐写原理,手写Python脚本提取隐藏信息

从像素到秘密:手写Python脚本破解LSB隐写的核心技术 当你面对一张看似普通的图片,是否曾想过它可能隐藏着重要信息?在CTF竞赛和数字取证领域,LSB(最低有效位)隐写术是最基础却最常被忽视的技术之一。大多数…...

环境配置与基础教程:高效数据加载黑科技:替代默认 DataLoader,使用 NVIDIA DALI 加速 CPU 到 GPU 数据搬运

一、开篇:你的GPU真的在偷懒吗? 如果你是一位深度学习工程师,这个场景一定不陌生:你花重金租了一台搭载H100或A100的服务器,batch size拉满,模型架构精心调优,但打开nvidia-smi一看——GPU利用率只有20%-30%,大部分时间都在空转。CPU使用率却已经飙到100%,风扇呼呼作…...

攻克:C8051Fxxx uVision驱动在Keil Debug中“隐身”的安装与配置实战

1. 问题背景与现象分析 第一次接触C8051Fxxx系列芯片的开发者,大概率会在Keil uVision环境中遇到一个让人抓狂的问题:明明按照官方文档安装了驱动,但在Debug选项列表中死活找不到"C8051Fxxx uVision"这个关键驱动。我当年接手公司一…...

电源管理芯片瞬态测试技术解析与优化方案

1. 电源管理芯片瞬态测试的重要性与挑战在当今移动通信和便携式电子设备中,处理器工作频率已进入GHz时代,这对电源管理系统提出了前所未有的严苛要求。作为电源管理系统的核心组件,低压差线性稳压器(LDO)和降压型开关稳压器(Buck Regulator)的…...

告别NuWriter!手把手教你用命令行打包新唐NUC980 SPI NAND完整系统镜像

新唐NUC980 SPI NAND量产化镜像构建实战指南 在嵌入式设备量产过程中,传统烧录方式往往成为效率瓶颈。当面对新唐NUC980这类基于SPI NAND的工控设备时,产线工程师常需要反复切换工具链、分步烧录不同组件,不仅耗时费力,还容易因人…...

通过审计日志追溯APIKey使用情况保障安全

🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 通过审计日志追溯APIKey使用情况保障安全 效果展示类,从安全管理角度出发,说明如何在Taotoken控制台查看AP…...

基于LangChain与本地LLM构建私有化知识库问答系统实践

1. 项目概述:从零构建一个垂直领域的知识库与问答系统最近在整理个人技术资料时,我遇到了一个非常典型的问题:手头积累了大量来自不同渠道的电子书、技术文档、知乎专栏文章以及各种开源项目的README,内容虽然优质,但过…...

Arm架构在中国市场的机遇、挑战与实战指南

1. 项目概述:Arm架构的“中国故事”与我的观察最近几年,在技术圈和投资圈里,“Arm架构”和“中国市场”这两个词的组合热度一直居高不下。作为一名长期关注处理器架构和产业生态的从业者,我几乎每周都能在行业交流、客户会议甚至供…...