向量记录与可恢复的 Process 步骤
同一个 ID 为什么覆盖旧记录
沿用1.36.0环境,保存vector.py。这里使用真实InMemoryCollection,二维向量由我们明确给定以隔离索引行为,未调用embedding模型;它不能衡量中文语义检索质量。Record同时携带键、业务文本和向量:键控制更新身份,向量控制相似检索,文本用于有来源的回答。
import asyncio
from dataclasses import dataclass
from typing import Annotated
from semantic_kernel.connectors.in_memory import InMemoryCollection
from semantic_kernel.data.vector import VectorStoreField,vectorstoremodel
@vectorstoremodel(collection_name='stock')
@dataclass
class Record:
id:Annotated[str,VectorStoreField('key')]
text:Annotated[str,VectorStoreField('data')]
vector:Annotated[list[float]|None,VectorStoreField('vector',dimensions=2)]=None
async def main():
async with InMemoryCollection(record_type=Record) as c:
await c.ensure_collection_exists()
await c.upsert([Record('A','refund policy',[1.,0.]),Record('B','shipping',[0.,1.])])
result=await c.search(vector=[1.,0.],top=1)
rows=[x async for x in result.results]
assert rows[0].record.id=='A'
await c.upsert(Record('A','refund revised',[1.,0.]))
assert (await c.get('A')).text=='refund revised'
await c.delete('A')
assert await c.get('A') is None
print('search, update and delete passed')
asyncio.run(main())查询向量[1,0]与A同方向,因此A排在B前;upsert同ID替换文本,delete之后get返回None。保存的向量不因文本变化自动重算;真实文档修订必须同时更新embedding并保持模型/维度版本一致。生产后端增加租户过滤、持久化和索引迁移;内存集合退出就消失,不能用它证明故障恢复。
Process 的事件不是模型的函数选择
Auto function choice允许模型提出函数;ProcessBuilder的事件连线由开发者固定,模型没有决定是否跳过审批的权力。下面是实际SDK步骤:无效数量进入invalid,修复输入后进入waiting,绑定原数量的批准进入completed。保存process_lab.py并运行,无模型调用。
import asyncio
from pydantic import Field
from semantic_kernel import Kernel
from semantic_kernel.functions import kernel_function
from semantic_kernel.kernel_pydantic import KernelBaseModel
from semantic_kernel.processes import ProcessBuilder
from semantic_kernel.processes.kernel_process.kernel_process_step import KernelProcessStep
from semantic_kernel.processes.kernel_process.kernel_process_step_state import KernelProcessStepState
from semantic_kernel.processes.kernel_process.kernel_process_event import KernelProcessEvent
from semantic_kernel.processes.local_runtime.local_kernel_process import start
class ApprovalState(KernelBaseModel):
status:str='new'
amount:int=0
committed:int=0
class Approval(KernelProcessStep[ApprovalState]):
state:ApprovalState=Field(default_factory=ApprovalState)
async def activate(self,state:KernelProcessStepState[ApprovalState]):
self.state=state.state or ApprovalState()
@kernel_function
def prepare(self,amount:int):
self.state.amount=amount
self.state.status='waiting' if amount>0 else 'invalid'
@kernel_function
def approve(self,amount:int):
if self.state.status!='waiting' or amount!=self.state.amount:
self.state.status='rejected'
return
self.state.committed+=1
self.state.status='completed'
async def main():
b=ProcessBuilder(name='approval')
s=b.add_step(Approval)
b.on_input_event('prepare').send_event_to(target=s,function_name='prepare',parameter_name='amount')
b.on_input_event('approve').send_event_to(target=s,function_name='approve',parameter_name='amount')
async with await start(b.build(),Kernel(),KernelProcessEvent(id='prepare',data=-1)) as ctx:
failed=await ctx.get_state()
assert failed.steps[0].state.state.status=='invalid'
# Reconstruct runtime from SDK snapshot and repair invalid input.
async with await start(failed,Kernel(),KernelProcessEvent(id='prepare',data=2)) as ctx:
waiting=await ctx.get_state()
assert waiting.steps[0].state.state.status=='waiting'
async with await start(waiting,Kernel(),KernelProcessEvent(id='approve',data=2)) as ctx:
complete=await ctx.get_state()
state=complete.steps[0].state.state
assert state.status=='completed' and state.committed==1
print(state)
asyncio.run(main())每次start构建新的本地运行上下文;get_state取得SDK的Process快照,其中包含步骤state,因此恢复后数量仍为2。invalid是本例的业务失败状态,不是吞掉框架异常;网络/程序异常仍需调用方处理。批准数量不匹配会转为rejected,不能改用新参数继续旧授权。
本例证明SDK快照在同进程重建运行时后恢复,不证明跨机器持久化。持久部署需将业务状态存数据库,并在新进程重建相同版本的Process定义;类与函数工厂不能直接当可信JSON从用户输入恢复。Python实验性Process与.NET Process支持边界分别核对,不能复制跨语言方法名。
ChatCompletionAgent则封装模型、历史及工具交互;同一插件可被Agent自动选择,也可被Process确定性调用。选择Agent并不能赋予数据库事务或审批持久化,选择Process也不会自动让内容生成变得确定。上一节auto.py示范模型选择,本节示范确定事件,两者都应经过第三章的执行过滤器。
练习
向量实验中删除A后再查询,确认B是剩余候选,而不是把零相关当成可靠答案。Process在waiting数量2时收到批准数量3,必须rejected且committed仍为0。给出为什么不能将一次start返回当作业务成功的解释。