转载--Hermes Agent 16 | 扩展机制:General Plugin、Memory Provider、Context Engine 三条扩展线

原文连接:https://mp.weixin.qq.com/s?__biz=MzAwMTYwNzE2Mg==&mid=2651039834&idx=1&sn=5f30a4f08070f3243de79cd893d16d9f&chksm=81201ccfb65795d94603f45b8b08b218d0c37315c2496aa8228693a1459a8fd444cb2f98876b&cur_album_id=4469690989619855360&scene=189#wechat_redirect

框架的生命力不在于它能做什么,而在于它允许别人用它做什么。


先看全景:三条扩展线各管什么

维度 General Plugin Memory Provider Context Engine
解决什么 加工具、加 hook、加斜杠命令、加 CLI 子命令、加技能 换记忆后端(Honcho、Mem0、Hindsight...) 换上下文压缩策略
可以有几个 多个同时加载 只能启用 1 个外部 provider(内置始终在) 只能启用 1 个
注册位置 默认 ~/.hermes/plugins/<name>/ plugins/memory/<name>/ (内置)或默认 $HERMES_HOME/plugins/<name>/ plugins/context_engine/<name>/ (仓库内)或 General Plugin
基类 无(通过 PluginContext API) MemoryProvider ABC ContextEngine ABC
配置路径 plugins.disabled 禁用列表 memory.provider context.engine

一个常见的误解是"我要扩展 Hermes 就得写插件"。不一定。如果你只是想接一个 MCP 服务器,不需要写插件------配 config.yaml 就行(下一讲专门讲 MCP)。如果你只是想加一段系统提示词,写 .hermes.md 就行。插件是给需要运行代码的扩展准备的。

下文出现的 ~/.hermes/... 都指默认 profile 。如果你启用了 profile,这些路径都会随 HERMES_HOME 切换。


General Plugin:完整的扩展框架

目录结构

一个 General Plugin 的最小结构(默认位于 ~/.hermes/plugins/my-plugin/):

复制代码
~/.hermes/plugins/my-plugin/
├── plugin.yaml      # 清单文件(必需)
└── __init__.py      # 入口模块,必须提供 register(ctx) 函数

plugin.yaml 清单

复制代码
name: my-plugin
version: 1.0.0
description: A brief description of what this plugin does
author: your-name
requires_env:
  - MY_API_KEY            # 简单环境变量名
  - name: MY_SECRET       # 或带描述的结构
    description: "API secret for My Service"
provides_tools:
  - my_tool_name
provides_hooks:
  - pre_tool_call

PluginManifesthermes_cli/plugins.py 第 92 行)解析这些字段。requires_env 声明的环境变量会在 hermes plugins 命令里展示缺失状态,但不会阻止加载 ------插件自己在 check_fn 里决定是否可用。

register(ctx) 入口

插件的唯一入口点是模块级的 register() 函数。启动时 PluginManager._load_plugin() 导入模块后调用它,传入一个 PluginContext 实例:

复制代码
# __init__.py
def register(ctx):
    ctx.register_tool(
        name="my_tool",
        toolset="my-plugin",
        schema={...},
        handler=my_handler,
        check_fn=lambda: bool(os.getenv("MY_API_KEY")),
    )
    ctx.register_hook("pre_tool_call", my_pre_tool_hook)

PluginContext(第 124 行)是 General Plugin 能做的所有事情的门面。它提供 8 类核心注册方法------我们逐个看。

PluginContext 的 8 类注册能力

1. register_tool --- 注册工具
复制代码
ctx.register_tool(
    name="my_tool",
    toolset="my-plugin",
    schema={"name": "my_tool", "description": "...", "parameters": {...}},
    handler=my_handler,       # def handler(args: dict, **kwargs) -> str
    check_fn=lambda: True,    # 返回 False 则工具对模型不可见
    is_async=False,
    description="Human-readable description",
)

内部直接代理到 tools.registry.register()(第 148 行)。注册完的工具和内置工具一模一样------模型不知道它来自插件还是内置。

防遮蔽机制 :如果你的插件工具名和内置工具重名(比如叫 terminal),注册会被拒绝------registry.register() 在第 204 行检查 existing.toolset != toolset,非 MCP 来源的重名一律拒绝。这是故意的:防止插件悄悄劫持核心工具。

2. register_hook --- 注册生命周期钩子
复制代码
ctx.register_hook("pre_tool_call", my_callback)

