Machine Learning Mastery

AI Agent Observability: Logging, Tracing, and Debugging Explained

6.9内容质量

TL;DR · AI 摘要

AI Agent Observability: Logging, Tracing, and Debugging Explained - MachineLearningMastery.com AI Agent Observability: L...

核心要点

  • 主题聚焦:AI Agent Observability: Logging, Tracing, and De
  • 来源:Machine Learning Mastery,建议结合原文判断细节。
  • AI 分析暂不可用,本条为保底评分与摘要。
#AI#编程#后端#云计算#安全
打开原文

AI Agent 可观测性:日志、追踪与调试解析 - MachineLearningMastery.com

AI Agent 可观测性:日志、追踪与调试解析

作者

Shittu Olumide

于

2026年10月1日

分类

人工智能

0

分享

发布

在本文中,你将了解 AI Agent 可观测性的含义,为什么传统监控工具无法满足智能体系统的观测需求,以及如何为 AI Agent 实现结构化日志、分布式追踪和实用调试流程。

我们将涵盖的主题包括:

  • 为什么 AI Agent 的失败表现看似成功,这种特性使得标准监控工具变得不足。
  • 如何为智能体运行、工具调用和模型推理步骤实现结构化日志和基于 OpenTelemetry 的追踪。
  • 如何解读追踪瀑布图、通过指标跟踪令牌成本,并利用这些数据调试真实智能体故障。

一个处理客户支持工单的智能体关闭了一个工单,给出了干净、专业但完全错误的答案。它调用了一次退款查询工具,几秒后又用稍有不同的参数再次调用该工具,然后基于第二次结果而非第一次结果自信地做出了回答。没有任何崩溃。没有触发任何错误。整个过程的运行时间仪表板始终显示绿色。唯一发现这个问题的原因是客户两天后回复时感到困惑,而到那时已无人能重建该次运行中实际发生的情况。

这种失败正是本文存在的原因。传统服务要么返回 200 错误码,要么抛出可搜索的异常信息。而智能体即使两者都没有,仍可能完全错误,且为第一类系统构建的工具对第二类系统几乎毫无洞察力。本文将逐步讲解需要做出哪些改变——日志、追踪和调试——并附带真实代码示例。

AI Agent 可观测性的真实含义

AI Agent 可观测性是指将智能体执行的每个模型调用、工具执行和推理步骤作为结构化数据进行记录,以便在出现问题时,能够精确重建发生了什么以及原因,而不是猜测或重新运行相同提示并希望问题重现。

它借鉴了可观测性工程师已知的三大支柱——日志、指标和追踪——但之所以需要单独命名并形成独立学科,原因在于智能体实际失败的方式。领域研究者 Aryan Kargwal 在 Digital Applied 2026 年可观测性指南中明确指出:智能体系统以看似成功的方式失败——输出内容错误但格式正确、不必要的工具调用,或语法有效但语义错误的操作。这些情况都不会触发错误处理程序。一个健康检查报告“正常”几乎无法提供任何有用信息来判断智能体在特定运行中是否真正完成了正确操作。

成本和延迟不再与请求数量相关,而是与令牌数量相关。一个“缓慢”的请求可能会消耗正常令牌预算的十倍,而基于每秒请求数的监控系统在结构上对此毫无察觉。多步骤链式调用会加剧这一问题:一个用户请求可能触发多次模型调用、若干工具调用以及几次检索查询,而每个环节都是独立的故障点,单一的聚合错误指标无法区分具体问题所在。更令人担忧的是,提示信息本身经常包含真实个人或机密信息,这意味着将完整提示文本直接记录到后端的简单日志方式,在产生任何调试价值之前就已经造成了真正的合规风险。

两种体系的并列对比让这种转变变得具体:

| 信号 | 传统应用 | LLM/AI代理 | |------|----------|------------| | 延迟驱动因素 | CPU、I/O、网络 | 令牌数量、模型规模、上下文窗口 | | 成本单位 | 每秒请求数 | 消耗的令牌数 | | 故障模式 | 异常、超时 | 幻觉、上下文溢出、工具错误 | | 调试信息 | 堆栈跟踪 | 提示、完成结果以及二者之间的推理链 |

日志记录

