Skip to content

知识库

知识库适合处理这类问题:材料很多,不可能每次都塞进 prompt;用户每次问的问题又只需要其中一小部分事实。它的工作方式是先把材料切成片段并向量化,提问时检索相关片段,再把这些片段作为本次请求的上下文交给模型。

Agently 的知识库路径由三部分组成:

  • embedding agent:把文本变成向量。
  • 向量集合:保存文档片段和 metadata。
  • 请求上下文:把检索结果挂到本次 Agent 请求里。

Agently 内置 Chroma 适配器作为参考实现,包路径是 agently.integrations.chromadb。如果团队已经有 Milvus、pgvector、Elasticsearch 或内部检索服务,也可以把检索结果整理成同样的上下文形态再交给 Agent。

先判断是不是知识库问题

场景更合适的能力
每次请求都要带一小份固定事实agent.info(..., always=True)
每次从大量文档里找相关片段Knowledge Base
记住用户连续对话里的上下文Session
存放跨 execution 的业务状态外部数据库或 TriggerFlow runtime_resources
把 live client 缓存在 flow 里TriggerFlow runtime_resources

知识库解决的是“从大语料里找本次相关事实”。它不是会话记忆,也不是业务数据库。

一条请求链路长什么样

text
文档片段
  |
  v
embedding agent  ->  向量集合
                         |
用户问题 -> embedding agent -> 相似度检索 -> 相关片段
                                                |
                                                v
                                      agent.info(..., always=False)
                                                |
                                                v
                                             本次请求

always=False 很关键。检索片段只属于这一次问题,不应该污染 Agent 后续请求的固定上下文。

最小用法

python
from agently import Agently
from agently.integrations.chromadb import ChromaCollection

embedding_agent = Agently.create_agent().set_settings("OpenAICompatible", {
    "base_url": "https://api.openai.com/v1",
    "api_key": "${ENV.OPENAI_API_KEY}",
    "model": "${ENV.EMBEDDING_MODEL}",
})

collection = ChromaCollection(
    collection_name="agently-docs",
    embedding_agent=embedding_agent,
)

collection.add([
    {
        "id": "triggerflow-lifecycle",
        "document": "TriggerFlow execution 有 open、sealed、closed 三个状态。",
        "metadata": {"section": "triggerflow"},
    },
    {
        "id": "model-plugins",
        "document": "Agently 内置 OpenAICompatible、OpenAIResponsesCompatible 和 AnthropicCompatible。",
        "metadata": {"section": "models"},
    },
])

question = "TriggerFlow 有哪些生命周期状态?"
chunks = collection.query(question, top_n=3)

agent = Agently.create_agent()
result = (
    agent
    .info({"retrieved": chunks}, always=False)
    .input(question)
    .output({"answer": (str, "基于检索片段回答", True)})
    .get_result()
)
answer = await result.async_get_data(ensure_keys=["answer"])

这段代码有两个边界:

  • 索引阶段把文档片段写入集合。
  • 请求阶段只把本问检索出的片段放进 info(..., always=False)

文档片段怎么设计

知识库质量主要卡在切片和 metadata,而不只是 embedding 模型。

设计点建议
片段大小一段能独立解释一个事实或步骤,不要整篇塞进去
id稳定、可追溯,最好能回到原文位置
metadata放 tenant、source、version、section、权限标签
更新原文变更后重新索引对应片段,不要只追加新版
返回给模型带上片段正文,也带上 source/id,便于回答引用和排错

如果检索片段本身质量差,后面的 prompt 很难补回来。先让片段可读、可追溯,再调相似度参数。

按 metadata 过滤

多租户、权限、文档类型这些边界,应该在检索阶段先过滤。

python
chunks = collection.query(
    user_question,
    top_n=5,
    where={
        "tenant_id": current_user.tenant_id,
        "doc_type": "policy",
    },
)

不要把不该看的片段先检出来,再指望模型“不要使用”。权限边界应在模型前处理。

在 TriggerFlow 里使用

知识库检索经常是工作流中的一个 chunk。

python
async def retrieve(data):
    collection = data.require_resource("collection")
    chunks = collection.query(data.input["question"], top_n=5)
    await data.async_set_state("retrieved_chunks", chunks)
    return {"question": data.input["question"], "chunks": chunks}


async def answer(data):
    agent = data.require_resource("agent")
    payload = data.input
    result = (
        agent
        .info({"retrieved": payload["chunks"]}, always=False)
        .input(payload["question"])
        .output({"answer": (str, "回答", True)})
        .get_result()
    )
    final = await result.async_get_data(ensure_keys=["answer"])
    await data.async_set_state("answer", final)
    return final


flow.to(retrieve).to(answer)

execution = flow.create_execution(
    runtime_resources={"collection": collection, "agent": agent},
)
snapshot = await execution.async_start({"question": "报销额度怎么算?"})

collectionagent 是 live 对象,放进 runtime_resources。检索出的片段和最终回答是业务数据,放进 state

自动回写要谨慎

有些系统会把 Agent 的答案再写回知识库,用来形成自更新上下文:

python
collection.add([{
    "id": new_id(),
    "document": answer["answer"],
    "metadata": {"source": "agent_answer"},
}])

这条路要有审核或置信边界。未经审核的模型输出直接进入知识库,会把错误答案变成未来检索事实。更稳妥的做法是:先写入待审核集合,人工或规则通过后再进入正式集合。

常见误用

写法后果
把整篇文档当一个 chunk检索命中后上下文太大,模型抓不住具体事实
检索片段用 always=True 挂到 Agent后续请求继续携带旧片段
用知识库保存会话历史历史没有轮次结构,也难以清理
把 collection client 放进 TriggerFlow statesave/load 不可靠,snapshot 也不该暴露 live 对象
自动 ingest 模型答案容易自我污染

另见