10 种合法的 hook name(VALID_HOOKS,第 54 行):

Hook 触发时机 回调签名 特殊能力
pre_tool_call 工具执行前 (tool_name, args, task_id, session_id, tool_call_id) 返回 {"action": "block", "message": "..."}阻止执行
post_tool_call 工具执行后 (tool_name, args, result, task_id) 观察用
pre_llm_call LLM 调用前 (session_id, user_message, conversation_history, ...) 返回 {"context": "..."}注入上下文
post_llm_call LLM 调用后 (session_id, user_message, assistant_response, ...) 观察用
pre_api_request API 请求前 请求元数据 观察用
post_api_request API 响应后 响应元数据 观察用
on_session_start 新会话开始 (session_id, model, platform) 观察用
on_session_end 会话结束 (session_id, completed, interrupted, ...) 观察用
on_session_finalize 会话清理 (session_id, platform) 观察用
on_session_reset /new/reset (session_id, platform) 观察用

注意 pre_tool_callpre_llm_call------它们不只是观察,而是有主动干预能力pre_tool_call 返回 {"action": "block"} 可以阻止工具执行(实现策略控制),pre_llm_call 返回的字符串会被注入到上下文里(实现动态上下文增强)。

hook 调用被 try/except 包裹(invoke_hook 第 632 行)------一个坏掉的插件 hook 不会打断 Agent 主循环。

3. register_command --- 注册会话内斜杠命令
复制代码
ctx.register_command(
    name="status",
    handler=lambda args: f"Plugin status: OK",
    description="Show plugin status",
)

用户在 CLI 或 Gateway 里输入 /status 就能触发。handler 签名是 fn(raw_args: str) -> str | None,支持同步和异步。

如果名字和内置命令冲突(比如 /help),注册会被拒绝(第 245 行通过 resolve_command() 检查)。

4. register_cli_command --- 注册终端 CLI 子命令
复制代码
ctx.register_cli_command(
    name="incident-admin",
    help="Manage the incident plugin from the terminal",
    setup_fn=setup_argparser,    # 收到 argparse subparser
    handler_fn=handle_command,
)

注册后用户可以运行 hermes incident-admin <args>。这和会话内斜杠命令不同------它是一个独立的终端命令,不进入 Agent 对话。

这里要特别区分两条链路:General Plugin 的 CLI 子命令PluginContext.register_cli_command();而 Memory Provider 自己的 CLI 子命令 (比如 hermes honcho ...)走的是 provider 的 cli.py + plugins.memory.discover_plugin_cli_commands() 这条专用发现链,不属于 PluginContext 的注册面。

5. register_context_engine --- 注册上下文引擎
复制代码
ctx.register_context_engine(my_engine_instance)

只允许注册一个 (第 303 行检查)。第二个插件尝试注册会被拒绝并打 warning。引擎实例必须继承 ContextEngine ABC(第 312 行检查)。

6. register_skill --- 注册插件附带的技能
复制代码
ctx.register_skill(
    name="my-workflow",
    path=Path(__file__).parent / "skills" / "my-workflow" / "SKILL.md",
    description="A reusable workflow provided by this plugin",
)

注册后技能以 plugin_name:skill_name 的限定名可用(比如 my-plugin:my-workflow),通过 skill_view("my-plugin:my-workflow") 加载。插件技能不出现在系统提示词的技能索引里------它们是显式加载的,不会自动注入。

7. inject_message --- 注入消息到对话
复制代码
ctx.inject_message("Here's some data from an external system", role="user")

仅在 CLI 模式下可用(Gateway 模式返回 False)。如果 Agent 正在运行,消息进入中断队列;如果 Agent 空闲,消息作为下一轮输入排队。

8. dispatch_tool --- 从插件内调用已注册的工具
复制代码
result = ctx.dispatch_tool("delegate_task", {"task": "Summarize this file"})

让插件的斜杠命令可以调用 Agent 的工具------比如一个 /research 命令内部调用 delegate_task 来启动子 Agent。

到这里,PluginContext 的核心注册面就讲完了。一个容易搞混的点是:Memory Provider 不通过 PluginContext 注册自己 。它们走的是 plugins/memory/__init__.py 的专用加载器,在那里用一个专门的 collector 捕获 register_memory_provider(...) 调用。

三种插件来源

