基于MCP协议构建AI决策谱系可观测性:从链路追踪到安全审计
2026/5/27 4:31:58 网站建设 项目流程

1. 项目概述:当安全遇上可观测性

最近在做一个挺有意思的项目,客户那边有个挺头疼的问题:他们的AI应用,特别是那些基于大语言模型(LLM)的智能体,在调用外部工具和数据源时,安全审计和问题追溯变得异常困难。比如,一个智能客服在处理用户退款请求时,它可能调用了订单查询接口、支付网关接口,甚至触发了内部审批流程。如果最终决策出了问题,比如错误地批准了不该退的款,你很难说清楚到底是哪个环节的判断逻辑出了岔子,是模型本身“想”错了,还是它从某个接口拿到的数据本身就是错的,又或者是某个工具执行时权限配置不当?传统的日志监控,面对这种由AI驱动的、动态调用链构成的决策过程,就像隔着一层毛玻璃看东西,模糊不清。

这就是“MCP Security in Action: Decision-Lineage Observability”这个项目要啃的硬骨头。MCP,也就是Model Context Protocol,你可以把它理解成一套让LLM应用安全、标准化地连接和使用外部工具与数据的“交通规则”和“通信协议”。而“Decision-Lineage Observability”,我把它翻译为“决策谱系可观测性”,听起来有点学术,但核心目标很直接:不仅要看到AI应用“做了什么”(比如调用了哪个API,返回了什么),更要能完整、清晰地追溯它“为什么这么做”的整个决策逻辑链条。这就像给AI的每一次思考和行动都装上了一个高精度的“黑匣子”和“行车记录仪”,不仅能记录最终结果,还能完整复现导致这个结果的每一个判断依据、数据来源和工具调用。

这个项目不是简单的日志聚合,它关乎在AI原生时代,我们如何为那些越来越自主、越来越复杂的智能应用构建可信赖的“安全底座”。对于风控、金融、医疗、法律等对决策可解释性和合规性要求极高的领域,这种能力几乎是刚需。接下来,我会拆解我们是如何设计并实现这套系统的,从核心思路到技术选型,再到实操中的坑与技巧,希望能给正在探索类似方向的同行一些参考。

2. 核心思路与架构设计

2.1 为什么是“谱系”而不仅仅是“链路”?

在分布式系统监控里,我们熟悉“调用链”(Trace)的概念,它追踪一个请求流经了哪些服务。但在AI智能体的决策场景下,仅仅知道“流经了哪里”远远不够。一个决策的生成,往往是多轮思考(Chain-of-Thought)、多次工具调用、多次数据查询交织的结果,其内在逻辑是树状或图状的,而非简单的线性链条。

决策谱系(Decision Lineage)这个概念,强调的正是这种因果关联与上下文继承关系。它需要记录:

  1. 决策触发点:用户的原始输入(Prompt)是什么?会话的上下文(Context)包含了哪些历史信息?
  2. 思维过程:模型在生成最终答复或采取行动前,内部产生了哪些“思考”(如果模型支持输出中间推理步骤)?这些思考步骤如何影响了后续的工具调用选择?
  3. 工具调用谱系:调用了哪个工具(Tool)或数据源?调用时的具体参数是什么?这个调用是基于前一步的哪个思考或哪个数据结果触发的?
  4. 数据血缘:工具返回的结果数据是什么?这些结果数据又如何被整合、加工,并作为输入影响了模型的下一次思考或下一个工具调用?
  5. 最终决策与依据:模型的最终输出是什么?这个输出是基于谱系中的哪些节点(思考步骤、工具结果)综合得出的?

因此,我们的架构设计必须能捕获并关联这些异构的、具有复杂依赖关系的事件数据。我们放弃了改造现有APM(应用性能监控)产品的想法,因为它们的模型更多是为RPC服务调用设计的,难以承载丰富的语义信息(如Prompt内容、工具函数描述、返回数据片段)。

2.2 基于MCP协议的标准化采集

MCP协议在这里起到了关键作用。它定义了LLM应用与工具(或数据源,统称Server)之间标准的发现、调用和通信格式。我们的可观测性Agent(一个轻量的SDK或Sidecar)会嵌入到MCP Client(即LLM应用)和MCP Server(工具端)中。

