mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 16:01:59 +00:00
964eeaa829
在画布上拖拽编排 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 个内置模板全部冒烟通过,含真实模型端到端验证。
81 lines
2.6 KiB
Python
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}
|