AS AgentScopeAPI 手册 / API Reference
查阅Reference

接线规则Wiring rules

装配台与运行时读的是同一份目录。一块积木只属于一个槽;陷阱积木能干净地装上去,但依然是错的。The bench and the runtime read this one catalog. A block belongs to exactly one slot; a trap block wires up cleanly and is still the wrong answer.

槽位Slot契约Contract 必填Required数量Cardinality 积木Blocks
storage
状态存储Persistence
StorageBase必填yes单块oneAsyncSQLAlchemyStorageSQLite,任意异步 SQLAlchemy URLSQLite via any async SQLAlchemy URLRedisStorageRedis 存储Redis-backed storageAsyncSQLAlchemyStoragePostgres,同一个类换个 URLPostgres, same class, different URL
message_bus
消息总线Transport
MessageBus必填yes单块oneInMemoryMessageBus单进程总线Single-process busRedisMessageBusRedis 发布订阅总线Redis pub/sub bus
workspace_manager
工作区沙箱Execution sandbox
WorkspaceManagerBase必填yes单块oneLocalWorkspaceManager本机目录,按 agent 隔离Host directories, per-agent isolationDockerWorkspaceManager每个工作区一个容器One container per workspaceE2BWorkspaceManager远端托管沙箱Remote managed sandbox
extra_credentials
模型凭据Model backends
list[Type[CredentialBase]]选填no可多块manyTapeCredential确定性离线模型Deterministic offline modelOpenAICredential真实供应商与密钥Real provider, real keys
extra_agent_tools
工具工厂Tool factory
AgentToolFactory选填no单块onebuild_triage_tools每个 agent 四个单一职责工具Four single-purpose tools per agentbuild_do_anything_tool一个什么都能做的工具One tool that runs anything
resource_access_policy
资源访问策略Access policy
ResourceAccessPolicyBase选填no单块oneRefundAccessPolicy你写的 ResourceAccessPolicyBase 子类Your ResourceAccessPolicyBase subclassPermissionMode.BYPASS把闸门整个关掉Turn the whole gate off
channels
接入渠道Channels
list[Type[ChannelBase]]选填no可多块manyFeishuChannel飞书收件箱Feishu / Lark inboxDiscordChannelDiscord 服务器Discord server
extra_agent_middlewares
循环内闸门In-loop gates
AgentMiddlewareFactory选填no单块oneReplyBudgetControlMiddleware每条回复的 token 上限Token ceiling per replyTracingMiddleware可观测,但不设上限Observability, no ceiling
extra_middlewares
入口闸门Entry gates
list[Middleware]选填no可多块manyTrustedIdentityMiddleware签名身份替代裸请求头Signed identity replaces the header

每块积木Every block

AsyncSQLAlchemyStorage single storage

storage=AsyncSQLAlchemyStorage("sqlite+aiosqlite:///./capstone.db")

create_tables=True 会在启动时建表。多副本部署要关掉它,把 alembic upgrade head 当作独立发布步骤——两个副本同时抢同一次迁移是不安全的。

create_tables=True provisions the schema at boot. For multiple replicas turn it off and run alembic upgrade head as a separate deploy step: two replicas racing on the same migration is unsafe.

pip install "sqlalchemy[asyncio]" aiosqlite greenlet

RedisStorage multi storage

storage=RedisStorage(url="redis://localhost:6379/0")

很快,但重启后的持久性就取决于淘汰策略了。agent 重启后回来发现某个键已被淘汰,那是数据完整性事故,不是缓存未命中。

Fast, but restart durability then depends on your eviction policy. An agent that resumes after a restart to a key that was evicted is a data-integrity bug, not a cache miss.

pip install redis

InMemoryMessageBus single message_bus

message_bus=InMemoryMessageBus()

不用装任何中间件,笔记本上就能跑。但它跨不了进程:两个副本永远互相看不见对方的收件箱。

No broker to install, so the laptop run works. It cannot cross processes: two replicas will never see each other's inbox.

RedisMessageBus multi message_bus

message_bus=RedisMessageBus(url="redis://localhost:6379/1")

传输后端与持久化后端刻意分开:完全可以一边存 SQL,一边用 Redis 传输。

The transport backend is separate from persistence on purpose: keep SQL rows and Redis transport together.

pip install redis

LocalWorkspaceManager single workspace_manager

workspace_manager=LocalWorkspaceManager("./workspaces")

这里的隔离只是目录边界,不是安全边界:工具仍然碰得到宿主机。别把这个服务暴露出去。

Isolation here is a directory boundary, not a security boundary: a tool can still reach the host. Do not expose this service.

DockerWorkspaceManager sandbox workspace_manager

workspace_manager=DockerWorkspaceManager(...)

只换这一块积木,「agent 执行了一条命令」就变成了「agent 在一个出不来的容器里执行了一条命令」。

Swapping this one block is what turns “the agent ran a command” into “the agent ran a command inside a container it cannot leave”.

pip install aiodocker

E2BWorkspaceManager sandbox workspace_manager

workspace_manager=E2BWorkspaceManager(...)

本机不需要 Docker,但沙箱租约从此有了寿命:一轮长任务可能跑过租期而变得无法恢复。

No local Docker daemon needed, but the sandbox lease now has a lifetime: a long turn can outlive it and become unrecoverable.