采集点设计:

  • 在MCP Client侧(LLM应用):Agent捕获每一次listTools(列举可用工具)、callTool(调用工具)的请求。关键是要在请求中注入一个全局唯一的trace_id,并将本次调用的上下文(如当前的会话ID、用户ID、上游的思考步骤ID)关联起来。同时,需要记录触发此次工具调用的模型思考内容(如果可用)。
  • 在MCP Server侧(工具服务):Agent捕获工具执行的开始、结束事件,记录输入参数、执行结果(可能需脱敏)、执行耗时、错误信息。同样,它需要识别并继承来自Client的trace_id和上下文信息。
  • 在LLM推理框架层:如果使用LangChain、LlamaIndex或自定义的Agent循环框架,我们需要在关键的执行节点(如AgentExecutor的每一步)埋点,记录模型生成的中间推理文本(如果开启了相关设置)、工具调用的选择逻辑等。

一个核心挑战是上下文传递trace_id可以通过MCP协议的请求头(Header)进行传递。但对于更复杂的谱系关系(比如“工具B的调用依赖于工具A的结果”),我们需要在Client侧的Agent中维护一个轻量的内存图谱,在本地记录这些依赖关系,随后一并上报。

2.3 存储与查询架构选型

采集到的数据是半结构化的,包含大量文本(Prompt、结果)、嵌套的JSON(参数、工具定义)和关系数据(依赖谱系)。这对存储系统提出了要求:

  1. 高吞吐写入:AI应用可能产生大量事件。
  2. 灵活的模式:不同工具、不同框架上报的数据结构可能不同。
  3. 强大的关联查询能力:需要能轻松地通过trace_idsession_id等字段关联查询所有相关事件,并能进行一定的图遍历查询(例如,“找出导致最终决策D的所有上游工具调用”)。

我们评估了几种方案:

  • Elasticsearch:强大的全文搜索和聚合能力,适合日志检索。但对于复杂的关系查询和图谱展示,需要额外的处理,且写入成本较高。
  • 图数据库(如Neo4j, NebulaGraph):天然适合存储和查询谱系关系。但存储非结构化的文本和JSON性能不是最优,且生态工具(如仪表盘)相对少。
  • 时序数据库(如ClickHouse):写入和聚合查询性能极佳,但关系查询能力弱。

我们的最终选择是混合架构

  • 核心事件与关系存储:PostgreSQL + 图扩展。使用PostgreSQL的jsonb类型存储每个事件的详细信息(如工具调用请求、响应、元数据)。同时,我们维护一张边表(edge table),专门记录事件之间的依赖关系(parent_event_id,child_event_id,relationship_type)。对于简单的谱系查询,用递归CTE(Common Table Expressions)就可以实现。如果谱系非常复杂,可以考虑使用PostgreSQL的图扩展(如Apache AGE)。PostgreSQL的稳定性和生态是我们的定心丸。
  • 辅助分析与检索:Elasticsearch。将事件的主要字段(如trace_id,session_id,tool_name,timestamp, 以及脱敏后的关键文本摘要)同步到Elasticsearch。这用于支撑控制台中的全局搜索、过滤和聚合分析(例如,“过去24小时调用最频繁的错误工具是哪个?”)。
  • 流处理与实时管道:Apache Kafka + Flink。所有Agent上报的数据先发送到Kafka。Flink流处理作业负责进行实时的数据清洗(如脱敏)、丰富(如根据IP解析地理位置)、关联(将Client和Server的事件通过trace_id拼接),然后分别写入PostgreSQL和Elasticsearch。这套管道也为我们后续做实时预警(如检测异常调用模式)打下了基础。

注意:数据脱敏必须前置。在Agent端或流处理的最早阶段,就必须根据预定义的规则(如匹配信用卡号、身份证号、手机号的正则表达式)对Payload中的敏感信息进行掩码或哈希处理。明文敏感信息绝不能进入消息队列或存储层。

3. 核心数据模型与谱系构建

3.1 定义核心事件实体