从最熟悉的支柱开始,因为它仍然是所有其他内容构建的基础,只是应用方式不同。对于代理系统而言,值得记录的事件非常具体:哪些工具被调用及参数是什么、返回了什么内容、每个步骤消耗了多少令牌、每个环节耗时多久,以及过程中的任何错误——所有信息都应结构化存储,而非以需要人工解析的自由文本形式记录。

真正让代理日志有价值的是将每条日志与特定运行实例关联。凌晨2点时,如果三条不同用户的请求在同一分钟内触发了三个不同的运行实例,仅记录“工具调用失败”的日志几乎毫无价值。通过将当前追踪ID附加到每条日志(OpenTelemetry在设置追踪后会自动完成此操作),才能将一堆零散的日志信息过滤到具体出错的运行实例。

python
import logging
from opentelemetry import trace

# 标准Python日志记录,此处没有使用任何特殊功能
logger = logging.getLogger("agent")
logging.basicConfig(level=logging.INFO)
tracer = trace.get_tracer("agent-service")

def call_tool(tool_name: str, arguments: dict):
    # get_current_span() 会获取当前正在运行的追踪跨度
    # 使下述日志能够关联到具体追踪和步骤
    span = trace.get_current_span()
    trace_id = format(span.get_span_context().trace_id, "032x")
    
    logger.info(
        "tool_call_started",
        extra={
            "trace_id": trace_id,
            "tool_name": tool_name,
            "arguments": arguments,
        },
    )
    
    try:
        result = execute_tool(tool_name, arguments)
        logger.info(
            "tool_call_succeeded",
            extra={"trace_id": trace_id, "tool_name": tool_name, "result_length": len(str(result))},
        )
        return result
    except Exception as e:
        logger.error(
            "tool_call_failed",
            extra={"trace_id": trace_id, "tool_name": tool_name, "error": str(e)},
        )
        raise

get_current_span() 会获取当前正在活动的 span,

因此下面的日志行可以追溯到确切的跟踪

并定位到发生的具体步骤

span

get_current_span

trace_id

format

get_span_context

"032x"

"tool_call_started"

extra

{

"trace_id"

"tool_name"

"arguments"

}

try

result

execute_tool

"tool_call_succeeded"

"result_length"

len

return

except

Exception

as

e

error

"tool_call_failed"

"error"

raise

在上述代码片段中有一些值得注意的细节。trace.get_current_span() 不需要你手动通过每个函数调用传递 trace ID —— 它会读取当前执行上下文中正在活动的 span,这正是使这种模式能够在实际代码库中广泛使用而无需在每一层传递 ID 参数的关键。

记录参数和结果长度(而非完整结果内容)是一个有意为之的选择,而非疏忽;完整的工具输出可能体积较大且包含敏感数据,长度或截取预览通常足以发现问题,而不会让每条日志都成为隐私风险。同时为同一工具调用同时记录开始和结束事件(而非仅记录结果),这使得你后续可以精确测量该特定调用耗时 —— 这将成为下一节中追踪系统正式化处理的原始数据。

追踪

日志记录会告诉你在特定时间点发生了什么。追踪的作用是将这些时间点串联成完整的形状 —— 从第一个请求到最终答案的完整代理运行记录,每个步骤都嵌套在触发它的步骤内部。这种嵌套结构正是回答“为什么代理会这么做”的关键,因为它不仅显示工具被调用的事实,还显示了哪个推理步骤决定调用该工具,以及调用前后发生了什么。

此处的术语来自 OpenTelemetry GenAI 语义约定,该规范为生成式 AI 定义了标准的 gen_ai.* span 类型和属性。为了避免每个团队自行发明 span 名称,规范定义了几个关键的操作类型:create_agent(代理首次定义时)、invoke_agent(单次代理运行)、invoke_workflow(多个代理相互交接的编排)、execute_tool(单个工具调用)、chat(实际模型推理调用本身)。每种类型都携带一组标准属性 —— 包括 gen_ai.request.model、gen_ai.usage.input_tokens、gen_ai.usage.output_tokens 和 gen_ai.response.finish_reasons 等 —— 因此一个团队的代理生成的追踪记录在结构上会与完全不同的框架生成的追踪记录保持一致。

手动为小型工具调用代理添加追踪的实际实现如下:

from opentelemetry import trace from opentelemetry.trace import Status, StatusCode tracer = trace.get_tracer("agent-service")

def run_agent(task: str) -> str:

