LlamaIndex:Agent、属性图与工程化

LlamaIndex:Agent、属性图与工程化

「本系列第 15/17 章」。基线:LlamaIndex 0.14.23。上一章停在“一次查询 / 一轮对话”;本章把工具循环、属性图和上线时必须守住的边界补齐。能跑通 Notebook 不等于能上服务:Settings 的单例、存储拓扑和 Agent 的停止条件,都是工程问题。把它们留给“以后再整理”,线上的第一周就会变成互相覆盖的全局状态和停不下来的工具循环。

1. Agent:ReActAgent 配 AgentWorkflow

0.14.23 的 Agent 是 Workflow Agent,入口在 llama_index.core.agent.workflow。不要再找 AgentRunner,也不要调用已经不存在的 ReActAgent.from_tools()ChatMode.REACT / OPENAI 已删除,工具场景必须显式建 Agent。把 ChatEngine 调到一个已经不存在的模式,得到的不是回退,而是立刻抛错。

1
2
3
4
5
6
7
8
9
10
11
from llama_index.core.agent.workflow import ReActAgent, AgentWorkflow
from llama_index.core.tools import QueryEngineTool

tool = QueryEngineTool.from_defaults(
query_engine=index.as_query_engine(),
name="knowledge_base",
description="检索政策与办理步骤",
)
agent = ReActAgent(tools=[tool], llm=llm, system_prompt="先检索再回答。")
workflow = AgentWorkflow(agents=[agent])
result = await workflow.run(user_msg="退货需要什么条件?")

ReActAgent 是单步执行单元:把工具说明、历史和 reasoning scratchpad 格式化成 Thought / Action / Action Input,或 Thought / Answer,再解析模型输出。空响应或格式失败不会马上停,而是加入纠错提示进入下一轮。真正的 run()、事件路由、工具并行和停止条件在 AgentWorkflow。打印 result 看到的是 AgentOutput 的文本;result.tool_calls 才是本轮轨迹。

便捷入口 AgentWorkflow.from_tools_or_functions() 会看 llm.metadata.is_function_calling_model:为真选 FunctionAgent,否则选 ReActAgent。本系列的本地 llama-server 路径应保持 is_function_calling_model=False,因此用 ReActAgent。只有当前 server、chat template 和实测响应都正确支持 tool calls 时,才改 flag 并改用 FunctionAgentFunctionAgent.take_step() 在 flag 为假时会抛 ValueError,不是自动降级。FunctionAgent 默认允许并行工具调用;工具必须真正异步,名字叫 acall 但内部阻塞,事件循环一样会停。

Workflow 按 iteration 循环:初始化 Memory 与状态 → take_step → 无 tool call 则 finalize 结束;有多个 tool call 可并发执行,再聚合观察结果。达到 max_iterations 时,force 直接报错,generate 再让 LLM 收个尾。普通工具异常会被转成错误观察返回给模型,而不是整条工作流炸掉。位置参数 run(...) 已 deprecated,使用关键字 run(user_msg=...)。需要看过程时,用 handler.stream_events() 观察 AgentStreamToolCallToolCallResultstreaming=True 只让 LLM token 进入事件流,不会把工具本身变成流式。

多 Agent 必须有非默认的 name / description,并指定存在的 root_agent。handoff 是工作流动态加上的工具,切换当前 Agent,不会因为 return_direct 而结束整个 Workflow。RAG 作为工具时,把 QueryEngine 包成 QueryEngineTool,描述写清楚适用问题,避免模型把闲聊也送进检索。

CodeActAgent 需要你自己提供 code_execute_fn。框架不提供安全沙箱,不能对不可信代码直接 exec。它不属于本系列默认落地路径,知道边界即可。

2. 图:KnowledgeGraphIndex 换成 PropertyGraphIndex

KnowledgeGraphIndexKGTableRetrieverKnowledgeGraphRAGRetriever 均已 deprecated。新项目用 PropertyGraphIndex。旧三元组 API 只强调 (主体, 关系, 客体);属性图里的实体和关系都有稳定 ID、label、任意 properties,并回指原始 LlamaIndex Node。需要“谁在何时以何种属性关联谁”,而不是只存一条边标签,就走属性图。

默认 store 是 SimplePropertyGraphStore;默认抽取器是 SimpleLLMPathExtractorImplicitPathExtractor。前者用 LLM 抽自由路径,后者从已有 NodeRelationship 生成隐式路径。还可以换 SchemaLLMPathExtractor 做预定义约束,或 DynamicLLMPathExtractor 做动态类型。transformations 是 BaseIndex 在图抽取之前的通用切分;kg_extractors 才是插入时的图抽取链。两者顺序不要反着配。

embed_kg_nodes=True 时,原始 node 按嵌入模式取文本,KG node 按 str(kg_node) 嵌入。graph store 若不能向量查询,就落到独立 vector store。SimplePropertyGraphStore 支持基本节点、关系、路径和本地持久化,但不实现 schema、structured query、vector query,所以默认还要配向量后端。检索入口是 PGRetriever,按 store 能力组合向量召回与结构化查询。不要假设“建了属性图就自动会 Cypher”。