一切可观测性的基础是清晰的数据模型。我们定义了以下几种核心事件类型:

  1. 会话开始事件 (SessionStart):标志一次用户与AI智能体交互的开始。包含session_id,user_id(匿名化后),initial_prompt,timestamp等。
  2. LLM思考事件 (LlmThought):记录模型的一次中间推理步骤。包含thought_id,parent_thought_id(形成思考树),content(推理文本),model_name等。这个事件的捕获依赖于LLM框架的支持程度。
  3. 工具调用请求事件 (ToolCallRequest):记录LLM决定调用一个工具的时刻。这是谱系的关键节点。包含:
    • event_id,trace_id,session_id
    • tool_name,server_name
    • arguments(JSON格式的调用参数)
    • invocation_context:一个JSON字段,用于存放触发此次调用的上下文,例如引用的前一个thought_id,或前一个tool_call_result的ID。
  4. 工具执行事件 (ToolExecution):在MCP Server端记录。包含event_id,trace_id,start_time,end_time,status(success/error),error_message,output(JSON格式的结果,已脱敏)。它通过trace_idToolCallRequest关联。
  5. 工具调用结果事件 (ToolCallResult):在MCP Client端收到工具响应后记录。包含event_id,corresponding_request_id(关联的请求事件ID),result_data(处理后的结果),以及usage(如果工具调用消耗了Token或其他资源)。
  6. 最终响应事件 (FinalResponse):记录AI智能体返回给用户的最终答案或采取的行动。包含response_content, 以及一个derived_from数组,里面列出所有直接导致此响应的上游事件ID(如关键的Thought ID和ToolCallResult ID)。

3.2 谱系关系的建立与存储

事件之间的关系通过边表(decision_lineage_edges)来存储。这张表结构简单但至关重要:

CREATE TABLE decision_lineage_edges ( id BIGSERIAL PRIMARY KEY, source_event_id UUID NOT NULL, target_event_id UUID NOT NULL, relationship_type VARCHAR(50) NOT NULL, -- 如 'triggered_by', 'used_result_of', 'followed_by' created_at TIMESTAMPTZ DEFAULT NOW(), FOREIGN KEY (source_event_id) REFERENCES events(id), FOREIGN KEY (target_event_id) REFERENCES events(id) );

关系建立逻辑示例:

  • 当一次ToolCallRequest(A) 是由一个LlmThought(B) 触发时,我们会创建一条边:source=B, target=A, type='triggered_by'
  • 当一个ToolCallResult(C) 对应一个ToolCallRequest(A) 时,创建边:source=A, target=C, type='produced'
  • 当模型的下一个LlmThought(D) 明显引用了ToolCallResult(C) 的内容时,创建边:source=C, target=D, type='used_result_of'
  • 最终的FinalResponse(E) 会指向所有它依据的源头,创建多条边,如source=C, target=E, type='derived_from'source=B, target=E, type='derived_from'

这个图谱的构建,一部分依赖Agent在本地上下文中记录的明确依赖(这是最准确的),另一部分依赖流处理作业(Flink)进行事后关联分析。例如,通过分析ToolCallRequestinvocation_context字段,可以解析出它引用了哪些上游事件的ID,从而自动创建边。

3.3 查询示例:追溯一个错误决策

假设我们发现智能体给用户提供了一个错误的股票信息。我们可以通过以下步骤追溯:

  1. 定位最终响应:在控制台通过session_id或关键词搜索到错误的FinalResponse事件。
  2. 展开谱系图:系统根据decision_lineage_edges表,递归查询所有指向这个FinalResponse的上游事件(ToolCallResult,LlmThought,ToolCallRequest),以及这些事件之间的相互关系。
  3. 可视化排查:在UI上,我们会看到一个从右向左(或从上到下)的谱系图。最终响应在右侧,左侧是它的各个“祖先”节点。
    • 如果发现一个ToolCallResult中的股票价格是错误的,我们可以点击它,查看对应的ToolExecution事件详情,检查工具执行时是否有错误日志,或者输入参数是否正确。
    • 如果工具执行成功但数据不对,问题可能出在数据源(MCP Server)本身。这时,我们可以继续查看这个工具(数据源)的其他调用记录,判断是普遍性问题还是偶发性问题。
    • 如果工具返回的数据是正确的,但模型的LlmThought事件显示它错误地解读了数据,那么问题可能出在Prompt设计或模型能力上。
  4. 根本原因定位:通过谱系图,我们能快速将问题范围缩小到“数据问题”、“工具执行问题”、“模型理解问题”或“流程逻辑问题”中的某一类,极大提升了排查效率。