discover_and_load()(第 415 行)按顺序扫描三个来源:

  1. 用户插件 :默认 ~/.hermes/plugins/<name>/------最常用,直接建目录写代码;启用 profile 时会落到当前 HERMES_HOME/plugins/<name>/

  2. 项目插件./.hermes/plugins/<name>/------需要 HERMES_ENABLE_PROJECT_PLUGINS=true 才启用。安全考虑默认关闭,因为项目目录可能来自不可信的 git clone

  3. pip 插件 :通过 hermes_agent.plugins entry-point group 发现。适合做成可分发的 Python 包

用户可以通过 config.yaml 禁用特定插件:

复制代码
plugins:
  disabled:
    - noisy-plugin
    - buggy-plugin

Memory Provider:换一个记忆后端

为什么需要可插拔的记忆后端

第 5-6 讲拆过内置记忆------MEMORY.md(2,200 字符)和 USER.md(1,375 字符)。这套系统简单、零依赖、对 prompt cache 友好。但它有明确的上限:空间有限、纯文本、无语义检索。

如果你需要大规模的跨会话记忆、用户画像建模、语义召回------内置记忆不够用。这时候换一个外部 Memory Provider。

8 个内置 Provider

plugins/memory/ 目录下自带 8 个 provider:

Provider 特点
honcho Dialectic reasoning,server-side 推理式记忆,per-peer 多 Agent 画像隔离
hindsight 本地向量存储 + 语义检索
holographic 多文件架构(store.py + retrieval.py),全息式记忆编码
mem0 Mem0 云服务集成
byterover ByteRover 集成
openviking OpenViking 集成
retaindb 本地数据库存储
supermemory SuperMemory 集成

每个 provider 都有自己的 plugin.yaml__init__.py(实现 MemoryProvider ABC)和 README.md

MemoryProvider ABC

agent/memory_provider.py(第 42 行)定义了 provider 必须实现的接口:

复制代码
class MemoryProvider(ABC):

    @property
    @abstractmethod
    def name(self) -> str: ...

    @abstractmethod
    def is_available(self) -> bool: ...

    @abstractmethod
    def initialize(self, session_id: str, **kwargs) -> None: ...

    @abstractmethod
    def get_tool_schemas(self) -> List[Dict[str, Any]]: ...

4 个抽象方法必须实现。另外还有一组可选方法和 hook:

核心可选方法

方法 说明 默认行为
system_prompt_block() 返回注入系统提示词的静态文本 空字符串
prefetch(query) 每轮 API 调用前,召回相关上下文 空字符串
queue_prefetch(query) 每轮结束后,预队列下一轮的召回 无操作
sync_turn(user, assistant) 每轮结束后,持久化对话内容 无操作
handle_tool_call(name, args) 处理 provider 暴露的工具调用 抛异常
shutdown() 干净关闭 无操作

可选 hook

Hook 触发时机 典型用途
on_turn_start(turn, message) 每轮开始 轮次计数、定期维护
on_session_end(messages) 会话结束 结束时提取知识、总结
on_pre_compress(messages) 上下文压缩前 在消息被丢弃前做 flush / curate / 提前提取洞见
on_memory_write(action, target, content) 内置记忆写入时 镜像内置记忆的写入到外部后端
on_delegation(task, result) 子 Agent 完成时 在父 Agent 侧观察委派结果

on_pre_compress 值得单独说------当上下文压缩即将丢弃旧消息时,provider 有机会先从中提取关键信息。当前 run_agent.py 的真实实现重点是在压缩前通知 provider,让它有机会自行 flush / curate / 落盘;不要把它理解成"返回一段文本,框架就一定会自动拼进压缩 prompt"。

initialize() 的 kwargs

initialize() 收到的 kwargs 非常丰富(第 67-81 行注释):

复制代码
kwargs always include:
  - hermes_home (str): 当前 HERMES_HOME 目录
  - platform (str): "cli", "telegram", "discord", "cron" 等

kwargs may also include:
  - agent_context (str): "primary", "subagent", "cron", "flush"
  - agent_identity (str): profile 名称
  - agent_workspace (str): 工作区名称
  - parent_session_id (str): 子 Agent 场景下的父 session_id
  - user_id (str): 平台用户标识

agent_context 特别重要------如果值是 "cron""flush",provider 应该跳过写入。因为 cron 的系统提示词和正常对话不同,写入会污染用户画像。

运行时边界:当前只挂 1 个外部 Provider