持久化时,StorageContext.persist() 会调用 property_graph_store.persist(...)。外部图数据库的语义由集成决定,不一定写成本地 JSON。加载后能否接着 upsert,要看该 store 是否把 ID 和来源键保存完整。

3. 工程化:存什么、隔离什么、装什么

持久化。 StorageContext 是容器,不是数据库。它聚合 docstore、index_store、vector_stores、graph_store,以及可选的 property_graph_storepersist() / load_index_from_storage() 只管这些 simple store;外部 Qdrant、图数据库的生命周期由集成自己负责。Pipeline 的 persist() 只存 cache 与 docstore,不存 transformations 或 vector store 客户端。换 embedding 维数必须重建,不能指望加载旧向量再“自动对齐”。from_vector_store() 也不验证 collection 里已经有什么。

隔离。 Settings 是进程内可变单例。服务进程应在启动时固定 llm / embed_model,按租户注入 Retriever、QueryEngine 与 Memory,而不是在请求里改全局。每个会话一份 Memory.from_defaults(...) 或独立 chat-store key。AgentWorkflow 默认也会在 context 里放一份记忆,跨用户复用同一个 workflow 实例时,先确认记忆键有没有分开。

安装面。 pip install llama-index 只有 core + OpenAI + nltk。本地路径再装 llama-index-llms-openai-likellama-index-embeddings-openai-like;外部库再装对应 llama-index-vector-stores-*。约 620 个集成是独立项目,出现 ModuleNotFoundError 时先查发行包名。不要把整个 llama-index-integrations 目录当成一个可安装包。

异步与观测。 aquery / arun / ainsert 成对存在,但不少基类 async 会回退同步实现。事件循环里的阻塞通常来自某个集成。需要查调用次数时,从 synthesizer 的 K 和 ChatEngine 是否改写追问起算,Agent 再加 tool 循环的 iteration,而不是只数 HTTP 入口次数。Callback 与 instrumentation 要在创建模型之前挂上,后改 Settings.callback_manager 不保证写进已缓存的 LLM。

拓扑复查。 第 13 章的 stores_text 在上线后仍然有效:文本在外部库时,不要再假设本地 docstore 能按 ID 回填;文本只在本地时,不要单独备份向量库却忘掉 docstore。权限过滤放在 store 或 postprocessor,不放在 Agent 的 system prompt 里指望模型自觉。

4. 收束

到这里,LlamaIndex 侧可以落地一条最小闭环:选对摄取路径、按 stores_text 放存储、用 compact 回答、用 Memory.from_defaults 做多轮、用 ReActAgent 接工具、用 PropertyGraphIndex 做新图项目。旧词作为反清单贴在旁边:ServiceContext 会抛错,ChatMode.REACT 已删除,ChatMemoryBuffer 已 deprecated,KnowledgeGraphIndex 不要再开新项目。

上线前用一张短清单自检,比再读一遍 API 名称有用。强制项:元包之外的集成是否显式安装;Settings 是否只在进程启动时赋值;每个会话是否独立 Memory;摄取是否只走一条嵌入路径;外部库的 stores_text 是否与 from_vector_store / 本地回填一致;本地 LLM 的 is_function_calling_model 是否与 Agent 类型一致。禁止项:请求里改全局 Settings、ChatMode.REACT、新项目里的 KnowledgeGraphIndex、在 Pipeline 之后再让 Index 嵌一遍。

图项目再加两条:抽取器是否写在 kg_extractors 而不是误当作普通 transformationsSimplePropertyGraphStore 是否已经配了独立向量后端。Agent 项目再加两条:max_iterations 与失败观察是否能让循环停下来;工具描述是否窄到不会把闲聊送进检索。这些不是风格问题,是 0.14.23 里已经写死的行为。

也可以把工程化理解成“让第 12 到 14 章的对象在进程里活得足够久”。Notebook 里 Settings 改来改去没有关系,因为只有你一个人跑;服务里同一份单例会被所有请求看见。Notebook 里 Memory 跟着一个 engine 实例走即可;服务里必须按会话切开,否则用户 A 的追问会污染用户 B 的改写检索。Notebook 里索引可以每次从目录重建;服务里要分清哪些状态在 persist 目录、哪些在外部库、哪些根本不该被 StorageContext 以为自己管着。

下一章把训练、GGUF、llama-server 与这条 RAG 链按命令串起来。若你只做云端 RAG,也可以把下一章的 1–5 步整段裁掉,只保留对象边界;工程清单仍然适用,因为它管的是进程与存储,不是 GGUF。本地闭环与云端闭环共享同一套编排错误,只是 LLM 的 api_base 不同。对象对了,换端点很容易;对象错了,换一家云厂商也救不回来。先修对象,然后再换供应商。

文章互动

阅读 --

留言

0 条留言

正在加载留言…