尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

DeepSeek Harness框架源码解析:AI Agent会话管理与执行机制

DeepSeek Harness框架源码解析:AI Agent会话管理与执行机制 在探索AI Agent开发框架时你是否曾困惑于一个Agent的完整生命周期是如何被管理的当对话被打断或系统重启后Agent如何“记住”之前的上下文并继续执行任务近期DeepSeek推出的Harness框架以其清晰的架构和强大的会话管理能力为这些问题提供了优秀的解决方案。本文将深入解析DeepSeek Harness的核心源码重点剖析Agent的运行机制、Turn/Step的执行流程以及关键的Session重建过程。无论你是正在构建自己的AI Agent系统还是希望理解现代Agent框架的设计哲学这篇文章都将为你提供从源码层面出发的深度解读。1. DeepSeek Harness 框架概览与核心概念在深入源码之前我们首先需要理解DeepSeek Harness框架的基本定位和它试图解决的核心问题。Harness是一个用于构建、管理和运行AI Agent的框架它提供了一套标准化的接口和组件让开发者能够专注于Agent的业务逻辑而无需重复处理会话状态管理、工具调用编排、上下文持久化等底层复杂性。1.1 什么是AI Agent框架AI Agent框架可以类比为Web开发中的Spring或Django。在Web领域框架处理HTTP请求解析、路由分发、会话管理、数据库连接池等通用问题开发者只需关注业务控制器和视图。同样在AI Agent领域一个框架需要处理与LLM的交互封装不同模型供应商如DeepSeek、OpenAI、Anthropic的API调用处理流式响应、token计数和费用计算。工具Tools的注册与调用允许Agent扩展能力例如执行代码、查询数据库、调用外部API。框架需要管理工具的发现、参数验证和执行。会话Session与状态管理维护多轮对话Multi-Turn的上下文包括用户消息、AI回复、工具调用结果等。这是实现“记忆”和持续对话能力的基础。执行流程Orchestration的控制决定Agent在接收到用户输入后是直接思考回复还是需要先调用某个工具获取信息亦或是进入一个多步骤的规划循环。DeepSeek Harness正是围绕这些核心关切设计的。从网络热词如“agent框架”、“harness和agent区别”可以看出社区对Agent框架的底层机制抱有浓厚兴趣。1.2 Harness 核心组件解析根据对Harness源码的梳理其核心架构主要围绕以下几个关键抽象构建理解它们是读懂源码的前提Agent框架的核心实体。它封装了LLM模型、可用的工具集、系统提示词System Prompt以及决定其行为逻辑的“运行时”Runtime。一个Agent代表了一个具备特定能力和目标的AI助手。Session会话是Agent运行时的上下文容器。它保存了一次对话或任务执行过程中的完整状态包括历史消息Message、工具调用记录、环境变量以及任何自定义的元数据。Session是持久化的单元允许Agent在服务重启后恢复状态。这直接回应了热词中“session管理”、“session重建”等高频关注点。Turn一轮交互。通常指从接收用户输入或外部事件开始到Agent产生响应可能是文本回复或工具调用结束的一个完整周期。一个Session包含多个Turn。Step步骤是Turn内部的更细粒度单元。在一个复杂的Turn中Agent的思考过程可能被分解为多个Step例如Step 1 - 理解用户意图并规划Step 2 - 调用工具AStep 3 - 解析工具A的结果Step 4 - 根据结果决定调用工具B或生成最终回复。Step机制使得Agent的推理过程变得可观察、可调试和可控制。Runtime运行时代理了Agent的执行逻辑。它定义了从接收输入、处理历史、调用LLM、解析响应可能是工具调用指令到执行工具并生成最终输出的完整工作流。Harness可能提供了不同的Runtime实现例如简单的单次调用Runtime或支持ReActReasoning and Acting等复杂模式的Runtime。Tool工具是Agent能力的扩展。一个Tool通常包含名称、描述、参数模式JSON Schema和一个执行函数。框架负责将Tool的描述注入到给LLM的提示词中并解析LLM输出的工具调用指令转发给对应的Tool执行。在接下来的章节中我们将以“一个用户提问如何引发Agent执行一系列Step最终生成回答并且整个Session能够被保存和重建”为主线串联起这些核心组件的工作机制。2. 环境准备与源码定位为了能够跟随本文进行源码分析你需要准备好相应的环境。本文的分析基于DeepSeek Harness的公开源码例如其在GitHub上的仓库。请注意框架可能处于快速迭代中具体类名和路径请以你查阅的实际版本为准。2.1 获取源码与基础环境克隆仓库访问DeepSeek Harness的官方GitHub仓库可通过“deepseek harness github”等热词搜索找到使用Git克隆到本地。git clone harness-github-repo-url cd deepseek-harness语言与工具链Harness框架很可能是用Python编写的这是当前AI Agent生态的主流语言。确保你的本地环境有Python 3.8版本。建议使用虚拟环境隔离依赖。python -m venv venv source venv/bin/activate # Linux/Mac # 或 venv\Scripts\activate # Windows安装依赖进入项目根目录安装开发依赖。通常框架会提供requirements.txt或pyproject.toml文件。pip install -e . # 以可编辑模式安装方便修改和调试 # 或 pip install -r requirements.txt2.2 关键源码目录结构在开始阅读前先熟悉一下典型的Harness项目结构这能帮助你快速定位我们即将分析的组件deepseek-harness/ ├── src/ │ └── harness/ │ ├── core/ # 核心抽象和基类 │ │ ├── agent.py # Agent基类定义 │ │ ├── session.py # Session基类定义 │ │ ├── turn.py # Turn相关逻辑 │ │ ├── step.py # Step相关逻辑 │ │ └── runtime.py # Runtime接口定义 │ ├── memory/ # 记忆/状态存储后端 │ │ ├── base.py │ │ ├── in_memory.py # 内存存储易失 │ │ └── persistent.py # 持久化存储如数据库 │ ├── tools/ # 工具系统 │ │ ├── base.py │ │ └── registry.py │ └── llm/ # LLM集成层 │ ├── client.py │ └── deepseek.py # DeepSeek模型特定集成 ├── examples/ # 使用示例 └── tests/ # 单元测试我们的分析将主要集中在src/harness/core/目录下的几个核心文件。通过阅读测试用例tests/和示例examples/可以更好地理解框架的使用方式。3. Agent 运行机制深度剖析Agent是Harness框架的起点和中心。创建一个Agent并运行它是整个流程的入口。本节我们将深入agent.py和相关文件理解Agent是如何被组装和驱动的。3.1 Agent 的初始化与配置一个Agent的创建通常需要几个关键要素LLM客户端、工具列表、系统提示词以及一个Runtime。让我们看一个简化的源码示例# 文件路径src/harness/core/agent.py (示意代码非逐行复制) from typing import List, Optional, Dict, Any from .runtime import BaseRuntime from .tools import BaseTool from .llm.client import LLMClient class Agent: def __init__( self, agent_id: str, llm_client: LLMClient, system_prompt: Optional[str] None, tools: Optional[List[BaseTool]] None, runtime: Optional[BaseRuntime] None, metadata: Optional[Dict[str, Any]] None, ): self.agent_id agent_id self.llm_client llm_client self.system_prompt system_prompt self.tools tools or [] self._runtime runtime or self._get_default_runtime() self.metadata metadata or {} # 初始化运行时将agent的配置如工具传递给runtime self._runtime.initialize_for_agent(self) def _get_default_runtime(self) - BaseRuntime: # 返回一个默认的运行时例如简单的单次对话Runtime from .runtime.simple_runtime import SimpleRuntime return SimpleRuntime()关键点解析llm_client这是与DeepSeek API或其他模型API通信的客户端。它封装了认证、请求格式、错误处理和流式响应。Harness通过抽象层支持多种LLM提供商。tools一个BaseTool对象的列表。每个Tool都必须实现execute方法。在初始化时Agent会将这些工具“告知”Runtime。runtime这是Agent的“大脑”或“引擎”。如果用户不指定框架会提供一个默认的Runtime如SimpleRuntime。复杂的Agent可能会使用ReActRuntime或PlanAndExecuteRuntime。Runtime决定了Agent如何处理输入、组织思考步骤Step和调用工具。initialize_for_agent这个方法调用至关重要。它让Runtime知晓当前Agent的配置例如Runtime需要将Tool的描述格式化成LLM能理解的提示词部分。3.2 Agent 执行流程从run方法开始用户与Agent交互的入口通常是agent.run(session_id, input_text)或类似的方法。这个方法触发了整个Turn的生命周期。# 文件路径src/harness/core/agent.py (示意代码) class Agent: # ... __init__ ... async def run( self, session_id: str, user_input: str, **kwargs ) - Dict[str, Any]: 执行一轮Agent交互。 1. 获取或创建Session。 2. 将用户输入添加到Session的历史中。 3. 委托给Runtime执行一个Turn。 4. 保存Session状态。 5. 返回结果。 # 1. 获取Session涉及Session管理下文详述 session await self._get_or_create_session(session_id) # 2. 将用户输入包装成Message加入Session历史 session.add_message(roleuser, contentuser_input) # 3. 核心委托Runtime执行Turn。 # Runtime会处理包括LLM调用、工具执行、Step生成在内的所有逻辑。 turn_result await self._runtime.execute_turn(session) # 4. 将Agent的回复也加入历史 session.add_message(roleassistant, contentturn_result[final_output]) # 5. 持久化Session状态如果使用持久化存储 await session.persist() # 6. 返回结果通常包含最终输出和可能的中间步骤信息 return { session_id: session_id, output: turn_result[final_output], turn_id: turn_result[turn_id], steps: turn_result.get(steps, []), # 包含所有的Step详情 **kwargs } async def _get_or_create_session(self, session_id: str) - Session: # 从Session存储后端内存或数据库加载Session。 # 如果不存在则创建一个新的。 # 这里体现了Session的重建能力。 from .session_manager import SessionManager return await SessionManager.get_instance().get_session(session_id, agent_idself.agent_id)流程总结 Agent的run方法是一个协调者。它不关心具体的推理逻辑而是负责管理Session生命周期获取、更新、保存并将核心的执行任务委托给Runtime。这种设计符合单一职责原则使得Agent本身保持轻量而将复杂的行为逻辑交给可插拔的Runtime。4. Turn 与 Step 执行机制解析Turn和Step是描述Agent工作流程的两个重要时间维度概念。Turn是宏观的一轮交互Step是Turn内部的微观执行步骤。Runtime是它们的导演。4.1 RuntimeTurn 执行的导演我们以一個支持多步推理的ReActRuntime为例来分析execute_turn内部发生了什么。# 文件路径src/harness/core/runtime/react_runtime.py (示意代码) class ReActRuntime(BaseRuntime): def __init__(self, max_steps: int 10): self.max_steps max_steps # 防止无限循环 async def execute_turn(self, session: Session) - Dict[str, Any]: 执行一个ReActReasoning and Acting循环。 每个循环包含一个Step思考(Reasoning) - 行动(Acting) - 观察(Observation)。 turn_id generate_turn_id() steps [] # 记录本Turn的所有Step final_output None # Step 0: 准备初始上下文包括系统提示、历史消息、工具描述 context self._prepare_context(session) for step_index in range(self.max_steps): # Step 1: 推理 (Reasoning) - LLM生成思考过程和下一步动作 step_result await self._reasoning_step(context, step_index) steps.append(step_result) # 检查LLM是否决定结束给出最终答案 if step_result.get(action) final_answer: final_output step_result.get(thought, ) step_result.get(answer, ) break # Step 2: 行动 (Acting) - 执行工具调用 if step_result.get(action) tool_call: tool_name step_result[tool_name] tool_args step_result[tool_args] # 从Agent注册的工具中查找并执行 tool self._find_tool(tool_name) if tool: observation await tool.execute(**tool_args) # 将观察结果添加到上下文中供下一步推理使用 context.append_observation(observation) else: context.append_observation(fError: Tool {tool_name} not found.) # Step 3: 观察 (Observation) 的结果已添加到context循环继续... # 如果达到最大步数强制结束 if step_index self.max_steps - 1: final_output Reached maximum reasoning steps. Unable to complete task. break return { turn_id: turn_id, final_output: final_output, steps: steps # 包含了完整的思考链 } async def _reasoning_step(self, context, step_index) - Dict: 执行单步推理调用LLM解析其响应。 # 构建包含当前上下文历史工具结果的提示词 prompt self._build_react_prompt(context) # 调用LLM llm_response await self.llm_client.generate(prompt) # **关键**解析LLM的响应文本。 # ReAct格式通常要求LLM以特定格式输出如 # Thought: 我需要计算... # Action: calculator # Action Input: {expression: 22} # 或者 # Thought: 我已经得到答案。 # Final Answer: 结果是4。 parsed_result self._parse_llm_response(llm_response) return { step_id: f{context.turn_id}_step_{step_index}, thought: parsed_result.get(thought), action: parsed_result.get(action), # tool_call 或 final_answer tool_name: parsed_result.get(tool_name), tool_args: parsed_result.get(tool_args), answer: parsed_result.get(answer), }核心机制解读循环控制ReActRuntime通过一个for循环实现多步推理并用max_steps防止死循环。Step的生成每一次循环迭代对应一个Step。每个Step的核心是_reasoning_step方法它调用一次LLM。上下文Context的演化context对象随着循环不断增长。它最初包含系统提示和对话历史。在每个Step中如果执行了工具工具的“观察结果”observation会被追加到context中作为下一步LLM推理的新信息。这就是Agent能够根据工具反馈进行后续思考的原因。输出解析_parse_llm_response是连接LLM自由文本输出和框架结构化逻辑的桥梁。它需要从LLM的回复中精确提取出“思考”、“动作类型”、“工具名”、“工具参数”或“最终答案”。这里通常使用正则表达式或要求LLM输出严格的JSON格式。4.2 Step 的数据结构一个Step不仅仅是一个过程它也是一个可以被记录和检查的数据对象。在Harness中Step的信息可能被这样定义# 文件路径src/harness/core/step.py from dataclasses import dataclass, field from typing import Any, Dict, Optional from datetime import datetime dataclass class Step: 表示Agent执行过程中的一个步骤。 step_id: str turn_id: str session_id: str agent_id: str # 步骤内容 thought: Optional[str] None # LLM的思考过程 action_type: str # 如reasoning, tool_call, final_answer tool_name: Optional[str] None tool_arguments: Optional[Dict[str, Any]] None tool_output: Optional[Any] None # 工具执行结果 observation: Optional[str] None # 整合后的观察可能由tool_output转化而来 final_answer: Optional[str] None # 元数据 start_time: datetime field(default_factorydatetime.now) end_time: Optional[datetime] None status: str created # created, running, success, failed error: Optional[str] None def to_dict(self) - Dict[str, Any]: return {field.name: getattr(self, field.name) for field in fields(self)}这个结构化的Step对象会被添加到steps列表中并最终随着Session一起被持久化。这使得开发者可以事后审查Agent的完整推理链对于调试和优化提示词至关重要。这也解释了为什么网络热词中会出现“step 7 v5.7”、“error: an error occurred while performing the step”等与步骤执行相关的搜索。5. Session 管理与重建机制Session管理是Harness框架实现有状态对话和持久化的基石。它直接关系到“session重建”、“meterpreter session closed”等运维和调试场景。5.1 Session 的生命周期与存储Session对象在第一次与某个session_id交互时被创建并在每次Turn结束后被更新和保存。# 文件路径src/harness/core/session.py class Session: def __init__(self, session_id: str, agent_id: str, created_at: datetime None): self.session_id session_id self.agent_id agent_id self.created_at created_at or datetime.now() self.updated_at self.created_at self.messages: List[Message] [] # 对话历史 self.metadata: Dict[str, Any] {} # 自定义元数据 self.steps: List[Step] [] # 本Session所有Turn的所有Step self.turn_counter: int 0 def add_message(self, role: str, content: str): self.messages.append(Message(rolerole, contentcontent, timestampdatetime.now())) self.updated_at datetime.now() def add_step(self, step: Step): self.steps.append(step) self.updated_at datetime.now() async def persist(self): 将会话状态保存到持久化存储。 from .memory import get_memory_backend memory_backend get_memory_backend() await memory_backend.save_session(self)存储后端抽象get_memory_backend()返回一个存储后端实例。Harness框架可能提供了多种实现InMemoryBackend存储在进程内存中。服务重启后数据丢失。适用于开发和测试。PersistentBackend存储到数据库如SQLite、PostgreSQL、Redis或文件系统。这是生产环境必需的特性也是实现Session重建的前提。代码中可能通过环境变量配置选择后端。5.2 Session 的重建过程Session重建发生在Agent的_get_or_create_session方法中。当一个新的请求携带一个已存在的session_id时框架需要从存储后端加载出完整的Session状态让Agent“接着上次的话茬”继续工作。# 文件路径src/harness/core/session_manager.py (示意代码) class SessionManager: _instance None classmethod def get_instance(cls): if cls._instance is None: cls._instance SessionManager() return cls._instance def __init__(self): self.memory_backend get_memory_backend() # 获取配置的存储后端 async def get_session(self, session_id: str, agent_id: str) - Session: # 尝试从存储加载 session_data await self.memory_backend.load_session(session_id, agent_id) if session_data: # **重建Session对象** session self._reconstruct_session(session_data) print(fSession {session_id} loaded from memory backend.) return session else: # 创建新Session new_session Session(session_idsession_id, agent_idagent_id) print(fNew session {session_id} created.) return new_session def _reconstruct_session(self, session_data: Dict) - Session: 将存储的字典数据反序列化为Session对象。 session Session( session_idsession_data[session_id], agent_idsession_data[agent_id], created_atdatetime.fromisoformat(session_data[created_at]) ) session.updated_at datetime.fromisoformat(session_data[updated_at]) session.messages [Message.from_dict(m) for m in session_data[messages]] session.steps [Step.from_dict(s) for s in session_data.get(steps, [])] session.metadata session_data.get(metadata, {}) session.turn_counter session_data.get(turn_counter, 0) return session重建的关键数据完整性存储后端必须保存足够的信息来完全重建Session对象包括完整的messages历史、所有的steps记录以及metadata。反序列化_reconstruct_session方法负责将存储的原始数据通常是JSON或字典转换回框架内的对象实例如Message,Step。这些类需要实现from_dict这样的类方法。上下文恢复重建后的Session其messages列表是完整的。当这个Session被送入Runtime执行新的Turn时Runtime会将这些历史消息作为上下文的一部分发送给LLM从而实现对话的连续性。5.3 应对“Session Died”问题网络热词中出现了“meterpreter session 19 closed. reason: died”这虽然是另一个领域渗透测试的术语但其反映的问题在Agent会话管理中同样存在会话意外终止。在Harness框架的上下文中可能导致Session“死亡”的原因包括服务进程重启使用InMemoryBackend时所有Session丢失。存储后端故障数据库连接断开或文件损坏。会话过期框架可能实现了TTL生存时间机制自动清理旧会话。手动清理运维操作清除了会话数据。Harness的应对策略持久化存储使用PersistentBackend是根本解决方案。确保session.persist()在每次Turn后可靠执行。容错与重试在load_session时如果数据损坏或丢失框架可以捕获异常并选择记录错误、返回空触发新建会话或尝试修复。心跳与保活对于长生命周期的Session例如持续数小时的复杂任务框架可以设计一个“心跳”机制定期更新Session的updated_at时间戳防止被过期清理。明确的关闭原因当Session被主动结束或因为错误结束时框架应该在元数据中记录close_reason便于问题排查而不是简单地删除数据。6. 常见问题与排查思路在实际使用和基于Harness进行二次开发时你可能会遇到一些问题。以下是一些常见问题的排查思路。问题现象可能原因排查步骤与解决方案Agent不调用工具1. 工具未正确注册到Agent/Runtime。2. 工具的描述name/description不清晰LLM无法理解或匹配。3. 系统提示词未明确指示Agent使用工具。4. LLM输出格式解析失败。1. 检查agent.tools列表是否包含目标工具实例。2. 检查工具的描述是否准确说明了其功能和输入参数。简化描述进行测试。3. 在系统提示词中加入“你可以使用以下工具...”的明确指令。4. 打印LLM的原始响应检查_parse_llm_response逻辑是否能正确提取工具调用信息。Session重建后上下文丢失1. 使用的存储后端是内存型服务重启后数据丢失。2.session.persist()方法未被调用或调用失败。3. 存储的数据序列化/反序列化出错关键字段丢失。1. 确认配置了PersistentBackend如数据库。2. 在agent.run()方法中确保await session.persist()被执行且无异常。3. 检查存储后端中保存的Session数据JSON结构是否完整特别是messages和steps字段。Runtime陷入无限循环1.max_steps设置过大或逻辑有误。2. LLM始终无法生成final_answer动作或在工具调用后陷入循环。3. 工具执行结果未能有效帮助LLM推进任务。1. 合理设置max_steps如5-10步并在循环内添加强制中断逻辑。2. 优化提示词明确要求LLM在获得足够信息后必须给出最终答案。3. 检查工具输出格式确保其清晰、简洁能被LLM有效利用。添加调试日志打印每一步的thought和observation。LLM API调用失败或超时1. 网络问题或API密钥错误。2. 请求的token长度超限。3. 流式响应处理不当。1. 检查LLMClient的配置endpoint, api_key。在客户端添加重试和超时机制。2. 在_prepare_context方法中实现历史消息的截断或总结防止上下文过长。3. 确保异步流式响应被正确await和拼接。Step信息记录不全1. Runtime中未将每个Step对象添加到Session的steps列表。2. Step对象序列化时丢失了某些字段。1. 在Runtime的execute_turn方法中确保每个生成的step_result都被构造成Step对象并通过session.add_step()保存。2. 检查Step.to_dict()和Step.from_dict()方法确保所有必要字段都被包含。7. 最佳实践与工程建议基于对Harness源码的分析我们可以提炼出一些在构建生产级Agent系统时的最佳实践。7.1 设计可观测的Agent系统Harness框架内建的Step记录机制为可观测性打下了良好基础。在生产中应进一步结构化日志不仅记录Step还将关键事件Session创建、Turn开始、LLM调用、工具执行、错误以结构化格式JSON输出到日志系统如ELK、Loki。链路追踪为每个用户请求生成唯一的trace_id贯穿整个Session、Turn、Step以及所有下游服务工具调用、数据库查询调用便于问题定位。指标监控收集关键指标如每秒Turn数、平均Step数、工具调用成功率、LLM响应延迟、Token消耗量。这些指标是容量规划和成本控制的基础。7.2 实现健壮的Session持久化后端选型对于生产环境优先选择支持事务、高可用的数据库如PostgreSQL或Redis配合RDB/AOF持久化。利用数据库的TTL功能实现自动会话清理。序列化优化Message和Step对象可能包含复杂嵌套结构。使用高效的序列化库如orjson并考虑对大型tool_output进行压缩或分页存储。定期备份与归档对重要的会话数据如客户服务记录进行定期备份。对于过期会话可以归档到冷存储如对象存储以减轻主数据库压力。7.3 优化Runtime与提示工程自定义RuntimeHarness的Runtime是可插拔的。根据你的业务场景你可能需要定制Runtime。例如一个客服Agent的Runtime可能需要在第一个Step先调用“查询用户订单”的工具。提示词模板化不要将提示词硬编码在Runtime中。将其设计为可配置的模板支持变量注入如工具描述、历史消息。这便于进行A/B测试和迭代优化。工具设计的原子性工具应保持功能单一、接口明确。一个做“天气查询”的工具就不要同时去查“新闻”。原子性的工具更容易被LLM正确调用和组合。7.4 安全与权限考量工具调用沙箱化对于执行代码、访问文件系统或网络请求的工具必须在安全的沙箱环境中运行限制其资源CPU、内存、网络和权限。用户输入验证与清理在将用户输入放入提示词或传递给工具之前进行必要的验证和清理防止提示词注入攻击。Session隔离确保不同用户的Session数据严格隔离防止数据泄露。在load_session时必须验证session_id与当前用户身份的关联性。通过深入剖析DeepSeek Harness的源码我们不仅理解了其Agent、Turn、Step、Session等核心概念的设计与联动更掌握了一套构建可维护、可观测、高可用的AI Agent系统的设计模式。从Agent的组装、Runtime的流程控制到Session的持久化与重建每一个环节都体现了工程化思维。在实际项目中你可以直接应用这些模式也可以借鉴其思想打造更适合自己业务场景的Agent框架。
返回列表