从抽象设计看,MemoryManageragent/memory_manager.py 第 83 行)支持维护 provider 列表;但当前运行时更保守:内置记忆仍然走独立的 MemoryStore / memory 工具链,MemoryManager 只在配置了 memory.provider 时才初始化,用来托管那个唯一的外部 provider。

源码注释解释了为什么只允许一个外部 provider:

复制代码
Only one external provider runs at a time to prevent tool schema bloat
and conflicting memory backends.

如果同时跑 Honcho 和 Mem0,两个 provider 各自暴露记忆工具,模型会困惑"该用哪个"。所以更准确的说法是:内置记忆一直在线,但当前 MemoryManager 本身并不是"内置 + 外部"的统一容器,它是"外部 provider 编排层"。

激活方式

在默认 ~/.hermes/config.yaml 里设置:

复制代码
memory:
  provider: honcho

run_agent.py 在初始化阶段(第 1312-1389 行)读取这个配置,调用 load_memory_provider("honcho"),然后 MemoryManager.add_provider() 把它加进去。

如果 provider 的 is_available() 返回 False(比如缺少 API Key),加载会静默跳过,Agent 继续用内置记忆。


Context Engine:换一种上下文管理策略

内置方案:ContextCompressor

默认的上下文管理是"压缩"------当 token 用量超过阈值(默认 75% context length),ContextCompressor 把旧消息总结成一段摘要,腾出空间。第 18 讲会详细拆压缩器。

但压缩不是唯一的策略。比如 LCM(Large Context Management)可能用知识图谱来组织上下文,把消息转化为可检索的节点而不是简单丢弃。Context Engine 就是为这种替代方案准备的扩展点。

ContextEngine ABC

agent/context_engine.py(第 32 行)定义了上下文引擎的接口:

复制代码
class ContextEngine(ABC):

    @property
    @abstractmethod
    def name(self) -> str: ...

    @abstractmethod
    def update_from_response(self, usage: Dict[str, Any]) -> None: ...

    @abstractmethod
    def should_compress(self, prompt_tokens: int = None) -> bool: ...

    @abstractmethod
    def compress(self, messages: List[Dict[str, Any]],
                 current_tokens: int = None) -> List[Dict[str, Any]]: ...

3 个抽象方法 + 1 个 name 属性必须实现。

update_from_response(usage):每次 LLM 调用后,引擎从响应的 usage 字典里更新 token 计数。引擎需要维护 6 个类属性:

复制代码
last_prompt_tokens: int = 0
last_completion_tokens: int = 0
last_total_tokens: int = 0
threshold_tokens: int = 0     # 触发压缩的阈值
context_length: int = 0       # 模型的最大 context length
compression_count: int = 0    # 已压缩次数

run_agent.py 直接读取这些属性来显示状态和做预检。

should_compress(prompt_tokens):返回 True 就触发压缩。内置压缩器的判断逻辑是 prompt_tokens > threshold_tokens,但自定义引擎可以有自己的策略------比如按消息数量、按知识图谱大小、按时间窗口。

compress(messages, current_tokens):核心方法。接收完整消息列表,返回一个更短的列表。只要返回值是合法的 OpenAI 格式消息序列,引擎内部怎么做都行------总结、裁剪、转存到外部、构建 DAG、什么都可以。

可选的工具暴露 :引擎可以通过 get_tool_schemas()handle_tool_call() 给 Agent 提供工具。比如一个 LCM 引擎可能暴露 lcm_grep(在知识图谱里搜索)、lcm_expand(展开某个节点的上下文)。

模型切换支持update_model() 方法在用户切换模型时被调用。默认实现重新计算 threshold_tokens

复制代码
def update_model(self, model, context_length, **kwargs):
    self.context_length = context_length
    self.threshold_tokens = int(context_length * self.threshold_percent)

如果你的引擎对模型有依赖(比如用特定模型做摘要),在这里切换。

激活方式

复制代码
context:
  engine: my-engine

引擎可以通过两种方式提供:

  1. 仓库内目录引擎:plugins/context_engine/my-engine/

  2. General Plugin:在 register(ctx) 里调用 ctx.register_context_engine(engine)

加载优先级:先查 plugins/context_engine/ 目录,再查 General Plugin 注册的引擎。

这里和 Memory Provider 并不完全对称 。当前源码没有一条独立的 HERMES_HOME/plugins/context_engine/... 用户目录扫描链;用户自定义 Context Engine 更现实的入口是写成 General Plugin,然后在 register(ctx) 里显式注册。