整个运行的根Span;所有后续步骤都嵌套在此Span内

这会生成父-子层级结构

with tracer.start_as_current_span("invoke_agent") as agent_span: agent_span.set_attributes({ "gen_ai.system": "openai", "agent.name": "support-agent", "gen_ai.request.model": "gpt-4o", })

messages = [ {"role": "system", "content": "You are a support assistant."}, {"role": "user", "content": task}, ]

while True:

模型调用本身会生成自己的子Span

with tracer.start_as_current_span("chat") as chat_span: response = model_client.chat.completions.create( model="gpt-4o", messages=messages, tools=AVAILABLE_TOOLS ) choice = response.choices[0]

chat_span.set_attributes({ "gen_ai.response.model": response.model, "gen_ai.usage.input_tokens": response.usage.prompt_tokens, "gen_ai.usage.output_tokens": response.usage.completion_tokens, })

if choice.finish_reason != "tool_calls": agent_span.set_status(Status(StatusCode.OK)) return choice.message.content

每个工具调用都会生成自己的子Span,嵌套在代理运行下,

而不是chat Span下,因为工具调用是并列步骤,

而不是推理的子步骤

for tool_call in choice.message.tool_calls: with tracer.start_as_current_span("execute_tool") as tool_span: tool_span.set_attributes({ "gen_ai.tool.name": tool_call.function.name, "gen_ai.tool.call.id": tool_call.id, })

try: result = call_tool(tool_call.function.name, tool_call.function.arguments) except Exception as e: tool_span.record_exception(e) tool_span.set_status(Status(StatusCode.ERROR, str(e))) raise

messages.append({ "role": "tool", "content": str(result), "tool_call_id": tool_call.id, })

嵌套结构才是这里的关键所在。每个聊天跨度和每个执行工具跨度都在属于其上方运行的with tracer.start_as_current_span(…)代码块内打开,这正是OpenTelemetry用来自动构建父子关系的方式——你永远不需要手动设置“这个跨度属于那个跨度”,这种父子关系是通过代码中with块的结构隐式实现的。在工具跨度上使用record_exception配合set_status(StatusCode.ERROR, …)可以让失败的工具调用在追踪查看器中清晰可见,而不是悄无声息地消失在返回的字符串中,这对本文后面调试部分至关重要。而将令牌使用属性明确分配到聊天跨度而非顶层的invoke_agent跨度上,使得追踪查看器后续可以展示单次运行中每个模型调用的令牌成本细分,而不仅仅是整体的总和。

![可观测性仪表板中的追踪瀑布视图截图(点击放大)](image-link) 作者图片

链式可视化:解读追踪瀑布图

上一节的跨度列表作为原始数据意义不大。真正让它们变得有用的是瀑布视图——按嵌套深度堆叠的跨度,横向拉伸以反映每个跨度的持续时间,这样整个代理运行过程就能在几秒钟内以一张图片呈现。在接触真实仪表板之前,先学会用纯文本解读这种结构很有价值,因为无论哪种形式,其结构都是一致的:

code
invoke_agent (1850ms)
├── chat (920ms) ← 决定调用两个工具
│   ├── execute_tool: refund_lookup (310ms)
│   └── execute_tool: refund_lookup (295ms) ← 同一工具再次调用
└── chat (530ms) ← 最终答案
code
invoke_agent
1850ms
├──
920ms
←
决定
调用
两个
│
refund_lookup
310ms
└──
295ms
再次
调用
同一
工具
530ms
最终
答案

仅凭这个瀑布图就能传达超过一小时猜测才能获得的信息。两个refund_lookup调用作为同一聊天跨度下的兄弟节点,正是本文开头所提到导致失败的冗余工具调用类型,而且这种问题只需一瞥就能发现,而不是埋藏在日志墙中。在真实仪表板中,这种结构会以水平条形图形式呈现,值得培养的观察习惯很简单:寻找相对于兄弟节点异常宽的条形,因为那里才是时间和令牌预算实际消耗的位置;寻找本不该重复的工具调用;以及寻找模型在任务明显不需要工具时仍主动调用工具的聊天跨度。所有这些都不需要阅读任何日志行。这些信息都直接体现在追踪本身的结构中。

令牌追踪与成本指标

