Files
AgentLab/backend/app/tools/builtin/knowledge.py
T
ThearchyHelios 964eeaa829 AgentLab:可视化 Agent 编排实验台
在画布上拖拽编排 agent 工作流,并实时看着它执行。

后端 FastAPI + LangGraph:把画布 JSON 编译成 StateGraph,15 种节点
(模型/Agent/多Agent、工具/沙箱、分支/循环/子图、记忆/检索/整形、
人工介入/结构校验)。选 LangGraph 是因为可视化编排真正难的是暂停、
恢复和回放,而 checkpointer + interrupt() 正好把这几件事做在了底层。

前端 React + React Flow:节点元数据驱动属性面板与连接桩,运行时
节点高亮、边流动画、卡片内流式 token 全部由同一份事件流驱动。

关键实现:
- Agent 内工具审批用 @task 缓存模型调用,interrupt 重放时不重复计费
- 沙箱保持只读根文件系统,代码经 exec+base64 写入(put_archive 会被
  只读容器拒绝),断网、非 root、内存/CPU/超时限额
- 检索为向量与 BM25 混合,本地哈希 embedding 零配置可用
- 分支条件走 AST 白名单求值器;HTTP 工具逐 IP 拦截内网与元数据地址
- Mock provider 让无 API Key 也能跑通完整链路

7 个内置模板全部冒烟通过,含真实模型端到端验证。
2026-09-13 17:27:00 +08:00

81 lines
2.6 KiB
Python

from __future__ import annotations
from typing import Any
from pydantic import BaseModel, Field
from app.db.base import SessionLocal
from app.memory import kb, store
from app.tools.registry import ToolContext, register
class KbSearchArgs(BaseModel):
query: str = Field(description="检索问题")
limit: int = Field(default=5, ge=1, le=20)
collection: str | None = Field(default=None, description="限定知识库集合,留空则用节点配置的")
@register(
name="kb_search",
category="知识",
description="在知识库里做混合检索(向量 + 关键词),返回最相关的片段。",
args_schema=KbSearchArgs,
)
async def kb_search(ctx: ToolContext, **kwargs: Any) -> dict[str, Any]:
args = KbSearchArgs(**kwargs)
async with SessionLocal() as session:
hits = await kb.search(
session,
collection=args.collection or ctx.collection,
query=args.query,
limit=args.limit,
)
return {"query": args.query, "hits": hits, "count": len(hits)}
class MemoryRecallArgs(BaseModel):
query: str = Field(description="想回忆什么")
limit: int = Field(default=5, ge=1, le=20)
@register(
name="memory_recall",
category="知识",
description="从长期记忆里取回与当前问题相关的内容。",
args_schema=MemoryRecallArgs,
)
async def memory_recall(ctx: ToolContext, **kwargs: Any) -> dict[str, Any]:
args = MemoryRecallArgs(**kwargs)
async with SessionLocal() as session:
hits = await store.recall(
session, scope=ctx.memory_scope, query=args.query, limit=args.limit
)
return {"scope": ctx.memory_scope, "memories": hits, "count": len(hits)}
class MemoryWriteArgs(BaseModel):
content: str = Field(description="要记住的内容,一条一个事实,写完整句子")
kind: str = Field(default="fact", description="fact | preference | episode")
importance: float = Field(default=0.5, ge=0.0, le=1.0)
@register(
name="memory_write",
category="知识",
description="把一条值得长期记住的信息写入记忆。相似内容会自动去重。",
args_schema=MemoryWriteArgs,
dangerous=False,
)
async def memory_write(ctx: ToolContext, **kwargs: Any) -> dict[str, Any]:
args = MemoryWriteArgs(**kwargs)
async with SessionLocal() as session:
item = await store.remember(
session,
scope=ctx.memory_scope,
content=args.content,
kind=args.kind,
importance=args.importance,
meta={"run_id": ctx.run_id, "node_id": ctx.node_id},
)
return {"id": item.id, "scope": item.scope, "saved": True}