摘要:// 创建 KeyValuePairKeyValuePair pair = new KeyValuePair("Age",25);// 访问键和值Console.WriteLine($"Key:{pair.K
在pocketflow的例子中看到了一个基于LLM的简历评估程序的例子,感觉还挺好玩的,为了练习一下C#,我最近使用C#重写了一个。
准备不同的简历:
查看效果:
不足之处是现实的简历应该是pdf格式的,后面可以考虑转化为图片然后用VLM来试试。
KeyValuePair学习使用到了。KeyValuePair 是 C# 中一个用于表示键值对的结构体(struct),它是一个泛型类型,可以存储两个相关联的值:一个键(key)和一个值(value)。主要特点:
不可变性:KeyValuePair 是一个只读结构,一旦创建就不能修改其键或值。
泛型实现
public struct KeyValuePair其中 TKey 是键的类型,TValue 是值的类型。
使用示例:
// 创建 KeyValuePairKeyValuePair pair = new KeyValuePair("Age",25);
// 访问键和值
Console.WriteLine($"Key:{pair.Key}"); // 输出: Key: Age
Console.WriteLine($"Value:{pair.Value}"); // 输出: Value: 25
// 在集合中使用
List> pairs = new List>
{
new KeyValuePair("John",25),
new KeyValuePair("Mary",30)
};
KeyValuePair 通常在以下场景中使用:
作为 Dictionary 的枚举结果
Dictionary dict = new Dictionary;dict.Add("One",1);
dict.Add("Two",2);
foreach (KeyValuePair kvp in dict)
{
Console.WriteLine($"Key:{kvp.Key}, Value:{kvp.Value}");
}
方法返回键值对
public KeyValuePair GetPersonInfo{
return new KeyValuePair("Age",25);
}
LINQ 操作中
var dict = new Dictionary;// ... 添加一些数据
var filtered = dict.Where(kvp => kvp.Value >10)
.Select(kvp => kvp.Key);
需要注意的是,如果需要可修改的键值对集合,应该使用 Dictionary 而不是 KeyValuePair 的集合。Dictionary 提供了更多的功能和更好的性能。KeyValuePair 主要用于表示单个键值对关系,特别是在遍历或传递数据时。
YAML内容解析根据这个提示词:
string prompt = $@"评估以下简历并确定候选人是否符合高级技术职位的要求。
资格标准:
- 至少具有相关领域的学士学位
- 至少3年相关工作经验
- 与职位相关的强大技术技能
简历内容:
{content}
请以YAML格式返回您的评估:
```yaml
candidate_name: [候选人姓名]
qualifies: [true/false]
reasons:
- [资格认定/不认定的第一个原因]
- [第二个原因(如果适用)]
```
";
LLM会返回一个YAML格式的内容,如下所示:
需要解析这个YAML格式内容。
public static Dictionary ParseSimpleYaml(string yaml){
var result = new Dictionary;
string lines = yaml.Split('\n');
string currentKey = ;
List currentList = ;
int currentIndentation =0;
for (int i =0; i < lines.Length; i++)
{
string line = lines[i].Trim;
if (string.IsOrEmpty(line) || line.StartsWith("#"))
continue;
int indentation = lines[i].TakeWhile(c => c == ' ').Count;
// Handle list items
if (line.StartsWith("- "))
{
if (currentList == )
{
currentList = new List;
result[currentKey] = currentList;
}
string listItem = line.Substring(2).Trim;
currentList.Add(listItem);
continue;
}
// Parse key-value pairs
int colonIndex = line.IndexOf(':');
if (colonIndex >0)
{
currentKey = line.Substring(0, colonIndex).Trim;
stringvalue = line.Substring(colonIndex +1).Trim;
currentIndentation = indentation;
currentList = ;
// Check if this is a multi-line value with |
if (value == "|")
{
StringBuilder multiline = new StringBuilder;
i++;
// Collect all indented lines
while (i < lines.Length && (lines[i].StartsWith(" ") || string.IsOrWhiteSpace(lines[i])))
{
if (!string.IsOrWhiteSpace(lines[i]))
multiline.AppendLine(lines[i].Substring(4)); // Remove indentation
i++;
}
i--; // Step back to process the next non-indented line in the outer loop
result[currentKey] = multiline.ToString.Trim;
}
elseif (!string.IsOrEmpty(value))
{
// Simple key-value
result[currentKey] = value;
}
}
}
return result;
}
解析结果:
image-20250528190559917C#条件运算符和空合并运算符 string name = evaluation.TryGetValue("candidate_name", out var candidateNameValue)? candidateNameValue?.ToString ?? "未知"
: "未知";
evaluation.TryGetValue("candidate_name", out var candidateNameValue)
TryGetValue 是 Dictionary 类的一个方法
它尝试获取键为 "candidate_name" 的值
如果找到了值,返回 true,并将值存储在 candidateNameValue 变量中
如果没找到值,返回 false,candidateNameValue 将为默认值
? 条件运算符(三元运算符)
格式为:condition ? value IfTrue : value If False
如果 TryGetValue 返回 true,执行 :前面的部分
如果 TryGetValue 返回 false,执行 : 后面的 "未知"
candidateNameValue?.ToString
?. 是空条件运算符
如果 candidateNameValue 不为 ,则调用 ToString
如果 candidateNameValue 为 ,则返回
?? "未知"
?? 是空合并运算符
如果左边的值为 ,则使用右边的值("未知")
动态类型转换List> evaluations = ;var objectList = shared["evaluations"] as List;
if (objectList != )
{
evaluations = objectList
.Select(item => item as Dictionary)
.Where(dict => dict != )
.ToList;
}
这种写法是因为在C#中处理动态类型转换时需要特别小心,尤其是在处理集合类型时。
首先,shared 是一个 Dictionary,这意味着存储在其中的值都是 object 类型。
shared["evaluations"] 实际上存储的是一个列表,但由于存在字典中时是 object 类型,我们需要安全地将其转换回实际的类型。
代码使用了两步转换的原因是:
第一步:var objectList = shared["evaluations"] as List;
这一步将 object 转换为 List,因为列表中的每个元素此时仍然是 object 类型
第二步将列表中的每个 object 转换为 Dictionary,因为每个评估结果实际上是一个字典
使用 as 操作符而不是直接类型转换(比如 (List)shared["evaluations"])的原因是:
as 操作符在转换失败时会返回 ,而不是抛出异常
这样可以安全地进行类型检查和转换,避免运行时错误
使用 Where(dict => dict != ) 可以过滤掉任何转换失败的项,确保最终的列表中只包含有效的字典对象
这种写法虽然看起来有点复杂,但它是一种安全和健壮的方式来处理动态类型转换,特别是在处理可能包含不同类型数据的集合时。这种方式可以:
避免运行时异常
确保类型安全
优雅地处理可能的空值或无效数据
全部代码已上传至GitHub,地址:https://github.com/Ming-jiayou/PocketFlowSharp/tree/main/PocketFlowSharpSamples.Console/Resume_Qualification_Demo
来源:opendotnet