4. 安全策略与风险控制实现

可观测性本身不是目的,基于可观测性实现主动安全防御和合规审计才是。我们在系统中内置了多层安全策略。

4.1 实时策略引擎与预警

流处理管道(Flink)不仅是数据搬运工,还是一个实时策略执行引擎。我们定义了一系列可配置的规则(Rule),当事件流经时进行匹配和触发。

规则示例:

  1. 敏感工具调用监控
    rule_id: "sensitive_tool_access" description: "监控对‘用户数据删除’或‘财务审批’等高危工具的调用" condition: > event_type == 'ToolCallRequest' AND tool_name IN ('delete_user_data', 'approve_payment', 'grant_admin_role') actions: - type: "real_time_alert" # 实时发送告警到Slack/钉钉 - type: "risk_score_increment" # 为该session增加风险分数 - type: "require_manual_approval" # 触发人工审核拦截(如果集成审批流)
  2. 异常调用模式检测
    rule_id: "anomalous_sequence" description: "检测短时间内同一会话对多个敏感工具的连续调用" condition: > WITHIN 2 MINUTES, COUNT(ToolCallRequest WHERE tool_name IN ('sensitive_list')) BY session_id > 5 actions: - type: "throttle_session" # 限制该会话后续工具调用速率 - type: "alert"
  3. 数据泄露风险检测
    rule_id: "pii_in_tool_output" description: "检测工具返回结果中是否包含未脱敏的个人身份信息(PII)" condition: > event_type == 'ToolExecution' AND status == 'success' AND REGEXP_MATCH(output, pii_patterns) # pii_patterns是预定义的正则表达式组 actions: - type: "redact_and_log" # 在存储前强制脱敏,并记录违规日志 - type: "alert"

这些规则在Flink作业中实现为状态化的流处理逻辑。例如,检测异常序列需要维护一个键控(keyed bysession_id)的滑动窗口状态。

4.2 审计与合规报告

所有事件及其谱系关系,本身就是一份完整的审计日志。我们基于此提供了两类报告:

  • 按需追溯报告:安全或合规人员可以针对某个具体事件(如一次客户投诉对应的会话)生成详细的PDF报告,报告中包含完整的谱系图、每个节点的原始数据(脱敏后)和时间线,用于向内部或外部审计方说明决策过程。
  • 定期聚合报告:每周/每月自动生成报告,展示诸如“各高危工具调用次数TOP 10”、“触发敏感规则最多的会话来源”、“平均决策链条长度”等指标,帮助管理者从宏观把握AI应用的安全态势和运营模式。

4.3 权限与数据隔离

可观测性平台本身也必须是安全的。

  • 多租户数据隔离:在存储层(PostgreSQL/ES),通过tenant_id字段严格隔离不同业务部门或客户的数据。查询API必须校验当前用户的租户权限。
  • 细粒度访问控制(RBAC)
    • 一线研发工程师:只能查看自己负责的应用相关的会话和谱系。
    • 安全工程师:可以查看所有数据,可以配置安全规则。
    • 审计员:拥有只读权限,可以生成和导出审计报告,但无法修改任何配置。
  • 查询审计:平台自身对所有查询操作进行日志记录,防止数据被恶意遍历或泄露。

5. 实施部署与性能调优

5.1 Agent的轻量化与无侵入集成

Agent的稳定性是整套系统的基石。我们坚持以下几个原则:

  • 低开销:Agent采用异步非阻塞方式上报数据,主线程的延迟增加控制在毫秒级。本地采用有界队列缓冲事件,防止内存溢出。
  • 高容错:网络故障或后端服务不可用时,Agent会将事件暂存于本地磁盘(LevelDB),并在恢复后重试。同时设置合理的TTL和丢弃策略,避免磁盘被撑满。
  • 无侵入性:对于主流LLM框架(LangChain, LlamaIndex),我们提供封装好的ObservableAgentExecutor等组件,用户只需替换原有类即可。对于自定义框架,提供通用的装饰器(Decorator)或AOP切面,让用户能轻松装饰他们的工具调用函数和LLM调用函数。

一个给Python MCP Client添加Agent的简化示例:

from mcp_observability.agent import ObservabilityAgent from mcp import ClientSession class InstrumentedClientSession(ClientSession): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.agent = ObservabilityAgent(service_name="my_ai_assistant") async def call_tool(self, tool_name, arguments): # 1. 创建工具调用请求事件 request_event = self.agent.record_tool_call_request( session_id=self.current_session_id, tool_name=tool_name, arguments=arguments, invocation_context=self.get_current_context() # 获取当前思维链ID等 ) try: # 2. 执行实际调用 result = await super().call_tool(tool_name, arguments) # 3. 记录成功结果 self.agent.record_tool_call_result( request_event_id=request_event.id, result_data=result, status="success" ) return result except Exception as e: # 4. 记录失败结果 self.agent.record_tool_call_result( request_event_id=request_event.id, error_message=str(e), status="error" ) raise e

5.2 后端服务的伸缩性与稳定性

  • Kafka分区策略:我们按tenant_idevent_type进行复合分区。这样可以保证同一租户的同类事件有序,同时不同租户/类型的数据可以并行处理,提高吞吐量。
  • Flink作业优化
    • 状态后端:使用RocksDB作为状态后端,并定期做检查点(Checkpoint)到持久化存储(如S3),确保故障恢复。
    • 水位线(Watermark)与乱序处理:网络延迟可能导致事件乱序到达。我们根据事件时间(timestamp)设置合理的水位线和允许的乱序时间(如5秒),在大多数情况下能正确关联事件。对于严重超时的事件,我们有一个小的延迟处理侧输出流(side output)进行补救性关联。
    • 维表关联:有些规则需要查询静态数据(如“高危工具列表”)。我们将这些数据加载到Flink的广播状态(Broadcast State)中,实现高效的流维表关联。
  • PostgreSQL优化
    • 分区表:事件表按时间(如按月)进行分区,便于历史数据的管理和快速删除。
    • 索引策略:对trace_id,session_id,event_type,tool_name,timestamp建立复合索引,以优化最常见的查询模式(如按会话查询、按工具名和时间范围查询)。
    • 连接池:使用Pgbouncer等连接池中间件,应对查询API的高并发请求。
  • 缓存层:对于热门的会话查询或谱系图查询,在查询API前增加Redis缓存层,缓存渲染好的谱系图JSON或常用聚合结果,显著降低数据库压力。

5.3 监控系统自身

我们使用另一套独立的、更轻量的监控系统(Prometheus + Grafana)来监控可观测性平台本身:

  • Agent指标:上报速率、队列大小、错误计数。
  • Kafka指标:各主题的堆积延迟(Lag)。
  • Flink指标:各算子的处理速率、背压情况、Checkpoint成功率。
  • PostgreSQL/ES指标:CPU、内存、磁盘IO、查询延迟。
  • API指标:请求量、延迟、错误率。

确保“观察者”自身健康,是服务可靠性的基本要求。

6. 典型问题排查与实战心得

在实际落地和运维这套系统的过程中,我们遇到了不少典型问题,也积累了一些心得。

6.1 常见问题速查表

问题现象可能原因排查步骤与解决方案
谱系图不完整,事件断链1. Agent上下文丢失(如异步调用未正确传递上下文)。
2.trace_id在跨进程/网络调用时丢失。
3. 流处理关联逻辑有误,未能正确建立边。
1. 检查Agent SDK的上下文传播机制,确保使用了类似contextvars或线程本地存储来传递trace_id
2. 检查MCP Server是否接收并正确回传了包含trace_id的请求头。
3. 检查Flink关联作业的日志,查看是否有匹配失败的事件被路由到了侧输出流。
查询响应缓慢1. 数据库未对常用查询条件建立索引。
2. 谱系图递归查询深度过大(例如超过10层)。
3. 返回的事件数据字段过多、过大(如包含完整的LLM长文本)。
1. 使用EXPLAIN ANALYZE分析慢查询SQL,针对性添加复合索引。
2. 在UI上默认限制谱系图展开深度(如5层),并提供“加载更多”按钮。
3. 查询API默认只返回事件的核心元数据和摘要,提供“展开详情”的单独接口按需加载完整数据。
Agent导致应用性能下降1. Agent同步阻塞上报。
2. 本地队列满,触发同步等待。
3. 序列化(如将大JSON转为字符串)耗时过长。
1. 确保上报逻辑是异步的(如使用后台线程或asyncio任务)。
2. 调整本地队列大小和批处理上报的阈值与间隔,在内存开销和实时性间取得平衡。
3. 对大的Payload字段进行采样或截断,只记录关键部分。或使用更高效的序列化库(如orjson)。
安全规则误报率高规则条件过于宽泛或静态。1. 引入机器学习进行基线学习。例如,为每个(user, tool)组合学习一个正常的调用频率基线,动态调整异常阈值。
2. 增加规则条件,如结合用户角色、访问时间、来源IP等上下文信息进行综合判断。
3. 建立规则调优流程,定期回顾误报案例,优化规则逻辑。
数据存储成本增长过快所有事件全量存储,且保留时间过长。1. 实施数据分级存储:近7天的热数据存在PostgreSQL/ES;7天到1年的温数据转存至对象存储(如S3)并建立外部表映射;1年以上的冷数据归档。
2. 对历史事件进行聚合摘要。例如,将超过一个月的完整事件明细压缩为仅保留关键路径和统计信息的“摘要事件”,大幅减少存储占用。