自定义工具注册:registry.register() 全流程

不管是内置工具、插件工具还是 MCP 工具,最终都走同一个注册入口。

ToolEntry 的完整字段

ToolEntrytools/registry.py 第 76 行)用 __slots__ 定义了 10 个字段:

复制代码
__slots__ = (
    "name",                  # 工具名(模型调用时用)
    "toolset",               # 所属工具集(分组用)
    "schema",                # OpenAI 格式的函数定义
    "handler",               # 处理函数
    "check_fn",              # 可用性检查
    "requires_env",          # 需要的环境变量列表
    "is_async",              # 是否异步 handler
    "description",           # 人类可读描述
    "emoji",                 # 展示用 emoji
    "max_result_size_chars",  # 结果最大字符数
)

register() 的关键行为

ToolRegistry.register()(第 176 行)有几个值得注意的行为:

1. 防遮蔽 :同名工具来自不同 toolset 时,注册被拒绝------除非两者都是 MCP toolset(以 "mcp-" 开头),MCP 之间允许覆盖(应对 server refresh 或 tool name 冲突)。

2. toolset check_fn 共享 :同一个 toolset 的所有工具共享一个 check_fn(第 226 行,first-write-wins)。当某个 toolset 的 check_fn 返回 False,该 toolset 下的所有工具对模型都不可见。

3. 线程安全 :整个注册过程在 self._lockthreading.RLock)保护下执行。MCP 的动态工具发现可能在任何时候触发 nuke-and-repave 刷新,RLock 保证读写不会撕裂。

handler 的约定

工具的 handler 函数需要满足:

复制代码
def my_handler(args: dict, **kwargs) -> str:
    # args: 模型传的参数字典
    # **kwargs: 框架传的额外上下文(task_id, user_task 等)
    # 返回值: JSON 字符串
    return json.dumps({"result": "success"})

返回值必须是字符串 (通常是 JSON)。异步 handler 设 is_async=True,注册表在分发时自动桥接。

内置工具的发现方式

discover_builtin_tools()(第 56 行)不是盲目导入 tools/ 下的所有 .py 文件。它先用 AST 解析 检查每个文件的顶层是否有 registry.register() 调用(第 28-53 行的 _module_registers_tools() / _is_registry_register_call()),只导入确实有注册调用的模块。这避免了"导入一个工具模块就拉起它的全部依赖"的问题。


实战:开发一个连接内部 API 的自定义插件

假设你的团队有一个内部服务 https://internal-api.example.com/incidents\,你希望 Agent 能查询当前的线上事故。

第一步:创建目录和清单

复制代码
mkdir -p ~/.hermes/plugins/incident-query

这里仍然以默认 profile 为例;如果你在用 profile,这个目录对应当前 HERMES_HOME/plugins/incident-query/

~/.hermes/plugins/incident-query/plugin.yaml

复制代码
name: incident-query
version: 1.0.0
description: Query internal incident management API
author: your-team
requires_env:
  - name: INCIDENT_API_TOKEN
    description: "API token for the incident service"
provides_tools:
  - query_incidents

第二步:实现工具

~/.hermes/plugins/incident-query/__init__.py

复制代码
import json
import os
import httpx

SCHEMA = {
    "name": "query_incidents",
    "description": "Query the internal incident management system. "
                   "Returns active incidents with severity, status, and owner.",
    "parameters": {
        "type": "object",
        "properties": {
            "status": {
                "type": "string",
                "enum": ["active", "resolved", "all"],
                "description": "Filter by incident status",
            },
            "severity": {
                "type": "string",
                "enum": ["P0", "P1", "P2", "P3", "all"],
                "description": "Filter by severity level",
            },
        },

        "required": [ ],

    },
}


def _check_available():
    return bool(os.getenv("INCIDENT_API_TOKEN"))


def _handler(args: dict, **kwargs) -> str:
    token = os.getenv("INCIDENT_API_TOKEN", "")
    status = args.get("status", "active")
    severity = args.get("severity", "all")

    try:
        resp = httpx.get(
            "https://internal-api.example.com/incidents",
            params={"status": status, "severity": severity},
            headers={"Authorization": f"Bearer {token}"},
            timeout=10,
        )
        resp.raise_for_status()
        incidents = resp.json()
        return json.dumps(incidents, indent=2)
    except Exception as e:
        return json.dumps({"error": str(e)})