追踪非常适合深入理解单次运行的细节。但要发现数千次运行中的趋势,就需要用到指标。根据OpenTelemetry GenAI规范,代理系统真正需要关注的两个核心指标是gen_ai.client.token.usage和gen_ai.client.operation.duration,分别以计数器和直方图的形式进行跟踪。

from opentelemetry import metrics import time meter = metrics.get_meter("agent-service") token_counter = meter.create_counter( "gen_ai.client.token.usage", unit="token", description="Tokens consumed, broken down by model and input/output", ) duration_histogram = meter.create_histogram( "gen_ai.client.operation.duration", unit="s", description="Duration of each model call, in seconds", ) def tracked_chat_call(messages: list, model: str = "gpt-4o") -> str: attrs = {"gen_ai.system": "openai", "gen_ai.request.model": model} start = time.time() try: response = model_client.chat.completions.create(model=model, messages=messages) # Recording input and output tokens as two separate calls, # not one combined total, is what preserves the actual # cost structure, since input and output tokens are # priced differently on nearly every provider token_counter.add(response.usage.prompt_tokens, {attrs, "gen_ai.token.type": "input"}) token_counter.add(response.usage.completion_tokens, {attrs, "gen_ai.token.type": "output"}) return response.choices[0].message.content finally: duration_histogram.record(time.time() - start, attrs)

指标

时间

计量器

get_meter

token_counter

create_counter

"gen_ai.client.token.usage"

单位

"token"

描述

"按模型和输入/输出细分的消耗令牌数"

duration_histogram

create_histogram

"gen_ai.client.operation.duration"

"秒"

"每次模型调用持续时间(以秒为单位)"

tracked_chat_call

列表

attrs

start

将输入和输出令牌作为两次独立调用记录,

而不是合并为一个总数,这样可以保留实际

成本结构,因为几乎所有提供商对输入和输出令牌

的定价方式不同

add

*

"gen_ai.token.type"

"输入"

"输出"

finally

record

-

将输入和输出令牌作为两次独立计数器调用记录,而不是合并为一个总令牌数,这个选择的影响比看起来更重要。单个合并计数器会隐藏你真正需要关注的信息——例如,系统提示词可能悄然增长到如此规模,以至于在每次调用中都远远超过了实际用户输入。只有将它们分开记录,才能在仪表板上清晰可见,而不是被平均值掩盖。

当这两个指标开始流动时,根据 Uptrace 的建议,一组简单的告警条件可以覆盖生产环境中大部分实际发生的问题:

指标

告警条件

重要性

令牌使用率

10 分钟内超过基线两倍

通常表示出现失控循环或提示注入尝试

操作持续时间,p99

超过 30 秒

模型过载或上下文窗口过大

错误率

5 分钟内超过 2%

限流或配额耗尽,需在用户察觉前捕获

输入与输出令牌比例

持续高于 10:1

系统提示词可能已臃肿,需要精简

监控管道:从代码到后端的追踪

到目前为止的所有内容都假设追踪和指标会被发送到某个有用的地方,而这一基础设施需要专门关注,而不是作为后续考虑。在典型设置中,你的应用程序会将遥测数据导出到 OpenTelemetry Collector——一个独立进程,它接收数据,可选地进行转换或过滤,然后将其转发到你实际存储和查看追踪的地方。这个中间层处理了两个实际问题,而无需修改一行应用程序代码。

  • 第一个考虑因素是采样。在开发阶段对所有追踪进行全量捕获是合理的,但由于LLM调用速度较慢且跨度较大,生产环境中的全量捕获会迅速变得昂贵,且无法带来成比例的价值。实际采用的模式是根据具体情况采用不同的采样策略:开发阶段进行全量捕获,生产环境中常规成功调用通常采样5%到10%,而对于真正有价值的内容(如每个错误、每个高token请求和每个完整的代理运行)则进行100%捕获,因为这些正是您需要回溯分析的关键场景。
  • 第二个考虑因素是隐私保护,这应作为首要设计决策来处理,而不是后期添加的补丁。提示和完成内容应记录在span事件中而非span属性中,因为属性始终会被索引并导出且无大小限制,而事件可以在Collector层级进行过滤、截断或完全丢弃。通过配置Collector,可以在数据到达存储后端之前,从所有经过流水线的span中剥离或哈希提示内容,这意味着合规要求无需在应用中每个被监控的调用点进行代码修改。