pip install e2b

TapeCredential lab extra_credentials

extra_credentials=[TapeCredential]

凭据本身是 pydantic 模型,它的 get_chat_model_class() 决定服务会构造哪个模型。指向脚本模型后,每一条校验都变得可复现。

A credential is a pydantic model whose get_chat_model_class() decides which model the service builds. Point it at a scripted model and every check becomes reproducible.

OpenAICredential prod extra_credentials

extra_credentials=[OpenAICredential]

内置即可用;只有要发布自己的模型族时,才需要写进 extra_credentials。

Built in already. You only list a credential in extra_credentials when you ship a model family of your own.

AsyncSQLAlchemyStorage multi storage

storage=AsyncSQLAlchemyStorage("postgresql+asyncpg://user:pass@host/capstone")

从笔记本到集群,改动只有一个字符串——这正是契约的价值。把 create_tables 设为 False,让 alembic 独占 schema,否则副本之间会抢迁移。

The swap from laptop to cluster is one string, which is the point of the contract: set create_tables=False and let alembic own the schema, or replicas race on migration.

pip install "sqlalchemy[asyncio]" asyncpg

build_triage_tools gate extra_agent_tools

extra_agent_tools=build_triage_tools

工厂按 (user_id, agent_id, session_id) 被调用,所以查订单的工具可以直接绑定当前租户的数据,而不必指望模型老实传租户号。

The factory is called with (user_id, agent_id, session_id), so the order-lookup tool can be bound to this tenant's data instead of trusting the model to pass a tenant id.

build_do_anything_tool trap extra_agent_tools

extra_agent_tools=build_do_anything_tool

工具变少,能力变模糊。一个万能 shell 工具会让每次权限判断都取决于模型的心情,而且没法给它标只读——因为它什么都能做。

Fewer tools, bigger schema. A single shell-shaped tool makes every permission decision depend on the model's mood, and no read-only flag can be attached to a tool that can do anything.

它能装配成功。但它会让 C4 的权限闸门变成笑话:一个工具、一个参数,参数意味着什么由模型自己决定。

It compiles. It also makes C4's permission gate a joke: one tool, one argument, and the model decides what the argument means.

RefundAccessPolicy gate resource_access_policy

resource_access_policy=RefundAccessPolicy()

服务级:在任何 agent 运行之前生效,一次作用于所有 agent。动钱的那份资源,租户边界就落在这里。

Service-level, so it fires before any agent runs and applies to every agent at once. This is where the money-moving resource gets its tenant boundary.

PermissionMode.BYPASS trap resource_access_policy

resource_access_policy=PermissionMode.BYPASS

形状不对,层次也不对:PermissionMode 是循环内每个 agent 的开关,resource_access_policy 是循环外的服务边界。用 BYPASS 去解一个策略问题,等于把问题删掉。

Wrong shape and wrong layer: PermissionMode is a per-agent switch inside the loop, resource_access_policy is the service boundary outside it. Reaching for BYPASS to fix a policy question deletes the question.

就算真能塞进去,BYPASS 也是在把「这个调用方能看这份资源吗」一律答成「能,永远」。

Even if you could slot it, BYPASS answers "should this caller see this resource?" with "yes, always".

ReplyBudgetControlMiddleware gate extra_agent_middlewares

extra_agent_middlewares=build_gate_middlewares

ReplyBudgetControlMiddleware(token_budget=…, input_token_weight=…, output_token_weight=…, hint_message=…) 按输入/输出 token 加权,并在每一步推理时检查,所以失控的工具循环会在同一个回复里被掐断。这是提示词做不到的事。

ReplyBudgetControlMiddleware(token_budget=..., input_token_weight=..., output_token_weight=..., hint_message=...) is weighted and checked at every reasoning step, so a runaway tool loop is cut off inside the same reply that started it. Prompt wording cannot do this.

TracingMiddleware single extra_agent_middlewares

extra_agent_middlewares=build_gate_middlewares

记录循环做了什么,但不会拦它。放在预算旁边很有用,当成预算则没用。

Records what the loop did; never stops it. Useful next to a budget, useless as a substitute for one.

TrustedIdentityMiddleware prod extra_middlewares

extra_middlewares=[Middleware(TrustedIdentityMiddleware)]

目录里没有内置的鉴权积木——这就是缺口本身:deps.get_current_user_id 直接把请求里的 X-User-ID 当身份,于是 agent 能花出去的钱,取决于客户端随口声明了谁。而 ASGI 中间件是唯一能先做校验的那一层。

There is no built-in auth block in the catalog. That is the gap: deps.get_current_user_id takes X-User-ID straight off the request, so the identity an agent can spend is whatever the client claims. An ASGI middleware is the only layer that can verify it first.

FeishuChannel prod channels

channels=[FeishuChannel]

渠道是一个无人值守的入口:审批提示从此出现在某人的聊天窗口里,而看到消息的那个人就是审批人。

A channel is an unattended entry point: the approval prompt now arrives in someone's chat, and whoever reads it is the approver.

pip install lark-oapi

DiscordChannel prod channels

channels=[DiscordChannel]

同一个槽,不同的影响面:服务器成员列表不是访问策略。上线前先和 C4 配对。

Same slot, different blast radius: a server member list is not an access policy. Pair it with C4 before you ship it.

pip install discord.py