6.2 实操心得与避坑指南

  1. “采样”是好朋友,不是敌人:在初期,我们试图记录每一个事件,很快数据量就爆炸了。对于生产环境,尤其是高频交互的AI应用,全量记录既不经济,也没必要。我们后来引入了智能采样策略:对于成功且低风险的会话,按1%或更低的比率采样;对于触发了任何警告规则、或最终用户反馈了问题的会话,进行100%全量记录。这既控制了成本,又确保了所有“有意思”的案例都被捕获。

  2. 定义清晰的隐私边界:在项目启动前,必须联合法务、安全和业务部门,明确哪些数据可以记录,哪些必须脱敏或不能记录。例如,用户的原始提问(Prompt)可能包含敏感信息,我们是否应该记录?我们的方案是:记录一个经过泛化处理的Prompt版本(如移除具体人名、地址),同时只记录一个Prompt的哈希值用于去重分析。原始Prompt只在内存中处理,不入库。这个规则必须固化到Agent的代码和流处理的脱敏规则中。

  3. UI/UX设计至关重要:一个强大的后端配上一个难用的前端,效果等于零。谱系图的可视化是核心难点。我们迭代了好几个版本:

    • 初版:直接展示所有节点和边,复杂会话直接变成“毛线团”。
    • 改进版:引入“折叠”功能。将多次连续的同类型工具调用(如连续查询三次天气)折叠成一个聚合节点,点击可展开。
    • 当前版:支持按“时间线”和“依赖关系”两种视图切换。时间线视图清晰展示发生了什么,依赖关系视图(力导向图)则专注于逻辑因果。并且提供了强大的筛选功能,可以只显示“错误事件”、“特定工具事件”或“包含特定关键词的思考事件”,让用户快速聚焦问题。
  4. 与现有运维体系集成:不要做成又一个信息孤岛。我们将严重的安全告警(如检测到疑似攻击模式)对接到了公司的统一告警平台(如PagerDuty)。也将关键的聚合指标(如“日均工具调用错误率”)输出到了团队已有的Grafana监控大盘上。这样,开发和运维同学可以在他们熟悉的工具里看到AI应用的健康状态。

  5. 从“可观测”到“可行动”:可观测性最终是为了更好地行动。我们建立了一个闭环流程:当通过谱系分析定位到一个高频的、由特定工具错误引发的bad case后,我们会生成一个优化任务卡。这个任务卡可能指向工具提供方(修复API),可能指向Prompt工程师(优化系统指令),也可能指向模型微调团队(针对特定场景增强模型能力)。让每一次深入的问题追溯,都能转化为一次明确的系统改进。

实施“MCP Security in Action: Decision-Lineage Observability”项目,本质上是在为AI智能体的“自动驾驶”铺设数字化的“交通监控网络”和“事故鉴定系统”。它让原本黑盒的、难以审计的AI决策过程变得透明、可追溯、可管控。这套系统的价值,不仅体现在事后追责和问题排查上,更体现在通过持续的观察和分析,能够主动发现流程缺陷、优化工具性能、训练更可靠的模型,从而推动AI应用朝着更安全、更稳健、更可信的方向演进。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询