processors: transform: trace_statements: - context: spanevent statements: # 从所有经过Collector的span事件中剥离提示和完成内容,无需任何应用代码修改 - delete_matching_keys(attributes, "gen_ai.prompt.content") - delete_matching_keys(attributes, "gen_ai.completion.content")

processors

transform

trace_statements

context

spanevent

statements

从所有经过Collector的span事件中剥离提示和完成内容,无需任何应用代码修改

delete_matching_keys

attributes

"gen_ai.prompt.content"

"gen_ai.completion.content"

MCP追踪

这是一个值得特别关注的最新补充功能,因为它填补了现有大多数相关主题覆盖内容尚未触及的空白。在OpenTelemetry的Model Context Protocol语义约定(规范版本1.39新增)出现之前,MCP工具调用背后的协议机制(包括调用了哪个方法、属于哪个会话以及使用了哪个协议版本)在追踪中实际上是不可见的。您可以看到工具执行了什么以及返回了什么,但无法看到更底层的协议信息。

需要理解的关键点在于这些新属性是如何附加的。MCP监控不是为协议层创建第二个独立的span,而是通过向现有的execute_tool span添加mcp.method.name、mcp.session.id和mcp.protocol.version等属性,将额外的细节叠加到已有span上,而不是使追踪的噪声翻倍。对于需要调用多个MCP服务器的代理开发人员来说,这决定了追踪是保持可读性还是变成大量近似重复span的墙。

调试工作流程

这是所有前期工作真正发挥作用的地方。一旦实现追踪功能,实际的调试流程通常会呈现出相似的模式,无论其背后的平台是什么:从用户报告的异常输出开始,提取生成该输出的追踪ID,打开瀑布流视图,扫描实际出错的跨度(span)——一个意外宽泛的聊天跨度,重复的 execute_tool 调用,或树状结构中的错误状态。找到具体步骤后,该跨度的属性和事件会明确告知你传递了哪些参数以及返回了什么内容,通常足以直接理解错误所在,无需重新运行或猜测。

随着实践的深入,还有两种更高级的技术值得关注。重放(Replay),有时被称为时间旅行调试(time-travel debugging),允许你以精确到时间点的方式重新运行代理会话——本质上是恢复代理在特定步骤的确切状态并从中继续执行,这也是 AgentOps 的标志性能力。另一个值得关注的新趋势是自然语言追踪查询(natural-language trace querying),工程师无需手动扫描瀑布流,而是可以直接向平台提问,例如“为什么代理会进入这个循环”,平台会通过分析追踪数据本身生成答案——这也是 LangSmith 产品内置的直接能力。这些技术不会取代上述基础方法,但正是这些基础方法让团队在积累足够追踪数据后,能够提出这类问题并实现这些高级功能。

2026 年顶尖团队实际使用的工具

如本文所示,通过原始 OpenTelemetry 手动构建这套系统是可行的,且能保持对供应商的中立性,但大多数团队最终会选择一个平台来存储、可视化和查询生成的追踪数据。根据 Digital Applied 2026 年的分析,这一决策首先取决于部署模式,而非功能特性,因为这一选择本身就会排除掉大部分选项。

  • 自托管平台(如 Langfuse 和 Arize Phoenix)适合有真实数据驻留要求或需要大规模精细成本控制的团队,代价是需要自行承担运维开销。
  • 托管 SDK(如 LangSmith 和 Braintrust)则以速度换取所有权:你只需添加一个 SDK,供应商负责后端和存储,通常从第一天起就内置评估工具。
  • 代理网关(如 Helicone)则通过一个路由层将流量置于网关后,几乎无需代码修改即可跨数百个模型追踪成本和使用情况,代价是网关本身会成为需要规划真实可用性的单点故障。

| 平台 | 部署模式 | 免费层级 | OpenTelemetry 支持 | 2025 至 2026 信号 | |--------------|----------------|--------------------|---------------------|-------------------------------------------| | Langfuse | 自托管或云 | 免费自托管 | 是 | 2026 年 1 月被 ClickHouse 收购 | | Arize Phoenix| 自托管 | 开源免费 | 是(原生 OTLP) | 活跃增长的开源项目 | | LangSmith | 托管 SDK | 每月 5,000 条追踪 | 内置自然语言追踪查询 | | | Braintrust | 托管 SDK | 每月 100 万跨度 | $80M B 轮融资(2026 年 2 月) | | | Helicone | 代理网关 | 每月 10,000 次请求 | 通过网关 | 跨 300+ 模型的成本追踪 | | AgentOps | SDK | 以时间旅行回放调试著称 | | | | Datadog LLM Observability | 托管,扩展现有 APM | 每月 40,000 个 LLM 跨度 | 仅计费 LLM 跨度,不计工具或检索跨度 | |

