Agent · Chat · tool¶
门面层 —— 你日常打交道的那一层。from rein import Agent, Chat, tool
Agent¶
无状态的 agent 蓝图:定义「怎么跑」,每次运行产出独立 Session(所以同一个 Agent 可并发用于多个会话而不串台)。
构造¶
Agent(
model: str | None = None,
*,
provider: Provider | None = None,
fallback: list | None = None,
tools: list | None = None,
system: str | None = None,
config: LoopConfig | None = None,
compaction: CompactionStrategy | None = None,
)
| 参数 | 类型 | 说明 |
|---|---|---|
model |
str |
LiteLLM 寻址 "厂商/模型"(如 "anthropic/claude-opus-4-8")。首次 run 时才懒建 provider。 |
provider |
Provider |
直接注入 Provider(测试用 MockProvider)。注入后忽略 model/fallback。 |
fallback |
list |
备用模型,元素可为模型字符串或 Provider 对象;主模型限流/报错时按序自动切换。 |
tools |
list |
工具列表,元素可为裸函数或 Tool 对象,自动登记。 |
system |
str |
系统提示,放在每次会话最前面。 |
config |
LoopConfig |
熔断 + 权限配置。默认 LoopConfig()。 |
compaction |
CompactionStrategy |
上下文压缩策略;给定后每次问模型前自动压缩超窗历史。 |
示例
from rein import Agent, LoopConfig, MockProvider
# 1) 最简:只给模型
a = Agent("anthropic/claude-opus-4-8")
# 2) 注入 Mock(测试,无 key)
b = Agent(provider=MockProvider(["你好"]))
# 3) 带系统提示 + 主备 + 审批
c = Agent(
"anthropic/claude-opus-4-8",
fallback=["openai/gpt-4o"],
system="你是严谨的助手。",
config=LoopConfig(permission="ask"),
)
懒建 + 可注入
给 model 时,Provider 在首次运行才创建(import litellm 也延迟到那时)。给 provider= 则直接用注入的 —— 这让测试无 key 可跑。两者都没给会在运行时报清晰错误。
agent.run(prompt)¶
同步跑一次。新建一份独立 Session(system + user),交给 loop 跑完,返回 RunResult。
已在事件循环里会报错
run() = asyncio.run(arun())。若当前已在事件循环中(Jupyter / Web 框架),会报错并引导你改用 await agent.arun(...) —— 它绝不偷偷嵌套事件循环。
agent.arun(prompt)¶
run 的异步版,异步内核。在已有事件循环的环境(Web/Jupyter)里用它。
agent.astream(prompt)¶
流式跑一次,实时把模型吐字发出来。逐个产出 StreamChunk。
async for chunk in agent.astream("讲个笑话"):
if chunk.type == "text":
print(chunk.text, end="", flush=True)
elif chunk.type == "done":
print("\n用量:", chunk.usage.output_tokens)
Info
流式只关注「实时文本 + 最终用量」;需要完整 RunResult / 会话状态请用 run/arun。只做异步 astream(流式本就该异步)。
agent.resume(session, *, approve=True, answer=None)¶
从一个中断态 session 恢复执行(同步)。见 人工审批与断点续跑。
| 参数 | 说明 |
|---|---|
session |
中断时拿到的 result.session(可先存盘再读回) |
approve |
True=批准执行待办工具;False=拒绝(回填错误,模型自愈) |
answer |
need_input 场景:把补充信息作为 user 消息注入后继续 |
r = agent.run("删库") # permission="ask"
if r.status == "interrupted":
r = agent.resume(r.session, approve=False)
agent.aresume(session, *, approve=True, answer=None)¶
resume 的异步版。
agent.run_interactive(prompt, approver=None)¶
CLI 友好的「带审批」运行:permission="ask" 时每遇审批中断就问一次,自动续跑到 done。
| 参数 | 说明 |
|---|---|
prompt |
用户输入 |
approver |
审批回调 approver(interrupt) -> bool;默认在终端用 input() 问 y/N。测试/自动化可注入。 |
# 终端交互
agent.run_interactive("帮我清理目录")
# 注入自动审批策略(测试)
agent.run_interactive("...", approver=lambda itr: True)
Note
它和服务端「产出中断态交给上层 + resume」是同一套机制,不是两套。
agent.chat(session=None)¶
开一个多轮会话句柄(Chat),历史跨轮保留。传入 session 可从已有会话继续 —— 这是企业级无状态服务的关键(见下)。
注册扩展(装饰器/方法)¶
这些在对应章节详讲,这里给索引:
| 装饰器/方法 | 作用 | 详见 |
|---|---|---|
@agent.tool |
注册工具(返回原函数) | 写工具 |
@agent.middleware |
洋葱中间件 | 扩展参考 |
@agent.before_tool / @agent.after_tool |
工具步前/后钩子 | 扩展参考 |
@agent.before_model / @agent.after_model |
模型步前/后钩子 | 扩展参考 |
agent.on(event, handler) |
只读事件订阅 | 扩展参考 |
Chat¶
会话句柄:持有一份持续的 Session,支撑多轮对话(历史跨轮保留)。用 agent.chat() 创建。
chat.send(prompt) / chat.asend(prompt)¶
发一轮消息,返回本轮 RunResult;会话历史已更新到 chat.session。每轮会把单轮运行态(阶段/熔断计数)复位,但保留历史。
chat.session¶
当前的完整 Session(随每轮更新),可序列化存盘。
企业级:无状态多轮服务¶
Chat 默认是内存句柄(适合脚本/CLI)。在无状态 Web 服务里(多用户、跨请求、跨进程),用 agent.chat(session=...) 把会话状态从你的 store 进出 —— Agent 无状态蓝图,一个实例并发服务所有用户:
from rein import Agent, MemorySessionStore # 生产换成你的 RedisSessionStore
agent = Agent("anthropic/...", system="你是客服助手")
store = MemorySessionStore()
def handle(conversation_id: str, user_msg: str) -> str:
chat = agent.chat(session=store.load(conversation_id)) # None → 自动新建
result = chat.send(user_msg)
store.save(conversation_id, chat.session) # 写回
return result.output
redis 版 store 实现 save/load 两个方法即可(见 持久化参考);完整示例见仓库 examples/enterprise_chat.py。
tool(模块级)¶
把一个普通函数包成 Tool 对象,供 Agent(tools=[...]) 组合使用。
from rein import Agent, tool
@tool
def add(a: int, b: int) -> int:
"求和"
return a + b
agent = Agent("...", tools=[add]) # add 此时是 Tool 对象
和 @agent.tool 的区别
@agent.tool 注册后返回原函数(你照常能调用它);模块级 @tool 返回的是 Tool 对象(用于组合)。想保留函数本体可调用,就用 @agent.tool。