def register(ctx):
    ctx.register_tool(
        name="query_incidents",
        toolset="incident-query",
        schema=SCHEMA,
        handler=_handler,
        check_fn=_check_available,
        description="Query internal incident API",
    )

第三步:配置凭证

在默认 ~/.hermes/.env 里加一行:

复制代码
INCIDENT_API_TOKEN=your-api-token-here

第四步:验证

复制代码
# 确认插件被发现
hermes plugins

# 在对话中验证
hermes chat -q "现在有哪些 P0 事故?"

Agent 会在工具列表中看到 query_incidents,当用户问到事故相关问题时自动调用。

加一个安全 hook(可选)

如果你想确保 Agent 不会在公开群组里泄露事故详情,可以加一个 pre_tool_call hook:

复制代码
def _guard_hook(tool_name, args, task_id=None, **kwargs):
    if tool_name != "query_incidents":
        return None
    platform = kwargs.get("platform", "cli")
    chat_type = kwargs.get("chat_type", "dm")
    if platform != "cli" and chat_type != "dm":
        return {
            "action": "block",
            "message": "Incident queries are only allowed in DMs for security reasons.",
        }
    return None


def register(ctx):
    ctx.register_tool(...)  # 同上
    ctx.register_hook("pre_tool_call", _guard_hook)

这样在群组里调用 query_incidents 会被拦截,只有私聊和 CLI 模式允许。


小结

这一讲把 Hermes 的三条扩展线拆完了。

General Plugin 是最灵活的扩展方式------PluginContext 提供 8 类核心注册能力,从工具到 hook 到斜杠命令到技能。三种来源(用户目录、项目目录、pip 包)覆盖了开发到分发的全生命周期。10 种 hook 中 pre_tool_callpre_llm_call 有主动干预能力(阻止执行、注入上下文),其余是观察用。要特别记住:Memory Provider 的注册和 CLI 扩展并不走这条 PluginContext 链。

Memory Provider 是单选替换------8 个内置 provider 按 memory.provider 配置激活,和内置记忆并存。MemoryProvider ABC 定义了完整的生命周期(initialize → prefetch → sync_turn → shutdown)和 5 个可选 hook。当前运行时里,内置记忆仍然走独立 MemoryStore,外部 provider 由 MemoryManager 单独托管;on_pre_compress 的现实意义,更接近"压缩前通知 provider 自行抢救信息",而不是"自动把返回文本拼进压缩 prompt"。

Context Engine 是上下文管理的替换点------默认是 ContextCompressor,可以换成基于知识图谱或其他策略的引擎。ContextEngine ABC 要求实现 3 个核心方法(update_from_response / should_compress / compress),可选暴露工具给 Agent 使用。对用户自定义扩展来说,最稳妥的落点通常是 General Plugin,而不是把它想成和 Memory Provider 完全对称的一条用户目录扫描链。

三条线的设计原则一致:单一职责、基类约束、安全隔离。插件不能遮蔽内置工具,外部 provider 不能踢掉内置记忆,hook 的异常不会打断主循环。扩展能力越大,约束也越明确。

相关推荐
微软技术栈1 小时前
技术速递|面向初学者的 GitHub Copilot CLI:交互模式与非交互模式
人工智能·github·copilot
暗夜猎手-大魔王1 小时前
hermes源码学习1-基本架构
人工智能·学习
前端不太难1 小时前
AI的下一场战争:从算力到存力
人工智能·状态模式
君为先-bey1 小时前
VideoReward: 人类反馈优化视频生成文献深度阅读分析
人工智能·音视频·扩散模型
龙侠九重天1 小时前
C# 构建 AI Agent 系统 — 我的实践笔记
开发语言·人工智能·语言模型·自然语言处理·大模型·agent·智能体
甄心爱学习1 小时前
【项目实训(个人12)】
人工智能·python·算法
协享科技1 小时前
前端 SSE 流式响应处理实践:从接收、解析到渲染
前端·人工智能·程序人生·go·ai编程·sse
程序大视界1 小时前
AI正在“接管“法槌?2026年法律AI全面入侵:合同审查99.2%准确率,律师该何去何从?
人工智能·ai法律
暗夜猎手-大魔王1 小时前
转载--Hermes Agent 12 | 沙箱与执行环境:六种终端后端的安全隔离
人工智能·安全