综合应用

以下是将所有上述内容整合成一个完整脚本的方式,使本文介绍的概念能够实际应用。这将追踪、令牌指标和结构化日志围绕一个小型代理进行整合。

import logging import time from opentelemetry import trace, metrics from opentelemetry.trace import Status, StatusCode tracer = trace.get_tracer("agent-service") meter = metrics.get_meter("agent-service") logger = logging.getLogger("agent") token_counter = meter.create_counter("gen_ai.client.token.usage", unit="token") duration_histogram = meter.create_histogram("gen_ai.client.operation.duration", unit="s") def run_instrumented_agent(task: str) -> str: with tracer.start_as_current_span("invoke_agent") as agent_span: trace_id = format(agent_span.get_span_context().trace_id, "032x") agent_span.set_attributes({"agent.name": "support-agent", "gen_ai.request.model": "gpt-4o"}) logger.info("agent_run_started", extra={"trace_id": trace_id, "task": task[:100]}) messages = [{"role": "user", "content": task}] while True: with tracer.start_as_current_span("chat") as chat_span: start = time.time() response = model_client.chat.completions.create( model="gpt-4o", messages=messages, tools=AVAILABLE_TOOLS ) duration_histogram.record(time.time() - start, {"gen_ai.request.model": "gpt-4o"}) usage = response.usage token_counter.add(usage.prompt_tokens, {"gen_ai.token.type": "input"}) token_counter.add(usage.completion_tokens, {"gen_ai.token.type": "output"}) chat_span.set_attributes({ "gen_ai.usage.input_tokens": usage.prompt_tokens, "gen_ai.usage.output_tokens": usage.completion_tokens, }) choice = response.choices[0] if choice.finish_reason != "tool_calls": agent_span.set_status(Status(StatusCode.OK)) logger.info("agent_run_completed", extra={"trace_id": trace_id}) return choice.message.content for tool_call in choice.message.tool_calls: with tracer.start_as_current_span("execute_tool") as tool_span: tool_span.set_attribute("gen_ai.tool.name", tool_call.function.name) logger.info( "tool_call_started", extra={"trace_id": trace_id, "tool_name": tool_call.function.name}, ) try: result = call_tool(tool_call.function.name, tool_call.function.arguments) except Exception as e: tool_span.record_exception(e) tool_span.set_status(Status(StatusCode.ERROR, str(e))) logger.error( "tool_call_failed", extra={"trace_id": trace_id, "tool_name": tool_call.function.name}, ) raise messages.append({"role": "tool", "content": str(result), "tool_call_id": tool_call.id})

58

59

60

61

run_instrumented_agent

"agent_run_started"

"task"

100

"agent_run_completed"

set_attribute

使用配置为将数据导出到上述表格中选择的后端的Collector运行此脚本,单次调用run_instrumented_agent将生成完整的追踪记录,包含每个工具调用的嵌套跨度、每个模型调用记录的令牌指标,以及携带追踪ID的结构化日志行,这些日志行将所有内容重新关联在一起——这正是本文一直构建的完整设置。

结论

未监控的代理所带来的风险比未监控的网络服务更大,原因在于其故障在被仔细检查前往往表现得看似正常。退款查询被调用了两次,基于过时数据得出的自信回答——这些现象本身并不会触发任何警报。日志记录能告诉你每一步发生了什么,追踪能展示这些步骤之间的真实关联,而调试则是将这些记录转化为明确答案而非猜测的关键。在代理开始处理真正重要的任务之前,就应构建日志记录、追踪和调试这三个能力,而不是等到第一位客户发现问题时才匆忙补救。

更多相关内容

  • Python中的日志记录
  • 可观测性工具:构建可靠AI应用
  • PyTorch机器学习模型调试:从...
  • Python调试工具指南
  • AI代理技术栈详解
  • 三层次解析AI代理的记忆机制

/.entry /think